From 6ecc27e7cabbcaa8961ac1ad6170d067478b3a76 Mon Sep 17 00:00:00 2001
From: johnnyq
Date: Mon, 9 Nov 2026 01:22:35 -0500
Subject: [PATCH] Version ITFlow's scripts and stylesheets so browsers fetch
the new copies after an update instead of needing a cache clear: add
staticFileUrl() in functions/app.php, which adds ?v= and the file's
modification time to the address, and send every script and stylesheet tag
through it, libs included, with page-relative addresses changed to start from
the site root; add js/tinymce_defaults.js to set TinyMCE's cache_suffix so
the plugins, theme and skins it loads itself are versioned too; load ticket
collision detection only when a ticket is shown, so a ticket that isn't found
no longer throws a script error; add changelog entries and a CONTRIBUTING
rule that new tags must use staticFileUrl()
---
CHANGELOG.md | 4 ++
CONTRIBUTING.md | 2 +
admin/api_keys.php | 2 +-
admin/categories.php | 4 +-
admin/custom_links.php | 2 +-
admin/document_template.php | 4 +-
admin/getting_started.php | 24 ++++-----
admin/mail_queue.php | 2 +-
admin/managed_instance.php | 2 +-
admin/modals/category/category_form.php | 2 +-
admin/modals/custom_link/custom_link_form.php | 2 +-
.../document_template_form.php | 2 +-
.../document_template_use.php | 2 +-
admin/modals/role/role_form.php | 2 +-
admin/modals/tag/tag_form.php | 2 +-
admin/modals/tax/tax_form.php | 2 +-
.../ticket_status/ticket_status_form.php | 2 +-
admin/post/users.php | 4 +-
admin/project_template.php | 2 +-
admin/software_template.php | 2 +-
admin/starter_content.php | 2 +-
admin/tags.php | 2 +-
admin/text_messages.php | 2 +-
admin/ticket_statuses.php | 2 +-
admin/ticket_template.php | 4 +-
admin/update.php | 2 +-
admin/users.php | 2 +-
admin/vendor_template.php | 2 +-
agent/asset.php | 2 +-
agent/assets.php | 2 +-
agent/calendar.php | 12 ++---
agent/certificates.php | 2 +-
agent/client_onboarding.php | 26 +++++-----
agent/client_overview.php | 2 +-
agent/clients.php | 2 +-
agent/contacts.php | 2 +-
agent/credentials.php | 2 +-
agent/dashboard.php | 2 +-
agent/document.php | 2 +-
agent/domains.php | 2 +-
agent/expense_import.php | 2 +-
agent/expenses.php | 2 +-
agent/files.php | 6 +--
agent/includes/client_side_nav.php | 2 +-
agent/includes/inc_canned_response_picker.php | 2 +-
agent/income.php | 2 +-
agent/income_import.php | 2 +-
agent/invoice.php | 6 +--
agent/invoices.php | 2 +-
agent/locations.php | 2 +-
agent/modals/asset/asset_bulk_add_ticket.php | 2 +-
agent/modals/asset/asset_interface_add.php | 2 +-
agent/modals/asset/asset_interface_edit.php | 2 +-
agent/modals/calendar/calendar_event_add.php | 2 +-
agent/modals/calendar/calendar_event_edit.php | 2 +-
.../modals/client/client_bulk_add_ticket.php | 2 +-
agent/modals/client/client_delete.php | 2 +-
agent/modals/client/client_export_data.php | 2 +-
.../modals/contract/contract_countersign.php | 2 +-
agent/modals/contract/contract_form.php | 2 +-
.../document/document_add_from_template.php | 2 +-
.../modals/document/document_version_view.php | 2 +-
agent/modals/document/document_view.php | 2 +-
agent/modals/network/network_add.php | 2 +-
agent/modals/network/network_edit.php | 2 +-
agent/modals/project/project_add.php | 2 +-
agent/modals/project/project_edit.php | 2 +-
.../recurring_ticket/recurring_ticket_add.php | 4 +-
.../recurring_ticket_edit.php | 2 +-
agent/modals/share/share_add.php | 2 +-
agent/modals/sms/sms_ticket_open.php | 2 +-
agent/modals/ticket/ticket_add.php | 4 +-
agent/modals/ticket/ticket_change_client.php | 2 +-
agent/modals/ticket/ticket_edit_schedule.php | 2 +-
agent/network.php | 2 +-
agent/networks.php | 2 +-
agent/product.php | 2 +-
agent/products.php | 2 +-
agent/project.php | 2 +-
agent/projects.php | 2 +-
agent/quote.php | 6 +--
agent/reconcile.php | 2 +-
agent/recurring_invoice.php | 6 +--
agent/recurring_tickets.php | 2 +-
agent/reports/asset_lifecycle.php | 2 +-
agent/reports/audit_activity.php | 2 +-
agent/reports/budget.php | 2 +-
agent/reports/canned_responses.php | 2 +-
agent/reports/client_profitability.php | 2 +-
agent/reports/client_review.php | 2 +-
agent/reports/client_ticket_time_detail.php | 2 +-
agent/reports/credential_rotation.php | 2 +-
agent/reports/document_templates.php | 2 +-
agent/reports/documentation_coverage.php | 2 +-
agent/reports/expense_by_vendor.php | 2 +-
agent/reports/expense_summary.php | 2 +-
agent/reports/income_by_client.php | 2 +-
agent/reports/income_summary.php | 2 +-
agent/reports/index.php | 2 +-
agent/reports/inventory.php | 2 +-
agent/reports/license_templates.php | 2 +-
agent/reports/mileage.php | 2 +-
agent/reports/outstanding_balances.php | 2 +-
agent/reports/profit_loss.php | 2 +-
agent/reports/quote_pipeline.php | 2 +-
agent/reports/recurring_by_client.php | 2 +-
agent/reports/renewals.php | 2 +-
agent/reports/satisfaction.php | 2 +-
agent/reports/sla_by_client.php | 2 +-
agent/reports/sla_summary.php | 2 +-
agent/reports/tax_summary.php | 2 +-
agent/reports/ticket_aging.php | 2 +-
agent/reports/ticket_by_client.php | 2 +-
agent/reports/ticket_summary.php | 2 +-
agent/reports/ticket_templates.php | 2 +-
agent/reports/tickets_unbilled.php | 2 +-
agent/reports/time_by_tech.php | 2 +-
agent/reports/vendor_templates.php | 2 +-
agent/services.php | 2 +-
agent/shares.php | 2 +-
agent/software.php | 2 +-
agent/ticket.php | 16 +++---
agent/ticket_kanban.php | 6 +--
agent/tickets.php | 2 +-
agent/trips.php | 2 +-
agent/unbilled_time.php | 4 +-
agent/user/mfa_enforcement.php | 12 ++---
agent/user/modals/canned_response_save.php | 2 +-
agent/vendors.php | 2 +-
client/contract.php | 2 +-
client/includes/footer.php | 33 ++++++------
client/includes/header.php | 14 ++---
client/join.php | 8 +--
client/login_reset.php | 8 +--
client/signup.php | 8 +--
functions/app.php | 18 +++++++
functions/demo.php | 4 +-
guest/guest_approve_ticket_task.php | 2 +-
guest/guest_pay_invoice_stripe.php | 2 +-
guest/guest_view_contract.php | 2 +-
guest/guest_view_ticket.php | 2 +-
guest/includes/guest_header.php | 12 ++---
guest/share.php | 4 +-
includes/canned_response_form.php | 2 +-
includes/contract_placeholders.php | 2 +-
includes/footer.php | 51 ++++++++++---------
includes/header.php | 18 +++----
includes/managed_blocked.php | 6 +--
includes/modal_footer.php | 8 +--
join.php | 8 +--
js/tinymce_defaults.js | 23 +++++++++
login.php | 12 ++---
setup/index.php | 20 ++++----
153 files changed, 347 insertions(+), 296 deletions(-)
create mode 100644 js/tinymce_defaults.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 525c1100e..d5a31fdb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@ Update from Maintenance > Update — Queue Update hands the job to cron, which u
- Phone numbers: outside North America, a number shown with its country code kept the 0 - or the 8 in Russia - that is only dialled within the country, as "+49 0172 1234567" for a German mobile, wherever it showed: ITFlow's pages, the client portal (a contact's own profile, and the Contact us details), emails, PDFs and exports. Click-to-call dialled it that way too, and so failed. Numbers are now written the way each country writes them: "+49 172 1234567", and "0172 1234567" without the country code. This covers every country, not only the ones ITFlow had its own rules for. A German landline such as Munich's is split after its area code ("+49 89 1234567"), Italy keeps the 0 its landlines are dialled with from abroad ("+39 06 1234 5678"), and South African numbers read "+27 82 123 4567". A number typed with or without that leading 0, or with its country code at the front, shows and dials the same. North American numbers look as they did.
- Lists: on a list with only a row or two, a row's actions menu was cut off at the bottom of the list, hiding its last options - Delete among them. Menus now open over the page, and still turn upward when there's no room below. This covers every list, the board and the client portal.
- Client portal: for a client whose name has an apostrophe - O'Brien Dental, say - adding a contact or saying one has left, setting up autopay, and the alert that a card payment went through but couldn't be recorded all ended in an error. The contact was saved but nobody was told, and that payment alert never reached you. They work now.
+- Updates: after an update, pages could act up - menus, buttons, editors and forms not working, or pages looking half old and half new - until each person cleared their browser's cache, and sometimes closed the browser and cleared it again. Browsers kept the copies of ITFlow's scripts and stylesheets from before the update and went on using them with the new pages, for days or weeks. Each one is now asked for by an address that changes when the file does, so after an update browsers fetch what changed straight away and keep using what didn't. This starts with this update: nobody needs to clear their cache for it.
### Developer Updates
@@ -84,6 +85,9 @@ Update from Maintenance > Update — Queue Update hands the job to cron, which u
- `demoModeReset()` (`cron/demo_reset.php`, registered only in demo mode, daily at 02:00, `interval_safe` false, ends the cycle) keeps the companies row, the timezone, `config_cron_last_dispatch_at` and the `cron_jobs` rows (columns db.sql still has), drops every table and loads db.sql, then `seedDefaultData()`, `setupSaveRegion()`, the three demo agents with a new vault key under the demo password, every `starterContentLoad()` pack and `demoDataLoad()` with the vault opened in-process and the session globals set to the demo admin, and a portal user for the first demo client's primary contact; it empties every folder in uploads/ but settings/, keeping their index.php and .htaccess. A reset that stopped part way leaves no settings - `php cron/demo_reset.php` still runs then. The domain and certificate refreshers aren't registered in demo mode. The Mail Queue job cancels queued mail with a reason, and `certificateConnect()` connects only to a public address on port 443, by that address (`demoModePublicAddress()`).
- `js/bootstrap_defaults.js`, loaded straight after `bootstrap.bundle.min.js` in `includes/footer.php` and `client/includes/footer.php`, sets `bootstrap.Dropdown.Default.popperConfig` to `{strategy: 'fixed'}`, so dropdown menus are placed against the viewport and a `.table-responsive` wrapper (overflow auto) no longer clips them. Dropdowns in a navbar are unaffected - Bootstrap doesn't use Popper there.
- `client/post.php`: `$session_client_name` is the client's name as stored (`client/includes/check_login.php`), so the five `appNotify()` and `logAudit()` calls that put it in their text now pass it through `escapeSql()`. `smsSaveMedia()` pins curl to the address it checked (`CURLOPT_RESOLVE`), so a name that resolves differently the second time can't point the download somewhere private.
+- `staticFileUrl($path)` in `functions/app.php` returns `$path?v=` and the file's `filemtime()`, or the path as it is when the file isn't there. Every `
+
-
+
-
+
+
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+