From bb3a1837ac5aa8dc8d6792a64370fef3a9755305 Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Thu, 9 Apr 2026 13:01:14 +0200
Subject: [PATCH 1/4] Add file-based asset versioning helper
---
lib/TemplateUtility.php | 70 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/lib/TemplateUtility.php b/lib/TemplateUtility.php
index 1db34f95b..f718a73dd 100755
--- a/lib/TemplateUtility.php
+++ b/lib/TemplateUtility.php
@@ -1170,6 +1170,76 @@ public static function _printQuickActionMenuHolder()
echo '';
}
+ /**
+ * Returns an asset URL with a file-based version query parameter.
+ *
+ * @param string Relative asset path
+ * @return string
+ */
+ public static function getVersionedAssetURL($assetPath)
+ {
+ $assetPath = (string) $assetPath;
+ if ($assetPath == '')
+ {
+ return $assetPath;
+ }
+
+ $parsedURL = parse_url($assetPath);
+ if ($parsedURL === false || isset($parsedURL['scheme']) || isset($parsedURL['host']))
+ {
+ return $assetPath;
+ }
+
+ $path = isset($parsedURL['path']) ? $parsedURL['path'] : '';
+ if ($path == '')
+ {
+ return $assetPath;
+ }
+
+ $legacyRootPath = realpath(LEGACY_ROOT);
+ if ($legacyRootPath === false)
+ {
+ return $assetPath;
+ }
+
+ $normalizedPath = ltrim(str_replace('\\', '/', $path), '/');
+ if ($normalizedPath == '')
+ {
+ return $assetPath;
+ }
+
+ $assetFilePath = realpath(
+ $legacyRootPath . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $normalizedPath)
+ );
+ if ($assetFilePath === false)
+ {
+ return $assetPath;
+ }
+
+ $legacyRootPrefix = rtrim($legacyRootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+ if ($assetFilePath !== $legacyRootPath && strpos($assetFilePath, $legacyRootPrefix) !== 0)
+ {
+ return $assetPath;
+ }
+
+ $fileMTime = @filemtime($assetFilePath);
+ if ($fileMTime === false)
+ {
+ return $assetPath;
+ }
+
+ $queryString = isset($parsedURL['query']) ? $parsedURL['query'] . '&' : '';
+ $queryString .= 'v=' . (int) $fileMTime;
+
+ $versionedAssetURL = $path . '?' . $queryString;
+ if (isset($parsedURL['fragment']))
+ {
+ $versionedAssetURL .= '#' . $parsedURL['fragment'];
+ }
+
+ return $versionedAssetURL;
+ }
+
/**
* Prints template header HTML.
*
From ba32bd053adbd907b75c80412f5dae0089769a8a Mon Sep 17 00:00:00 2001
From: anonymoususer72041
<247563575+anonymoususer72041@users.noreply.github.com>
Date: Thu, 9 Apr 2026 13:09:54 +0200
Subject: [PATCH 2/4] Switch JavaScript and stylesheet includes to file-based
cache busting
---
installtest.php | 20 +++++++++++-
installwizard.php | 20 +++++++++---
lib/Display.php | 5 ++-
lib/TemplateUtility.php | 46 ++++++++++++++++++++-------
modules/careers/Blank.tpl | 16 +++++-----
modules/careers/Blank2.tpl | 8 ++---
modules/careers/BlankNoMargin.tpl | 12 +++----
modules/import/MassImport.tpl | 2 +-
modules/import/MassImportEdit.tpl | 2 +-
modules/install/notinstalled.php | 8 +++--
modules/install/phpVersion.php | 8 +++--
modules/login/ForgotPassword.tpl | 6 ++--
modules/login/Login.tpl | 8 ++---
modules/reports/GraphView.tpl | 4 +--
modules/settings/NewInstallWizard.tpl | 6 ++--
modules/settings/PreviewPageTop.tpl | 2 +-
modules/tests/CATSTestReporter.php | 8 +++--
modules/wizard/Show.tpl | 6 ++--
18 files changed, 126 insertions(+), 61 deletions(-)
diff --git a/installtest.php b/installtest.php
index 6e98dde1b..5c5bc2db4 100755
--- a/installtest.php
+++ b/installtest.php
@@ -33,6 +33,24 @@
include_once(LEGACY_ROOT . '/constants.php');
include_once(LEGACY_ROOT . '/lib/InstallationTests.php');
+/* Version check before including TemplateUtility for asset URL versioning. */
+$phpVersion = phpversion();
+$phpVersionParts = explode('.', $phpVersion);
+if ($phpVersionParts[0] >= 5)
+{
+ include_once(LEGACY_ROOT . '/lib/TemplateUtility.php');
+}
+else
+{
+ $php4 = true;
+}
+
+$mainCSSURL = 'main.css';
+if (!isset($php4))
+{
+ $mainCSSURL = call_user_func(array('TemplateUtility', 'getVersionedAssetURL'), $mainCSSURL);
+}
+
define('REQUIRED_SCHEMA_VERSION', '1200');
@@ -45,7 +63,7 @@
CATS - Installation Test Script
-
+
+
+
+
+
diff --git a/lib/Display.php b/lib/Display.php
index 6cea272db..b58ad4094 100755
--- a/lib/Display.php
+++ b/lib/Display.php
@@ -108,7 +108,10 @@ public function startTable()
$sheet = $this->_profileLib->getProfileStylesheet();
if ($profileStylesheet === false || strcmp($profileStylesheet, $sheet))
{
- echo sprintf('', $sheet);
+ echo sprintf(
+ '',
+ TemplateUtility::getVersionedAssetURL($sheet)
+ );
$profileStylesheet = $sheet;
}
diff --git a/lib/TemplateUtility.php b/lib/TemplateUtility.php
index f718a73dd..2f8c13961 100755
--- a/lib/TemplateUtility.php
+++ b/lib/TemplateUtility.php
@@ -1278,11 +1278,22 @@ private static function _printCommonHeader($pageTitle, $headIncludes = array())
CATSUtility::getIndexName(), '?m=rss" />', "\n";
/* Core JS files */
- echo '', "\n";
- echo '', "\n";
- echo '', "\n";
- echo '', "\n";
- echo '', "\n";
+ $coreJavaScriptFiles = array(
+ 'js/lib.js',
+ 'js/quickAction.js',
+ 'js/calendarDateInput.js',
+ 'js/submodal/subModal.js',
+ 'js/jquery-1.3.2.min.js'
+ );
+ foreach ($coreJavaScriptFiles as $coreJavaScriptFile)
+ {
+ $versionedFilename = self::getVersionedAssetURL($coreJavaScriptFile);
+ if ($versionedFilename === $coreJavaScriptFile)
+ {
+ $versionedFilename .= $javascriptAntiCache;
+ }
+ echo '', "\n";
+ }
echo '', "\n";
if (isset($_SESSION['CATS']) && $_SESSION['CATS']->isLoggedIn())
{
@@ -1354,22 +1365,35 @@ private static function _printCommonHeader($pageTitle, $headIncludes = array())
foreach ($headIncludes as $key => $filename)
{
- $extension = substr($filename, strrpos($filename, '.') + 1);
+ $path = parse_url($filename, PHP_URL_PATH);
+ if ($path === false || $path === null)
+ {
+ $path = $filename;
+ }
+ $extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
- $filename .= $javascriptAntiCache;
+ $versionedFilename = self::getVersionedAssetURL($filename);
+ if ($versionedFilename === $filename)
+ {
+ $versionedFilename .= $javascriptAntiCache;
+ }
if ($extension == 'js')
{
- echo '', "\n";
+ echo '', "\n";
}
else if ($extension == 'css')
{
- echo '', "\n";
+ echo '', "\n";
}
}
- echo '', "\n";
- echo '', "\n";
+ echo '', "\n";
+ echo '', "\n";
echo '', "\n\n";
}
diff --git a/modules/careers/Blank.tpl b/modules/careers/Blank.tpl
index 906e27f0c..07ddbb5a5 100755
--- a/modules/careers/Blank.tpl
+++ b/modules/careers/Blank.tpl
@@ -4,16 +4,16 @@
_($this->siteName); ?> - Careers
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
diff --git a/modules/install/phpVersion.php b/modules/install/phpVersion.php
index 7c17273ca..c877d5bd4 100755
--- a/modules/install/phpVersion.php
+++ b/modules/install/phpVersion.php
@@ -1,12 +1,14 @@
+
+
CATS - Installation Wizard Script
-
-
-
+
+
+
diff --git a/modules/login/ForgotPassword.tpl b/modules/login/ForgotPassword.tpl
index 72ab65706..6351a50cb 100755
--- a/modules/login/ForgotPassword.tpl
+++ b/modules/login/ForgotPassword.tpl
@@ -5,9 +5,9 @@
OpenCATS - Login
-
-
-
+
+
+
diff --git a/modules/login/Login.tpl b/modules/login/Login.tpl
index 370b6c438..ea480f598 100755
--- a/modules/login/Login.tpl
+++ b/modules/login/Login.tpl
@@ -5,10 +5,10 @@
opencats - Login
-
-
-
-
+
+
+
+
diff --git a/modules/reports/GraphView.tpl b/modules/reports/GraphView.tpl
index ce785b323..1b62170f3 100755
--- a/modules/reports/GraphView.tpl
+++ b/modules/reports/GraphView.tpl
@@ -8,8 +8,8 @@
-
-
+
+
-
-
+
+
+
diff --git a/modules/settings/PreviewPageTop.tpl b/modules/settings/PreviewPageTop.tpl
index c7c6e6a0d..9de351659 100755
--- a/modules/settings/PreviewPageTop.tpl
+++ b/modules/settings/PreviewPageTop.tpl
@@ -3,7 +3,7 @@
-
+
', "\n";
+ echo ' ', "\n";
foreach ($headIncludes as $key => $value)
{
- echo ' ', "\n";
+ echo ' ', "\n";
}
echo ' ', "\n";
diff --git a/modules/wizard/Show.tpl b/modules/wizard/Show.tpl
index 068521df5..ea799d8df 100755
--- a/modules/wizard/Show.tpl
+++ b/modules/wizard/Show.tpl
@@ -6,10 +6,10 @@
-
-
+
+
jsInclude != ''): ?>
-
+
', "\n";
}
echo '', "\n";
@@ -1373,10 +1359,6 @@ private static function _printCommonHeader($pageTitle, $headIncludes = array())
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$versionedFilename = self::getVersionedAssetURL($filename);
- if ($versionedFilename === $filename)
- {
- $versionedFilename .= $javascriptAntiCache;
- }
if ($extension == 'js')
{