From 20ee6670d0b881bbb445f4706515326438edcacf Mon Sep 17 00:00:00 2001 From: Peter Donker Date: Wed, 17 Jun 2026 11:40:57 +0200 Subject: [PATCH 1/3] Add ability to skip backing up files in the FileInstaller --- .../Library/Services/Installer/Installers/FileInstaller.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DNN Platform/Library/Services/Installer/Installers/FileInstaller.cs b/DNN Platform/Library/Services/Installer/Installers/FileInstaller.cs index fced96c85f5..0958874ce20 100644 --- a/DNN Platform/Library/Services/Installer/Installers/FileInstaller.cs +++ b/DNN Platform/Library/Services/Installer/Installers/FileInstaller.cs @@ -32,6 +32,9 @@ public class FileInstaller : ComponentInstallerBase /// Gets the name of the Item Node (file). protected virtual string ItemNodeName => "file"; + /// Gets or sets a value indicating whether any existing files should be backed up during installation. + protected bool BackupFiles { get; set; } = true; + /// Gets the PhysicalBasePath for the files. protected virtual string PhysicalBasePath { @@ -177,7 +180,7 @@ protected virtual bool InstallFile(InstallFile insFile) if (this.Package.InstallerInfo.IgnoreWhiteList || Util.IsFileValid(insFile, this.Package.InstallerInfo.AllowableFiles)) { // Install File - if (File.Exists(this.PhysicalBasePath + insFile.FullName)) + if (File.Exists(this.PhysicalBasePath + insFile.FullName) && this.BackupFiles) { Util.BackupFile(insFile, this.PhysicalBasePath, this.Log); } From d2c1930b708707b5369b2d399d38cba6e5ec2f34 Mon Sep 17 00:00:00 2001 From: Peter Donker Date: Wed, 17 Jun 2026 11:43:07 +0200 Subject: [PATCH 2/3] Add AppOffline.htm to solution which is copied in during local install unpacking process. Also skip backing up main dlls during unpacking. --- .../Services/Installer/LocalUpgradeService.cs | 27 +++ .../Website/DotNetNuke.Website.csproj | 1 + .../Resources/AppOffline/App_Offline.htm | 200 ++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 DNN Platform/Website/Resources/AppOffline/App_Offline.htm diff --git a/DNN Platform/Library/Services/Installer/LocalUpgradeService.cs b/DNN Platform/Library/Services/Installer/LocalUpgradeService.cs index a8bdc560458..82b194a4ecc 100644 --- a/DNN Platform/Library/Services/Installer/LocalUpgradeService.cs +++ b/DNN Platform/Library/Services/Installer/LocalUpgradeService.cs @@ -187,6 +187,7 @@ public async Task StartLocalUpgrade(IReadOnlyList upgrades, Ca /// public async Task StartLocalUpgrade(LocalUpgradeInfo upgrade, CancellationToken cancellationToken) { + this.SetAppOffline(); var upgradeZipPath = Path.Combine(this.UpgradeDirectoryPath, upgrade.PackageName + ".zip"); using var fileStream = File.OpenRead(upgradeZipPath); using var archive = new ZipArchive(fileStream, ZipArchiveMode.Read); @@ -218,6 +219,7 @@ await FileSystemUtils.UnzipResourcesAsync( .Where(entry => !upgradeSettings.UpgradeExclude.Any(filter => entry.FullName.StartsWith(filter, StringComparison.OrdinalIgnoreCase))), this.appStatus.ApplicationMapPath, cancellationToken); + this.SetAppOnline(); } private static async Task ReadZippedAssemblyVersion(ZipArchiveEntry assemblyEntry, CancellationToken cancellationToken) @@ -246,12 +248,37 @@ await assemblyStream.CopyToAsync( } } + private void SetAppOffline() + { + var appOfflineFile = Path.Combine(Globals.HostMapPath, "AppOffline", "App_Offline.htm"); + if (!File.Exists(appOfflineFile)) + { + appOfflineFile = Path.Combine(this.appStatus.ApplicationMapPath, "Resources", "AppOffline", "App_Offline.htm"); + } + + var targetFile = Path.Combine(this.appStatus.ApplicationMapPath, "App_Offline.htm"); + if (!File.Exists(targetFile)) + { + File.Copy(appOfflineFile, targetFile); + } + } + + private void SetAppOnline() + { + var appOfflineFile = Path.Combine(this.appStatus.ApplicationMapPath, "App_Offline.htm"); + if (File.Exists(appOfflineFile)) + { + File.Delete(appOfflineFile); + } + } + private class LocalUpgradeAssemblyInstaller : AssemblyInstaller { public LocalUpgradeAssemblyInstaller(InstallerInfo installerInfo) { this.Package = new PackageInfo(); this.Package.AttachInstallerInfo(installerInfo); + this.BackupFiles = false; } public async Task AddFiles(IEnumerable assemblyEntries, CancellationToken cancellationToken) diff --git a/DNN Platform/Website/DotNetNuke.Website.csproj b/DNN Platform/Website/DotNetNuke.Website.csproj index f8e658b25ab..054deb3b834 100644 --- a/DNN Platform/Website/DotNetNuke.Website.csproj +++ b/DNN Platform/Website/DotNetNuke.Website.csproj @@ -767,6 +767,7 @@ + diff --git a/DNN Platform/Website/Resources/AppOffline/App_Offline.htm b/DNN Platform/Website/Resources/AppOffline/App_Offline.htm new file mode 100644 index 00000000000..cdd647e8b9b --- /dev/null +++ b/DNN Platform/Website/Resources/AppOffline/App_Offline.htm @@ -0,0 +1,200 @@ + + + + + + Site Offline - Upgrade in Progress + + + + + + +
+ +
+
+ +
+
+
+ +
+
+

