diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 1e62c68c4b..f3cc8e86ff 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -34,13 +34,12 @@ namespace Umbraco.Core.Composing private readonly object _typesLock = new object(); private readonly Dictionary _types = new Dictionary(); - private readonly Lazy _typesListFilePath = new Lazy(GetTypesListFilePath); - private readonly Lazy _typesHashFilePath = new Lazy(GetTypesHashFilePath); - private string _cachedAssembliesHash; private string _currentAssembliesHash; private IEnumerable _assemblies; private bool _reportedChange; + private static LocalTempStorage _localTempStorage = LocalTempStorage.Unknown; + private static string _fileBasePath; /// /// Initializes a new instance of the class. @@ -64,8 +63,9 @@ namespace Umbraco.Core.Composing // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - if (File.Exists(TypesListFilePath)) - File.Delete(TypesListFilePath); + var typesListFilePath = GetTypesListFilePath(); + if (File.Exists(typesListFilePath)) + File.Delete(typesListFilePath); WriteCacheTypesHash(); } @@ -75,24 +75,15 @@ namespace Umbraco.Core.Composing // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 - if (File.Exists(TypesListFilePath)) - File.Delete(TypesListFilePath); + var typesListFilePath = GetTypesListFilePath(); + if (File.Exists(typesListFilePath)) + File.Delete(typesListFilePath); // always set to true if we're not detecting (generally only for testing) RequiresRescanning = true; } } - /// - /// Gets the plugin list file path. - /// - private string TypesListFilePath => _typesListFilePath.Value; - - /// - /// Gets the plugin hash file path. - /// - private string TypesHashFilePath => _typesHashFilePath.Value; - /// /// Gets or sets the set of assemblies to scan. /// @@ -144,9 +135,10 @@ namespace Umbraco.Core.Composing if (_cachedAssembliesHash != null) return _cachedAssembliesHash; - if (!File.Exists(TypesHashFilePath)) return string.Empty; + var typesHashFilePath = GetTypesHashFilePath(); + if (!File.Exists(typesHashFilePath)) return string.Empty; - var hash = File.ReadAllText(TypesHashFilePath, Encoding.UTF8); + var hash = File.ReadAllText(typesHashFilePath, Encoding.UTF8); _cachedAssembliesHash = hash; return _cachedAssembliesHash; @@ -185,7 +177,8 @@ namespace Umbraco.Core.Composing /// private void WriteCacheTypesHash() { - File.WriteAllText(TypesHashFilePath, CurrentAssembliesHash, Encoding.UTF8); + var typesHashFilePath = GetTypesHashFilePath(); + File.WriteAllText(typesHashFilePath, CurrentAssembliesHash, Encoding.UTF8); } /// @@ -302,7 +295,8 @@ namespace Umbraco.Core.Composing { try { - File.Delete(TypesListFilePath); + var typesListFilePath = GetTypesListFilePath(); + File.Delete(typesListFilePath); } catch { @@ -318,10 +312,11 @@ namespace Umbraco.Core.Composing { var cache = new Dictionary, IEnumerable>(); - if (File.Exists(TypesListFilePath) == false) + var typesListFilePath = GetTypesListFilePath(); + if (File.Exists(typesListFilePath) == false) return cache; - using (var stream = GetFileStream(TypesListFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) + using (var stream = GetFileStream(typesListFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout)) using (var reader = new StreamReader(stream)) { while (true) @@ -359,9 +354,49 @@ namespace Umbraco.Core.Composing } // internal for tests - internal static string GetTypesListFilePath() => GetFilePath("list"); + internal static string GetTypesListFilePath() => GetFileBasePath() + ".list"; - private static string GetTypesHashFilePath() => GetFilePath("hash"); + private static string GetTypesHashFilePath() => GetFileBasePath() + ".hash"; + + private static string GetFileBasePath() + { + var localTempStorage = GlobalSettings.LocalTempStorageLocation; + if (_localTempStorage != localTempStorage) + { + string path; + switch (GlobalSettings.LocalTempStorageLocation) + { + case LocalTempStorage.AspNetTemp: + path = Path.Combine(HttpRuntime.CodegenDir, "UmbracoData", "umbraco-types"); + break; + case LocalTempStorage.EnvironmentTemp: + // include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back + // to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not + // utilizing an old path - assuming we cannot have SHA1 collisions on AppDomainAppId + var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1(); + var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", appDomainHash); + path = Path.Combine(cachePath, "umbraco-types"); + break; + case LocalTempStorage.Default: + default: + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/TypesCache"); + path = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName); + break; + } + + _fileBasePath = path; + _localTempStorage = localTempStorage; + } + + // ensure that the folder exists + var directory = Path.GetDirectoryName(_fileBasePath); + if (directory == null) + throw new InvalidOperationException($"Could not determine folder for path \"{_fileBasePath}\"."); + if (Directory.Exists(directory) == false) + Directory.CreateDirectory(directory); + + return _fileBasePath; + } private static string GetFilePath(string extension) { @@ -399,7 +434,8 @@ namespace Umbraco.Core.Composing // internal for tests internal void WriteCache() { - using (var stream = GetFileStream(TypesListFilePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) + var typesListFilePath = GetTypesListFilePath(); + using (var stream = GetFileStream(typesListFilePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout)) using (var writer = new StreamWriter(stream)) { foreach (var typeList in _types.Values) @@ -429,11 +465,13 @@ namespace Umbraco.Core.Composing /// Generally only used for resetting cache, for example during the install process. public void ClearTypesCache() { - if (File.Exists(TypesListFilePath)) - File.Delete(TypesListFilePath); + var typesListFilePath = GetTypesListFilePath(); + if (File.Exists(typesListFilePath)) + File.Delete(typesListFilePath); - if (File.Exists(TypesHashFilePath)) - File.Delete(TypesHashFilePath); + var typesHashFilePath = GetTypesHashFilePath(); + if (File.Exists(typesHashFilePath)) + File.Delete(typesHashFilePath); _runtimeCache.ClearCacheItem(CacheKey); } @@ -611,7 +649,8 @@ namespace Umbraco.Core.Composing // else proceed, typeList = new TypeList(baseType, attributeType); - var scan = RequiresRescanning || File.Exists(TypesListFilePath) == false; + var typesListFilePath = GetTypesListFilePath(); + var scan = RequiresRescanning || File.Exists(typesListFilePath) == false; if (scan) { diff --git a/src/Umbraco.Core/Configuration/LocalTempStorage.cs b/src/Umbraco.Core/Configuration/LocalTempStorage.cs index d41f7d1925..1231ee7156 100644 --- a/src/Umbraco.Core/Configuration/LocalTempStorage.cs +++ b/src/Umbraco.Core/Configuration/LocalTempStorage.cs @@ -2,8 +2,9 @@ namespace Umbraco.Core.Configuration { internal enum LocalTempStorage { + Unknown = 0, Default, AspNetTemp, EnvironmentTemp } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 8985fd436d..fb0b3fc670 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -44,17 +44,19 @@ namespace Umbraco.Core.Migrations.Upgrade // cannot go back in time if (currentVersion > UmbracoVersion.SemanticVersion) - throw new InvalidOperationException($"Version {currentVersion} cannot be upgraded to {UmbracoVersion.SemanticVersion}."); + throw new InvalidOperationException($"Version {currentVersion} cannot be downgraded to {UmbracoVersion.SemanticVersion}."); switch (currentVersion.Major) { case 7: + // upgrading from version 7 return "{orig-" + currentVersion + "}"; case 8: // fixme remove when releasing - // this is very temp and for my own website - zpqrtbnk + // upgrading from version 8 + // should never happen, this is very temp and for my own website - zpqrtbnk return "{04F54303-3055-4700-8F76-35A37F232FF5}"; // right before the variants migration default: - throw new InvalidOperationException($"Version {currentVersion} should have an upgrade state in the key-value table."); + throw new InvalidOperationException($"Version {currentVersion} is not supported by the migration plan."); } } diff --git a/src/Umbraco.Core/Services/Implement/AuditService.cs b/src/Umbraco.Core/Services/Implement/AuditService.cs index bcd19a72ac..272d550a43 100644 --- a/src/Umbraco.Core/Services/Implement/AuditService.cs +++ b/src/Umbraco.Core/Services/Implement/AuditService.cs @@ -159,7 +159,7 @@ namespace Umbraco.Core.Services.Implement /// public IAuditEntry Write(int performingUserId, string perfomingDetails, string performingIp, DateTime eventDateUtc, int affectedUserId, string affectedDetails, string eventType, string eventDetails) { - if (performingUserId < 0) throw new ArgumentOutOfRangeException(nameof(performingUserId)); + if (performingUserId < 0 && performingUserId != Constants.Security.SuperId) throw new ArgumentOutOfRangeException(nameof(performingUserId)); if (string.IsNullOrWhiteSpace(perfomingDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(perfomingDetails)); if (string.IsNullOrWhiteSpace(eventType)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventType)); if (string.IsNullOrWhiteSpace(eventDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventDetails)); diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 03ea7391f9..a636e956f9 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -366,7 +366,8 @@ namespace Umbraco.Tests.Persistence.Repositories var group = MockedUserGroup.CreateUserGroup(); userGroupRepository.AddOrUpdateGroupWithUsers(@group, new[] { user.Id }); - + + user.AddGroup(group); return user; } diff --git a/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs b/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs index 56b61f1fc9..77ded444b6 100644 --- a/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs +++ b/src/Umbraco.Web/Composing/CompositionRoots/WebMappingProfilesCompositionRoot.cs @@ -7,6 +7,8 @@ namespace Umbraco.Web.Composing.CompositionRoots { public void Compose(IServiceRegistry container) { + container.Register(); + container.Register(); container.Register(); container.Register(); container.Register(); @@ -15,13 +17,12 @@ namespace Umbraco.Web.Composing.CompositionRoots container.Register(); container.Register(); container.Register(); + container.Register(); container.Register(); container.Register(); container.Register(); - container.Register(); container.Register(); - container.Register(); - container.Register(); + container.Register(); } } } diff --git a/src/Umbraco.Web/Editors/CurrentUserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs index 28afd2135c..a68ad4c813 100644 --- a/src/Umbraco.Web/Editors/CurrentUserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -33,12 +33,12 @@ namespace Umbraco.Web.Editors /// public IEnumerable PostSetUserTour(UserTourStatus status) { - if (status == null) throw new ArgumentNullException("status"); + if (status == null) throw new ArgumentNullException(nameof(status)); List userTours; if (Security.CurrentUser.TourData.IsNullOrWhiteSpace()) { - userTours = new List {status}; + userTours = new List { status }; Security.CurrentUser.TourData = JsonConvert.SerializeObject(userTours); Services.UserService.Save(Security.CurrentUser); return userTours; diff --git a/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs b/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs index db32908352..01ff458bd5 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentTypeBasicResolver.cs @@ -24,9 +24,9 @@ namespace Umbraco.Web.Models.Mapping { ContentTypeBasic contentTypeBasic; if (source is IContent content) - contentTypeBasic = Mapper.Map(content.ContentType); + contentTypeBasic = Mapper.Map(content.ContentType); else if (source is IMedia media) - contentTypeBasic = Mapper.Map(media.ContentType); + contentTypeBasic = Mapper.Map(media.ContentType); else throw new NotSupportedException($"Expected TSource to be IContent or IMedia, got {typeof(TSource).Name}."); diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 547b2d3595..fbf0093c62 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -71,7 +71,7 @@ namespace Umbraco.Web.Security if (_currentUser == null) { var id = GetUserId(); - return id ? _userService.GetUserById(id.Result) : null; + _currentUser = id ? _userService.GetUserById(id.Result) : null; } return _currentUser;