Merge pull request #1296 from itflow-org/develop

Develop to master for 26.08.2 Release
This commit is contained in:
Johnny
2026-08-10 12:10:21 -04:00
committed by GitHub
14 changed files with 82 additions and 37 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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)) {

View File

@@ -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

View File

@@ -304,8 +304,8 @@ while ($row = mysqli_fetch_assoc($sql)) {
},
events: [
<?php
$sql = mysqli_query($mysqli, "SELECT calendar_color, calendar_id, calendar_name, event_all_day, event_id, event_repeat,
event_title FROM calendar_events LEFT JOIN calendars ON event_calendar_id = calendar_id $client_event_query");
$sql = mysqli_query($mysqli, "SELECT calendar_color, calendar_id, calendar_name, event_all_day, event_end, event_id,
event_repeat, event_start, event_title FROM calendar_events LEFT JOIN calendars ON event_calendar_id = calendar_id $client_event_query");
// Repeating events are stored as a single row, so the occurrences have to
// be materialised here - the bundled FullCalendar build has no rrule

View File

@@ -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%')

View File

@@ -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%')

View File

@@ -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%')

View File

@@ -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

View File

@@ -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");
}

View File

@@ -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;
}

2
db.sql
View File

@@ -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

View File

@@ -45,7 +45,7 @@ $feed_key = escapeSql($_GET['key']);
$sql = mysqli_query(
$mysqli,
"SELECT calendar_id, calendar_name FROM calendars
"SELECT calendar_color, calendar_feed_busy_only, calendar_id, calendar_name FROM calendars
WHERE calendar_feed_key = '$feed_key'
AND calendar_feed_key IS NOT NULL
AND calendar_archived_at IS NULL

View File

@@ -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");