We'll be right back

+

The site is currently being upgraded

+

+ Our application is temporarily offline while we perform an upgrade. + Thank you for your patience — the site will be back online again soon. +

+ +
+
+ +
+
+

This page is shown automatically while the site is offline for maintenance.

+
+
+ + From 1779561b03a2a2e61e700f2903005ba46ff8945e Mon Sep 17 00:00:00 2001 From: Peter Donker Date: Wed, 17 Jun 2026 12:38:10 +0200 Subject: [PATCH 3/3] Propegate the backup switch to the relevant installers --- .../Library/Services/Installer/Installers/CleanupInstaller.cs | 2 +- .../Services/Installer/Installers/ResourceFileInstaller.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DNN Platform/Library/Services/Installer/Installers/CleanupInstaller.cs b/DNN Platform/Library/Services/Installer/Installers/CleanupInstaller.cs index 267ff0a8022..3763c79dc8a 100644 --- a/DNN Platform/Library/Services/Installer/Installers/CleanupInstaller.cs +++ b/DNN Platform/Library/Services/Installer/Installers/CleanupInstaller.cs @@ -201,7 +201,7 @@ protected bool CleanupFile(InstallFile insFile) try { // Backup File - if (File.Exists(this.PhysicalBasePath + insFile.FullName)) + if (File.Exists(this.PhysicalBasePath + insFile.FullName) && this.BackupFiles) { Util.BackupFile(insFile, this.PhysicalBasePath, this.Log); } diff --git a/DNN Platform/Library/Services/Installer/Installers/ResourceFileInstaller.cs b/DNN Platform/Library/Services/Installer/Installers/ResourceFileInstaller.cs index 3119af9dabd..abe193ad312 100644 --- a/DNN Platform/Library/Services/Installer/Installers/ResourceFileInstaller.cs +++ b/DNN Platform/Library/Services/Installer/Installers/ResourceFileInstaller.cs @@ -122,7 +122,7 @@ protected override bool InstallFile(InstallFile insFile) writer.WriteElementString("name", fileName); var physicalPath = Path.Combine(this.PhysicalBasePath, entry.FullName); - if (File.Exists(physicalPath)) + if (File.Exists(physicalPath) && this.BackupFiles) { Util.BackupFile( new InstallFile(entry.FullName, this.Package.InstallerInfo),