@@ -26,6 +26,16 @@ namespace DotNetNuke.Services.Installer;
2626/// <summary>The default <see cref="ILocalUpgradeService"/> implementation.</summary>
2727public class LocalUpgradeService : ILocalUpgradeService
2828{
29+ private static readonly string [ ] DefaultUpgradeExclude = new [ ]
30+ {
31+ "App_Data/Database.mdf" ,
32+ "Config/DotNetNuke.config" ,
33+ "Install/InstallWizard" ,
34+ "favicon.ico" ,
35+ "robots.txt" ,
36+ "web.config" ,
37+ } ;
38+
2939 private readonly IApplicationInfo application ;
3040 private readonly IApplicationStatusInfo appStatus ;
3141 private readonly IDirectory directory ;
@@ -49,62 +59,150 @@ public async Task<IReadOnlyList<LocalUpgradeInfo>> GetLocalUpgrades(Cancellation
4959 IReadOnlyList < LocalUpgradeInfo > localUpgrades ;
5060 if ( this . directory . Exists ( this . UpgradeDirectoryPath ) )
5161 {
62+ // clean up any old temp files
63+ var tempFiles = this . directory . GetFiles ( this . UpgradeDirectoryPath , "*.resources" , SearchOption . TopDirectoryOnly ) ;
64+ foreach ( var tempFile in tempFiles )
65+ {
66+ try
67+ {
68+ File . Delete ( tempFile ) ;
69+ }
70+ catch ( Exception ex )
71+ {
72+ Exceptions . LogException ( ex ) ;
73+ }
74+ }
75+
5276 var upgradeFiles = this . directory . GetFiles ( this . UpgradeDirectoryPath , "*.zip" , SearchOption . TopDirectoryOnly ) ;
53- localUpgrades = await Task . WhenAll ( upgradeFiles . Select ( file => GetLocalUpgradeInfo ( file , this . application , cancellationToken ) ) ) ;
77+ localUpgrades = await Task . WhenAll ( upgradeFiles . Select ( file => this . GetLocalUpgradeInfo ( file , cancellationToken ) ) ) ;
5478 }
5579 else
5680 {
5781 localUpgrades = [ ] ;
5882 }
5983
6084 return localUpgrades ;
85+ }
6186
62- static async Task < LocalUpgradeInfo > GetLocalUpgradeInfo ( string file , IApplicationInfo application , CancellationToken cancellationToken )
87+ /// <inheritdoc />
88+ public async Task < LocalUpgradeInfo > GetLocalUpgradeInfo ( string file , CancellationToken cancellationToken )
89+ {
90+ try
6391 {
64- try
92+ using var archiveStream = File . OpenRead ( file ) ;
93+ return await this . GetLocalUpgradeInfo ( Path . GetFileNameWithoutExtension ( file ) , archiveStream , cancellationToken ) ;
94+ }
95+ catch ( Exception exception )
96+ {
97+ Exceptions . LogException ( exception ) ;
98+ return new LocalUpgradeInfo
6599 {
66- using var archiveStream = File . OpenRead ( file ) ;
67- var archive = new ZipArchive ( archiveStream , ZipArchiveMode . Read ) ;
68- var mainAssemblyEntry = archive . FileEntries ( )
69- . Where ( entry => string . Equals ( "DotNetNuke.dll" , entry . Name , StringComparison . OrdinalIgnoreCase ) )
70- . Where ( entry => string . Equals ( "bin" , Path . GetDirectoryName ( entry . FullName ) , StringComparison . OrdinalIgnoreCase ) )
71- . SingleOrDefault ( ) ;
72-
73- Version mainAssemblyVersion = null ;
74- if ( mainAssemblyEntry is not null )
75- {
76- mainAssemblyVersion = await ReadZippedAssemblyVersion ( mainAssemblyEntry , cancellationToken ) ;
77- }
100+ PackageName = Path . GetFileNameWithoutExtension ( file ) ,
101+ Version = null ,
102+ IsValid = false ,
103+ IsOutdated = false ,
104+ } ;
105+ }
106+ }
78107
79- return new LocalUpgradeInfo
80- {
81- PackageName = Path . GetFileNameWithoutExtension ( file ) ,
82- Version = mainAssemblyVersion ,
83- IsValid = mainAssemblyVersion is not null ,
84- IsOutdated = mainAssemblyVersion is not null && mainAssemblyVersion <= application . Version ,
85- } ;
108+ /// <inheritdoc />
109+ public async Task < LocalUpgradeInfo > GetLocalUpgradeInfo ( string packageName , Stream archiveStream , CancellationToken cancellationToken )
110+ {
111+ try
112+ {
113+ var archive = new ZipArchive ( archiveStream , ZipArchiveMode . Read ) ;
114+ var mainAssemblyEntry = archive . FileEntries ( )
115+ . Where ( entry => string . Equals ( "DotNetNuke.dll" , entry . Name , StringComparison . OrdinalIgnoreCase ) )
116+ . Where ( entry => string . Equals ( "bin" , Path . GetDirectoryName ( entry . FullName ) , StringComparison . OrdinalIgnoreCase ) )
117+ . SingleOrDefault ( ) ;
118+
119+ Version mainAssemblyVersion = null ;
120+ if ( mainAssemblyEntry is not null )
121+ {
122+ mainAssemblyVersion = await ReadZippedAssemblyVersion ( mainAssemblyEntry , cancellationToken ) ;
86123 }
87- catch ( Exception exception )
124+
125+ var upgradeInfo = archive . FileEntries ( )
126+ . Where ( entry => string . Equals ( "App_Data/Upgrade/upgrade.json" , entry . FullName , StringComparison . OrdinalIgnoreCase ) )
127+ . SingleOrDefault ( ) ;
128+ var upgradeSettings = new LocalUpgradeSettings
88129 {
89- Exceptions . LogException ( exception ) ;
90- return new LocalUpgradeInfo
91- {
92- PackageName = Path . GetFileNameWithoutExtension ( file ) ,
93- Version = null ,
94- IsValid = false ,
95- IsOutdated = false ,
96- } ;
130+ MinimumDnnVersion = "0.0.0" ,
131+ } ;
132+ if ( upgradeInfo is not null )
133+ {
134+ var upgradeInfoFile = upgradeInfo . ReadTextFile ( ) ;
135+ upgradeSettings = Json . Deserialize < LocalUpgradeSettings > ( upgradeInfoFile ) ;
97136 }
137+
138+ var res = new LocalUpgradeInfo
139+ {
140+ PackageName = packageName ,
141+ Version = mainAssemblyVersion ,
142+ IsValid = mainAssemblyVersion is not null ,
143+ IsOutdated = mainAssemblyVersion is not null && mainAssemblyVersion <= this . application . Version ,
144+ MinDnnVersion = Version . Parse ( upgradeSettings . MinimumDnnVersion ) ,
145+ } ;
146+ res . CanInstall = res . IsValid
147+ && ! res . IsOutdated
148+ && ( res . MinDnnVersion is null || res . MinDnnVersion <= this . application . Version ) ;
149+
150+ return res ;
151+ }
152+ catch ( Exception exception )
153+ {
154+ Exceptions . LogException ( exception ) ;
155+ return new LocalUpgradeInfo
156+ {
157+ PackageName = packageName ,
158+ Version = null ,
159+ IsValid = false ,
160+ IsOutdated = false ,
161+ MinDnnVersion = null ,
162+ CanInstall = false ,
163+ } ;
164+ }
165+ }
166+
167+ /// <inheritdoc />
168+ public async Task DeleteLocalUpgrade ( string packageName , CancellationToken cancellationToken )
169+ {
170+ var upgrades = await this . GetLocalUpgrades ( cancellationToken ) ;
171+ var upgrade = upgrades . FirstOrDefault ( u => u . PackageName . Equals ( packageName , StringComparison . InvariantCultureIgnoreCase ) ) ;
172+ var packagePath = Path . Combine ( this . UpgradeDirectoryPath , upgrade . PackageName + ".zip" ) ;
173+
174+ if ( File . Exists ( packagePath ) )
175+ {
176+ File . Delete ( packagePath ) ;
98177 }
99178 }
100179
101180 /// <inheritdoc />
102181 public async Task StartLocalUpgrade ( IReadOnlyList < LocalUpgradeInfo > upgrades , CancellationToken cancellationToken )
103182 {
104183 var upgrade = upgrades . Where ( u => u . IsValid && ! u . IsOutdated ) . OrderBy ( u => u . Version ) . First ( ) ;
184+ await this . StartLocalUpgrade ( upgrade , cancellationToken ) ;
185+ }
186+
187+ /// <inheritdoc />
188+ public async Task StartLocalUpgrade ( LocalUpgradeInfo upgrade , CancellationToken cancellationToken )
189+ {
105190 var upgradeZipPath = Path . Combine ( this . UpgradeDirectoryPath , upgrade . PackageName + ".zip" ) ;
106191 using var fileStream = File . OpenRead ( upgradeZipPath ) ;
107192 using var archive = new ZipArchive ( fileStream , ZipArchiveMode . Read ) ;
193+ var upgradeInfo = archive . FileEntries ( )
194+ . Where ( entry => string . Equals ( "App_Data/Upgrade/upgrade.json" , entry . FullName , StringComparison . OrdinalIgnoreCase ) )
195+ . SingleOrDefault ( ) ;
196+ var upgradeSettings = new LocalUpgradeSettings
197+ {
198+ UpgradeExclude = DefaultUpgradeExclude ,
199+ } ;
200+ if ( upgradeInfo is not null )
201+ {
202+ var upgradeInfoFile = upgradeInfo . ReadTextFile ( ) ;
203+ upgradeSettings = Json . Deserialize < LocalUpgradeSettings > ( upgradeInfoFile ) ;
204+ }
205+
108206 var assemblyEntries = archive . FileEntries ( )
109207 . Where ( entry => string . Equals ( ".dll" , Path . GetExtension ( entry . Name ) , StringComparison . OrdinalIgnoreCase ) )
110208 . Where ( entry => string . Equals ( "bin" , Path . GetDirectoryName ( entry . FullName ) , StringComparison . OrdinalIgnoreCase ) )
@@ -115,7 +213,9 @@ public async Task StartLocalUpgrade(IReadOnlyList<LocalUpgradeInfo> upgrades, Ca
115213 installer . Commit ( ) ;
116214
117215 await FileSystemUtils . UnzipResourcesAsync (
118- archive . FileEntries ( ) . Where ( entry => ! assemblyEntries . Contains ( entry ) ) ,
216+ archive . FileEntries ( )
217+ . Where ( entry => ! assemblyEntries . Contains ( entry ) )
218+ . Where ( entry => ! upgradeSettings . UpgradeExclude . Any ( filter => entry . FullName . StartsWith ( filter , StringComparison . OrdinalIgnoreCase ) ) ) ,
119219 this . appStatus . ApplicationMapPath ,
120220 cancellationToken ) ;
121221 }
0 commit comments