diff --git a/.editorconfig b/.editorconfig index 5f3b4d684a..d2f3002c12 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,16 @@ indent_size = 4 # Trim trailing whitespace, limited support. # https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces trim_trailing_whitespace = true + +[*.{cs,vb}] +dotnet_style_predefined_type_for_locals_parameters_members = true:error + +dotnet_naming_rule.private_members_with_underscore.symbols = private_fields +dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore +dotnet_naming_rule.private_members_with_underscore.severity = suggestion + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9f22544f35..6c180fa693 100644 --- a/.gitignore +++ b/.gitignore @@ -121,6 +121,9 @@ src/Umbraco.Web.UI.Client/bower_components/* /src/Umbraco.Web.UI/Umbraco/preview /src/Umbraco.Web.UI/Umbraco/preview.old +# ignore rule for clearing out Belle (avoid rebuilding all the time) +preserve.belle + #Ignore Rule for output of generated documentation files from Grunt docserve src/Umbraco.Web.UI.Client/docs/api src/*.boltdata/ diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index d06b5e7136..5e836f602b 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -43,7 +43,7 @@ - + diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt index 7af4a38c14..5fb0a79ee3 100644 --- a/build/NuSpecs/tools/Web.config.install.xdt +++ b/build/NuSpecs/tools/Web.config.install.xdt @@ -4,8 +4,8 @@
-
- +
+
@@ -67,190 +67,6 @@ > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/build/NuSpecs/tools/trees.config.install.xdt b/build/NuSpecs/tools/trees.config.install.xdt index 5a549e3fd8..dc59b5db10 100644 --- a/build/NuSpecs/tools/trees.config.install.xdt +++ b/build/NuSpecs/tools/trees.config.install.xdt @@ -86,9 +86,6 @@ - + + \ No newline at end of file diff --git a/build/build.ps1 b/build/build.ps1 index 013be89cb4..1c8d429333 100644 --- a/build/build.ps1 +++ b/build/build.ps1 @@ -372,8 +372,6 @@ Write-Host "Add web.config transforms to NuGet package" mv "$($this.BuildTemp)\WebApp\Views\Web.config" "$($this.BuildTemp)\WebApp\Views\Web.config.transform" - # fixme - that one does not exist in .bat build either? - #mv "$($this.BuildTemp)\WebApp\Xslt\Web.config" "$($this.BuildTemp)\WebApp\Xslt\Web.config.transform" }) $ubuild.DefineMethod("RestoreNuGet", diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index 68b4c3b157..bba5655b7a 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -22,9 +22,7 @@ namespace Umbraco.Core.Cache [UmbracoWillObsolete("This cache key is only used for the legacy 'library' caching, remove in v8")] public const string MediaCacheKey = "UL_GetMedia"; - - public const string MacroXsltCacheKey = "macroXslt_"; - + [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] public const string MacroCacheKey = "UmbracoMacroCache"; @@ -39,54 +37,14 @@ namespace Umbraco.Core.Cache [UmbracoWillObsolete("This cache key is only used for legacy template business logic caching, remove in v8")] public const string TemplateFrontEndCacheKey = "template"; - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string UserContextCacheKey = "UmbracoUserContext"; - public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout"; - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string UserCacheKey = "UmbracoUser"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string UserGroupPermissionsCacheKey = "UmbracoUserGroupPermissions"; - [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] public const string ContentTypeCacheKey = "UmbracoContentType"; [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:"; - - [Obsolete("No longer used and will be removed in v8")] - public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string LanguageCacheKey = "UmbracoLanguageCache"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string DomainCacheKey = "UmbracoDomainList"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string StylesheetCacheKey = "UmbracoStylesheet"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty"; - - [Obsolete("This is no longer used and will be removed from the codebase in the future")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string DataTypeCacheKey = "UmbracoDataTypeDefinition"; - public const string DataTypePreValuesCacheKey = "UmbracoPreVal"; - + public const string IdToKeyCacheKey = "UI2K__"; public const string KeyToIdCacheKey = "UK2I__"; } diff --git a/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs b/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs index 6baa2cf0ed..9da202b5aa 100644 --- a/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs +++ b/src/Umbraco.Core/Cache/DictionaryCacheProviderBase.cs @@ -24,8 +24,13 @@ namespace Umbraco.Core.Cache protected abstract object GetEntry(string key); // read-write lock the underlying cache - protected abstract IDisposable ReadLock { get; } - protected abstract IDisposable WriteLock { get; } + //protected abstract IDisposable ReadLock { get; } + //protected abstract IDisposable WriteLock { get; } + + protected abstract void EnterReadLock(); + protected abstract void ExitReadLock(); + protected abstract void EnterWriteLock(); + protected abstract void ExitWriteLock(); protected string GetCacheKey(string key) { @@ -88,21 +93,31 @@ namespace Umbraco.Core.Cache public virtual void ClearAllCache() { - using (WriteLock) + try { + EnterWriteLock(); foreach (var entry in GetDictionaryEntries() .ToArray()) RemoveEntry((string) entry.Key); } + finally + { + ExitWriteLock(); + } } public virtual void ClearCacheItem(string key) { var cacheKey = GetCacheKey(key); - using (WriteLock) + try { + EnterWriteLock(); RemoveEntry(cacheKey); } + finally + { + ExitWriteLock(); + } } public virtual void ClearCacheObjectTypes(string typeName) @@ -110,15 +125,16 @@ namespace Umbraco.Core.Cache var type = TypeFinder.GetTypeByName(typeName); if (type == null) return; var isInterface = type.IsInterface; - using (WriteLock) + try { + EnterWriteLock(); foreach (var entry in GetDictionaryEntries() .Where(x => { // entry.Value is Lazy and not null, its value may be null // remove null values as well, does not hurt // get non-created as NonCreatedValue & exceptions as null - var value = GetSafeLazyValue((Lazy)x.Value, true); + var value = GetSafeLazyValue((Lazy) x.Value, true); // if T is an interface remove anything that implements that interface // otherwise remove exact types (not inherited types) @@ -127,14 +143,19 @@ namespace Umbraco.Core.Cache .ToArray()) RemoveEntry((string) entry.Key); } + finally + { + ExitWriteLock(); + } } public virtual void ClearCacheObjectTypes() { var typeOfT = typeof(T); var isInterface = typeOfT.IsInterface; - using (WriteLock) + try { + EnterWriteLock(); foreach (var entry in GetDictionaryEntries() .Where(x => { @@ -142,7 +163,7 @@ namespace Umbraco.Core.Cache // remove null values as well, does not hurt // compare on exact type, don't use "is" // get non-created as NonCreatedValue & exceptions as null - var value = GetSafeLazyValue((Lazy)x.Value, true); + var value = GetSafeLazyValue((Lazy) x.Value, true); // if T is an interface remove anything that implements that interface // otherwise remove exact types (not inherited types) @@ -151,6 +172,10 @@ namespace Umbraco.Core.Cache .ToArray()) RemoveEntry((string) entry.Key); } + finally + { + ExitWriteLock(); + } } public virtual void ClearCacheObjectTypes(Func predicate) @@ -158,8 +183,9 @@ namespace Umbraco.Core.Cache var typeOfT = typeof(T); var isInterface = typeOfT.IsInterface; var plen = CacheItemPrefix.Length + 1; - using (WriteLock) + try { + EnterWriteLock(); foreach (var entry in GetDictionaryEntries() .Where(x => { @@ -167,7 +193,7 @@ namespace Umbraco.Core.Cache // remove null values as well, does not hurt // compare on exact type, don't use "is" // get non-created as NonCreatedValue & exceptions as null - var value = GetSafeLazyValue((Lazy)x.Value, true); + var value = GetSafeLazyValue((Lazy) x.Value, true); if (value == null) return true; // if T is an interface remove anything that implements that interface @@ -178,30 +204,44 @@ namespace Umbraco.Core.Cache })) RemoveEntry((string) entry.Key); } + finally + { + ExitWriteLock(); + } } public virtual void ClearCacheByKeySearch(string keyStartsWith) { var plen = CacheItemPrefix.Length + 1; - using (WriteLock) + try { + EnterWriteLock(); foreach (var entry in GetDictionaryEntries() .Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith)) .ToArray()) RemoveEntry((string) entry.Key); } + finally + { + ExitWriteLock(); + } } public virtual void ClearCacheByKeyExpression(string regexString) { var plen = CacheItemPrefix.Length + 1; - using (WriteLock) + try { + EnterWriteLock(); foreach (var entry in GetDictionaryEntries() .Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString)) .ToArray()) RemoveEntry((string) entry.Key); } + finally + { + ExitWriteLock(); + } } #endregion @@ -212,12 +252,18 @@ namespace Umbraco.Core.Cache { var plen = CacheItemPrefix.Length + 1; IEnumerable entries; - using (ReadLock) + try { + EnterReadLock(); entries = GetDictionaryEntries() .Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith)) .ToArray(); // evaluate while locked } + finally + { + ExitReadLock(); + } + return entries .Select(x => GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null .Where(x => x != null); // backward compat, don't store null values in the cache @@ -228,12 +274,17 @@ namespace Umbraco.Core.Cache const string prefix = CacheItemPrefix + "-"; var plen = prefix.Length; IEnumerable entries; - using (ReadLock) + try { + EnterReadLock(); entries = GetDictionaryEntries() .Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString)) .ToArray(); // evaluate while locked } + finally + { + ExitReadLock(); + } return entries .Select(x => GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null .Where(x => x != null); // backward compat, don't store null values in the cache @@ -243,10 +294,15 @@ namespace Umbraco.Core.Cache { cacheKey = GetCacheKey(cacheKey); Lazy result; - using (ReadLock) + try { + EnterReadLock(); result = GetEntry(cacheKey) as Lazy; // null if key not found } + finally + { + ExitReadLock(); + } return result == null ? null : GetSafeLazyValue(result); // return exceptions as null } diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs index 6f97651042..5554655d83 100644 --- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs @@ -79,24 +79,26 @@ namespace Umbraco.Core.Cache #region Lock - protected override IDisposable ReadLock + private bool _entered; + + protected override void EnterReadLock() => EnterWriteLock(); + + protected override void EnterWriteLock() { - // there's no difference between ReadLock and WriteLock here - get { return WriteLock; } + if (HasContextItems) + { + System.Threading.Monitor.Enter(ContextItems.SyncRoot, ref _entered); + } } - protected override IDisposable WriteLock - { - // NOTE - // could think about just overriding base.Locker to return a different - // object but then we'd create a ReaderWriterLockSlim per request, - // which is less efficient than just using a basic monitor lock. + protected override void ExitReadLock() => ExitWriteLock(); - get + protected override void ExitWriteLock() + { + if (_entered) { - return HasContextItems - ? (IDisposable) new MonitorLock(ContextItems.SyncRoot) - : new NoopLocker(); + _entered = false; + System.Threading.Monitor.Exit(ContextItems.SyncRoot); } } @@ -113,8 +115,9 @@ namespace Umbraco.Core.Cache Lazy result; - using (WriteLock) + try { + EnterWriteLock(); result = ContextItems[cacheKey] as Lazy; // null if key not found // cannot create value within the lock, so if result.IsValueCreated is false, just @@ -127,6 +130,10 @@ namespace Umbraco.Core.Cache ContextItems[cacheKey] = result; } } + finally + { + ExitWriteLock(); + } // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache // exceptions (but try again and again) and silently eat them - however at diff --git a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs index ad46201c0c..068bf3605e 100644 --- a/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/HttpRuntimeCacheProvider.cs @@ -50,14 +50,26 @@ namespace Umbraco.Core.Cache #region Lock - protected override IDisposable ReadLock + protected override void EnterReadLock() { - get { return new ReadLock(_locker); } + _locker.EnterReadLock(); } - protected override IDisposable WriteLock + protected override void EnterWriteLock() { - get { return new WriteLock(_locker); } + _locker.EnterWriteLock();; + } + + protected override void ExitReadLock() + { + if (_locker.IsReadLockHeld) + _locker.ExitReadLock(); + } + + protected override void ExitWriteLock() + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); } #endregion @@ -118,10 +130,16 @@ namespace Umbraco.Core.Cache // reads. We first try with a normal ReadLock for maximum concurrency and take the penalty of // having to re-lock in case there's no value. Would need to benchmark to figure out whether // it's worth it, though... - using (new ReadLock(_locker)) + try { + _locker.EnterReadLock(); result = _cache.Get(cacheKey) as Lazy; // null if key not found } + finally + { + if (_locker.IsReadLockHeld) + _locker.ExitReadLock(); + } var value = result == null ? null : GetSafeLazyValue(result); if (value != null) return value; @@ -195,11 +213,17 @@ namespace Umbraco.Core.Cache var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value)); var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration); - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); //NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update! _cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null) diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs index 70e37addb8..b3a643dbfb 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs @@ -37,20 +37,32 @@ namespace Umbraco.Core.Cache public virtual void ClearAllCache() { - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); MemoryCache.DisposeIfDisposable(); MemoryCache = new MemoryCache("in-memory"); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public virtual void ClearCacheItem(string key) { - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); if (MemoryCache[key] == null) return; MemoryCache.Remove(key); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public virtual void ClearCacheObjectTypes(string typeName) @@ -58,8 +70,9 @@ namespace Umbraco.Core.Cache var type = TypeFinder.GetTypeByName(typeName); if (type == null) return; var isInterface = type.IsInterface; - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); foreach (var key in MemoryCache .Where(x => { @@ -76,12 +89,18 @@ namespace Umbraco.Core.Cache .ToArray()) // ToArray required to remove MemoryCache.Remove(key); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public virtual void ClearCacheObjectTypes() { - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); var typeOfT = typeof (T); var isInterface = typeOfT.IsInterface; foreach (var key in MemoryCache @@ -101,12 +120,18 @@ namespace Umbraco.Core.Cache .ToArray()) // ToArray required to remove MemoryCache.Remove(key); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public virtual void ClearCacheObjectTypes(Func predicate) { - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); var typeOfT = typeof(T); var isInterface = typeOfT.IsInterface; foreach (var key in MemoryCache @@ -127,30 +152,47 @@ namespace Umbraco.Core.Cache .ToArray()) // ToArray required to remove MemoryCache.Remove(key); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public virtual void ClearCacheByKeySearch(string keyStartsWith) { - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); foreach (var key in MemoryCache .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) .Select(x => x.Key) .ToArray()) // ToArray required to remove MemoryCache.Remove(key); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } public virtual void ClearCacheByKeyExpression(string regexString) { - using (new WriteLock(_locker)) + try { + _locker.EnterWriteLock(); foreach (var key in MemoryCache .Where(x => Regex.IsMatch(x.Key, regexString)) .Select(x => x.Key) .ToArray()) // ToArray required to remove MemoryCache.Remove(key); } + finally + { + if (_locker.IsWriteLockHeld) + _locker.ExitWriteLock(); + } } #endregion @@ -160,12 +202,18 @@ namespace Umbraco.Core.Cache public IEnumerable GetCacheItemsByKeySearch(string keyStartsWith) { KeyValuePair[] entries; - using (new ReadLock(_locker)) + try { + _locker.EnterReadLock(); entries = MemoryCache .Where(x => x.Key.InvariantStartsWith(keyStartsWith)) .ToArray(); // evaluate while locked } + finally + { + if (_locker.IsReadLockHeld) + _locker.ExitReadLock(); + } return entries .Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null .Where(x => x != null) // backward compat, don't store null values in the cache @@ -175,12 +223,18 @@ namespace Umbraco.Core.Cache public IEnumerable GetCacheItemsByKeyExpression(string regexString) { KeyValuePair[] entries; - using (new ReadLock(_locker)) + try { + _locker.EnterReadLock(); entries = MemoryCache .Where(x => Regex.IsMatch(x.Key, regexString)) .ToArray(); // evaluate while locked } + finally + { + if (_locker.IsReadLockHeld) + _locker.ExitReadLock(); + } return entries .Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy)x.Value)) // return exceptions as null .Where(x => x != null) // backward compat, don't store null values in the cache @@ -190,10 +244,16 @@ namespace Umbraco.Core.Cache public object GetCacheItem(string cacheKey) { Lazy result; - using (new ReadLock(_locker)) + try { + _locker.EnterReadLock(); result = MemoryCache.Get(cacheKey) as Lazy; // null if key not found } + finally + { + if (_locker.IsReadLockHeld) + _locker.ExitReadLock(); + } return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null } diff --git a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs new file mode 100644 index 0000000000..11f123f2d0 --- /dev/null +++ b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs @@ -0,0 +1,41 @@ +using System; + +namespace Umbraco.Core.Collections +{ + /// + /// Represents a composite key of (string, string) for fast dictionaries. + /// + /// + /// The string parts of the key are case-insensitive. + /// Null is a valid value for both parts. + /// + public struct CompositeStringStringKey : IEquatable + { + private readonly string _key1; + private readonly string _key2; + + /// + /// Initializes a new instance of the struct. + /// + public CompositeStringStringKey(string key1, string key2) + { + _key1 = key1?.ToLowerInvariant() ?? "NULL"; + _key2 = key2?.ToLowerInvariant() ?? "NULL"; + } + + public bool Equals(CompositeStringStringKey other) + => _key2 == other._key2 && _key1 == other._key1; + + public override bool Equals(object obj) + => obj is CompositeStringStringKey other && _key2 == other._key2 && _key1 == other._key1; + + public override int GetHashCode() + => _key2.GetHashCode() * 31 + _key1.GetHashCode(); + + public static bool operator ==(CompositeStringStringKey key1, CompositeStringStringKey key2) + => key1._key2 == key2._key2 && key1._key1 == key2._key1; + + public static bool operator !=(CompositeStringStringKey key1, CompositeStringStringKey key2) + => key1._key2 != key2._key2 || key1._key1 != key2._key1; + } +} diff --git a/src/Umbraco.Core/Collections/ConcurrentHashSet.cs b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs index 39cb701e69..4cad6e9f15 100644 --- a/src/Umbraco.Core/Collections/ConcurrentHashSet.cs +++ b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs @@ -50,10 +50,16 @@ namespace Umbraco.Core.Collections /// The object to remove from the .The is read-only. public bool Remove(T item) { - using (new WriteLock(_instanceLocker)) + try { + _instanceLocker.EnterWriteLock(); return _innerSet.Remove(item); } + finally + { + if (_instanceLocker.IsWriteLockHeld) + _instanceLocker.ExitWriteLock(); + } } @@ -86,10 +92,16 @@ namespace Umbraco.Core.Collections /// The object to add to the .The is read-only. public void Add(T item) { - using (new WriteLock(_instanceLocker)) + try { + _instanceLocker.EnterWriteLock(); _innerSet.Add(item); } + finally + { + if (_instanceLocker.IsWriteLockHeld) + _instanceLocker.ExitWriteLock(); + } } /// @@ -101,13 +113,20 @@ namespace Umbraco.Core.Collections { var clone = GetThreadSafeClone(); if (clone.Contains(item)) return false; - using (new WriteLock(_instanceLocker)) + try { + _instanceLocker.EnterWriteLock(); + //double check if (_innerSet.Contains(item)) return false; _innerSet.Add(item); return true; } + finally + { + if (_instanceLocker.IsWriteLockHeld) + _instanceLocker.ExitWriteLock(); + } } /// @@ -116,10 +135,16 @@ namespace Umbraco.Core.Collections /// The is read-only. public void Clear() { - using (new WriteLock(_instanceLocker)) + try { + _instanceLocker.EnterWriteLock(); _innerSet.Clear(); } + finally + { + if (_instanceLocker.IsWriteLockHeld) + _instanceLocker.ExitWriteLock(); + } } /// @@ -147,10 +172,16 @@ namespace Umbraco.Core.Collections private HashSet GetThreadSafeClone() { HashSet clone = null; - using (new WriteLock(_instanceLocker)) + try { + _instanceLocker.EnterWriteLock(); clone = new HashSet(_innerSet, _innerSet.Comparer); } + finally + { + if (_instanceLocker.IsWriteLockHeld) + _instanceLocker.ExitWriteLock(); + } return clone; } diff --git a/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs index 56538a8058..26a95af801 100644 --- a/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs +++ b/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs @@ -76,7 +76,6 @@ namespace Umbraco.Core.Composing.CompositionRoots container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); - container.RegisterSingleton(); } } } diff --git a/src/Umbraco.Core/Composing/LightInjectExtensions.cs b/src/Umbraco.Core/Composing/LightInjectExtensions.cs index c7b51404fe..68ba48c803 100644 --- a/src/Umbraco.Core/Composing/LightInjectExtensions.cs +++ b/src/Umbraco.Core/Composing/LightInjectExtensions.cs @@ -311,15 +311,15 @@ namespace Umbraco.Core.Composing // we HAVE to let LightInject throw - and catch at THE OUTERMOST if InvalidOperationException in LightInject.Anything! return factory.GetInstance(tService, serviceName, args); - try - { - return factory.GetInstance(tService, serviceName, args); - } - catch (Exception e) - { - LightInjectException.TryThrow(e, implementingType); - throw; - } + //try + //{ + // return factory.GetInstance(tService, serviceName, args); + //} + //catch (Exception e) + //{ + // LightInjectException.TryThrow(e, implementingType); + // throw; + //} } /// diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/DeveloperElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/DeveloperElement.cs deleted file mode 100644 index 66531a1ab1..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/DeveloperElement.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class DeveloperElement : ConfigurationElement, IDeveloperSection - { - private AppCodeFileExtensionsElement _default; - - [ConfigurationProperty("appCodeFileExtensions")] - internal AppCodeFileExtensionsElement AppCodeFileExtensions - { - get - { - if (_default != null) - { - return _default; - } - - //here we need to check if this element is defined, if it is not then we'll setup the defaults - var prop = Properties["appCodeFileExtensions"]; - var autoFill = this[prop] as ConfigurationElement; - if (autoFill != null && autoFill.ElementInformation.IsPresent == false) - { - var collection = new AppCodeFileExtensionsCollection - { - new FileExtensionElement {RawValue = "cs"}, - new FileExtensionElement {RawValue = "vb"} - }; - _default = new AppCodeFileExtensionsElement - { - AppCodeFileExtensionsCollection = collection - }; - - return _default; - } - - return (AppCodeFileExtensionsElement)base["appCodeFileExtensions"]; - } - } - - IEnumerable IDeveloperSection.AppCodeFileExtensions - { - get { return AppCodeFileExtensions.AppCodeFileExtensionsCollection; } - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/DistributedCallElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/DistributedCallElement.cs deleted file mode 100644 index af035d2e0e..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/DistributedCallElement.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class DistributedCallElement : ConfigurationElement, IDistributedCallSection - { - [ConfigurationProperty("enable", DefaultValue = false)] - internal bool Enabled - { - get { return (bool)base["enable"]; } - } - - [ConfigurationProperty("user")] - internal InnerTextConfigurationElement UserId - { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["user"], - //set the default - 0); - } - } - - [ConfigurationCollection(typeof(ServerCollection), AddItemName = "server")] - [ConfigurationProperty("servers", IsDefaultCollection = true)] - internal ServerCollection Servers - { - get { return (ServerCollection)base["servers"]; } - } - - bool IDistributedCallSection.Enabled - { - get { return Enabled; } - } - - int IDistributedCallSection.UserId - { - get { return UserId; } - } - - IEnumerable IDistributedCallSection.Servers - { - get { return Servers; } - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs deleted file mode 100644 index 40aa6af0db..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IDeveloperSection.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IDeveloperSection : IUmbracoConfigurationSection - { - IEnumerable AppCodeFileExtensions { get; } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs deleted file mode 100644 index 96f695c8c7..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IDistributedCallSection.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IDistributedCallSection : IUmbracoConfigurationSection - { - bool Enabled { get; } - - int UserId { get; } - - IEnumerable Servers { get; } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs deleted file mode 100644 index c4b5781ca3..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ILink.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.ComponentModel; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("This is no longer used and will be removed in future versions")] - public interface ILink - { - string Application { get; } - - string ApplicationUrl { get; } - - string Language { get; } - - string UserType { get; } - - string HelpUrl { get; } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs deleted file mode 100644 index 7dbb44ba25..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepositoriesSection.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - - public interface IRepositoriesSection : IUmbracoConfigurationSection - { - IEnumerable Repositories { get; } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs deleted file mode 100644 index cff1d40145..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IRepository - { - string Name { get; } - Guid Id { get; } - string RepositoryUrl { get; } - string WebServiceUrl { get; } - bool HasCustomWebServiceUrl { get; } - string RestApiUrl { get; } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs deleted file mode 100644 index 26b1db056e..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IServer.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public interface IServer - { - string ForcePortnumber { get; } - string ForceProtocol { get; } - string ServerAddress { get; } - - string AppId { get; } - string ServerName { get; } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs index a08bae87fb..e156b46d6c 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ITemplatesSection.cs @@ -4,13 +4,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { public interface ITemplatesSection : IUmbracoConfigurationSection { - bool UseAspNetMasterPages { get; } - - bool EnableSkinSupport { get; } - RenderingEngine DefaultRenderingEngine { get; } - - [Obsolete("This has no affect and will be removed in future versions")] - bool EnableTemplateFolders { get; } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs index 085a826626..09cc698756 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IUmbracoSettingsSection.cs @@ -14,18 +14,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IRequestHandlerSection RequestHandler { get; } ITemplatesSection Templates { get; } - - IDeveloperSection Developer { get; } - - + ILoggingSection Logging { get; } IScheduledTasksSection ScheduledTasks { get; } - IDistributedCallSection DistributedCall { get; } - - IRepositoriesSection PackageRepositories { get; } - IProvidersSection Providers { get; } IWebRoutingSection WebRouting { get; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs deleted file mode 100644 index 1a228d33f2..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesCollection.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class RepositoriesCollection : ConfigurationElementCollection, IEnumerable - { - internal void Add(RepositoryElement item) - { - BaseAdd(item); - } - - protected override ConfigurationElement CreateNewElement() - { - return new RepositoryElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((RepositoryElement)element).Id; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IRepository; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs deleted file mode 100644 index 0f1063adb0..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoriesElement.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class RepositoriesElement : ConfigurationElement, IRepositoriesSection - { - - [ConfigurationCollection(typeof(RepositoriesCollection), AddItemName = "repository")] - [ConfigurationProperty("", IsDefaultCollection = true)] - internal RepositoriesCollection Repositories - { - get { return (RepositoriesCollection) base[""]; } - set { base[""] = value; } - } - - IEnumerable IRepositoriesSection.Repositories - { - get { return Repositories; } - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs deleted file mode 100644 index d311a4423b..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryConfigExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Linq; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - public static class RepositoryConfigExtensions - { - //Our package repo - private static readonly Guid RepoGuid = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66"); - - public static IRepository GetDefault(this IRepositoriesSection repos) - { - var found = repos.Repositories.FirstOrDefault(x => x.Id == RepoGuid); - if (found == null) - throw new InvalidOperationException("No default package repository found with id " + RepoGuid); - return found; - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs deleted file mode 100644 index ae583ca7c3..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RepositoryElement.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class RepositoryElement : ConfigurationElement, IRepository - { - [ConfigurationProperty("name", IsRequired = true)] - public string Name - { - get { return (string)base["name"]; } - set { base["name"] = value; } - } - - [ConfigurationProperty("guid", IsRequired = true)] - public Guid Id - { - get { return (Guid)base["guid"]; } - set { base["guid"] = value; } - } - - [ConfigurationProperty("repositoryurl", DefaultValue = "http://packages.umbraco.org")] - public string RepositoryUrl - { - get { return (string)base["repositoryurl"]; } - set { base["repositoryurl"] = value; } - } - - [ConfigurationProperty("webserviceurl", DefaultValue = "/umbraco/webservices/api/repository.asmx")] - public string WebServiceUrl - { - get { return (string)base["webserviceurl"]; } - set { base["webserviceurl"] = value; } - } - - public bool HasCustomWebServiceUrl - { - get - { - var prop = Properties["webserviceurl"]; - return (string) prop.DefaultValue != (string) this[prop]; - } - } - - [ConfigurationProperty("restapiurl", DefaultValue = "https://our.umbraco.org/webapi/packages/v1")] - public string RestApiUrl - { - get { return (string)base["restapiurl"]; } - set { base["restapiurl"] = value; } - } - - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs index 8ae5816569..9ce88a8f75 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("authCookieName")] internal InnerTextConfigurationElement AuthCookieName { - get { return GetOptionalTextElement("authCookieName", Constants.Web.AuthCookieName); } + get { return GetOptionalTextElement("authCookieName", "UMB_UCONTEXT"); } } [ConfigurationProperty("authCookieDomain")] diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ServerCollection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ServerCollection.cs deleted file mode 100644 index dfaa5c5247..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ServerCollection.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using System.Configuration; - -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class ServerCollection : ConfigurationElementCollection, IEnumerable - { - protected override ConfigurationElement CreateNewElement() - { - return new ServerElement(); - } - - protected override object GetElementKey(ConfigurationElement element) - { - return ((ServerElement)element).Value; - } - - IEnumerator IEnumerable.GetEnumerator() - { - for (var i = 0; i < Count; i++) - { - yield return BaseGet(i) as IServer; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ServerElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ServerElement.cs deleted file mode 100644 index 30c836bc07..0000000000 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ServerElement.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Umbraco.Core.Configuration.UmbracoSettings -{ - internal class ServerElement : InnerTextConfigurationElement, IServer - { - public string ForcePortnumber - { - get - { - return RawXml.Attribute("forcePortnumber") == null - ? null - : RawXml.Attribute("forcePortnumber").Value; - } - } - - public string ForceProtocol - { - get - { - return RawXml.Attribute("forceProtocol") == null - ? null - : RawXml.Attribute("forceProtocol").Value; - } - } - - string IServer.ServerAddress - { - get { return Value; } - } - - public string AppId - { - get - { - return RawXml.Attribute("appId") == null - ? null - : RawXml.Attribute("appId").Value; - } - } - public string ServerName - { - get - { - return RawXml.Attribute("serverName") == null - ? null - : RawXml.Attribute("serverName").Value; - } - } - } -} diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs index 9eb843313c..d4d0f230ce 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs @@ -5,50 +5,16 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { internal class TemplatesElement : UmbracoConfigurationElement, ITemplatesSection { - [ConfigurationProperty("useAspNetMasterPages")] - internal InnerTextConfigurationElement UseAspNetMasterPages - { - get { return GetOptionalTextElement("useAspNetMasterPages", true); } - } - - [ConfigurationProperty("enableSkinSupport")] - internal InnerTextConfigurationElement EnableSkinSupport - { - get { return GetOptionalTextElement("enableSkinSupport", true); } - } - [ConfigurationProperty("defaultRenderingEngine", IsRequired = true)] internal InnerTextConfigurationElement DefaultRenderingEngine { get { return GetOptionalTextElement("defaultRenderingEngine", RenderingEngine.Mvc); } } - - [Obsolete("This has no affect and will be removed in future versions")] - [ConfigurationProperty("enableTemplateFolders")] - internal InnerTextConfigurationElement EnableTemplateFolders - { - get { return GetOptionalTextElement("enableTemplateFolders", false); } - } - - bool ITemplatesSection.UseAspNetMasterPages - { - get { return UseAspNetMasterPages; } - } - - bool ITemplatesSection.EnableSkinSupport - { - get { return EnableSkinSupport; } - } - + RenderingEngine ITemplatesSection.DefaultRenderingEngine { get { return DefaultRenderingEngine; } } - [Obsolete("This has no affect and will be removed in future versions")] - bool ITemplatesSection.EnableTemplateFolders - { - get { return EnableTemplateFolders; } - } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs index 0cf97b2560..7a08ec3b18 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoSettingsSection.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings internal BackOfficeElement BackOffice { get { return (BackOfficeElement)this["backOffice"]; } - } + } [ConfigurationProperty("content")] internal ContentElement Content @@ -37,14 +37,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { return (TemplatesElement)this["templates"]; } } - - [ConfigurationProperty("developer")] - internal DeveloperElement Developer - { - get { return (DeveloperElement)this["developer"]; } - } - - + [ConfigurationProperty("logging")] internal LoggingElement Logging { @@ -57,55 +50,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return (ScheduledTasksElement)this["scheduledTasks"]; } } - [ConfigurationProperty("distributedCall")] - internal DistributedCallElement DistributedCall - { - get { return (DistributedCallElement)this["distributedCall"]; } - } - - private RepositoriesElement _defaultRepositories; - - [ConfigurationProperty("repositories")] - internal RepositoriesElement PackageRepositories - { - get - { - - if (_defaultRepositories != null) - { - return _defaultRepositories; - } - - //here we need to check if this element is defined, if it is not then we'll setup the defaults - var prop = Properties["repositories"]; - var repos = this[prop] as ConfigurationElement; - if (repos != null && repos.ElementInformation.IsPresent == false) - { - var collection = new RepositoriesCollection - { - new RepositoryElement() {Name = "Umbraco package Repository", Id = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66")} - }; - - - _defaultRepositories = new RepositoriesElement() - { - Repositories = collection - }; - - return _defaultRepositories; - } - - //now we need to ensure there is *always* our umbraco repo! its hard coded in the codebase! - var reposElement = (RepositoriesElement)base["repositories"]; - if (reposElement.Repositories.All(x => x.Id != new Guid("65194810-1f85-11dd-bd0b-0800200c9a66"))) - { - reposElement.Repositories.Add(new RepositoryElement() { Name = "Umbraco package Repository", Id = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66") }); - } - - return reposElement; - } - } - [ConfigurationProperty("providers")] internal ProvidersElement Providers { @@ -141,11 +85,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings IBackOfficeSection IUmbracoSettingsSection.BackOffice { get { return BackOffice; } - } - - IDeveloperSection IUmbracoSettingsSection.Developer - { - get { return Developer; } } ILoggingSection IUmbracoSettingsSection.Logging @@ -157,17 +96,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { return ScheduledTasks; } } - - IDistributedCallSection IUmbracoSettingsSection.DistributedCall - { - get { return DistributedCall; } - } - - IRepositoriesSection IUmbracoSettingsSection.PackageRepositories - { - get { return PackageRepositories; } - } - + IProvidersSection IUmbracoSettingsSection.Providers { get { return Providers; } diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index 4c859469fd..7d8476989a 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -121,9 +121,7 @@ public const string Templates = "templates"; public const string RelationTypes = "relationTypes"; - - public const string Xslt = "xslt"; - + public const string Languages = "languages"; /// diff --git a/src/Umbraco.Core/Constants-Composing.cs b/src/Umbraco.Core/Constants-Composing.cs index 734a715c2d..1e8c9886d2 100644 --- a/src/Umbraco.Core/Constants-Composing.cs +++ b/src/Umbraco.Core/Constants-Composing.cs @@ -21,7 +21,6 @@ public const string StylesheetFileSystem = "StylesheetFileSystem"; public const string MasterpageFileSystem = "MasterpageFileSystem"; public const string ViewFileSystem = "ViewFileSystem"; - public const string XsltFileSystem = "XsltFileSystem"; public const string JavascriptLibraryFileSystem = "JavascriptLibraryFileSystem"; } } diff --git a/src/Umbraco.Core/Constants-PackageRepository.cs b/src/Umbraco.Core/Constants-PackageRepository.cs new file mode 100644 index 0000000000..bdcb86932b --- /dev/null +++ b/src/Umbraco.Core/Constants-PackageRepository.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Defines the constants used for the Umbraco package repository + /// + public static class PackageRepository + { + public const string RestApiBaseUrl = "https://our.umbraco.org/webapi/packages/v1"; + public const string DefaultRepositoryName = "Umbraco package Repository"; + public const string DefaultRepositoryId = "65194810-1f85-11dd-bd0b-0800200c9a66"; + } + } +} diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index a5f140ddba..0da5431e91 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -23,13 +23,6 @@ namespace Umbraco.Core public const string InstallerCookieName = "umb_installId"; - /// - /// The auth cookie name - /// - [Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - public const string AuthCookieName = "UMB_UCONTEXT"; - } } } diff --git a/src/Umbraco.Core/EnumExtensions.cs b/src/Umbraco.Core/EnumExtensions.cs new file mode 100644 index 0000000000..58e14bcadf --- /dev/null +++ b/src/Umbraco.Core/EnumExtensions.cs @@ -0,0 +1,22 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods for various enumerations. + /// + public static class EnumExtensions + { + /// + /// Determines whether a variation has all flags set. + /// + public static bool Has(this ContentVariation variation, ContentVariation values) + => (variation & values) == values; + + /// + /// Determines whether a variation has at least a flag set. + /// + public static bool HasAny(this ContentVariation variation, ContentVariation values) + => (variation & values) != ContentVariation.Unknown; + } +} diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index d5d4a66122..6ae96c9d53 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -92,21 +92,6 @@ namespace Umbraco.Core } } - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")] - public static IEnumerable ForEach(this IEnumerable items, Action action) - { - if (items != null) - { - foreach (TItem item in items) - { - action(item); - } - } - - return items; - } - /// The flatten list. /// The items. /// The select child. diff --git a/src/Umbraco.Core/Events/MacroErrorEventArgs.cs b/src/Umbraco.Core/Events/MacroErrorEventArgs.cs index bdda23ba3a..75312508a7 100644 --- a/src/Umbraco.Core/Events/MacroErrorEventArgs.cs +++ b/src/Umbraco.Core/Events/MacroErrorEventArgs.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Events /// /// Filename, file path, fully qualified class name, or other key used by the macro engine to do it's processing of the faulting macro. /// - public string ItemKey { get; set; } + public string MacroSource { get; set; } /// /// Exception raised. diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 39e7fa30fc..5d7088b0e1 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -26,10 +26,8 @@ namespace Umbraco.Core.IO private ShadowWrapper _partialViewsFileSystem; private ShadowWrapper _stylesheetsFileSystem; private ShadowWrapper _scriptsFileSystem; - private ShadowWrapper _xsltFileSystem; private ShadowWrapper _masterPagesFileSystem; private ShadowWrapper _mvcViewsFileSystem; - private ShadowWrapper _javaScriptLibraryFileSystem; // well-known file systems lazy initialization private object _wkfsLock = new object(); @@ -103,16 +101,7 @@ namespace Umbraco.Core.IO return _scriptsFileSystem; } } - - public IFileSystem XsltFileSystem - { - get - { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - return _xsltFileSystem; - } - } - + public IFileSystem MasterPagesFileSystem { get @@ -163,7 +152,6 @@ namespace Umbraco.Core.IO var partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews); var stylesheetsFileSystem = new PhysicalFileSystem(SystemDirectories.Css); var scriptsFileSystem = new PhysicalFileSystem(SystemDirectories.Scripts); - var xsltFileSystem = new PhysicalFileSystem(SystemDirectories.Xslt); var masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages); var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews); var javaScriptLibraryFileSystem = new PhysicalFileSystem(SystemDirectories.JavaScriptLibrary); @@ -172,7 +160,6 @@ namespace Umbraco.Core.IO _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", () => IsScoped()); _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", () => IsScoped()); _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", () => IsScoped()); - _xsltFileSystem = new ShadowWrapper(xsltFileSystem, "xslt", () => IsScoped()); _masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", () => IsScoped()); _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", () => IsScoped()); _javascriptLibraryFileSystem = new ShadowWrapper(javaScriptLibraryFileSystem, "Lib", () => IsScoped()); @@ -366,14 +353,13 @@ namespace Umbraco.Core.IO if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); var typed = _wrappers.ToArray(); - var wrappers = new ShadowWrapper[typed.Length + 7]; + var wrappers = new ShadowWrapper[typed.Length + 6]; var i = 0; while (i < typed.Length) wrappers[i] = typed[i++]; wrappers[i++] = _macroPartialFileSystem; wrappers[i++] = _partialViewsFileSystem; wrappers[i++] = _stylesheetsFileSystem; wrappers[i++] = _scriptsFileSystem; - wrappers[i++] = _xsltFileSystem; wrappers[i++] = _masterPagesFileSystem; wrappers[i] = _mvcViewsFileSystem; diff --git a/src/Umbraco.Core/IO/MasterPageHelper.cs b/src/Umbraco.Core/IO/MasterPageHelper.cs index e619ec98d8..049db04b9a 100644 --- a/src/Umbraco.Core/IO/MasterPageHelper.cs +++ b/src/Umbraco.Core/IO/MasterPageHelper.cs @@ -27,13 +27,7 @@ namespace Umbraco.Core.IO { return _masterPageFileSystem.FileExists(GetFilePath(t)); } - - [Obsolete("This is only used for legacy purposes and will be removed in future versions")] - internal string GetPhysicalFilePath(ITemplate t) - { - return _masterPageFileSystem.GetFullPath(GetFilePath(t.Alias)); - } - + private string GetFilePath(ITemplate t) { return GetFilePath(t.Alias); diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index b96b0d54bc..5534936a04 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -335,35 +335,35 @@ namespace Umbraco.Core.IO // fixme - what's below belongs to the upload property editor, not the media filesystem! - public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream, int? languageId = null, string segment = null) + public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { var property = GetProperty(content, propertyTypeAlias); - var oldpath = property.GetValue(languageId, segment) is string svalue ? GetRelativePath(svalue) : null; + var oldpath = property.GetValue(culture, segment) is string svalue ? GetRelativePath(svalue) : null; var filepath = StoreFile(content, property.PropertyType, filename, filestream, oldpath); - property.SetValue(GetUrl(filepath), languageId, segment); - SetUploadFile(content, property, filepath, filestream, languageId, segment); + property.SetValue(GetUrl(filepath), culture, segment); + SetUploadFile(content, property, filepath, filestream, culture, segment); } - public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath, int? languageId = null, string segment = null) + public void SetUploadFile(IContentBase content, string propertyTypeAlias, string filepath, string culture = null, string segment = null) { var property = GetProperty(content, propertyTypeAlias); // fixme delete? - var oldpath = property.GetValue(languageId, segment) is string svalue ? GetRelativePath(svalue) : null; + var oldpath = property.GetValue(culture, segment) is string svalue ? GetRelativePath(svalue) : null; if (string.IsNullOrWhiteSpace(oldpath) == false && oldpath != filepath) DeleteFile(oldpath); - property.SetValue(GetUrl(filepath), languageId, segment); + property.SetValue(GetUrl(filepath), culture, segment); using (var filestream = OpenFile(filepath)) { - SetUploadFile(content, property, filepath, filestream, languageId, segment); + SetUploadFile(content, property, filepath, filestream, culture, segment); } } // sets a file for the FileUpload property editor // ie generates thumbnails and populates autofill properties - private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream, int? languageId = null, string segment = null) + private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream, string culture = null, string segment = null) { // will use filepath for extension, and filestream for length - UploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, languageId, segment); + UploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, culture, segment); } #endregion diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs index 7d3bfd3ae0..37eb1a09b9 100644 --- a/src/Umbraco.Core/IO/SystemDirectories.cs +++ b/src/Umbraco.Core/IO/SystemDirectories.cs @@ -46,8 +46,6 @@ namespace Umbraco.Core.IO public static string WebServices => IOHelper.ReturnPath("umbracoWebservicesPath", Umbraco.EnsureEndsWith("/") + "webservices"); - public static string Xslt => IOHelper.ReturnPath("umbracoXsltPath", "~/xslt"); - //by default the packages folder should exist in the data folder public static string Packages => IOHelper.ReturnPath("umbracoPackagesPath", Data + IOHelper.DirSepChar + "packages"); diff --git a/src/Umbraco.Core/IO/ViewHelper.cs b/src/Umbraco.Core/IO/ViewHelper.cs index 1645355b72..63f7504f18 100644 --- a/src/Umbraco.Core/IO/ViewHelper.cs +++ b/src/Umbraco.Core/IO/ViewHelper.cs @@ -67,10 +67,9 @@ namespace Umbraco.Core.IO modelNamespaceAlias = "ContentModels"; // either - // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage - // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage - // @inherits Umbraco.Web.Mvc.UmbracoTemplatePage - content.Append("@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"); + // @inherits Umbraco.Web.Mvc.UmbracoViewPage + // @inherits Umbraco.Web.Mvc.UmbracoViewPage + content.Append("@inherits Umbraco.Web.Mvc.UmbracoViewPage"); if (modelClassName.IsNullOrWhiteSpace() == false) { content.Append("<"); diff --git a/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs b/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs deleted file mode 100644 index e8dad743e1..0000000000 --- a/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs +++ /dev/null @@ -1,21 +0,0 @@ -// fixme - remove this file -//using log4net.Core; -//using log4net.Util; -//using System; -//using System.Collections.Concurrent; -//using System.Threading; -//using System.Threading.Tasks; - -//namespace Umbraco.Core.Logging -//{ -// /// -// /// An asynchronous appender based on -// /// -// /// -// /// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8 -// /// -// [Obsolete("Use the Log4Net.Async.ParallelForwardingAppender instead this will be removed in future versions")] -// public class ParallelForwardingAppender : Log4Net.Async.ParallelForwardingAppender -// { -// } -//} diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs index 26f2b34ae5..857d36d2da 100644 --- a/src/Umbraco.Core/Macros/MacroTagParser.cs +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -179,7 +179,7 @@ namespace Umbraco.Core.Macros // Check whether it's a single tag () or a tag with children (...) if (tag.Substring(tag.Length - 2, 1) != "/" && tag.IndexOf(" ") > -1) { - String closingTag = ""; + string closingTag = ""; // Tag with children are only used when a macro is inserted by the umbraco-editor, in the // following format: "", so we // need to delete extra information inserted which is the image-tag and the closing diff --git a/src/Umbraco.Core/Macros/XsltExtension.cs b/src/Umbraco.Core/Macros/XsltExtension.cs deleted file mode 100644 index 7bc9277ac3..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtension.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Umbraco.Core.Macros -{ - /// - /// Encapsulates what an xslt extension object is when used for macros - /// - internal sealed class XsltExtension - { - public XsltExtension(string ns, object extensionObject) - { - Namespace = ns; - ExtensionObject = extensionObject; - } - - public string Namespace { get; private set; } - public object ExtensionObject { get; private set; } - } -} diff --git a/src/Umbraco.Core/Macros/XsltExtensionAttribute.cs b/src/Umbraco.Core/Macros/XsltExtensionAttribute.cs deleted file mode 100644 index 8cde062046..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtensionAttribute.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Security.Permissions; -using System.Web; - -namespace Umbraco.Core.Macros -{ - /// - /// Allows App_Code XSLT extensions to be declared using the [XsltExtension] class attribute. - /// - /// - /// An optional XML namespace can be specified using [XsltExtension("MyNamespace")]. - /// - [AttributeUsage(AttributeTargets.Class)] - public class XsltExtensionAttribute : Attribute - { - public XsltExtensionAttribute() - { - Namespace = String.Empty; - } - - public XsltExtensionAttribute(string ns) - { - Namespace = ns; - } - - public string Namespace { get; set; } - - public override string ToString() - { - return Namespace; - } - } -} diff --git a/src/Umbraco.Core/Macros/XsltExtensionCollection.cs b/src/Umbraco.Core/Macros/XsltExtensionCollection.cs deleted file mode 100644 index 4d32382d06..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtensionCollection.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Macros -{ - internal class XsltExtensionCollection : BuilderCollectionBase - { - public XsltExtensionCollection(IEnumerable items) - : base(items) - { } - } -} diff --git a/src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs b/src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs deleted file mode 100644 index 45a7615e3d..0000000000 --- a/src/Umbraco.Core/Macros/XsltExtensionCollectionBuilder.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using LightInject; -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Macros -{ - // that one is special since it's not initialized with XsltExtension types, but with Xslt extension object types, - // which are then wrapped in an XsltExtension object when the collection is created. so, cannot really inherit - // from (Lazy)CollectionBuilderBase and have to re-implement it. but almost everything is copied from CollectionBuilderBase. - - internal class XsltExtensionCollectionBuilder : ICollectionBuilder - { - private readonly IServiceContainer _container; - private readonly List>> _producers = new List>>(); - private readonly object _locker = new object(); - private ServiceRegistration[] _registrations; - - public XsltExtensionCollectionBuilder(IServiceContainer container) - { - _container = container; - - // register the collection - container.Register(_ => CreateCollection(), new PerContainerLifetime()); - } - - public static XsltExtensionCollectionBuilder Register(IServiceContainer container) - { - // register the builder - per container - var builderLifetime = new PerContainerLifetime(); - container.Register(builderLifetime); - return container.GetInstance(); - } - - public XsltExtensionCollectionBuilder AddExtensionObjectProducer(Func> producer) - { - lock (_locker) - { - if (_registrations != null) - throw new InvalidOperationException("Cannot configure a collection builder after its types have been resolved."); - _producers.Add(producer); - } - return this; - } - - private void RegisterTypes() - { - lock (_locker) - { - if (_registrations != null) return; - - var prefix = GetType().FullName + "_"; - var i = 0; - foreach (var type in _producers.SelectMany(x => x()).Distinct()) - { - var name = $"{prefix}{i++:00000}"; - _container.Register(type, type, name); - } - - _registrations = _container.AvailableServices - .Where(x => x.ServiceName.StartsWith(prefix)) - .OrderBy(x => x.ServiceName) - .ToArray(); - } - } - - public XsltExtensionCollection CreateCollection() - { - RegisterTypes(); // will do it only once - - var exts = _registrations.SelectMany(r => r.ServiceType.GetCustomAttributes(true) - .Select(a => new XsltExtension(a.Namespace.IfNullOrWhiteSpace(r.ServiceType.FullName), _container.GetInstance(r.ServiceType, r.ServiceName)))); - - return new XsltExtensionCollection(exts); - } - } -} diff --git a/src/Umbraco.Core/Media/IImageUrlProvider.cs b/src/Umbraco.Core/Media/IImageUrlProvider.cs deleted file mode 100644 index 4a51edd412..0000000000 --- a/src/Umbraco.Core/Media/IImageUrlProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Core.Media -{ - // note: because this interface is obsolete is is *not* IDiscoverable, and in case the - // TypeLoader is asked to find types implementing this interface it will fall back - // to a complete scan. - - [Obsolete("IImageUrlProvider is no longer used and will be removed in future versions")] - public interface IImageUrlProvider // IDiscoverable - { - string Name { get; } - string GetImageUrlFromMedia(int mediaId, IDictionary parameters); - string GetImageUrlFromFileName(string specifiedSrc, IDictionary parameters); - } -} diff --git a/src/Umbraco.Core/Media/IThumbnailProvider.cs b/src/Umbraco.Core/Media/IThumbnailProvider.cs deleted file mode 100644 index 1e5a59bb39..0000000000 --- a/src/Umbraco.Core/Media/IThumbnailProvider.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace Umbraco.Core.Media -{ - // note: because this interface is obsolete is is *not* IDiscoverable, and in case the - // TypeLoader is asked to find types implementing this interface it will fall back - // to a complete scan. - - [Obsolete("Thumbnails are generated by ImageProcessor, use that instead")] - public interface IThumbnailProvider // : IDiscoverable - { - bool CanProvideThumbnail(string fileUrl); - string GetThumbnailUrl(string fileUrl); - } - -} diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index 3fdaf7b3d3..4ab2bf7a7c 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -41,9 +41,9 @@ namespace Umbraco.Core.Media /// /// The content item. /// The property type alias. - /// Variation language. + /// Variation language. /// Variation segment. - public void Reset(IContentBase content, string propertyTypeAlias, int? languageId, string segment) + public void Reset(IContentBase content, string propertyTypeAlias, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias)); @@ -53,7 +53,7 @@ namespace Umbraco.Core.Media if (autoFillConfig == null) return; // nothing // reset - Reset(content, autoFillConfig, languageId, segment); + Reset(content, autoFillConfig, culture, segment); } /// @@ -61,14 +61,14 @@ namespace Umbraco.Core.Media /// /// The content item. /// The auto-fill configuration. - /// Variation language. + /// Variation language. /// Variation segment. - public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, int? languageId, string segment) + public void Reset(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } /// @@ -77,9 +77,9 @@ namespace Umbraco.Core.Media /// The content item. /// The property type alias. /// The filesystem-relative filepath, or null to clear properties. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, string propertyTypeAlias, string filepath, int? languageId, string segment) + public void Populate(IContentBase content, string propertyTypeAlias, string filepath, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias)); @@ -92,7 +92,7 @@ namespace Umbraco.Core.Media if (autoFillConfig == null) return; // nothing // populate - Populate(content, autoFillConfig, filepath, languageId, segment); + Populate(content, autoFillConfig, filepath, culture, segment); } /// @@ -102,9 +102,9 @@ namespace Umbraco.Core.Media /// The property type alias. /// The filesystem-relative filepath, or null to clear properties. /// The stream containing the file data. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, int? languageId, string segment) + public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (propertyTypeAlias == null) throw new ArgumentNullException(nameof(propertyTypeAlias)); @@ -117,7 +117,7 @@ namespace Umbraco.Core.Media if (autoFillConfig == null) return; // nothing // populate - Populate(content, autoFillConfig, filepath, filestream, languageId, segment); + Populate(content, autoFillConfig, filepath, filestream, culture, segment); } /// @@ -127,9 +127,9 @@ namespace Umbraco.Core.Media /// The auto-fill configuration. /// The filesystem path to the uploaded file. /// The parameter is the path relative to the filesystem. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, int? languageId, string segment) + public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); @@ -137,7 +137,7 @@ namespace Umbraco.Core.Media // no file = reset, file = auto-fill if (filepath.IsNullOrWhiteSpace()) { - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } else { @@ -148,13 +148,13 @@ namespace Umbraco.Core.Media { var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.'); var size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null; - SetProperties(content, autoFillConfig, size, filestream.Length, extension, languageId, segment); + SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); } } catch (Exception ex) { _logger.Error(typeof(UploadAutoFillProperties), $"Could not populate upload auto-fill properties for file \"{filepath}\".", ex); - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } } } @@ -166,9 +166,9 @@ namespace Umbraco.Core.Media /// /// The filesystem-relative filepath, or null to clear properties. /// The stream containing the file data. - /// Variation language. + /// Variation language. /// Variation segment. - public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, int? languageId, string segment) + public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); @@ -176,50 +176,50 @@ namespace Umbraco.Core.Media // no file = reset, file = auto-fill if (filepath.IsNullOrWhiteSpace() || filestream == null) { - ResetProperties(content, autoFillConfig, languageId, segment); + ResetProperties(content, autoFillConfig, culture, segment); } else { var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.'); var size = _mediaFileSystem.IsImageFile(extension) ? (Size?)_mediaFileSystem.GetDimensions(filestream) : null; - SetProperties(content, autoFillConfig, size, filestream.Length, extension, languageId, segment); + SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); } } - private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, int? languageId, string segment) + private static void SetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, Size? size, long length, string extension, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); if (content.Properties.Contains(autoFillConfig.WidthFieldAlias)) - content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, languageId, segment); + content.Properties[autoFillConfig.WidthFieldAlias].SetValue(size.HasValue ? size.Value.Width.ToInvariantString() : string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.HeightFieldAlias)) - content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, languageId, segment); + content.Properties[autoFillConfig.HeightFieldAlias].SetValue(size.HasValue ? size.Value.Height.ToInvariantString() : string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.LengthFieldAlias)) - content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, languageId, segment); + content.Properties[autoFillConfig.LengthFieldAlias].SetValue(length, culture, segment); if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) - content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, languageId, segment); + content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(extension, culture, segment); } - private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, int? languageId, string segment) + private static void ResetProperties(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string culture, string segment) { if (content == null) throw new ArgumentNullException(nameof(content)); if (autoFillConfig == null) throw new ArgumentNullException(nameof(autoFillConfig)); if (content.Properties.Contains(autoFillConfig.WidthFieldAlias)) - content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.WidthFieldAlias].SetValue(string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.HeightFieldAlias)) - content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.HeightFieldAlias].SetValue(string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.LengthFieldAlias)) - content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.LengthFieldAlias].SetValue(string.Empty, culture, segment); if (content.Properties.Contains(autoFillConfig.ExtensionFieldAlias)) - content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, languageId, segment); + content.Properties[autoFillConfig.ExtensionFieldAlias].SetValue(string.Empty, culture, segment); } } } diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index 6ce300845c..7d4058a0e4 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -82,7 +82,8 @@ namespace Umbraco.Core.Migrations.Install typeof (UserLoginDto), typeof (ConsentDto), typeof (AuditEntryDto), - typeof (ContentVersionCultureVariationDto) + typeof (ContentVersionCultureVariationDto), + typeof (DocumentCultureVariationDto) }; /// diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index 8bec74173e..565d5be138 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -116,10 +116,12 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{9DF05B77-11D1-475C-A00A-B656AF7E0908}"); Chain("{6FE3EF34-44A0-4992-B379-B40BC4EF1C4D}"); Chain("{7F59355A-0EC9-4438-8157-EB517E6D2727}"); - Chain("{66B6821A-0DE3-4DF8-A6A4-65ABD211EDDE}"); + Chain("{66B6821A-0DE3-4DF8-A6A4-65ABD211EDDE}"); + Chain("{49506BAE-CEBB-4431-A1A6-24AD6EBBBC57}"); + Chain("{083A9894-903D-41B7-B6B3-9EAF2D4CCED0}"); // must chain to v8 final state (see at end of file) - Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + Chain("{A7540C58-171D-462A-91C5-7A9AA5CB8BFD}"); // UPGRADE FROM 7, MORE RECENT @@ -201,12 +203,19 @@ namespace Umbraco.Core.Migrations.Upgrade Chain("{79591E91-01EA-43F7-AC58-7BD286DB1E77}"); // 8.0.0 - Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + // AddVariationTables1 has been superceeded by AddVariationTables2 + //Chain("{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + Chain("{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); + + // however, need to take care of ppl in post-AddVariationTables1 state + Add("{941B2ABA-2D06-4E04-81F5-74224F1DB037}", "{76DF5CD7-A884-41A5-8DC6-7860D95B1DF5}"); + + Chain("{A7540C58-171D-462A-91C5-7A9AA5CB8BFD}"); // FINAL STATE - MUST MATCH LAST ONE ABOVE ! // whenever this changes, update all references in this file! - Add(string.Empty, "{941B2ABA-2D06-4E04-81F5-74224F1DB037}"); + Add(string.Empty, "{A7540C58-171D-462A-91C5-7A9AA5CB8BFD}"); } } } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs new file mode 100644 index 0000000000..96e82d281d --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables1A.cs @@ -0,0 +1,47 @@ +using System; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class AddVariationTables1A : MigrationBase + { + public AddVariationTables1A(IMigrationContext context) + : base(context) + { } + + // note - original AddVariationTables1 just did + // Create.Table().Do(); + // + // this is taking care of ppl left in this state + + public override void Migrate() + { + // note - original AddVariationTables1 just did + // Create.Table().Do(); + // + // it's been deprecated, not part of the main upgrade path, + // but we need to take care of ppl caught into the state + + // was not used + Delete.Column("available").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); + + // was not used + Delete.Column("availableDate").FromTable(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation).Do(); + + //special trick to add the column without constraints and return the sql to add them later + AddColumn("date", out var sqls); + //now we need to update the new column with some values because this column doesn't allow NULL values + Update.Table(ContentVersionCultureVariationDto.TableName).Set(new {date = DateTime.Now}).AllRows().Do(); + //now apply constraints (NOT NULL) to new table + foreach (var sql in sqls) Execute.Sql(sql).Do(); + + // name, languageId are now non-nullable + AlterColumn(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "name"); + AlterColumn(Constants.DatabaseSchema.Tables.ContentVersionCultureVariation, "languageId"); + + Create.Table().Do(); + + // fixme - data migration? + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs similarity index 54% rename from src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs rename to src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs index efd0f33e99..5b6c913195 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentVariationTable.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddVariationTables2.cs @@ -1,16 +1,18 @@ -using Umbraco.Core.Persistence.Dtos; +using System; +using Umbraco.Core.Persistence.Dtos; namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 { - public class AddContentVariationTable : MigrationBase + public class AddVariationTables2 : MigrationBase { - public AddContentVariationTable(IMigrationContext context) + public AddVariationTables2(IMigrationContext context) : base(context) { } public override void Migrate() { Create.Table().Do(); + Create.Table().Do(); // fixme - data migration? } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs new file mode 100644 index 0000000000..623a842525 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorMacroColumns.cs @@ -0,0 +1,43 @@ +using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class RefactorMacroColumns : MigrationBase + { + public RefactorMacroColumns(IMigrationContext context) + : base(context) + { } + + public override void Migrate() + { + if (ColumnExists(Constants.DatabaseSchema.Tables.Macro, "macroXSLT")) + { + //special trick to add the column without constraints and return the sql to add them later + AddColumn("macroType", out var sqls1); + //now we need to update the new column with some values because this column doesn't allow NULL values + Update.Table(Constants.DatabaseSchema.Tables.Macro).Set(new { macroType = (int)MacroTypes.Unknown}).AllRows().Do(); + //now apply constraints (NOT NULL) to new table + foreach (var sql in sqls1) Execute.Sql(sql).Do(); + + //special trick to add the column without constraints and return the sql to add them later + AddColumn("macroSource", out var sqls2); + + //populate the new macroSource column with legacy data + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroXSLT, macroType = {(int)MacroTypes.Unknown} WHERE macroXSLT IS NOT NULL").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptAssembly, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptAssembly IS NOT NULL").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptType, macroType = {(int)MacroTypes.UserControl} WHERE macroScriptType IS NOT NULL").Do(); + Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroPython, macroType = {(int)MacroTypes.PartialView} WHERE macroPython IS NOT NULL").Do(); + + //now apply constraints (NOT NULL) to new table + foreach (var sql in sqls2) Execute.Sql(sql).Do(); + + //now remove these old columns + Delete.Column("macroXSLT").FromTable(Constants.DatabaseSchema.Tables.Macro).Do(); + Delete.Column("macroScriptAssembly").FromTable(Constants.DatabaseSchema.Tables.Macro).Do(); + Delete.Column("macroScriptType").FromTable(Constants.DatabaseSchema.Tables.Macro).Do(); + Delete.Column("macroPython").FromTable(Constants.DatabaseSchema.Tables.Macro).Do(); + } + } + } +} diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs index 8b85d6992e..b7d869ee62 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/RefactorXmlColumns.cs @@ -67,13 +67,6 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 .Do(); } - private bool ColumnExists(string tableName, string columnName) - { - // that's ok even on MySql - var columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray(); - return columns.Any(x => x.TableName.InvariantEquals(tableName) && x.ColumnName.InvariantEquals(columnName)); - } - private void RemoveDuplicates() { const string sql = @"delete from cmsPreviewXml where versionId in ( diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 142d7540c2..86baccd946 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -20,7 +20,8 @@ namespace Umbraco.Core.Models private PublishedState _publishedState; private DateTime? _releaseDate; private DateTime? _expireDate; - private Dictionary _publishNames; + private Dictionary _publishInfos; + private HashSet _edited; private static readonly Lazy Ps = new Lazy(); @@ -30,8 +31,8 @@ namespace Umbraco.Core.Models /// Name of the content /// Parent object /// ContentType for the current Content object - public Content(string name, IContent parent, IContentType contentType) - : this(name, parent, contentType, new PropertyCollection()) + public Content(string name, IContent parent, IContentType contentType, string culture = null) + : this(name, parent, contentType, new PropertyCollection(), culture) { } /// @@ -41,8 +42,8 @@ namespace Umbraco.Core.Models /// Parent object /// ContentType for the current Content object /// Collection of properties - public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties) - : base(name, parent, contentType, properties) + public Content(string name, IContent parent, IContentType contentType, PropertyCollection properties, string culture = null) + : base(name, parent, contentType, properties, culture) { _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); _publishedState = PublishedState.Unpublished; @@ -55,8 +56,8 @@ namespace Umbraco.Core.Models /// Name of the content /// Id of the Parent content /// ContentType for the current Content object - public Content(string name, int parentId, IContentType contentType) - : this(name, parentId, contentType, new PropertyCollection()) + public Content(string name, int parentId, IContentType contentType, string culture = null) + : this(name, parentId, contentType, new PropertyCollection(), culture) { } /// @@ -66,8 +67,8 @@ namespace Umbraco.Core.Models /// Id of the Parent content /// ContentType for the current Content object /// Collection of properties - public Content(string name, int parentId, IContentType contentType, PropertyCollection properties) - : base(name, parentId, contentType, properties) + public Content(string name, int parentId, IContentType contentType, PropertyCollection properties, string culture = null) + : base(name, parentId, contentType, properties, culture) { _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); _publishedState = PublishedState.Unpublished; @@ -195,73 +196,116 @@ namespace Umbraco.Core.Models [IgnoreDataMember] public ITemplate PublishTemplate { get; internal set; } - + [IgnoreDataMember] public string PublishName { get; internal set; } - /// - [IgnoreDataMember] - public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; - - /// - public string GetPublishName(int? languageId) - { - if (languageId == null) return PublishName; - if (_publishNames == null) return null; - return _publishNames.TryGetValue(languageId.Value, out var name) ? name : null; - } - - // sets a publish name + // sets publish infos // internal for repositories - internal void SetPublishName(int? languageId, string name) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullOrEmptyException(nameof(name)); - - if (languageId == null) + // clear by clearing name + internal void SetPublishInfos(string culture, string name, DateTime date) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullOrEmptyException(nameof(name)); + + // this is the only place where we set PublishName (apart from factories etc), and we must ensure + // that we do have an invariant name, as soon as we have a variant name, else we would end up not + // being able to publish - and not being able to change the name, as PublishName is readonly. + // see also: DocumentRepository.EnsureInvariantNameValues() - which deals with Name. + // see also: U4-11286 + if (culture == null || string.IsNullOrEmpty(PublishName)) { PublishName = name; - return; - } + PublishDate = date; + } - // private method, assume that culture is valid - - if (_publishNames == null) - _publishNames = new Dictionary(); - - _publishNames[languageId.Value] = name; - } + if (culture != null) + { + // private method, assume that culture is valid - // clears a publish name - private void ClearPublishName(int? languageId) + if (_publishInfos == null) + _publishInfos = new Dictionary(StringComparer.OrdinalIgnoreCase); + + _publishInfos[culture] = (name, date); + } + } + + /// + [IgnoreDataMember] + //public IReadOnlyDictionary PublishNames => _publishNames ?? NoNames; + public IReadOnlyDictionary PublishNames => _publishInfos?.ToDictionary(x => x.Key, x => x.Value.Name) ?? NoNames; + + /// + public string GetPublishName(string culture) { - if (languageId == null) + if (culture == null) return PublishName; + if (_publishInfos == null) return null; + return _publishInfos.TryGetValue(culture, out var infos) ? infos.Name : null; + } + + // clears a publish name + private void ClearPublishName(string culture) + { + if (culture == null) { PublishName = null; return; } - if (_publishNames == null) return; - _publishNames.Remove(languageId.Value); - if (_publishNames.Count == 0) - _publishNames = null; + if (_publishInfos == null) return; + _publishInfos.Remove(culture); + if (_publishInfos.Count == 0) + _publishInfos = null; } - + // clears all publish names private void ClearPublishNames() { PublishName = null; - _publishNames = null; + _publishInfos = null; } /// - public bool IsCultureAvailable(int? languageId) - => !string.IsNullOrWhiteSpace(GetName(languageId)); - + public bool IsCulturePublished(string culture) + => !string.IsNullOrWhiteSpace(GetPublishName(culture)); + /// - public bool IsCulturePublished(int? languageId) - => !string.IsNullOrWhiteSpace(GetPublishName(languageId)); - + public DateTime GetDateCulturePublished(string culture) + { + if (_publishInfos != null && _publishInfos.TryGetValue(culture, out var infos)) + return infos.Date; + throw new InvalidOperationException($"Culture \"{culture}\" is not published."); + } + + /// + public IEnumerable PublishedCultures => _publishInfos?.Keys ?? Enumerable.Empty(); + + /// + public bool IsCultureEdited(string culture) + { + return string.IsNullOrWhiteSpace(GetPublishName(culture)) || (_edited != null && _edited.Contains(culture)); + } + + // sets a publish edited + internal void SetCultureEdited(string culture) + { + if (_edited == null) + _edited = new HashSet(StringComparer.OrdinalIgnoreCase); + _edited.Add(culture); + } + + // sets all publish edited + internal void SetCultureEdited(IEnumerable cultures) + { + _edited = new HashSet(cultures, StringComparer.OrdinalIgnoreCase); + } + + /// + public IEnumerable EditedCultures => Names.Keys.Where(IsCultureEdited); + + /// + public IEnumerable AvailableCultures => Names.Keys; + [IgnoreDataMember] public int PublishedVersionId { get; internal set; } @@ -269,62 +313,80 @@ namespace Umbraco.Core.Models public bool Blueprint { get; internal set; } /// - public virtual bool PublishAllValues() + public virtual bool TryPublishAllValues() { // the values we want to publish should be valid if (ValidateAll().Any()) - return false; + return false; //fixme this should return an attempt with error results + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + if (string.IsNullOrWhiteSpace(Name)) + throw new InvalidOperationException($"Cannot publish invariant culture without a name."); + PublishName = Name; + var now = DateTime.Now; + foreach (var (culture, name) in Names) + { + if (string.IsNullOrWhiteSpace(name)) + return false; //fixme this should return an attempt with error results + + SetPublishInfos(culture, name, now); + } // property.PublishAllValues only deals with supported variations (if any) foreach (var property in Properties) - property.PublishAllValues(); - - // Name and PublishName are managed by the repository, but Names and PublishNames - // must be managed here as they depend on the existing / supported variations. - PublishName = Name; - foreach (var (languageId, name) in Names) - SetPublishName(languageId, name); + property.PublishAllValues(); _publishedState = PublishedState.Publishing; return true; } /// - public virtual bool PublishValues(int? languageId = null, string segment = null) - { - // the variation should be supported by the content type - ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); - + public virtual bool TryPublishValues(string culture = null, string segment = null) + { + // the variation should be supported by the content type + ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); + // the values we want to publish should be valid - if (Validate(languageId, segment).Any()) - return false; - - // property.PublishValue throws on invalid variation, so filter them out - foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) - property.PublishValue(languageId, segment); + if (Validate(culture, segment).Any()) + return false; //fixme this should return an attempt with error results // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - SetPublishName(languageId, GetName(languageId)); + if (segment == null) + { + var name = GetName(culture); + if (string.IsNullOrWhiteSpace(name)) + return false; //fixme this should return an attempt with error results + + SetPublishInfos(culture, name, DateTime.Now); + } + + // property.PublishValue throws on invalid variation, so filter them out + foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false))) + property.PublishValue(culture, segment); _publishedState = PublishedState.Publishing; return true; } /// - public virtual bool PublishCultureValues(int? languageId = null) - { + public virtual bool PublishCultureValues(string culture = null) + { // the values we want to publish should be valid - if (ValidateCulture(languageId).Any()) + if (ValidateCulture(culture).Any()) return false; - + + // Name and PublishName are managed by the repository, but Names and PublishNames + // must be managed here as they depend on the existing / supported variations. + var name = GetName(culture); + if (string.IsNullOrWhiteSpace(name)) + throw new InvalidOperationException($"Cannot publish {culture ?? "invariant"} culture without a name."); + SetPublishInfos(culture, name, DateTime.Now); + // property.PublishCultureValues only deals with supported variations (if any) foreach (var property in Properties) - property.PublishCultureValues(languageId); - - // Name and PublishName are managed by the repository, but Names and PublishNames - // must be managed here as they depend on the existing / supported variations. - SetPublishName(languageId, GetName(languageId)); + property.PublishCultureValues(culture); _publishedState = PublishedState.Publishing; return true; @@ -339,42 +401,42 @@ namespace Umbraco.Core.Models // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - ClearPublishNames(); - + ClearPublishNames(); + _publishedState = PublishedState.Publishing; } /// - public virtual void ClearPublishedValues(int? languageId = null, string segment = null) + public virtual void ClearPublishedValues(string culture = null, string segment = null) { // the variation should be supported by the content type - ContentType.ValidateVariation(languageId, segment, throwIfInvalid: true); - + ContentType.ValidateVariation(culture, segment, throwIfInvalid: true); + // property.ClearPublishedValue throws on invalid variation, so filter them out - foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(languageId, segment, throwIfInvalid: false))) - property.ClearPublishedValue(languageId, segment); + foreach (var property in Properties.Where(x => x.PropertyType.ValidateVariation(culture, segment, throwIfInvalid: false))) + property.ClearPublishedValue(culture, segment); // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - ClearPublishName(languageId); + ClearPublishName(culture); _publishedState = PublishedState.Publishing; } /// - public virtual void ClearCulturePublishedValues(int? languageId = null) + public virtual void ClearCulturePublishedValues(string culture = null) { // property.ClearPublishedCultureValues only deals with supported variations (if any) foreach (var property in Properties) - property.ClearPublishedCultureValues(languageId); + property.ClearPublishedCultureValues(culture); // Name and PublishName are managed by the repository, but Names and PublishNames // must be managed here as they depend on the existing / supported variations. - ClearPublishName(languageId); - + ClearPublishName(culture); + _publishedState = PublishedState.Publishing; } - + private bool CopyingFromSelf(IContent other) { // copying from the same Id and VersionPk @@ -397,8 +459,8 @@ namespace Umbraco.Core.Models // clear all existing properties foreach (var property in Properties) foreach (var pvalue in property.Values) - if (property.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + if (property.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) + property.SetValue(null, pvalue.Culture, pvalue.Segment); // copy other properties var otherProperties = other.Properties; @@ -407,22 +469,22 @@ namespace Umbraco.Core.Models var alias = otherProperty.PropertyType.Alias; foreach (var pvalue in otherProperty.Values) { - if (!otherProperty.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) + if (!otherProperty.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) continue; var value = published ? pvalue.PublishedValue : pvalue.EditedValue; - SetValue(alias, value, pvalue.LanguageId, pvalue.Segment); + SetValue(alias, value, pvalue.Culture, pvalue.Segment); } - } - + } + // copy names - ClearNames(); + ClearNames(); foreach (var (languageId, name) in other.Names) SetName(languageId, name); Name = other.Name; } /// - public virtual void CopyValues(IContent other, int? languageId = null, string segment = null) + public virtual void CopyValues(IContent other, string culture = null, string segment = null) { if (other.ContentTypeId != ContentTypeId) throw new InvalidOperationException("Cannot copy values from a different content type."); @@ -437,31 +499,31 @@ namespace Umbraco.Core.Models // clear all existing properties foreach (var property in Properties) { - if (!property.PropertyType.ValidateVariation(languageId, segment, false)) + if (!property.PropertyType.ValidateVariation(culture, segment, false)) continue; foreach (var pvalue in property.Values) - if (pvalue.LanguageId == languageId && pvalue.Segment == segment) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + if (pvalue.Culture.InvariantEquals(culture) && pvalue.Segment.InvariantEquals(segment)) + property.SetValue(null, pvalue.Culture, pvalue.Segment); } // copy other properties var otherProperties = other.Properties; foreach (var otherProperty in otherProperties) { - if (!otherProperty.PropertyType.ValidateVariation(languageId, segment, false)) + if (!otherProperty.PropertyType.ValidateVariation(culture, segment, false)) continue; var alias = otherProperty.PropertyType.Alias; - SetValue(alias, otherProperty.GetValue(languageId, segment, published), languageId, segment); - } + SetValue(alias, otherProperty.GetValue(culture, segment, published), culture, segment); + } - // copy name - SetName(languageId, other.GetName(languageId)); + // copy name + SetName(culture, other.GetName(culture)); } /// - public virtual void CopyCultureValues(IContent other, int? languageId = null) + public virtual void CopyCultureValues(IContent other, string culture = null) { if (other.ContentTypeId != ContentTypeId) throw new InvalidOperationException("Cannot copy values from a different content type."); @@ -473,8 +535,8 @@ namespace Umbraco.Core.Models // clear all existing properties foreach (var property in Properties) foreach (var pvalue in property.Values) - if (pvalue.LanguageId == languageId && property.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) - property.SetValue(null, pvalue.LanguageId, pvalue.Segment); + if (pvalue.Culture.InvariantEquals(culture) && property.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) + property.SetValue(null, pvalue.Culture, pvalue.Segment); // copy other properties var otherProperties = other.Properties; @@ -483,15 +545,15 @@ namespace Umbraco.Core.Models var alias = otherProperty.PropertyType.Alias; foreach (var pvalue in otherProperty.Values) { - if (pvalue.LanguageId != languageId || !otherProperty.PropertyType.ValidateVariation(pvalue.LanguageId, pvalue.Segment, false)) + if (pvalue.Culture != culture || !otherProperty.PropertyType.ValidateVariation(pvalue.Culture, pvalue.Segment, false)) continue; var value = published ? pvalue.PublishedValue : pvalue.EditedValue; - SetValue(alias, value, pvalue.LanguageId, pvalue.Segment); + SetValue(alias, value, pvalue.Culture, pvalue.Segment); } - } - - // copy name - SetName(languageId, other.GetName(languageId)); + } + + // copy name + SetName(culture, other.GetName(culture)); } /// diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index 8203cba985..ff1925c72d 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -18,20 +18,20 @@ namespace Umbraco.Core.Models [DebuggerDisplay("Id: {Id}, Name: {Name}, ContentType: {ContentTypeBase.Alias}")] public abstract class ContentBase : TreeEntityBase, IContentBase { - protected static readonly Dictionary NoNames = new Dictionary(); + protected static readonly Dictionary NoNames = new Dictionary(); private static readonly Lazy Ps = new Lazy(); private int _contentTypeId; protected IContentTypeComposition ContentTypeBase; private int _writerId; private PropertyCollection _properties; - private Dictionary _names; + private Dictionary _names; /// /// Initializes a new instance of the class. /// - protected ContentBase(string name, int parentId, IContentTypeComposition contentType, PropertyCollection properties) - : this(name, contentType, properties) + protected ContentBase(string name, int parentId, IContentTypeComposition contentType, PropertyCollection properties, string culture = null) + : this(name, contentType, properties, culture) { if (parentId == 0) throw new ArgumentOutOfRangeException(nameof(parentId)); ParentId = parentId; @@ -40,22 +40,23 @@ namespace Umbraco.Core.Models /// /// Initializes a new instance of the class. /// - protected ContentBase(string name, IContentBase parent, IContentTypeComposition contentType, PropertyCollection properties) - : this(name, contentType, properties) + protected ContentBase(string name, IContentBase parent, IContentTypeComposition contentType, PropertyCollection properties, string culture = null) + : this(name, contentType, properties, culture) { if (parent == null) throw new ArgumentNullException(nameof(parent)); SetParent(parent); } - private ContentBase(string name, IContentTypeComposition contentType, PropertyCollection properties) + private ContentBase(string name, IContentTypeComposition contentType, PropertyCollection properties, string culture = null) { ContentTypeBase = contentType ?? throw new ArgumentNullException(nameof(contentType)); // initially, all new instances have Id = 0; // no identity VersionId = 0; // no versions + + SetName(culture, name); - Name = name; _contentTypeId = contentType.Id; _properties = properties ?? throw new ArgumentNullException(nameof(properties)); _properties.EnsurePropertyTypes(PropertyTypes); @@ -67,7 +68,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo DefaultContentTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeId); public readonly PropertyInfo PropertyCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); public readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId); - public readonly PropertyInfo NamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.Names); + public readonly PropertyInfo NamesSelector = ExpressionHelper.GetPropertyInfo>(x => x.Names); } protected void PropertiesChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -121,71 +122,6 @@ namespace Umbraco.Core.Models } } - /// - [DataMember] - public virtual IReadOnlyDictionary Names - { - get => _names ?? NoNames; - set - { - foreach (var (languageId, name) in value) - SetName(languageId, name); - } - } - - /// - public virtual void SetName(int? languageId, string name) - { - if (string.IsNullOrWhiteSpace(name)) - { - ClearName(languageId); - return; - } - - if (languageId == null) - { - Name = name; - return; - } - - if ((ContentTypeBase.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) == 0) - throw new NotSupportedException("Content type does not support varying name by culture."); - - if (_names == null) - _names = new Dictionary(); - - _names[languageId.Value] = name; - OnPropertyChanged(Ps.Value.NamesSelector); - } - - private void ClearName(int? languageId) - { - if (languageId == null) - { - Name = null; - return; - } - - if (_names == null) return; - _names.Remove(languageId.Value); - if (_names.Count == 0) - _names = null; - } - - protected virtual void ClearNames() - { - _names = null; - OnPropertyChanged(Ps.Value.NamesSelector); - } - - /// - public virtual string GetName(int? languageId) - { - if (languageId == null) return Name; - if (_names == null) return null; - return _names.TryGetValue(languageId.Value, out var name) ? name : null; - } - /// /// Gets the enumeration of property groups for the entity. /// fixme is a proxy, kill this @@ -199,6 +135,79 @@ namespace Umbraco.Core.Models /// [IgnoreDataMember] public IEnumerable PropertyTypes => ContentTypeBase.CompositionPropertyTypes; + + #region Cultures + + /// + [DataMember] + public virtual IReadOnlyDictionary Names + { + get => _names ?? NoNames; + set + { + foreach (var (culture, name) in value) + SetName(culture, name); + } + } + + /// + public virtual void SetName(string culture, string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + ClearName(culture); + return; + } + + if (culture == null) + { + Name = name; + return; + } + + if (!ContentTypeBase.Variations.HasAny(ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) + throw new NotSupportedException("Content type does not support varying name by culture."); + + if (_names == null) + _names = new Dictionary(StringComparer.OrdinalIgnoreCase); + + _names[culture] = name; + OnPropertyChanged(Ps.Value.NamesSelector); + } + + /// + public virtual string GetName(string culture) + { + if (culture == null) return Name; + if (_names == null) return null; + return _names.TryGetValue(culture, out var name) ? name : null; + } + + /// + public bool IsCultureAvailable(string culture) + => !string.IsNullOrWhiteSpace(GetName(culture)); + + private void ClearName(string culture) + { + if (culture == null) + { + Name = null; + return; + } + + if (_names == null) return; + _names.Remove(culture); + if (_names.Count == 0) + _names = null; + } + + protected virtual void ClearNames() + { + _names = null; + OnPropertyChanged(Ps.Value.NamesSelector); + } + + #endregion #region Has, Get, Set, Publish Property Value @@ -207,29 +216,29 @@ namespace Umbraco.Core.Models => Properties.Contains(propertyTypeAlias); /// - public virtual object GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false) + public virtual object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false) { return Properties.TryGetValue(propertyTypeAlias, out var property) - ? property.GetValue(languageId, segment, published) + ? property.GetValue(culture, segment, published) : null; } /// - public virtual TValue GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false) + public virtual TValue GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false) { if (!Properties.TryGetValue(propertyTypeAlias, out var property)) return default; - var convertAttempt = property.GetValue(languageId, segment, published).TryConvertTo(); + var convertAttempt = property.GetValue(culture, segment, published).TryConvertTo(); return convertAttempt.Success ? convertAttempt.Result : default; } /// - public virtual void SetValue(string propertyTypeAlias, object value, int? languageId = null, string segment = null) + public virtual void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null) { if (Properties.Contains(propertyTypeAlias)) { - Properties[propertyTypeAlias].SetValue(value, languageId, segment); + Properties[propertyTypeAlias].SetValue(value, culture, segment); return; } @@ -238,7 +247,7 @@ namespace Umbraco.Core.Models throw new InvalidOperationException($"No PropertyType exists with the supplied alias \"{propertyTypeAlias}\"."); var property = propertyType.CreateProperty(); - property.SetValue(value, languageId, segment); + property.SetValue(value, culture, segment); Properties.Add(property); } @@ -249,17 +258,17 @@ namespace Umbraco.Core.Models /// /// Sets the posted file value of a property. /// - public virtual void SetValue(string propertyTypeAlias, HttpPostedFile value, int? languageId = null, string segment = null) + public virtual void SetValue(string propertyTypeAlias, HttpPostedFile value, string culture = null, string segment = null) { - ContentExtensions.SetValue(this, propertyTypeAlias, new HttpPostedFileWrapper(value), languageId, segment); + ContentExtensions.SetValue(this, propertyTypeAlias, new HttpPostedFileWrapper(value), culture, segment); } /// /// Sets the posted file value of a property. /// - public virtual void SetValue(string propertyTypeAlias, HttpPostedFileBase value, int? languageId = null, string segment = null) + public virtual void SetValue(string propertyTypeAlias, HttpPostedFileBase value, string culture = null, string segment = null) { - ContentExtensions.SetValue(this, propertyTypeAlias, value, languageId, segment); + ContentExtensions.SetValue(this, propertyTypeAlias, value, culture, segment); } #endregion @@ -271,14 +280,14 @@ namespace Umbraco.Core.Models return Properties.Where(x => !x.IsAllValid()).ToArray(); } - public virtual Property[] Validate(int? languageId = null, string segment = null) + public virtual Property[] Validate(string culture = null, string segment = null) { - return Properties.Where(x => !x.IsValid(languageId, segment)).ToArray(); + return Properties.Where(x => !x.IsValid(culture, segment)).ToArray(); } - public virtual Property[] ValidateCulture(int? languageId = null) + public virtual Property[] ValidateCulture(string culture = null) { - return Properties.Where(x => !x.IsCultureValid(languageId)).ToArray(); + return Properties.Where(x => !x.IsCultureValid(culture)).ToArray(); } #endregion diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 5b7e825d68..0c232ff1e5 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -159,20 +159,7 @@ namespace Umbraco.Core.Models return Current.Services.MediaService.GetById(media.ParentId); } #endregion - - #region Variants - - /// - /// Returns true if the content has any property type that allows language variants - /// - public static bool HasPropertyTypeVaryingByCulture(this IContent content) - { - // fixme - what about CultureSegment? what about content.ContentType.Variations? - return content.PropertyTypes.Any(x => x.Variations == ContentVariation.CultureNeutral); - } - - #endregion - + /// /// Removes characters that are not valide XML characters from all entity properties /// of type string. See: http://stackoverflow.com/a/961504/5018 @@ -284,7 +271,7 @@ namespace Umbraco.Core.Models /// /// Sets the posted file value of a property. /// - public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, int? languageId = null, string segment = null) + public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value, string culture = null, string segment = null) { // ensure we get the filename without the path in IE in intranet mode // http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie @@ -303,7 +290,7 @@ namespace Umbraco.Core.Models if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); // fixme - er... why? - MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream, languageId, segment); + MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, value.InputStream, culture, segment); } /// @@ -312,7 +299,7 @@ namespace Umbraco.Core.Models /// This really is for FileUpload fields only, and should be obsoleted. For anything else, /// you need to store the file by yourself using Store and then figure out /// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself. - public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, int? languageId = null, string segment = null) + public static void SetValue(this IContentBase content, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { if (filename == null || filestream == null) return; @@ -321,7 +308,7 @@ namespace Umbraco.Core.Models if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); // fixme - er... why? - MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream, languageId, segment); + MediaFileSystem.SetUploadFile(content, propertyTypeAlias, filename, filestream, culture, segment); } /// diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index b686b97108..952fa0cd88 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -204,10 +204,10 @@ namespace Umbraco.Core.Models /// /// Validates that a variation is valid for the content type. /// - public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid) + public bool ValidateVariation(string culture, string segment, bool throwIfInvalid) { ContentVariation variation; - if (languageId.HasValue) + if (culture != null) { variation = segment != null ? ContentVariation.CultureSegment @@ -221,7 +221,7 @@ namespace Umbraco.Core.Models { variation = ContentVariation.InvariantNeutral; } - if ((Variations & variation) == 0) + if (!Variations.Has(variation)) { if (throwIfInvalid) throw new NotSupportedException($"Variation {variation} is invalid for content type \"{Alias}\"."); diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs index cf202ea54f..41aff25a81 100644 --- a/src/Umbraco.Core/Models/File.cs +++ b/src/Umbraco.Core/Models/File.cs @@ -146,13 +146,7 @@ namespace Umbraco.Core.Models /// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website) /// public string VirtualPath { get; set; } - - [Obsolete("This is no longer used and will be removed from the codebase in future versions")] - public virtual bool IsValid() - { - return true; - } - + // this exists so that class that manage name and alias differently, eg Template, // can implement their own cloning - (though really, not sure it's even needed) protected virtual void DeepCloneNameAndAlias(File clone) diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 3f7a335620..2f3fd06f5f 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -59,7 +59,7 @@ namespace Umbraco.Core.Models /// Gets the date and time the content was published. /// DateTime? PublishDate { get; } - + /// /// Gets or sets the date and time the content item should be published. /// @@ -80,15 +80,6 @@ namespace Umbraco.Core.Models /// ContentStatus Status { get; } - /// - /// Gets a value indicating whether a given culture is available. - /// - /// - /// A culture becomes available whenever the content name for this culture is - /// non-null, and it becomes unavailable whenever the content name is null. - /// - bool IsCultureAvailable(int? languageId); - /// /// Gets a value indicating whether a given culture is published. /// @@ -97,17 +88,31 @@ namespace Umbraco.Core.Models /// and the content published name for this culture is non-null. It becomes non-published /// whenever values for this culture are unpublished. /// - bool IsCulturePublished(int? languageId); + bool IsCulturePublished(string culture); + + /// + /// Gets the date a culture was published. + /// + DateTime GetDateCulturePublished(string culture); + + /// + /// Gets a value indicated whether a given culture is edited. + /// + /// + /// A culture is edited when it is not published, or when it is published but + /// it has changes. + /// + bool IsCultureEdited(string culture); /// /// Gets the name of the published version of the content for a given culture. /// /// /// When editing the content, the name can change, but this will not until the content is published. - /// When is null, gets the invariant + /// When is null, gets the invariant /// language, which is the value of the property. /// - string GetPublishName(int? languageId); + string GetPublishName(string culture); /// /// Gets the published names of the content. @@ -116,7 +121,22 @@ namespace Umbraco.Core.Models /// Because a dictionary key cannot be null this cannot get the invariant /// name, which must be get via the property. /// - IReadOnlyDictionary PublishNames { get; } + IReadOnlyDictionary PublishNames { get; } + + /// + /// Gets the available cultures. + /// + IEnumerable AvailableCultures { get; } + + /// + /// Gets the published cultures. + /// + IEnumerable PublishedCultures { get; } + + /// + /// Gets the edited cultures. + /// + IEnumerable EditedCultures { get; } // fixme - these two should move to some kind of service @@ -149,7 +169,8 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valie. /// - bool PublishAllValues(); + //fixme return an Attempt with some error results if it doesn't work + bool TryPublishAllValues(); /// /// Publishes values. @@ -159,7 +180,8 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valid. /// - bool PublishValues(int? languageId = null, string segment = null); + //fixme return an Attempt with some error results if it doesn't work + bool TryPublishValues(string culture = null, string segment = null); /// /// Publishes the culture/any values. @@ -169,7 +191,7 @@ namespace Umbraco.Core.Models /// The document must then be published via the content service. /// Values are not published if they are not valie. /// - bool PublishCultureValues(int? languageId = null); + bool PublishCultureValues(string culture = null); /// /// Clears all published values. @@ -179,12 +201,12 @@ namespace Umbraco.Core.Models /// /// Clears published values. /// - void ClearPublishedValues(int? languageId = null, string segment = null); + void ClearPublishedValues(string culture = null, string segment = null); /// /// Clears the culture/any published values. /// - void ClearCulturePublishedValues(int? languageId = null); + void ClearCulturePublishedValues(string culture = null); /// /// Copies values from another document. @@ -194,11 +216,11 @@ namespace Umbraco.Core.Models /// /// Copies values from another document. /// - void CopyValues(IContent other, int? languageId = null, string segment = null); + void CopyValues(IContent other, string culture = null, string segment = null); /// /// Copies culture/any values from another document. /// - void CopyCultureValues(IContent other, int? languageId = null); + void CopyCultureValues(IContent other, string culture = null); } } diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index aee8acd35e..93a6e82ada 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -31,19 +31,19 @@ namespace Umbraco.Core.Models /// Sets the name of the content item for a specified language. /// /// - /// When is null, sets the invariant + /// When is null, sets the invariant /// language, which sets the property. /// - void SetName(int? languageId, string value); + void SetName(string culture, string value); /// /// Gets the name of the content item for a specified language. /// /// - /// When is null, gets the invariant + /// When is null, gets the invariant /// language, which is the value of the property. /// - string GetName(int? languageId); + string GetName(string culture); /// /// Gets or sets the names of the content item. @@ -51,9 +51,18 @@ namespace Umbraco.Core.Models /// /// Because a dictionary key cannot be null this cannot get nor set the invariant /// name, which must be get or set via the property. - /// - IReadOnlyDictionary Names { get; set; } + /// + IReadOnlyDictionary Names { get; set; } + /// + /// Gets a value indicating whether a given culture is available. + /// + /// + /// A culture becomes available whenever the content name for this culture is + /// non-null, and it becomes unavailable whenever the content name is null. + /// + bool IsCultureAvailable(string culture); + /// /// List of properties, which make up all the data available for this Content object /// @@ -82,17 +91,17 @@ namespace Umbraco.Core.Models /// /// Gets the value of a Property /// - object GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false); + object GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false); /// /// Gets the typed value of a Property /// - TValue GetValue(string propertyTypeAlias, int? languageId = null, string segment = null, bool published = false); + TValue GetValue(string propertyTypeAlias, string culture = null, string segment = null, bool published = false); /// /// Sets the (edited) value of a Property /// - void SetValue(string propertyTypeAlias, object value, int? languageId = null, string segment = null); + void SetValue(string propertyTypeAlias, object value, string culture = null, string segment = null); /// /// Gets a value indicating whether the content and all its properties values are valid. @@ -102,11 +111,11 @@ namespace Umbraco.Core.Models /// /// Gets a value indicating whether the content and its properties values are valid. /// - Property[] Validate(int? languageId = null, string segment = null); + Property[] Validate(string culture = null, string segment = null); /// /// Gets a value indicating whether the content and its culture/any properties values are valid. /// - Property[] ValidateCulture(int? languageId = null); + Property[] ValidateCulture(string culture = null); } } diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs index 68e5923d66..7c8cc8eafe 100644 --- a/src/Umbraco.Core/Models/IContentTypeBase.cs +++ b/src/Umbraco.Core/Models/IContentTypeBase.cs @@ -51,7 +51,7 @@ namespace Umbraco.Core.Models /// /// Validates that a variation is valid for the content type. /// - bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid); + bool ValidateVariation(string culture, string segment, bool throwIfInvalid); /// /// Gets or Sets a list of integer Ids of the ContentTypes allowed under the ContentType diff --git a/src/Umbraco.Core/Models/IFile.cs b/src/Umbraco.Core/Models/IFile.cs index 9b974276c1..109d65f554 100644 --- a/src/Umbraco.Core/Models/IFile.cs +++ b/src/Umbraco.Core/Models/IFile.cs @@ -43,8 +43,6 @@ namespace Umbraco.Core.Models /// Gets or sets the file's virtual path (i.e. the file path relative to the root of the website) /// string VirtualPath { get; set; } - - [Obsolete("This is no longer used and will be removed from the codebase in future versions")] - bool IsValid(); + } } diff --git a/src/Umbraco.Core/Models/IMacro.cs b/src/Umbraco.Core/Models/IMacro.cs index 1100212190..472c135e32 100644 --- a/src/Umbraco.Core/Models/IMacro.cs +++ b/src/Umbraco.Core/Models/IMacro.cs @@ -51,35 +51,19 @@ namespace Umbraco.Core.Models /// [DataMember] bool DontRender { get; set; } - + /// - /// Gets or sets the path to user control or the Control Type to render + /// Gets or set the path to the macro source to render /// [DataMember] - string ControlType { get; set; } - + string MacroSource { get; set; } + /// - /// Gets or sets the name of the assembly, which should be used by the Macro + /// Gets or set the macro type /// - /// Will usually only be filled if the ScriptFile is a Usercontrol [DataMember] - [Obsolete("This is no longer used, we should remove it in v8, CustomControl macros are gone")] - string ControlAssembly { get; set; } - - /// - /// Gets or set the path to the Python file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - string ScriptPath { get; set; } - - /// - /// Gets or sets the path to the Xslt file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - string XsltPath { get; set; } - + MacroTypes MacroType { get; set; } + /// /// Gets or sets a list of Macro Properties /// diff --git a/src/Umbraco.Core/Models/IXsltFile.cs b/src/Umbraco.Core/Models/IXsltFile.cs deleted file mode 100644 index 028b5581c9..0000000000 --- a/src/Umbraco.Core/Models/IXsltFile.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Umbraco.Core.Models -{ - public interface IXsltFile : IFile - { - - } -} diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs index bd05abb1c1..dc411a0291 100644 --- a/src/Umbraco.Core/Models/Macro.cs +++ b/src/Umbraco.Core/Models/Macro.cs @@ -34,14 +34,11 @@ namespace Umbraco.Core.Models /// /// /// - /// - /// - /// /// /// /// - /// - public Macro(int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cacheByMember, bool dontRender, string scriptPath) + /// + public Macro(int id, Guid key, bool useInEditor, int cacheDuration, string @alias, string name, bool cacheByPage, bool cacheByMember, bool dontRender, string macroSource, MacroTypes macroType) : this() { Id = id; @@ -50,13 +47,11 @@ namespace Umbraco.Core.Models CacheDuration = cacheDuration; Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; - ControlType = controlType; - ControlAssembly = controlAssembly; - XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; - ScriptPath = scriptPath; + MacroSource = macroSource; + MacroType = macroType; } /// @@ -66,18 +61,13 @@ namespace Umbraco.Core.Models /// /// /// - /// - /// - /// /// /// /// - /// + /// public Macro(string @alias, string name, - string controlType = "", - string controlAssembly = "", - string xsltPath = "", - string scriptPath = "", + string macroSource, + MacroTypes macroType, bool cacheByPage = false, bool cacheByMember = false, bool dontRender = true, @@ -89,13 +79,11 @@ namespace Umbraco.Core.Models CacheDuration = cacheDuration; Alias = alias.ToCleanString(CleanStringType.Alias); Name = name; - ControlType = controlType; - ControlAssembly = controlAssembly; - XsltPath = xsltPath; CacheByPage = cacheByPage; CacheByMember = cacheByMember; DontRender = dontRender; - ScriptPath = scriptPath; + MacroSource = macroSource; + MacroType = macroType; } private string _alias; @@ -105,10 +93,8 @@ namespace Umbraco.Core.Models private bool _cacheByPage; private bool _cacheByMember; private bool _dontRender; - private string _scriptFile; - private string _scriptAssembly; - private string _scriptPath; - private string _xslt; + private string _macroSource; + private MacroTypes _macroType = MacroTypes.Unknown; private MacroPropertyCollection _properties; private List _addedProperties; private List _removedProperties; @@ -124,10 +110,8 @@ namespace Umbraco.Core.Models public readonly PropertyInfo CacheByPageSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByPage); public readonly PropertyInfo CacheByMemberSelector = ExpressionHelper.GetPropertyInfo(x => x.CacheByMember); public readonly PropertyInfo DontRenderSelector = ExpressionHelper.GetPropertyInfo(x => x.DontRender); - public readonly PropertyInfo ControlPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ControlType); - public readonly PropertyInfo ControlAssemblySelector = ExpressionHelper.GetPropertyInfo(x => x.ControlAssembly); - public readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.ScriptPath); - public readonly PropertyInfo XsltPathSelector = ExpressionHelper.GetPropertyInfo(x => x.XsltPath); + public readonly PropertyInfo ScriptPathSelector = ExpressionHelper.GetPropertyInfo(x => x.MacroSource); + public readonly PropertyInfo MacroTypeSelector = ExpressionHelper.GetPropertyInfo(x => x.MacroType); public readonly PropertyInfo PropertiesSelector = ExpressionHelper.GetPropertyInfo(x => x.Properties); } @@ -272,46 +256,23 @@ namespace Umbraco.Core.Models } /// - /// Gets or sets the path to user control or the Control Type to render + /// Gets or set the path to the Partial View to render /// [DataMember] - public string ControlType + public string MacroSource { - get { return _scriptFile; } - set { SetPropertyValueAndDetectChanges(value, ref _scriptFile, Ps.Value.ControlPathSelector); } - } - + get { return _macroSource; } + set { SetPropertyValueAndDetectChanges(value, ref _macroSource, Ps.Value.ScriptPathSelector); } + } + /// - /// Gets or sets the name of the assembly, which should be used by the Macro + /// Gets or set the path to the Partial View to render /// - /// Will usually only be filled if the ControlType is a Usercontrol [DataMember] - public string ControlAssembly + public MacroTypes MacroType { - get { return _scriptAssembly; } - set { SetPropertyValueAndDetectChanges(value, ref _scriptAssembly, Ps.Value.ControlAssemblySelector); } - } - - /// - /// Gets or set the path to the Python file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - public string ScriptPath - { - get { return _scriptPath; } - set { SetPropertyValueAndDetectChanges(value, ref _scriptPath, Ps.Value.ScriptPathSelector); } - } - - /// - /// Gets or sets the path to the Xslt file in use - /// - /// Optional: Can only be one of three Script, Python or Xslt - [DataMember] - public string XsltPath - { - get { return _xslt; } - set { SetPropertyValueAndDetectChanges(value, ref _xslt, Ps.Value.XsltPathSelector); } + get { return _macroType; } + set { SetPropertyValueAndDetectChanges(value, ref _macroType, Ps.Value.MacroTypeSelector); } } /// diff --git a/src/Umbraco.Core/Models/MacroTypes.cs b/src/Umbraco.Core/Models/MacroTypes.cs index 90eacf5171..310d6ccd7c 100644 --- a/src/Umbraco.Core/Models/MacroTypes.cs +++ b/src/Umbraco.Core/Models/MacroTypes.cs @@ -10,15 +10,11 @@ namespace Umbraco.Core.Models [DataContract(IsReference = true)] public enum MacroTypes { - [EnumMember] - Xslt = 1, [EnumMember] UserControl = 3, [EnumMember] Unknown = 4, [EnumMember] - Script = 6, - [EnumMember] PartialView = 7 } } diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs index bc8b7c4c89..9612fce728 100644 --- a/src/Umbraco.Core/Models/MediaType.cs +++ b/src/Umbraco.Core/Models/MediaType.cs @@ -48,7 +48,7 @@ namespace Umbraco.Core.Models /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset /// /// - public IMediaType DeepCloneWithResetIdentities(string alias) + public new IMediaType DeepCloneWithResetIdentities(string alias) { var clone = (MediaType)DeepClone(); clone.Alias = alias; diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs index 8219af17b9..f3092a0106 100644 --- a/src/Umbraco.Core/Models/Membership/IUser.cs +++ b/src/Umbraco.Core/Models/Membership/IUser.cs @@ -18,11 +18,7 @@ namespace Umbraco.Core.Models.Membership int[] StartContentIds { get; set; } int[] StartMediaIds { get; set; } string Language { get; set; } - - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - IUserType UserType { get; set; } - + DateTime? EmailConfirmedDate { get; set; } DateTime? InvitedDate { get; set; } @@ -37,14 +33,6 @@ namespace Umbraco.Core.Models.Membership IEnumerable AllowedSections { get; } - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - void RemoveAllowedSection(string sectionAlias); - - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - void AddAllowedSection(string sectionAlias); - /// /// Exposes the basic profile data /// @@ -65,4 +53,4 @@ namespace Umbraco.Core.Models.Membership /// string TourData { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Models/Membership/IUserType.cs b/src/Umbraco.Core/Models/Membership/IUserType.cs deleted file mode 100644 index 118d66074f..0000000000 --- a/src/Umbraco.Core/Models/Membership/IUserType.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using Umbraco.Core.Models.Entities; - -namespace Umbraco.Core.Models.Membership -{ - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - public interface IUserType : IEntity - { - string Alias { get; set; } - string Name { get; set; } - IEnumerable Permissions { get; set; } - - } -} diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 0e3ab70e0a..cc91f5ed47 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -284,162 +284,6 @@ namespace Umbraco.Core.Models.Membership get { return _allowedSections ?? (_allowedSections = new List(_userGroups.SelectMany(x => x.AllowedSections).Distinct())); } } - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - IUserType IUser.UserType - { - get - { - //the best we can do here is to return the user's first user group as a IUserType object - //but we should attempt to return any group that is the built in ones first - var groups = Groups.ToArray(); - if (groups.Length == 0) - { - //In backwards compatibility land, a user type cannot be null! so we need to return a fake one. - return new UserType - { - Alias = "temp", - Id = int.MinValue, - Key = Guid.Empty, - CreateDate = default(DateTime), - DeleteDate = null, - Name = "Temp", - Permissions = new List(), - UpdateDate = default(DateTime) - }; - } - var builtIns = new[] { Constants.Security.AdminGroupAlias, "writer", "editor", "translator" }; - var foundBuiltIn = groups.FirstOrDefault(x => builtIns.Contains(x.Alias)); - IUserGroup realGroup; - if (foundBuiltIn != null) - { - //if the group isn't IUserGroup we'll need to look it up - realGroup = foundBuiltIn as IUserGroup ?? Current.Services.UserService.GetUserGroupById(foundBuiltIn.Id); - - //return a mapped version of the group - return new UserType - { - Alias = realGroup.Alias, - Id = realGroup.Id, - Key = realGroup.Key, - CreateDate = realGroup.CreateDate, - DeleteDate = realGroup.DeleteDate, - Name = realGroup.Name, - Permissions = realGroup.Permissions, - UpdateDate = realGroup.UpdateDate - }; - } - - //otherwise return the first - //if the group isn't IUserGroup we'll need to look it up - realGroup = groups[0] as IUserGroup ?? Current.Services.UserService.GetUserGroupById(groups[0].Id); - //return a mapped version of the group - return new UserType - { - Alias = realGroup.Alias, - Id = realGroup.Id, - Key = realGroup.Key, - CreateDate = realGroup.CreateDate, - DeleteDate = realGroup.DeleteDate, - Name = realGroup.Name, - Permissions = realGroup.Permissions, - UpdateDate = realGroup.UpdateDate - }; - } - set - { - //if old APIs are still using this lets first check if the user is part of the user group with the alias specified - if (Groups.Any(x => x.Alias == value.Alias)) - return; - - //the only other option we have here is to lookup the group (and we'll need to use singletons here :( ) - var found = Current.Services.UserService.GetUserGroupByAlias(value.Alias); - if (found == null) - throw new InvalidOperationException("No user group was found with the alias " + value.Alias + ", this API (IUser.UserType) is obsolete, use user groups instead"); - - //if it's found, all we can do is add it, we can't really replace them - AddGroup(found.ToReadOnlyGroup()); - } - } - - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - void IUser.RemoveAllowedSection(string sectionAlias) - { - //don't do anything if they aren't allowed it already - if (AllowedSections.Contains(sectionAlias) == false) - return; - - var groups = Groups.ToArray(); - //our only option here is to check if a custom group is created for this user, if so we can remove it from that group, otherwise we'll throw - //now we'll check if the user has a special 1:1 user group created for itself. This will occur if this method is used and also during an upgrade. - //this comes in the alias form of userName + 'Group' - var customUserGroup = groups.FirstOrDefault(x => x.Alias == (Username + "Group")); - if (customUserGroup != null) - { - //if the group isn't IUserGroup we'll need to look it up - var realGroup = customUserGroup as IUserGroup ?? Current.Services.UserService.GetUserGroupById(customUserGroup.Id); - realGroup.RemoveAllowedSection(sectionAlias); - //now we need to flag this for saving (hack!) - GroupsToSave.Add(realGroup); - } - else - { - throw new InvalidOperationException("Cannot remove the allowed section using this obsolete API. Modify the user's groups instead"); - } - - } - - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - void IUser.AddAllowedSection(string sectionAlias) - { - //don't do anything if they are allowed it already - if (AllowedSections.Contains(sectionAlias)) - return; - - //This is here for backwards compat only. - //First we'll check if the user is part of the 'admin' group. If so then we can ensure that the admin group has this section available to it. - //otherwise, the only thing we can do is create a custom user group for this user and add this section. - //We are checking for admin here because if the user is an admin and an allowed section is being added, then it's assumed it's to be added - //for the whole admin group (i.e. Forms installer does this for admins) - var groups = Groups.ToArray(); - var admin = groups.FirstOrDefault(x => x.Alias == Constants.Security.AdminGroupAlias); - if (admin != null) - { - //if the group isn't IUserGroup we'll need to look it up - var realGroup = admin as IUserGroup ?? Current.Services.UserService.GetUserGroupById(admin.Id); - realGroup.AddAllowedSection(sectionAlias); - //now we need to flag this for saving (hack!) - GroupsToSave.Add(realGroup); - } - else - { - //now we'll check if the user has a special 1:1 user group created for itself. This will occur if this method is used and also during an upgrade. - //this comes in the alias form of userName + 'Group' - var customUserGroup = groups.FirstOrDefault(x => x.Alias == (Username + "Group")); - if (customUserGroup != null) - { - //if the group isn't IUserGroup we'll need to look it up - var realGroup = customUserGroup as IUserGroup ?? Current.Services.UserService.GetUserGroupById(customUserGroup.Id); - realGroup.AddAllowedSection(sectionAlias); - //now we need to flag this for saving (hack!) - GroupsToSave.Add(realGroup); - } - - //ok, so the user doesn't have a 1:1 group, we'll need to flag it for creation - var newUserGroup = new UserGroup - { - Alias = Username + "Group", - Name = "Group for " + Username - }; - newUserGroup.AddAllowedSection(sectionAlias); - //add this user to this new group - AddGroup(newUserGroup); - GroupsToSave.Add(newUserGroup); - } - } - /// /// This used purely for hacking backwards compatibility into this class for < 7.7 compat /// diff --git a/src/Umbraco.Core/Models/Membership/UserType.cs b/src/Umbraco.Core/Models/Membership/UserType.cs deleted file mode 100644 index d2e4a8f6cb..0000000000 --- a/src/Umbraco.Core/Models/Membership/UserType.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Reflection; -using System.Runtime.Serialization; -using Umbraco.Core.Models.Entities; -using Umbraco.Core.Strings; - -namespace Umbraco.Core.Models.Membership -{ - [Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - [Serializable] - [DataContract(IsReference = true)] - internal class UserType : EntityBase, IUserType - { - private string _alias; - private string _name; - private IEnumerable _permissions; - private static readonly Lazy Ps = new Lazy(); - private class PropertySelectors - { - public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name); - public readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); - public readonly PropertyInfo PermissionsSelector = ExpressionHelper.GetPropertyInfo>(x => x.Permissions); - } - - public UserType(string name, string alias) - { - Name = name; - Alias = alias; - } - - public UserType() - { - } - - [DataMember] - public string Alias - { - get { return _alias; } - set - { - SetPropertyValueAndDetectChanges( - value.ToCleanString(CleanStringType.Alias | CleanStringType.UmbracoCase), - ref _alias, - Ps.Value.AliasSelector); - } - } - - [DataMember] - public string Name - { - get { return _name; } - set { SetPropertyValueAndDetectChanges(value, ref _name, Ps.Value.NameSelector); } - } - - /// - /// The set of default permissions for the user type - /// - /// - /// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future. - /// - [DataMember] - public IEnumerable Permissions - { - get { return _permissions; } - set - { - SetPropertyValueAndDetectChanges(value, ref _permissions, Ps.Value.PermissionsSelector, - //Custom comparer for enumerable - new DelegateEqualityComparer>( - (enum1, enum2) => enum1.UnsortedSequenceEqual(enum2), - enum1 => enum1.GetHashCode())); - } - } - } -} diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 9e3f5fc2e0..709c37daaa 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Models { private List _values = new List(); private PropertyValue _pvalue; - private Dictionary _vvalues; + private Dictionary _vvalues; private static readonly Lazy Ps = new Lazy(); @@ -38,9 +38,14 @@ namespace Umbraco.Core.Models public class PropertyValue { + private string _culture; private string _segment; - public int? LanguageId { get; internal set; } + public string Culture + { + get => _culture; + internal set => _culture = value?.ToLowerInvariant(); + } public string Segment { get => _segment; @@ -50,7 +55,7 @@ namespace Umbraco.Core.Models public object PublishedValue { get; internal set; } public PropertyValue Clone() - => new PropertyValue { LanguageId = LanguageId, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue }; + => new PropertyValue { _culture = _culture, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue }; } // ReSharper disable once ClassNeverInstantiated.Local @@ -96,10 +101,10 @@ namespace Umbraco.Core.Models { // make sure we filter out invalid variations // make sure we leave _vvalues null if possible - _values = value.Where(x => PropertyType.ValidateVariation(x.LanguageId, x.Segment, false)).ToList(); - _pvalue = _values.FirstOrDefault(x => !x.LanguageId.HasValue && x.Segment == null); + _values = value.Where(x => PropertyType.ValidateVariation(x.Culture, x.Segment, false)).ToList(); + _pvalue = _values.FirstOrDefault(x => x.Culture == null && x.Segment == null); _vvalues = _values.Count > (_pvalue == null ? 0 : 1) - ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeIntStringKey(x.LanguageId, x.Segment), x => x) + ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeStringStringKey(x.Culture, x.Segment), x => x) : null; } } @@ -128,12 +133,12 @@ namespace Umbraco.Core.Models /// /// Gets the value. /// - public object GetValue(int? languageId = null, string segment = null, bool published = false) + public object GetValue(string culture = null, string segment = null, bool published = false) { - if (!PropertyType.ValidateVariation(languageId, segment, false)) return null; - if (!languageId.HasValue && segment == null) return GetPropertyValue(_pvalue, published); + if (!PropertyType.ValidateVariation(culture, segment, false)) return null; + if (culture == null && segment == null) return GetPropertyValue(_pvalue, published); if (_vvalues == null) return null; - return _vvalues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var pvalue) + return _vvalues.TryGetValue(new CompositeStringStringKey(culture, segment), out var pvalue) ? GetPropertyValue(pvalue, published) : null; } @@ -159,7 +164,7 @@ namespace Umbraco.Core.Models if (_vvalues != null) { var pvalues = _vvalues - .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false)) + .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) PublishPropertyValue(pvalue); @@ -168,29 +173,29 @@ namespace Umbraco.Core.Models // internal - must be invoked by the content item // does *not* validate the value - content item must validate first - internal void PublishValue(int? languageId = null, string segment = null) + internal void PublishValue(string culture = null, string segment = null) { - PropertyType.ValidateVariation(languageId, segment, true); + PropertyType.ValidateVariation(culture, segment, true); - (var pvalue, _) = GetPValue(languageId, segment, false); + (var pvalue, _) = GetPValue(culture, segment, false); if (pvalue == null) return; PublishPropertyValue(pvalue); } // internal - must be invoked by the content item // does *not* validate the value - content item must validate first - internal void PublishCultureValues(int? languageId = null) + internal void PublishCultureValues(string culture = null) { // if invariant and invariant-neutral is supported, publish invariant-neutral - if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false)) + if (culture == null && PropertyType.ValidateVariation(null, null, false)) PublishPropertyValue(_pvalue); // publish everything not invariant-neutral that matches the culture and is supported if (_vvalues != null) { var pvalues = _vvalues - .Where(x => x.Value.LanguageId == languageId) - .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false)) + .Where(x => x.Value.Culture.InvariantEquals(culture)) + .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) PublishPropertyValue(pvalue); @@ -206,7 +211,7 @@ namespace Umbraco.Core.Models if (_vvalues != null) { var pvalues = _vvalues - .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false)) + .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) ClearPublishedPropertyValue(pvalue); @@ -214,25 +219,25 @@ namespace Umbraco.Core.Models } // internal - must be invoked by the content item - internal void ClearPublishedValue(int? languageId = null, string segment = null) + internal void ClearPublishedValue(string culture = null, string segment = null) { - PropertyType.ValidateVariation(languageId, segment, true); - (var pvalue, _) = GetPValue(languageId, segment, false); + PropertyType.ValidateVariation(culture, segment, true); + (var pvalue, _) = GetPValue(culture, segment, false); if (pvalue == null) return; ClearPublishedPropertyValue(pvalue); } // internal - must be invoked by the content item - internal void ClearPublishedCultureValues(int? languageId = null) + internal void ClearPublishedCultureValues(string culture = null) { - if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false)) + if (culture == null && PropertyType.ValidateVariation(null, null, false)) ClearPublishedPropertyValue(_pvalue); if (_vvalues != null) { var pvalues = _vvalues - .Where(x => x.Value.LanguageId == languageId) - .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false)) + .Where(x => x.Value.Culture.InvariantEquals(culture)) + .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false)) .Select(x => x.Value); foreach (var pvalue in pvalues) ClearPublishedPropertyValue(pvalue); @@ -264,10 +269,10 @@ namespace Umbraco.Core.Models /// /// Sets a value. /// - public void SetValue(object value, int? languageId = null, string segment = null) + public void SetValue(object value, string culture = null, string segment = null) { - PropertyType.ValidateVariation(languageId, segment, true); - (var pvalue, var change) = GetPValue(languageId, segment, true); + PropertyType.ValidateVariation(culture, segment, true); + (var pvalue, var change) = GetPValue(culture, segment, true); var origValue = pvalue.EditedValue; var setValue = PropertyType.ConvertAssignedValue(value); @@ -278,9 +283,9 @@ namespace Umbraco.Core.Models } // bypasses all changes detection and is the *only* way to set the published value - internal void FactorySetValue(int? languageId, string segment, bool published, object value) + internal void FactorySetValue(string culture, string segment, bool published, object value) { - (var pvalue, _) = GetPValue(languageId, segment, true); + (var pvalue, _) = GetPValue(culture, segment, true); if (published && PropertyType.IsPublishing) pvalue.PublishedValue = value; @@ -301,24 +306,24 @@ namespace Umbraco.Core.Models return (_pvalue, change); } - private (PropertyValue, bool) GetPValue(int? languageId, string segment, bool create) + private (PropertyValue, bool) GetPValue(string culture, string segment, bool create) { - if (!languageId.HasValue && segment == null) + if (culture == null && segment == null) return GetPValue(create); var change = false; if (_vvalues == null) { if (!create) return (null, false); - _vvalues = new Dictionary(); + _vvalues = new Dictionary(); change = true; } - var k = new CompositeIntStringKey(languageId, segment); + var k = new CompositeStringStringKey(culture, segment); if (!_vvalues.TryGetValue(k, out var pvalue)) { if (!create) return (null, false); pvalue = _vvalues[k] = new PropertyValue(); - pvalue.LanguageId = languageId; + pvalue.Culture = culture; pvalue.Segment = segment; _values.Add(pvalue); change = true; @@ -343,7 +348,7 @@ namespace Umbraco.Core.Models if (_vvalues == null) return true; var pvalues = _vvalues - .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false)) + .Where(x => PropertyType.ValidateVariation(x.Value.Culture, x.Value.Segment, false)) .Select(x => x.Value) .ToArray(); @@ -354,11 +359,11 @@ namespace Umbraco.Core.Models /// Gets a value indicating whether the culture/any values are valid. /// /// An invalid value can be saved, but only valid values can be published. - public bool IsCultureValid(int? languageId) + public bool IsCultureValid(string culture) { // culture-neutral is supported, validate culture-neutral // includes mandatory validation - if (PropertyType.ValidateVariation(languageId, null, false) && !IsValidValue(GetValue(languageId))) + if (PropertyType.ValidateVariation(culture, null, false) && !IsValidValue(GetValue(culture))) return false; // either culture-neutral is not supported, or it is valid @@ -368,8 +373,8 @@ namespace Umbraco.Core.Models if (_vvalues == null) return true; var pvalues = _vvalues - .Where(x => x.Value.LanguageId == languageId) - .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false)) + .Where(x => x.Value.Culture.InvariantEquals(culture)) + .Where(x => PropertyType.ValidateVariation(culture, x.Value.Segment, false)) .Select(x => x.Value) .ToArray(); @@ -380,10 +385,10 @@ namespace Umbraco.Core.Models /// Gets a value indicating whether the value is valid. /// /// An invalid value can be saved, but only valid values can be published. - public bool IsValid(int? languageId = null, string segment = null) + public bool IsValid(string culture = null, string segment = null) { // single value -> validates mandatory - return IsValidValue(GetValue(languageId, segment)); + return IsValidValue(GetValue(culture, segment)); } /// diff --git a/src/Umbraco.Core/Models/PropertyGroupCollection.cs b/src/Umbraco.Core/Models/PropertyGroupCollection.cs index abec6febcb..0083bdb502 100644 --- a/src/Umbraco.Core/Models/PropertyGroupCollection.cs +++ b/src/Umbraco.Core/Models/PropertyGroupCollection.cs @@ -67,8 +67,10 @@ namespace Umbraco.Core.Models internal new void Add(PropertyGroup item) { - using (new WriteLock(_addLocker)) + try { + _addLocker.EnterWriteLock(); + //Note this is done to ensure existig groups can be renamed if (item.HasIdentity && item.Id > 0) { @@ -102,6 +104,11 @@ namespace Umbraco.Core.Models OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } + finally + { + if (_addLocker.IsWriteLockHeld) + _addLocker.ExitWriteLock(); + } } /// diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 8c5e318719..66299e7749 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -226,10 +226,10 @@ namespace Umbraco.Core.Models /// /// Validates that a variation is valid for the property type. /// - public bool ValidateVariation(int? languageId, string segment, bool throwIfInvalid) + public bool ValidateVariation(string culture, string segment, bool throwIfInvalid) { ContentVariation variation; - if (languageId.HasValue) + if (culture != null) { variation = segment != null ? ContentVariation.CultureSegment @@ -243,7 +243,7 @@ namespace Umbraco.Core.Models { variation = ContentVariation.InvariantNeutral; } - if ((Variations & variation) == 0) + if (!Variations.Has(variation)) { if (throwIfInvalid) throw new NotSupportedException($"Variation {variation} is invalid for property type \"{Alias}\"."); diff --git a/src/Umbraco.Core/Models/PropertyTypeCollection.cs b/src/Umbraco.Core/Models/PropertyTypeCollection.cs index 4788221854..dbe97248f9 100644 --- a/src/Umbraco.Core/Models/PropertyTypeCollection.cs +++ b/src/Umbraco.Core/Models/PropertyTypeCollection.cs @@ -80,8 +80,9 @@ namespace Umbraco.Core.Models item.IsPublishing = IsPublishing; // fixme redo this entirely!!! - using (new WriteLock(_addLocker)) + try { + _addLocker.EnterWriteLock(); var key = GetKeyForItem(item); if (key != null) { @@ -105,6 +106,11 @@ namespace Umbraco.Core.Models OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); } + finally + { + if (_addLocker.IsWriteLockHeld) + _addLocker.ExitWriteLock(); + } } /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs index f114d94116..f75e59bee0 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContent.cs @@ -3,17 +3,11 @@ using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { + /// /// /// Represents a cached content. /// - /// - /// SD: A replacement for INode which needs to occur since INode doesn't contain the document type alias - /// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable). - /// Stephan: initially, that was for cached published content only. Now, we're using it also for - /// cached preview (so, maybe unpublished) content. A better name would therefore be ICachedContent, as - /// has been suggested. However, can't change now. Maybe in v7? - /// public interface IPublishedContent : IPublishedElement { #region Content @@ -38,6 +32,8 @@ namespace Umbraco.Core.Models.PublishedContent int Level { get; } string Url { get; } + IReadOnlyDictionary CultureNames { get; } + /// /// Gets a value indicating whether the content is a content (aka a document) or a media. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs index 24c654604c..9d2cca3e6d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedProperty.cs @@ -21,7 +21,7 @@ /// Other caches that get their raw value from the database would consider that a property has "no /// value" if it is missing, null, or an empty string (including whitespace-only). /// - bool HasValue(int? languageId = null, string segment = null); + bool HasValue(string culture = null, string segment = null); /// /// Gets the source value of the property. @@ -35,7 +35,7 @@ /// If you're using that value, you're probably wrong, unless you're doing some internal /// Umbraco stuff. /// - object GetSourceValue(int? languageId = null, string segment = null); + object GetSourceValue(string culture = null, string segment = null); /// /// Gets the object value of the property. @@ -45,7 +45,7 @@ /// It can be null, or any type of CLR object. /// It has been fully prepared and processed by the appropriate converter. /// - object GetValue(int? languageId = null, string segment = null); + object GetValue(string culture = null, string segment = null); /// /// Gets the XPath value of the property. @@ -55,6 +55,6 @@ /// It must be either null, or a string, or an XPathNavigator. /// It has been fully prepared and processed by the appropriate converter. /// - object GetXPathValue(int? languageId = null, string segment = null); + object GetXPathValue(string culture = null, string segment = null); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs index a30726012e..528a545f8f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs @@ -54,7 +54,9 @@ namespace Umbraco.Core.Models.PublishedContent public virtual int SortOrder => _content.SortOrder; - public virtual string Name => _content.Name; + public virtual string Name => _content.Name; + + public virtual IReadOnlyDictionary CultureNames => _content.CultureNames; public virtual string UrlName => _content.UrlName; diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs new file mode 100644 index 0000000000..59ac875aa4 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedCultureName.cs @@ -0,0 +1,19 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + /// + /// Contains the culture specific data for a item + /// + public struct PublishedCultureName + { + public PublishedCultureName(string name, string urlName) : this() + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + UrlName = urlName ?? throw new ArgumentNullException(nameof(urlName)); + } + + public string Name { get; } + public string UrlName { get; } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs index 918bdb86e4..7e2a5b5498 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs @@ -53,15 +53,15 @@ namespace Umbraco.Core.Models.PublishedContent public string Alias => PropertyType.Alias; /// - public abstract bool HasValue(int? languageId = null, string segment = null); + public abstract bool HasValue(string culture = null, string segment = null); /// - public abstract object GetSourceValue(int? languageId = null, string segment = null); + public abstract object GetSourceValue(string culture = null, string segment = null); /// - public abstract object GetValue(int? languageId = null, string segment = null); + public abstract object GetValue(string culture = null, string segment = null); /// - public abstract object GetXPathValue(int? languageId = null, string segment = null); + public abstract object GetXPathValue(string culture = null, string segment = null); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs index f938880060..e20d8cb49c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs +++ b/src/Umbraco.Core/Models/PublishedContent/RawValueProperty.cs @@ -20,20 +20,20 @@ namespace Umbraco.Core.Models.PublishedContent private readonly Lazy _objectValue; private readonly Lazy _xpathValue; - public override object GetSourceValue(int? languageId = null, string segment = null) - => languageId == null & segment == null ? _sourceValue : null; + public override object GetSourceValue(string culture = null, string segment = null) + => culture == null & segment == null ? _sourceValue : null; - public override bool HasValue(int? languageId = null, string segment = null) + public override bool HasValue(string culture = null, string segment = null) { - var sourceValue = GetSourceValue(languageId, segment); + var sourceValue = GetSourceValue(culture, segment); return sourceValue is string s ? !string.IsNullOrWhiteSpace(s) : sourceValue != null; } - public override object GetValue(int? languageId = null, string segment = null) - => languageId == null & segment == null ? _objectValue.Value : null; + public override object GetValue(string culture = null, string segment = null) + => culture == null & segment == null ? _objectValue.Value : null; - public override object GetXPathValue(int? languageId = null, string segment = null) - => languageId == null & segment == null ? _xpathValue.Value : null; + public override object GetXPathValue(string culture = null, string segment = null) + => culture == null & segment == null ? _xpathValue.Value : null; public RawValueProperty(PublishedPropertyType propertyType, IPublishedElement content, object sourceValue, bool isPreviewing = false) : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index 31b5f2e513..817ef3ace8 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -14,14 +14,6 @@ namespace Umbraco.Core.Models /// Unknown, - /// - /// Content Item Type - /// - [UmbracoObjectType(Constants.ObjectTypes.Strings.ContentItemType)] - [FriendlyName("Content Item Type")] - [Obsolete("This is not used and will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - ContentItemType, /// /// Root @@ -69,17 +61,7 @@ namespace Umbraco.Core.Models [FriendlyName("Member Group")] [UmbracoUdiType(Constants.UdiEntityType.MemberGroup)] MemberGroup, - - //TODO: What is a 'Content Item' supposed to be??? - /// - /// Content Item - /// - [UmbracoObjectType(Constants.ObjectTypes.Strings.ContentItem)] - [FriendlyName("Content Item")] - [Obsolete("This is not used and will be removed in future versions")] - [EditorBrowsable(EditorBrowsableState.Never)] - ContentItem, - + /// /// "Media Type /// diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index f78de43f01..00dd3273bf 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -112,15 +112,6 @@ namespace Umbraco.Core.Models } - public static void ClearAllowedSections(this IUser user) - { - var allowed = user.AllowedSections.ToArray(); - foreach (var s in allowed) - { - user.RemoveAllowedSection(s); - } - } - /// /// Returns the culture info associated with this user, based on the language they're assigned to in the back office /// diff --git a/src/Umbraco.Core/Models/XsltFile.cs b/src/Umbraco.Core/Models/XsltFile.cs deleted file mode 100644 index a88cd2c127..0000000000 --- a/src/Umbraco.Core/Models/XsltFile.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace Umbraco.Core.Models -{ - /// - /// Represents a XSLT file - /// - [Serializable] - [DataContract(IsReference = true)] - public class XsltFile : File, IXsltFile - { - public XsltFile(string path) - : this(path, (Func) null) - { } - - internal XsltFile(string path, Func getFileContent) - : base(path, getFileContent) - { } - - /// - /// Indicates whether the current entity has an identity, which in this case is a path/name. - /// - /// - /// Overrides the default Entity identity check. - /// - public override bool HasIdentity - { - get { return string.IsNullOrEmpty(Path) == false; } - } - } -} diff --git a/src/Umbraco.Core/Packaging/DefaultPackageContext.cs b/src/Umbraco.Core/Packaging/DefaultPackageContext.cs deleted file mode 100644 index c08418ec86..0000000000 --- a/src/Umbraco.Core/Packaging/DefaultPackageContext.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using NuGet; -using Umbraco.Core.Configuration; - -namespace Umbraco.Core.Packaging -{ - internal class DefaultPackageContext : IPackageContext - { - public DefaultPackageContext(Func mapPath) - {} - - private readonly string _localPackageRepoFolderPath; - private readonly string _pluginInstallFolderPath; - private readonly Lazy _localPackageManager; - private readonly Lazy _localPackageRepository; - private readonly Lazy _publicPackageManager; - private readonly Lazy _privatePackageManager; - private readonly Lazy _publicPackageRepository; - private readonly Lazy _sprivatePackageRepository; - - /// - /// Gets the local path resolver. - /// - public IPackagePathResolver LocalPathResolver - { - get { return ((PackageManager)LocalPackageManager).PathResolver; } - } - - /// - /// Gets the local package manager. - /// - public IPackageManager LocalPackageManager - { - get { return _localPackageManager.Value; } - } - - /// - /// Gets the public package manager. - /// - public IPackageManager PublicPackageManager - { - get { return _publicPackageManager.Value; } - } - } -} diff --git a/src/Umbraco.Core/Packaging/IPackageContext.cs b/src/Umbraco.Core/Packaging/IPackageContext.cs deleted file mode 100644 index 41cf372ce6..0000000000 --- a/src/Umbraco.Core/Packaging/IPackageContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -using NuGet; - -namespace Umbraco.Core.Packaging -{ - internal interface IPackageContext - { - IPackageManager LocalPackageManager { get; } - IPackageManager PublicPackageManager { get; } - IPackagePathResolver LocalPathResolver { get; } - } -} diff --git a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs index 68afd4f244..79dd45b73b 100644 --- a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs +++ b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs @@ -17,10 +17,10 @@ namespace Umbraco.Core public const string NodeXml = /*TableNamePrefix*/ "cms" + "ContentXml"; public const string NodePreviewXml = /*TableNamePrefix*/ "cms" + "PreviewXml"; // fixme dbfix kill merge with ContentXml - public const string ContentType = /*TableNamePrefix*/ "cms" + "ContentType"; // fixme dbfixrename and split uElementType, uDocumentType + public const string ContentType = /*TableNamePrefix*/ "cms" + "ContentType"; // fixme dbfix rename and split uElementType, uDocumentType public const string ContentChildType = /*TableNamePrefix*/ "cms" + "ContentTypeAllowedContentType"; - public const string DocumentType = /*TableNamePrefix*/ "cms" + "DocumentType"; // fixme dbfixmust rename corresponding DTO - public const string ElementTypeTree = /*TableNamePrefix*/ "cms" + "ContentType2ContentType"; // fixme dbfixwhy can't we just use uNode for this? + public const string DocumentType = /*TableNamePrefix*/ "cms" + "DocumentType"; // fixme dbfix must rename corresponding DTO + public const string ElementTypeTree = /*TableNamePrefix*/ "cms" + "ContentType2ContentType"; // fixme dbfix why can't we just use uNode for this? public const string DataType = TableNamePrefix + "DataType"; public const string Template = /*TableNamePrefix*/ "cms" + "Template"; @@ -28,6 +28,7 @@ namespace Umbraco.Core public const string ContentVersion = TableNamePrefix + "ContentVersion"; public const string ContentVersionCultureVariation = TableNamePrefix + "ContentVersionCultureVariation"; public const string Document = TableNamePrefix + "Document"; + public const string DocumentCultureVariation = TableNamePrefix + "DocumentCultureVariation"; public const string DocumentVersion = TableNamePrefix + "DocumentVersion"; public const string MediaVersion = TableNamePrefix + "MediaVersion"; diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs index f427c5aa4a..eea3d5d537 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentNuDto.cs @@ -16,6 +16,12 @@ namespace Umbraco.Core.Persistence.Dtos [Column("published")] public bool Published { get; set; } + /// + /// Stores serialized JSON representing the content item's property and culture name values + /// + /// + /// Pretty much anything that would require a 1:M lookup is serialized here + /// [Column("data")] [SpecialDbType(SpecialDbTypes.NTEXT)] public string Data { get; set; } diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs index 104a707669..12878d5961 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentVersionCultureVariationDto.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Dtos [ExplicitColumns] internal class ContentVersionCultureVariationDto { - private const string TableName = Constants.DatabaseSchema.Tables.ContentVersionCultureVariation; + public const string TableName = Constants.DatabaseSchema.Tables.ContentVersionCultureVariation; [Column("id")] [PrimaryKeyColumn] @@ -25,21 +25,21 @@ namespace Umbraco.Core.Persistence.Dtos [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] public int LanguageId { get; set; } + // this is convenient to carry the culture around, but has no db counterpart + [Ignore] + public string Culture { get; set; } + [Column("name")] - [NullSetting(NullSetting = NullSettings.Null)] public string Name { get; set; } - [Column("available")] - public bool Available { get; set; } - - [Column("availableDate")] - [NullSetting(NullSetting = NullSettings.Null)] - public DateTime? AvailableDate { get; set; } + [Column("date")] + public DateTime Date { get; set; } + // fixme want? [Column("availableUserId")] // [ForeignKey(typeof(UserDto))] -- there is no foreign key so we can delete users without deleting associated content //[NullSetting(NullSetting = NullSettings.Null)] - public int AvailableUserId { get; set; } + public int PublishedUserId { get; set; } [Column("edited")] public bool Edited { get; set; } diff --git a/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs b/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs new file mode 100644 index 0000000000..be4cf7c023 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Dtos/DocumentCultureVariationDto.cs @@ -0,0 +1,34 @@ +using NPoco; +using Umbraco.Core.Persistence.DatabaseAnnotations; + +namespace Umbraco.Core.Persistence.Dtos +{ + [TableName(TableName)] + [PrimaryKey("id")] + [ExplicitColumns] + internal class DocumentCultureVariationDto + { + private const string TableName = Constants.DatabaseSchema.Tables.DocumentCultureVariation; + + [Column("id")] + [PrimaryKeyColumn] + public int Id { get; set; } + + [Column("nodeId")] + [ForeignKey(typeof(NodeDto))] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_NodeId", ForColumns = "nodeId,languageId")] + public int NodeId { get; set; } + + [Column("languageId")] + [ForeignKey(typeof(LanguageDto))] + [Index(IndexTypes.NonClustered, Name = "IX_" + TableName + "_LanguageId")] + public int LanguageId { get; set; } + + // this is convenient to carry the culture around, but has no db counterpart + [Ignore] + public string Culture { get; set; } + + [Column("edited")] + public bool Edited { get; set; } + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs b/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs index 821f092b38..8558ce4a35 100644 --- a/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/MacroDto.cs @@ -34,18 +34,6 @@ namespace Umbraco.Core.Persistence.Dtos [NullSetting(NullSetting = NullSettings.Null)] public string Name { get; set; } - [Column("macroScriptType")] - [NullSetting(NullSetting = NullSettings.Null)] - public string ScriptType { get; set; } - - [Column("macroScriptAssembly")] - [NullSetting(NullSetting = NullSettings.Null)] - public string ScriptAssembly { get; set; } - - [Column("macroXSLT")] - [NullSetting(NullSetting = NullSettings.Null)] - public string Xslt { get; set; } - [Column("macroCacheByPage")] [Constraint(Default = "1")] public bool CacheByPage { get; set; } @@ -58,10 +46,13 @@ namespace Umbraco.Core.Persistence.Dtos [Constraint(Default = "0")] public bool DontRender { get; set; } - //TODO: Rename this column! - actually please revamp all of the macros! :) - [Column("macroPython")] - [NullSetting(NullSetting = NullSettings.Null)] - public string MacroFilePath { get; set; } + [Column("macroSource")] + [NullSetting(NullSetting = NullSettings.NotNull)] + public string MacroSource { get; set; } + + [Column("macroType")] + [NullSetting(NullSetting = NullSettings.NotNull)] + public int MacroType { get; set; } [ResultColumn] [Reference(ReferenceType.Many, ReferenceMemberName = "Macro")] diff --git a/src/Umbraco.Core/Persistence/Dtos/StylesheetDto.cs b/src/Umbraco.Core/Persistence/Dtos/StylesheetDto.cs deleted file mode 100644 index f340d34d16..0000000000 --- a/src/Umbraco.Core/Persistence/Dtos/StylesheetDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using NPoco; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Persistence.Dtos -{ - [Obsolete("This is no longer used and will be removed from Umbraco in future versions")] - internal class StylesheetDto - { - [Column("nodeId")] - [PrimaryKeyColumn(AutoIncrement = false)] - [ForeignKey(typeof(NodeDto))] - public int NodeId { get; set; } - - [Column("filename")] - [Length(100)] - public string Filename { get; set; } - - [Column("content")] - [SpecialDbType(SpecialDbTypes.NTEXT)] - [NullSetting(NullSetting = NullSettings.Null)] - public string Content { get; set; } - } -} diff --git a/src/Umbraco.Core/Persistence/Dtos/StylesheetPropertyDto.cs b/src/Umbraco.Core/Persistence/Dtos/StylesheetPropertyDto.cs deleted file mode 100644 index 5ef1aed9cc..0000000000 --- a/src/Umbraco.Core/Persistence/Dtos/StylesheetPropertyDto.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using NPoco; -using Umbraco.Core.Persistence.DatabaseAnnotations; - -namespace Umbraco.Core.Persistence.Dtos -{ - [Obsolete("This is no longer used and will be removed from Umbraco in future versions")] - internal class StylesheetPropertyDto - { - [Column("nodeId")] - [PrimaryKeyColumn(AutoIncrement = false)] - public int NodeId { get; set; } - - [Column("stylesheetPropertyEditor")] - [NullSetting(NullSetting = NullSettings.Null)] - public bool? Editor { get; set; } - - [Column("stylesheetPropertyAlias")] - [NullSetting(NullSetting = NullSettings.Null)] - [Length(50)] - public string Alias { get; set; } - - [Column("stylesheetPropertyValue")] - [NullSetting(NullSetting = NullSettings.Null)] - [Length(400)] - public string Value { get; set; } - } -} diff --git a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs index a854a3a65f..3963c591f0 100644 --- a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Factories { public IMacro BuildEntity(MacroDto dto) { - var model = new Macro(dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.MacroFilePath); + var model = new Macro(dto.Id, dto.UniqueId, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.MacroSource, (MacroTypes)dto.MacroType); try { @@ -40,13 +40,11 @@ namespace Umbraco.Core.Persistence.Factories CachePersonalized = entity.CacheByMember, DontRender = entity.DontRender, Name = entity.Name, - MacroFilePath = entity.ScriptPath, + MacroSource = entity.MacroSource, RefreshRate = entity.CacheDuration, - ScriptAssembly = entity.ControlAssembly, - ScriptType = entity.ControlType, UseInEditor = entity.UseInEditor, - Xslt = entity.XsltPath, - MacroPropertyDtos = BuildPropertyDtos(entity) + MacroPropertyDtos = BuildPropertyDtos(entity), + MacroType = (int)entity.MacroType }; if (entity.HasIdentity) diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 4e2789bab7..d25f921f1a 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -3,12 +3,13 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.Persistence.Repositories; namespace Umbraco.Core.Persistence.Factories { internal static class PropertyFactory { - public static IEnumerable BuildEntities(PropertyType[] propertyTypes, IReadOnlyCollection dtos, int publishedVersionId) + public static IEnumerable BuildEntities(PropertyType[] propertyTypes, IReadOnlyCollection dtos, int publishedVersionId, ILanguageRepository languageRepository) { var properties = new List(); var xdtos = dtos.GroupBy(x => x.PropertyTypeId).ToDictionary(x => x.Key, x => (IEnumerable) x); @@ -26,7 +27,7 @@ namespace Umbraco.Core.Persistence.Factories if (xdtos.TryGetValue(propertyType.Id, out var propDtos)) { foreach (var propDto in propDtos) - property.FactorySetValue(propDto.LanguageId, propDto.Segment, propDto.VersionId == publishedVersionId, propDto.Value); + property.FactorySetValue(languageRepository.GetIsoCodeById(propDto.LanguageId), propDto.Segment, propDto.VersionId == publishedVersionId, propDto.Value); } property.ResetDirtyProperties(false); @@ -41,12 +42,12 @@ namespace Umbraco.Core.Persistence.Factories return properties; } - private static PropertyDataDto BuildDto(int versionId, Property property, int? nLanguageId, string segment, object value) + private static PropertyDataDto BuildDto(int versionId, Property property, int? languageId, string segment, object value) { var dto = new PropertyDataDto { VersionId = versionId, PropertyTypeId = property.PropertyTypeId }; - if (nLanguageId.HasValue) - dto.LanguageId = nLanguageId; + if (languageId.HasValue) + dto.LanguageId = languageId; if (segment != null) dto.Segment = segment; @@ -88,33 +89,50 @@ namespace Umbraco.Core.Persistence.Factories return dto; } - public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, out bool edited) + public static IEnumerable BuildDtos(int currentVersionId, int publishedVersionId, IEnumerable properties, ILanguageRepository languageRepository, out bool edited, out HashSet editedCultures) { var propertyDataDtos = new List(); edited = false; + editedCultures = null; // don't allocate unless necessary foreach (var property in properties) { if (property.PropertyType.IsPublishing) - { + { + // fixme + // why only CultureNeutral? + // then, the tree can only show when a CultureNeutral value has been modified, but not when + // a CultureSegment has been modified, so if I edit some french/mobile thing, the tree will + // NOT tell me that I have changes? + + var editingCultures = property.PropertyType.Variations.Has(ContentVariation.CultureNeutral); + if (editingCultures && editedCultures == null) editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase); + // publishing = deal with edit and published values foreach (var propertyValue in property.Values) { // deal with published value if (propertyValue.PublishedValue != null && publishedVersionId > 0) - propertyDataDtos.Add(BuildDto(publishedVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.PublishedValue)); + propertyDataDtos.Add(BuildDto(publishedVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.PublishedValue)); // deal with edit value if (propertyValue.EditedValue != null) - propertyDataDtos.Add(BuildDto(currentVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.EditedValue)); + propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue)); // deal with missing edit value (fix inconsistencies) else if (propertyValue.PublishedValue != null) - propertyDataDtos.Add(BuildDto(currentVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.PublishedValue)); + propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.PublishedValue)); // use explicit equals here, else object comparison fails at comparing eg strings var sameValues = propertyValue.PublishedValue == null ? propertyValue.EditedValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditedValue); edited |= !sameValues; + + if (editingCultures && // cultures can be edited, ie CultureNeutral is supported + propertyValue.Culture != null && propertyValue.Segment == null && // and value is CultureNeutral + !sameValues) // and edited and published are different + { + editedCultures.Add(propertyValue.Culture); // report culture as edited + } } } else @@ -123,7 +141,7 @@ namespace Umbraco.Core.Persistence.Factories { // not publishing = only deal with edit values if (propertyValue.EditedValue != null) - propertyDataDtos.Add(BuildDto(currentVersionId, property, propertyValue.LanguageId, propertyValue.Segment, propertyValue.EditedValue)); + propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue)); } edited = true; } diff --git a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs index c60feea9b8..489bd40ddf 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs @@ -18,14 +18,12 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Alias, dto => dto.Alias); CacheMap(src => src.CacheByPage, dto => dto.CacheByPage); CacheMap(src => src.CacheByMember, dto => dto.CachePersonalized); - CacheMap(src => src.ControlAssembly, dto => dto.ScriptAssembly); - CacheMap(src => src.ControlType, dto => dto.ScriptType); + CacheMap(src => src.MacroType, dto => dto.MacroType); CacheMap(src => src.DontRender, dto => dto.DontRender); CacheMap(src => src.Name, dto => dto.Name); CacheMap(src => src.CacheDuration, dto => dto.RefreshRate); - CacheMap(src => src.ScriptPath, dto => dto.MacroFilePath); + CacheMap(src => src.MacroSource, dto => dto.MacroSource); CacheMap(src => src.UseInEditor, dto => dto.UseInEditor); - CacheMap(src => src.XsltPath, dto => dto.Xslt); } } } diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index 7b12e2e69d..60172ef687 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -544,6 +544,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql SelectTop(this Sql sql, int count) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.SqlContext.SqlSyntax.SelectTop(sql, count); } @@ -554,6 +555,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql SelectCount(this Sql sql) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select("COUNT(*)"); } @@ -569,6 +571,7 @@ namespace Umbraco.Core.Persistence /// public static Sql SelectCount(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); var columns = fields.Length == 0 ? sql.GetColumns(withAlias: false) : fields.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); @@ -582,6 +585,7 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql SelectAll(this Sql sql) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select("*"); } @@ -597,6 +601,7 @@ namespace Umbraco.Core.Persistence /// public static Sql Select(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select(sql.GetColumns(columnExpressions: fields)); } @@ -613,6 +618,7 @@ namespace Umbraco.Core.Persistence /// public static Sql Select(this Sql sql, string tableAlias, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Select(sql.GetColumns(tableAlias: tableAlias, columnExpressions: fields)); } @@ -628,6 +634,7 @@ namespace Umbraco.Core.Persistence /// public static Sql AndSelect(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: fields))); } @@ -645,6 +652,7 @@ namespace Umbraco.Core.Persistence /// public static Sql AndSelect(this Sql sql, string tableAlias, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return sql.Append(", " + string.Join(", ", sql.GetColumns(tableAlias: tableAlias, columnExpressions: fields))); } @@ -657,6 +665,8 @@ namespace Umbraco.Core.Persistence /// The Sql statement. public static Sql Select(this Sql sql, Func, SqlRef> reference) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); + sql.Select(sql.GetColumns()); reference?.Invoke(new SqlRef(sql, null)); @@ -675,6 +685,8 @@ namespace Umbraco.Core.Persistence /// is added, so that it is possible to add (e.g. calculated) columns to the referencing Dto. public static Sql Select(this Sql sql, Func, SqlRef> reference, Func, Sql> sqlexpr) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); + sql.Select(sql.GetColumns()); sql = sqlexpr(sql); @@ -789,6 +801,7 @@ namespace Umbraco.Core.Persistence /// public static string Columns(this Sql sql, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return string.Join(", ", sql.GetColumns(columnExpressions: fields, withAlias: false)); } @@ -805,6 +818,7 @@ namespace Umbraco.Core.Persistence /// public static string Columns(this Sql sql, string alias, params Expression>[] fields) { + if (sql == null) throw new ArgumentNullException(nameof(sql)); return string.Join(", ", sql.GetColumns(columnExpressions: fields, withAlias: false, tableAlias: alias)); } diff --git a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs index 36dd10c3fb..b86898f97a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs @@ -4,10 +4,38 @@ namespace Umbraco.Core.Persistence.Repositories { public interface ILanguageRepository : IReadWriteQueryRepository { - ILanguage GetByCultureName(string cultureName); ILanguage GetByIsoCode(string isoCode); - int GetIdByIsoCode(string isoCode); - string GetIsoCodeById(int id); + /// + /// Gets a language identifier from its ISO code. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetIdByIsoCode(string isoCode); + + /// + /// Gets a language ISO code from its identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetIsoCodeById(int? id); + + /// + /// Gets the default language ISO code. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetDefaultIsoCode(); + + /// + /// Gets the default language identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetDefaultId(); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IXsltFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IXsltFileRepository.cs deleted file mode 100644 index 2e9e840036..0000000000 --- a/src/Umbraco.Core/Persistence/Repositories/IXsltFileRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.IO; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories -{ - public interface IXsltFileRepository : IReadRepository, IWriteRepository - { - bool ValidateXsltFile(XsltFile xsltFile); - Stream GetFileContentStream(string filepath); - void SetFileContent(string filepath, Stream content); - long GetFileSize(string filepath); - } -} diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 84ff426e91..e95e296e0a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -20,6 +20,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -35,12 +36,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement where TEntity : class, IUmbracoEntity where TRepository : class, IRepository { - protected ContentRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger) + protected ContentRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILanguageRepository languageRepository, ILogger logger) : base(scopeAccessor, cache, logger) - { } + { + LanguageRepository = languageRepository; + } protected abstract TRepository This { get; } + protected ILanguageRepository LanguageRepository { get; } + protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // fixme inject #region Versions @@ -464,11 +469,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement { propertyDataDtos.AddRange(propertyDataDtos1); if (temp.VersionId == temp.PublishedVersionId) // dirty corner case - propertyDataDtos.AddRange(propertyDataDtos1.Select(x => x.Clone(-1))); + propertyDataDtos.AddRange(propertyDataDtos1.Select(x => x.Clone(-1))); } if (temp.VersionId != temp.PublishedVersionId && indexedPropertyDataDtos.TryGetValue(temp.PublishedVersionId, out var propertyDataDtos2)) propertyDataDtos.AddRange(propertyDataDtos2); - var properties = PropertyFactory.BuildEntities(compositionProperties, propertyDataDtos, temp.PublishedVersionId).ToList(); + var properties = PropertyFactory.BuildEntities(compositionProperties, propertyDataDtos, temp.PublishedVersionId, LanguageRepository).ToList(); // deal with tags foreach (var property in properties) @@ -852,7 +857,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected virtual string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { var template = SqlContext.Templates.Get("Umbraco.Core.VersionableRepository.EnsureUniqueNodeName", tsql => tsql - .Select(x => NPocoSqlExtensions.Statics.Alias(x.NodeId, "id"), x => NPocoSqlExtensions.Statics.Alias(x.Text, "name")) + .Select(x => Alias(x.NodeId, "id"), x => Alias(x.Text, "name")) .From() .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType") && x.ParentId == SqlTemplate.Arg("parentId"))); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs index 86444a4cc3..4556c78fe6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -280,7 +281,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private string EnsureUniqueNodeName(string nodeName, int id = 0) { var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql - .Select(x => NPocoSqlExtensions.Statics.Alias(x.NodeId, "id"), x => NPocoSqlExtensions.Statics.Alias(x.Text, "name")) + .Select(x => Alias(x.NodeId, "id"), x => Alias(x.Text, "name")) .From() .Where(x => x.NodeObjectType == SqlTemplate.Arg("nodeObjectType"))); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index cc6247210b..3e96f4cf0e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -25,19 +25,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly IContentTypeRepository _contentTypeRepository; private readonly ITemplateRepository _templateRepository; private readonly ITagRepository _tagRepository; - private readonly ILanguageRepository _languageRepository; private readonly CacheHelper _cacheHelper; private PermissionRepository _permissionRepository; private readonly ContentByGuidReadRepository _contentByGuidReadRepository; private readonly IScopeAccessor _scopeAccessor; public DocumentRepository(IScopeAccessor scopeAccessor, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IContentSection settings) - : base(scopeAccessor, cacheHelper, logger) + : base(scopeAccessor, cacheHelper, languageRepository, logger) { _contentTypeRepository = contentTypeRepository ?? throw new ArgumentNullException(nameof(contentTypeRepository)); _templateRepository = templateRepository ?? throw new ArgumentNullException(nameof(templateRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); - _languageRepository = languageRepository; _cacheHelper = cacheHelper; _scopeAccessor = scopeAccessor; _contentByGuidReadRepository = new ContentByGuidReadRepository(this, scopeAccessor, cacheHelper, logger); @@ -179,9 +177,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement "DELETE FROM " + Constants.DatabaseSchema.Tables.TagRelationship + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.Domain + " WHERE domainRootStructureID = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.Document + " WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentCultureVariation + " WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.DocumentVersion + " WHERE id IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", "DELETE FROM " + Constants.DatabaseSchema.Tables.PropertyData + " WHERE versionId IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", "DELETE FROM cmsPreviewXml WHERE nodeId = @id", + "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersionCultureVariation + " WHERE versionId IN (SELECT id FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id)", "DELETE FROM " + Constants.DatabaseSchema.Tables.ContentVersion + " WHERE nodeId = @id", "DELETE FROM cmsContentXml WHERE nodeId = @id", "DELETE FROM " + Constants.DatabaseSchema.Tables.Content + " WHERE nodeId = @id", @@ -231,6 +231,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistNewItem(IContent entity) { + //fixme - stop doing this just so we have access to AddingEntity var content = (Content) entity; content.AddingEntity(); @@ -240,7 +241,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (entity.Template == null) entity.Template = entity.ContentType.DefaultTemplate; - // ensure unique name on the same level + // sanitize names: ensure we have an invariant name, and names are unique-ish + // (well, only invariant name is unique at the moment) + EnsureInvariantNameValues(entity, publishing); entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name); // ensure that strings don't contain characters that are invalid in xml @@ -307,7 +310,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement content.PublishedVersionId = content.VersionId; contentVersionDto.Id = 0; contentVersionDto.Current = true; - contentVersionDto.Text = content.Name; + contentVersionDto.Text = content.PublishName; Database.Insert(contentVersionDto); content.VersionId = contentVersionDto.Id; @@ -317,10 +320,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, out var edited); + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, content.PublishedVersionId, entity.Properties, LanguageRepository, out var edited, out var editedCultures); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); + // name also impacts 'edited' + if (content.PublishName != content.Name) + edited = true; + // persist the document dto // at that point, when publishing, the entity still has its old Published value // so we need to explicitely update the dto to persist the correct value @@ -331,12 +338,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(dto); // persist the variations - if ((content.ContentType.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) > 0) + if (content.ContentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)) { - foreach (var variationDto in GetVariationDtos(content, publishing)) - Database.Insert(variationDto); + // names also impact 'edited' + foreach (var (culture, name) in content.Names) + if (name != content.GetPublishName(culture)) + (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture); + + // insert content variations + Database.BulkInsertRecords(GetContentVariationDtos(content, publishing)); + + // insert document variations + Database.BulkInsertRecords(GetDocumentVariationDtos(content, publishing, editedCultures)); } + // refresh content + if (editedCultures != null) + content.SetCultureEdited(editedCultures); + // trigger here, before we reset Published etc OnUowRefreshedEntity(new ScopedEntityEventArgs(AmbientScope, entity)); @@ -402,7 +421,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Execute(Sql().Update(u => u.Set(x => x.Published, false)).Where(x => x.Id == content.PublishedVersionId)); } - // ensure unique name on the same level + // sanitize names: ensure we have an invariant name, and names are unique-ish + // (well, only invariant name is unique at the moment) + EnsureInvariantNameValues(entity, publishing); entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name, entity.Id); // ensure that strings don't contain characters that are invalid in xml @@ -456,26 +477,51 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(documentVersionDto); } - var versionToDelete = publishing ? new[] { content.VersionId, content.PublishedVersionId } : new[] { content.VersionId }; - - // replace the property data - var deletePropertyDataSql = Sql().Delete().WhereIn(x => x.VersionId, versionToDelete); + // replace the property data (rather than updating) + // only need to delete for the version that existed, the new version (if any) has no property data yet + var versionToDelete = publishing ? content.PublishedVersionId : content.VersionId; + var deletePropertyDataSql = Sql().Delete().Where(x => x.VersionId == versionToDelete); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, out var edited); + // insert property data + var propertyDataDtos = PropertyFactory.BuildDtos(content.VersionId, publishing ? content.PublishedVersionId : 0, entity.Properties, LanguageRepository, out var edited, out var editedCultures); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); - // replace the variations - if ((content.ContentType.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) > 0) - { - var deleteVariations = Sql().Delete().WhereIn(x => x.VersionId, versionToDelete); - Database.Execute(deleteVariations); + // name also impacts 'edited' + if (content.PublishName != content.Name) + edited = true; - foreach (var variationDto in GetVariationDtos(content, publishing)) - Database.Insert(variationDto); + if (content.ContentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)) + { + // names also impact 'edited' + foreach (var (culture, name) in content.Names) + if (name != content.GetPublishName(culture)) + (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(culture); + + // replace the content version variations (rather than updating) + // only need to delete for the version that existed, the new version (if any) has no property data yet + var deleteContentVariations = Sql().Delete().Where(x => x.VersionId == versionToDelete); + Database.Execute(deleteContentVariations); + + // replace the document version variations (rather than updating) + var deleteDocumentVariations = Sql().Delete().Where(x => x.NodeId == content.Id); + Database.Execute(deleteDocumentVariations); + + // fixme is we'd like to use the native NPoco InsertBulk here but it causes problems (not sure exaclty all scenarios) but by using SQL Server and updating a variants name will cause: Unable to cast object of type 'Umbraco.Core.Persistence.FaultHandling.RetryDbConnection' to type 'System.Data.SqlClient.SqlConnection'. + // (same in PersistNewItem above) + + // insert content variations + Database.BulkInsertRecords(GetContentVariationDtos(content, publishing)); + + // insert document variations + Database.BulkInsertRecords(GetDocumentVariationDtos(content, publishing, editedCultures)); } + // refresh content + if (editedCultures != null) + content.SetCultureEdited(editedCultures); + // update the document dto // at that point, when un/publishing, the entity still has its old Published value // so we need to explicitely update the dto to persist the correct value @@ -855,12 +901,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } // set variations, if varying - temps = temps.Where(x => x.ContentType.Variations.HasFlag(ContentVariation.CultureNeutral | ContentVariation.CultureSegment)).ToList(); + temps = temps.Where(x => x.ContentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)).ToList(); if (temps.Count > 0) { - var variations = GetVariations(temps); + // load all variations for all documents from database, in one query + var contentVariations = GetContentVariations(temps); + var documentVariations = GetDocumentVariations(temps); foreach (var temp in temps) - SetContentVariations(temp.Content, variations); + SetVariations(temp.Content, contentVariations, documentVariations); } return content; @@ -888,10 +936,11 @@ namespace Umbraco.Core.Persistence.Repositories.Implement content.Properties = properties[dto.DocumentVersionDto.Id]; // set variations, if varying - if ((contentType.Variations & (ContentVariation.CultureNeutral | ContentVariation.CultureSegment)) > 0) + if (contentType.Variations.HasAny(Models.ContentVariation.CultureNeutral | Models.ContentVariation.CultureSegment)) { - var variations = GetVariations(ltemp); - SetContentVariations(content, variations); + var contentVariations = GetContentVariations(ltemp); + var documentVariations = GetDocumentVariations(ltemp); + SetVariations(content, contentVariations, documentVariations); } // reset dirty initial properties (U4-1946) @@ -899,21 +948,20 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return content; } - private void SetContentVariations(Content content, IDictionary> variations) + private void SetVariations(Content content, IDictionary> contentVariations, IDictionary> documentVariations) { - if (variations.TryGetValue(content.VersionId, out var variation)) - foreach (var v in variation) - { - content.SetName(v.LanguageId, v.Name); - } - if (content.PublishedVersionId > 0 && variations.TryGetValue(content.PublishedVersionId, out variation)) - foreach (var v in variation) - { - content.SetPublishName(v.LanguageId, v.Name); - } + if (contentVariations.TryGetValue(content.VersionId, out var contentVariation)) + foreach (var v in contentVariation) + content.SetName(v.Culture, v.Name); + if (content.PublishedVersionId > 0 && contentVariations.TryGetValue(content.PublishedVersionId, out contentVariation)) + foreach (var v in contentVariation) + content.SetPublishInfos(v.Culture, v.Name, v.Date); + if (documentVariations.TryGetValue(content.Id, out var documentVariation)) + foreach (var v in documentVariation.Where(x => x.Edited)) + content.SetCultureEdited(v.Culture); } - private IDictionary> GetVariations(List> temps) + private IDictionary> GetContentVariations(List> temps) where T : class, IContentBase { var versions = new List(); @@ -923,7 +971,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement if (temp.PublishedVersionId > 0) versions.Add(temp.PublishedVersionId); } - if (versions.Count == 0) return new Dictionary>(); + if (versions.Count == 0) return new Dictionary>(); var dtos = Database.FetchByGroups(versions, 2000, batch => Sql() @@ -931,54 +979,145 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .From() .WhereIn(x => x.VersionId, batch)); - var variations = new Dictionary>(); + var variations = new Dictionary>(); foreach (var dto in dtos) { if (!variations.TryGetValue(dto.VersionId, out var variation)) - variations[dto.VersionId] = variation = new List(); + variations[dto.VersionId] = variation = new List(); - variation.Add(new CultureVariation + variation.Add(new ContentVariation { - LanguageId = dto.LanguageId, + Culture = LanguageRepository.GetIsoCodeById(dto.LanguageId), Name = dto.Name, - Available = dto.Available + Date = dto.Date }); } return variations; } - private IEnumerable GetVariationDtos(IContent content, bool publishing) + private IDictionary> GetDocumentVariations(List> temps) + where T : class, IContentBase { + var ids = temps.Select(x => x.Id); + + var dtos = Database.FetchByGroups(ids, 2000, batch => + Sql() + .Select() + .From() + .WhereIn(x => x.NodeId, batch)); + + var variations = new Dictionary>(); + + foreach (var dto in dtos) + { + if (!variations.TryGetValue(dto.NodeId, out var variation)) + variations[dto.NodeId] = variation = new List(); + + variation.Add(new DocumentVariation + { + Culture = LanguageRepository.GetIsoCodeById(dto.LanguageId), + Edited = dto.Edited + }); + } + + return variations; + } + + private IEnumerable GetContentVariationDtos(IContent content, bool publishing) + { + // create dtos for the 'current' (non-published) version, all cultures foreach (var (culture, name) in content.Names) yield return new ContentVersionCultureVariationDto { VersionId = content.VersionId, - LanguageId = culture, - Name = name + LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), + Culture = culture, + Name = name, + Date = content.UpdateDate }; + // if not publishing, we're just updating the 'current' (non-published) version, + // so there are no DTOs to create for the 'published' version which remains unchanged if (!publishing) yield break; + // create dtos for the 'published' version, for published cultures (those having a name) foreach (var (culture, name) in content.PublishNames) yield return new ContentVersionCultureVariationDto { VersionId = content.PublishedVersionId, - LanguageId = culture, - Name = name + LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), + Culture = culture, + Name = name, + Date = content.GetDateCulturePublished(culture) }; } - private class CultureVariation + private IEnumerable GetDocumentVariationDtos(IContent content, bool publishing, HashSet editedCultures) { - public int LanguageId { get; set; } + foreach (var (culture, name) in content.Names) + yield return new DocumentCultureVariationDto + { + NodeId = content.Id, + LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."), + Culture = culture, + Edited = !content.IsCulturePublished(culture) || editedCultures.Contains(culture) // if not published, always edited + }; + } + + private class ContentVariation + { + public string Culture { get; set; } public string Name { get; set; } - public bool Available { get; set; } + public DateTime Date { get; set; } + } + + private class DocumentVariation + { + public string Culture { get; set; } + public bool Edited { get; set; } } #region Utilities + /// + /// Ensures that the Name/PublishName properties are filled in and validates if all names are null + /// + private void EnsureInvariantNameValues(IContent content, bool publishing) + { + // here we have to ensure we have names and publish names, + // and to try and fix the situation if we have no name + // see also: U4-11286 + + // cannot save without an invariant name + if (string.IsNullOrWhiteSpace(content.Name)) + { + // no variant name = error + if (content.Names.Count == 0) + throw new InvalidOperationException("Cannot save content with an empty name."); + + // else pick the name for the default culture, or any name + var defaultCulture = LanguageRepository.GetDefaultIsoCode(); + if (defaultCulture != null && content.Names.TryGetValue(defaultCulture, out var cultureName)) + content.Name = cultureName; + else + content.Name = content.Names.First().Value; // only option is to take the first + } + + // cannot publish without an invariant name + if (publishing && string.IsNullOrWhiteSpace(content.PublishName)) + { + // no variant name = error + if (content.PublishNames.Count == 0) + throw new InvalidOperationException("Cannot publish content with an empty name."); + + // else... we cannot deal with it here because PublishName is readonly, so in reality, PublishName + // should not be null because it should have been set when preparing the content for publish. + // see also: Content.SetPublishInfos() - it deals with PublishName + } + } + protected override string EnsureUniqueNodeName(int parentId, string nodeName, int id = 0) { return EnsureUniqueNaming == false ? nodeName : base.EnsureUniqueNodeName(parentId, nodeName, id); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs index 857e059942..0dd7a25798 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/EntityRepository.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Scoping; +using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics; namespace Umbraco.Core.Persistence.Repositories.Implement { @@ -23,9 +24,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement internal class EntityRepository : IEntityRepository { private readonly IScopeAccessor _scopeAccessor; - public EntityRepository(IScopeAccessor scopeAccessor) + private readonly ILanguageRepository _langRepository; + + public EntityRepository(IScopeAccessor scopeAccessor, ILanguageRepository langRepository) { _scopeAccessor = scopeAccessor; + _langRepository = langRepository; } protected IUmbracoDatabase Database => _scopeAccessor.AmbientScope.Database; @@ -128,6 +132,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var dtos = page.Items; var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); + //TODO: For isContent will we need to build up the variation info? + if (isMedia) BuildProperties(entities, dtos); @@ -142,21 +148,36 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return dto == null ? null : BuildEntity(false, false, dto); } + private IEntitySlim GetEntity(Sql sql, bool isContent, bool isMedia) + { + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var dtos = Database.FetchOneToMany( + ddto => ddto.VariationInfo, + ddto => ddto.VersionId, + sql); + return dtos.Count == 0 ? null : BuildDocumentEntity(dtos[0]); + } + + var dto = Database.FirstOrDefault(sql); + if (dto == null) return null; + + var entity = BuildEntity(false, isMedia, dto); + + if (isMedia) + BuildProperties(entity, dto); + + return entity; + } + public IEntitySlim Get(Guid key, Guid objectTypeId) { var isContent = objectTypeId == Constants.ObjectTypes.Document || objectTypeId == Constants.ObjectTypes.DocumentBlueprint; var isMedia = objectTypeId == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, key); - var dto = Database.FirstOrDefault(sql); - if (dto == null) return null; - - var entity = BuildEntity(isContent, isMedia, dto); - - if (isMedia) - BuildProperties(entity, dto); - - return entity; + return GetEntity(sql, isContent, isMedia); } public virtual IEntitySlim Get(int id) @@ -172,15 +193,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectTypeId == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, id); - var dto = Database.FirstOrDefault(sql); - if (dto == null) return null; - - var entity = BuildEntity(isContent, isMedia, dto); - - if (isMedia) - BuildProperties(entity, dto); - - return entity; + return GetEntity(sql, isContent, isMedia); } public virtual IEnumerable GetAll(Guid objectType, params int[] ids) @@ -197,21 +210,38 @@ namespace Umbraco.Core.Persistence.Repositories.Implement : PerformGetAll(objectType); } + private IEnumerable GetEntities(Sql sql, bool isContent, bool isMedia) + { + //isContent is going to return a 1:M result now with the variants so we need to do different things + if (isContent) + { + var cdtos = Database.FetchOneToMany( + dto => dto.VariationInfo, + dto => dto.VersionId, + sql); + return cdtos.Count == 0 + ? Enumerable.Empty() + : cdtos.Select(BuildDocumentEntity).ToArray(); + } + + var dtos = Database.Fetch(sql); + if (dtos.Count == 0) return Enumerable.Empty(); + + var entities = dtos.Select(x => BuildEntity(false, isMedia, x)).ToArray(); + + if (isMedia) + BuildProperties(entities, dtos); + + return entities; + } + private IEnumerable PerformGetAll(Guid objectType, Action> filter = null) { var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint; var isMedia = objectType == Constants.ObjectTypes.Media; var sql = GetFullSqlForEntityType(isContent, isMedia, objectType, filter); - var dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); - - var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - - if (isMedia) - BuildProperties(entities, dtos); - - return entities; + return GetEntities(sql, isContent, isMedia); } public virtual IEnumerable GetAllPaths(Guid objectType, params int[] ids) @@ -251,18 +281,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var isMedia = objectType == Constants.ObjectTypes.Media; var sql = GetBaseWhere(isContent, isMedia, false, null, objectType); + var translator = new SqlTranslator(sql, query); sql = translator.Translate(); sql = AddGroupBy(isContent, isMedia, sql); - var dtos = Database.Fetch(sql); - if (dtos.Count == 0) return Enumerable.Empty(); - var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray(); - - if (isMedia) - BuildProperties(entities, dtos); - - return entities; + return GetEntities(sql, isContent, isMedia); } public UmbracoObjectTypes GetObjectType(int id) @@ -409,8 +433,33 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // return wrappedSql; //} - // the DTO corresponding to fields selected by GetBase + + /// + /// The DTO used to fetch results for a content item with it's variation info + /// + private class ContentEntityDto : BaseDto + { + [ResultColumn, Reference(ReferenceType.Many)] + public List VariationInfo { get; set; } + } + + /// + /// The DTO used in the 1:M result for content variation info + /// + private class ContentEntityVariationInfoDto + { + [Column("versionCultureId")] + public int VersionCultureId { get; set; } + [Column("versionCultureLangId")] + public int LanguageId { get; set; } + [Column("versionCultureName")] + public string Name { get; set; } + } + // ReSharper disable once ClassNeverInstantiated.Local + /// + /// the DTO corresponding to fields selected by GetBase + /// private class BaseDto { // ReSharper disable UnusedAutoPropertyAccessor.Local @@ -455,14 +504,21 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .AndSelect(x => x.SortOrder, x => x.UniqueId, x => x.Text, x => x.NodeObjectType, x => x.CreateDate) .Append(", COUNT(child.id) AS children"); - if (isContent) - sql - .AndSelect(x => x.Published, x => x.Edited); - if (isContent || isMedia) sql .AndSelect(x => NPocoSqlExtensions.Statics.Alias(x.Id, "versionId")) .AndSelect(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer); + + if (isContent) + { + sql + .AndSelect(x => x.Published, x => x.Edited) + //This MUST come last in the select statements since we will end up with a 1:M query + .AndSelect( + x => Alias(x.Id, "versionCultureId"), + x => Alias(x.LanguageId, "versionCultureLangId"), + x => Alias(x.Name, "versionCultureName")); + } } sql @@ -482,10 +538,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .InnerJoin().On((left, right) => left.NodeId == right.NodeId); } + //Any LeftJoin statements need to come last if (isCount == false) + { sql .LeftJoin("child").On((left, right) => left.NodeId == right.ParentId, aliasRight: "child"); + if (isContent) + sql + .LeftJoin().On((left, right) => left.Id == right.VersionId); + } + + filter?.Invoke(sql); return sql; @@ -542,8 +606,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement .AndBy(x => x.SortOrder, x => x.UniqueId, x => x.Text, x => x.NodeObjectType, x => x.CreateDate); if (isContent) + { sql - .AndBy(x => x.Published, x => x.Edited); + .AndBy(x => x.Published, x => x.Edited) + .AndBy(x => x.Id, x => x.LanguageId, x => x.Name); + } + if (isContent || isMedia) sql @@ -819,6 +887,30 @@ namespace Umbraco.Core.Persistence.Repositories.Implement return entity; } + /// + /// Builds the from a and ensures the AdditionalData is populated with variant info + /// + /// + /// + private EntitySlim BuildDocumentEntity(ContentEntityDto dto) + { + // EntitySlim does not track changes + var entity = new DocumentEntitySlim(); + BuildDocumentEntity(entity, dto); + var variantInfo = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + if (dto.VariationInfo != null) + { + foreach (var info in dto.VariationInfo) + { + var isoCode = _langRepository.GetIsoCodeById(info.LanguageId); + if (isoCode != null) + variantInfo[isoCode] = info.Name; + } + entity.AdditionalData["CultureNames"] = variantInfo; + } + return entity; + } + #endregion } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index ae9336593d..1ca91d8774 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// internal class LanguageRepository : NPocoRepositoryBase, ILanguageRepository { - private readonly Dictionary _codeIdMap = new Dictionary(); + private readonly Dictionary _codeIdMap = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _idCodeMap = new Dictionary(); public LanguageRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger) @@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement foreach (var language in languages) { _codeIdMap[language.IsoCode] = language.Id; - _idCodeMap[language.Id] = language.IsoCode; + _idCodeMap[language.Id] = language.IsoCode.ToLowerInvariant(); } } @@ -199,27 +199,22 @@ namespace Umbraco.Core.Persistence.Repositories.Implement var entity = factory.BuildEntity(dto); return entity; } - - public ILanguage GetByCultureName(string cultureName) - { - // use the underlying GetMany which will force cache all languages - // TODO we are cloning ALL in GetMany just to retrieve ONE, this is surely not optimized - return GetMany().FirstOrDefault(x => x.CultureName.InvariantEquals(cultureName)); - } - + public ILanguage GetByIsoCode(string isoCode) { TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way var id = GetIdByIsoCode(isoCode, throwOnNotFound: false); - return id > 0 ? Get(id) : null; + return id.HasValue ? Get(id.Value) : null; } // fast way of getting an id for an isoCode - avoiding cloning // _codeIdMap is rebuilt whenever PerformGetAll runs - public int GetIdByIsoCode(string isoCode) => GetIdByIsoCode(isoCode, throwOnNotFound: true); + public int? GetIdByIsoCode(string isoCode) => GetIdByIsoCode(isoCode, throwOnNotFound: true); - private int GetIdByIsoCode(string isoCode, bool throwOnNotFound) + private int? GetIdByIsoCode(string isoCode, bool throwOnNotFound) { + if (isoCode == null) return null; + TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way lock (_codeIdMap) { @@ -232,18 +227,50 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // fast way of getting an isoCode for an id - avoiding cloning // _idCodeMap is rebuilt whenever PerformGetAll runs - public string GetIsoCodeById(int id) => GetIsoCodeById(id, throwOnNotFound: true); + public string GetIsoCodeById(int? id) => GetIsoCodeById(id, throwOnNotFound: true); - private string GetIsoCodeById(int id, bool throwOnNotFound) + private string GetIsoCodeById(int? id, bool throwOnNotFound) { + if (id == null) return null; + TypedCachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way lock (_codeIdMap) // yes, we want to lock _codeIdMap { - if (_idCodeMap.TryGetValue(id, out var isoCode)) return isoCode; + if (_idCodeMap.TryGetValue(id.Value, out var isoCode)) return isoCode; } if (throwOnNotFound) throw new ArgumentException($"Id {id} does not correspond to an existing language.", nameof(id)); return null; } + + public string GetDefaultIsoCode() + { + return GetDefault()?.IsoCode; + } + + public int? GetDefaultId() + { + return GetDefault()?.Id; + } + + // do NOT leak that language, it's not deep-cloned! + private ILanguage GetDefault() + { + // get all cached, non-cloned + var all = TypedCachePolicy.GetAllCached(PerformGetAll); + + ILanguage first = null; + foreach (var language in all) + { + // if one language is default, return + if (language.IsDefaultVariantLanguage) + return language; + // keep track of language with lowest id + if (first == null || language.Id < first.Id) + first = language; + } + + return first; + } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index a08ecef98d..982a5bb885 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -25,8 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly ITagRepository _tagRepository; private readonly MediaByGuidReadRepository _mediaByGuidReadRepository; - public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection) - : base(scopeAccessor, cache, logger) + public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, ILanguageRepository languageRepository) + : base(scopeAccessor, cache, languageRepository, logger) { _mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); @@ -279,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(mediaVersionDto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -336,7 +336,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == media.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(media.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index 67dbf758db..98c38603b1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -23,8 +23,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly ITagRepository _tagRepository; private readonly IMemberGroupRepository _memberGroupRepository; - public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository) - : base(scopeAccessor, cache, logger) + public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository) + : base(scopeAccessor, cache, languageRepository, logger) { _memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); @@ -306,7 +306,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement Database.Insert(dto); // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); @@ -371,7 +371,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement // replace the property data var deletePropertyDataSql = SqlContext.Sql().Delete().Where(x => x.VersionId == member.VersionId); Database.Execute(deletePropertyDataSql); - var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, out _); + var propertyDataDtos = PropertyFactory.BuildDtos(member.VersionId, 0, entity.Properties, LanguageRepository, out _, out _); foreach (var propertyDataDto in propertyDataDtos) Database.Insert(propertyDataDto); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs index 8a0333a965..73dcb44fef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -139,7 +139,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement FileSystem.AddFile(filepath, content, true); } - public long GetFileSize(string filepath) + public new long GetFileSize(string filepath) { if (FileSystem.FileExists(filepath) == false) return -1; diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs index e204a708a4..32080c4a12 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/TemplateRepository.cs @@ -776,7 +776,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } else { - validExts.Add(_templateConfig.UseAspNetMasterPages ? "master" : "aspx"); + validExts.Add("master"); } // validate path and extension diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/XsltFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/XsltFileRepository.cs deleted file mode 100644 index 75d92eabf6..0000000000 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/XsltFileRepository.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using LightInject; -using Umbraco.Core.IO; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Persistence.Repositories.Implement -{ - /// - /// Represents the XsltFile Repository - /// - internal class XsltFileRepository : FileRepository, IXsltFileRepository - { - public XsltFileRepository([Inject("XsltFileSystem")] IFileSystem fileSystem) - : base(fileSystem) - { } - - public override XsltFile Get(string id) - { - var path = FileSystem.GetRelativePath(id); - - path = path.EnsureEndsWith(".xslt"); - - if (FileSystem.FileExists(path) == false) - return null; - - var created = FileSystem.GetCreated(path).UtcDateTime; - var updated = FileSystem.GetLastModified(path).UtcDateTime; - - var xsltFile = new XsltFile(path, file => GetFileContent(file.OriginalPath)) - { - Key = path.EncodeAsGuid(), - CreateDate = created, - UpdateDate = updated, - Id = path.GetHashCode(), - VirtualPath = FileSystem.GetUrl(path) - }; - - // reset dirty initial properties (U4-1946) - xsltFile.ResetDirtyProperties(false); - - return xsltFile; - } - - public override void Save(XsltFile entity) - { - base.Save(entity); - - // ensure that from now on, content is lazy-loaded - if (entity.GetFileContent == null) - entity.GetFileContent = file => GetFileContent(file.OriginalPath); - } - - public override IEnumerable GetMany(params string[] ids) - { - ids = ids - .Select(x => StringExtensions.EnsureEndsWith(x, ".xslt")) - .Distinct() - .ToArray(); - - if (ids.Any()) - { - foreach (var id in ids) - { - yield return Get(id); - } - } - else - { - var files = FindAllFiles("", "*.xslt"); - foreach (var file in files) - { - yield return Get(file); - } - } - } - - /// - /// Gets a list of all that exist at the relative path specified. - /// - /// - /// If null or not specified, will return the XSLT files at the root path relative to the IFileSystem - /// - /// - public IEnumerable GetXsltFilesAtPath(string rootPath = null) - { - return FileSystem.GetFiles(rootPath ?? string.Empty, "*.xslt").Select(Get); - } - - private static readonly List ValidExtensions = new List { "xslt" }; - - public bool ValidateXsltFile(XsltFile xsltFile) - { - // get full path - string fullPath; - try - { - // may throw for security reasons - fullPath = FileSystem.GetFullPath(xsltFile.Path); - } - catch - { - return false; - } - - // validate path and extension - var validDir = SystemDirectories.Xslt; - var isValidPath = IOHelper.VerifyEditPath(fullPath, validDir); - var isValidExtension = IOHelper.VerifyFileExtension(xsltFile.Path, ValidExtensions); - return isValidPath && isValidExtension; - } - - public Stream GetFileContentStream(string filepath) - { - if (FileSystem.FileExists(filepath) == false) return null; - - try - { - return FileSystem.OpenFile(filepath); - } - catch - { - return null; // deal with race conds - } - } - - public void SetFileContent(string filepath, Stream content) - { - FileSystem.AddFile(filepath, content, true); - } - } -} diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index efe05e951d..2b82ec7fac 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -126,8 +126,10 @@ namespace Umbraco.Core.Persistence /// public void Configure(string connectionString, string providerName) { - using (new WriteLock(_lock)) + try { + _lock.EnterWriteLock(); + _logger.Debug("Configuring."); if (Configured) throw new InvalidOperationException("Already configured."); @@ -173,6 +175,11 @@ namespace Umbraco.Core.Persistence _logger.Debug("Configured."); Configured = true; } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } } /// diff --git a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs index ea95f8708f..f235a95aa8 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueEditor.cs @@ -272,16 +272,16 @@ namespace Umbraco.Core.PropertyEditors /// /// /// - /// + /// /// /// /// /// The object returned will automatically be serialized into json notation. For most property editors /// the value returned is probably just a string but in some cases a json structure will be returned. /// - public virtual object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null) + public virtual object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) { - var val = property.GetValue(languageId, segment); + var val = property.GetValue(culture, segment); if (val == null) return string.Empty; switch (ValueTypes.ToStorageType(ValueType)) @@ -343,12 +343,8 @@ namespace Umbraco.Core.PropertyEditors continue; var xElement = new XElement(nodeName); - if (pvalue.LanguageId.HasValue) - { - var language = localizationService.GetLanguageById(pvalue.LanguageId.Value); - if (language == null) continue; // uh? - xElement.Add(new XAttribute("lang", language.IsoCode)); - } + if (pvalue.Culture != null) + xElement.Add(new XAttribute("lang", pvalue.Culture)); if (pvalue.Segment != null) xElement.Add(new XAttribute("segment", pvalue.Segment)); diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs index 9e31f94121..b5ed7c5917 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs @@ -61,7 +61,7 @@ namespace Umbraco.Core.PropertyEditors /// /// Converts a property value to a value for the editor. /// - object ToEditor(Property property, IDataTypeService dataTypeService, int? languageId = null, string segment = null); + object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null); // fixme - editing - document or remove these // why property vs propertyType? services should be injected! etc... diff --git a/src/Umbraco.Core/Publishing/ScheduledPublisher.cs b/src/Umbraco.Core/Publishing/ScheduledPublisher.cs index bb09195691..b16cd961f0 100644 --- a/src/Umbraco.Core/Publishing/ScheduledPublisher.cs +++ b/src/Umbraco.Core/Publishing/ScheduledPublisher.cs @@ -39,7 +39,7 @@ namespace Umbraco.Core.Publishing try { d.ReleaseDate = null; - d.PublishValues(); // fixme variants? + d.TryPublishValues(); // fixme variants? var result = _contentService.SaveAndPublish(d, d.GetWriterProfile().Id); _logger.Debug($"Result of publish attempt: {result.Result}"); if (result.Success == false) diff --git a/src/Umbraco.Core/ReadLock.cs b/src/Umbraco.Core/ReadLock.cs index 61025721fc..6e6ecf14b8 100644 --- a/src/Umbraco.Core/ReadLock.cs +++ b/src/Umbraco.Core/ReadLock.cs @@ -10,9 +10,11 @@ namespace Umbraco.Core /// Provides a convenience methodology for implementing locked access to resources. /// /// - /// Intended as an infrastructure class. + /// Intended as an infrastructure class. + /// This is a very unefficient way to lock as it allocates one object each time we lock, + /// so it's OK to use this class for things that happen once, where it is convenient, but not + /// for performance-critical code! /// - [Obsolete("stop using, allocates")] public class ReadLock : IDisposable { private readonly ReaderWriterLockSlim _rwLock; diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs index eeacba2b7c..a90d5b5b4c 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs @@ -52,7 +52,6 @@ namespace Umbraco.Core.Runtime composition.Container.RegisterSingleton(factory => factory.GetInstance().StylesheetsFileSystem, Constants.Composing.FileSystems.StylesheetFileSystem); composition.Container.RegisterSingleton(factory => factory.GetInstance().MasterPagesFileSystem, Constants.Composing.FileSystems.MasterpageFileSystem); composition.Container.RegisterSingleton(factory => factory.GetInstance().MvcViewsFileSystem, Constants.Composing.FileSystems.ViewFileSystem); - composition.Container.RegisterSingleton(factory => factory.GetInstance().XsltFileSystem, Constants.Composing.FileSystems.XsltFileSystem); // register manifest parser, will be injected in collection builders where needed composition.Container.RegisterSingleton(); @@ -72,12 +71,9 @@ namespace Umbraco.Core.Runtime composition.Container.RegisterSingleton(); composition.Container.RegisterSingleton(); - // register a server registrar, by default it's the db registrar unless the dev - // has the legacy dist calls enabled - fixme - should obsolete the legacy thing + // register a server registrar, by default it's the db registrar composition.Container.RegisterSingleton(f => { - if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled) - return new ConfigServerRegistrar(f.GetInstance(), f.GetInstance(), f.GetInstance()); if ("true".InvariantEquals(ConfigurationManager.AppSettings["umbracoDisableElectionForSingleServer"])) return new SingleServerRegistrar(f.GetInstance()); return new DatabaseServerRegistrar( diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index a1401e035a..53a474cef7 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -19,6 +19,8 @@ namespace Umbraco.Core private readonly ILogger _logger; private readonly Lazy _serverRegistrar; private readonly Lazy _mainDom; + private readonly IUmbracoSettingsSection _settings; + private readonly IGlobalSettings _globalSettings; private readonly HashSet _applicationUrls = new HashSet(); private RuntimeLevel _level; @@ -28,11 +30,13 @@ namespace Umbraco.Core /// A logger. /// A (lazy) server registrar. /// A (lazy) MainDom. - public RuntimeState(ILogger logger, Lazy serverRegistrar, Lazy mainDom) + public RuntimeState(ILogger logger, Lazy serverRegistrar, Lazy mainDom, IUmbracoSettingsSection settings, IGlobalSettings globalSettings) { _logger = logger; _serverRegistrar = serverRegistrar; _mainDom = mainDom; + _settings = settings; + _globalSettings = globalSettings; } private IServerRegistrar ServerRegistrar => _serverRegistrar.Value; @@ -103,15 +107,13 @@ namespace Umbraco.Core /// /// Ensures that the property has a value. /// - /// /// - /// - internal void EnsureApplicationUrl(IUmbracoSettingsSection settings, IGlobalSettings globalSettings, HttpRequestBase request = null) + internal void EnsureApplicationUrl(HttpRequestBase request = null) { // see U4-10626 - in some cases we want to reset the application url // (this is a simplified version of what was in 7.x) // note: should this be optional? is it expensive? - var url = request == null ? null : ApplicationUrlHelper.GetApplicationUrlFromCurrentRequest(request, globalSettings); + var url = request == null ? null : ApplicationUrlHelper.GetApplicationUrlFromCurrentRequest(request, _globalSettings); var change = url != null && !_applicationUrls.Contains(url); if (change) { @@ -120,7 +122,7 @@ namespace Umbraco.Core } if (ApplicationUrl != null && !change) return; - ApplicationUrl = new Uri(ApplicationUrlHelper.GetApplicationUrl(_logger, globalSettings, settings, request)); + ApplicationUrl = new Uri(ApplicationUrlHelper.GetApplicationUrl(_logger, _globalSettings, _settings, ServerRegistrar, request)); } private readonly ManualResetEventSlim _runLevel = new ManualResetEventSlim(false); diff --git a/src/Umbraco.Core/Security/MachineKeyGenerator.cs b/src/Umbraco.Core/Security/MachineKeyGenerator.cs index 283ca1ac3a..a20f04c919 100644 --- a/src/Umbraco.Core/Security/MachineKeyGenerator.cs +++ b/src/Umbraco.Core/Security/MachineKeyGenerator.cs @@ -26,8 +26,6 @@ namespace Umbraco.Core.Security validation=""HMACSHA256"" decryption=""AES"" />"; - var Xxx = 3; - return string.Format(c, GenerateAESDecryptionKey(), GenerateHMACSHA256ValidationKey()); } diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/EntityXmlSerializer.cs index 72838c3d55..e418c8d3e6 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/EntityXmlSerializer.cs @@ -295,10 +295,8 @@ namespace Umbraco.Core.Services var xml = new XElement("macro"); xml.Add(new XElement("name", macro.Name)); xml.Add(new XElement("alias", macro.Alias)); - xml.Add(new XElement("scriptType", macro.ControlType)); - xml.Add(new XElement("scriptAssembly", macro.ControlAssembly)); - xml.Add(new XElement("scriptingFile", macro.ScriptPath)); - xml.Add(new XElement("xslt", macro.XsltPath)); + xml.Add(new XElement("macroType", macro.MacroType)); + xml.Add(new XElement("macroSource", macro.MacroSource)); xml.Add(new XElement("useInEditor", macro.UseInEditor.ToString())); xml.Add(new XElement("dontRender", macro.DontRender.ToString())); xml.Add(new XElement("refreshRate", macro.CacheDuration.ToString(CultureInfo.InvariantCulture))); diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index cb56b39e2c..abbab0ef39 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -354,7 +354,7 @@ namespace Umbraco.Core.Services /// /// Saves and publishes a document branch. /// - IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0); + IEnumerable SaveAndPublishBranch(IContent content, bool force, string culture = null, string segment = null, int userId = 0); /// /// Saves and publishes a document branch. diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 91ac7c25ea..1f938c8e27 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -18,8 +18,6 @@ namespace Umbraco.Core.Services IPartialView GetPartialView(string path); IPartialView GetPartialViewMacro(string path); IEnumerable GetPartialViewMacros(params string[] names); - IXsltFile GetXsltFile(string path); - IEnumerable GetXsltFiles(params string[] names); Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = 0); Attempt CreatePartialViewMacro(IPartialView partialView, string snippetName = null, int userId = 0); bool DeletePartialView(string path, int userId = 0); @@ -305,27 +303,6 @@ namespace Umbraco.Core.Services /// The size of the script file. long GetScriptFileSize(string filepath); - /// - /// Gets the content of a XSLT file as a stream. - /// - /// The filesystem path to the XSLT file. - /// The content of the XSLT file. - Stream GetXsltFileContentStream(string filepath); - - /// - /// Sets the content of a XSLT file. - /// - /// The filesystem path to the XSLT file. - /// The content of the XSLT file. - void SetXsltFileContent(string filepath, Stream content); - - /// - /// Gets the size of a XSLT file. - /// - /// The filesystem path to the XSLT file. - /// The size of the XSLT file. - long GetXsltFileSize(string filepath); - /// /// Gets the content of a macro partial view as a stream. /// diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index d61ee5e310..aedf84f804 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -102,14 +102,7 @@ namespace Umbraco.Core.Services /// Id of the /// ILanguage GetLanguageById(int id); - - /// - /// Gets a by its culture code - /// - /// Culture Code - also refered to as the Friendly name - /// - ILanguage GetLanguageByCultureCode(string cultureName); - + /// /// Gets a by its iso code /// @@ -118,10 +111,37 @@ namespace Umbraco.Core.Services ILanguage GetLanguageByIsoCode(string isoCode); /// - /// Gets a language identifier by its iso code. + /// Gets a language identifier from its ISO code. /// - int GetLanguageIdByIsoCode(string isoCode); + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetLanguageIdByIsoCode(string isoCode); + /// + /// Gets a language ISO code from its identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetLanguageIsoCodeById(int id); + + /// + /// Gets the default language ISO code. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + string GetDefaultLanguageIsoCode(); + + /// + /// Gets the default language identifier. + /// + /// + /// This can be optimized and bypass all deep cloning. + /// + int? GetDefaultLanguageId(); + /// /// Gets all available languages /// diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index bfa6c5916d..919ca73273 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -163,6 +163,8 @@ namespace Umbraco.Core.Services.Implement /// public IContent Create(string name, Guid parentId, string contentTypeAlias, int userId = 0) { + //fixme - what about culture? + var parent = GetById(parentId); return Create(name, parent, contentTypeAlias, userId); } @@ -181,6 +183,8 @@ namespace Umbraco.Core.Services.Implement /// The content object. public IContent Create(string name, int parentId, string contentTypeAlias, int userId = 0) { + //fixme - what about culture? + var contentType = GetContentType(contentTypeAlias); if (contentType == null) throw new ArgumentException("No content type with that alias.", nameof(contentTypeAlias)); @@ -212,6 +216,8 @@ namespace Umbraco.Core.Services.Implement /// The content object. public IContent Create(string name, IContent parent, string contentTypeAlias, int userId = 0) { + //fixme - what about culture? + if (parent == null) throw new ArgumentNullException(nameof(parent)); using (var scope = ScopeProvider.CreateScope()) @@ -241,6 +247,8 @@ namespace Umbraco.Core.Services.Implement /// The content object. public IContent CreateAndSave(string name, int parentId, string contentTypeAlias, int userId = 0) { + //fixme - what about culture? + using (var scope = ScopeProvider.CreateScope()) { // locking the content tree secures content types too @@ -273,6 +281,8 @@ namespace Umbraco.Core.Services.Implement /// The content object. public IContent CreateAndSave(string name, IContent parent, string contentTypeAlias, int userId = 0) { + //fixme - what about culture? + if (parent == null) throw new ArgumentNullException(nameof(parent)); using (var scope = ScopeProvider.CreateScope()) @@ -864,11 +874,6 @@ namespace Umbraco.Core.Services.Implement return OperationResult.Cancel(evtMsgs); } - if (string.IsNullOrWhiteSpace(content.Name)) - { - throw new ArgumentException("Cannot save content with empty name."); - } - var isNew = content.IsNewEntity(); scope.WriteLock(Constants.Locks.ContentTree); @@ -1070,7 +1075,7 @@ namespace Umbraco.Core.Services.Implement try { d.ReleaseDate = null; - d.PublishValues(); // fixme variants? + d.TryPublishValues(); // fixme variants? result = SaveAndPublish(d, d.WriterId); if (result.Success == false) Logger.Error($"Failed to publish document id={d.Id}, reason={result.Result}."); @@ -1103,14 +1108,14 @@ namespace Umbraco.Core.Services.Implement } /// - public IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0) + public IEnumerable SaveAndPublishBranch(IContent content, bool force, string culture = null, string segment = null, int userId = 0) { segment = segment?.ToLowerInvariant(); - bool IsEditing(IContent c, int? l, string s) - => c.Properties.Any(x => x.Values.Where(y => y.LanguageId == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue)); + bool IsEditing(IContent c, string l, string s) + => c.Properties.Any(x => x.Values.Where(y => y.Culture == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue)); - return SaveAndPublishBranch(content, force, document => IsEditing(document, languageId, segment), document => document.PublishValues(languageId, segment), userId); + return SaveAndPublishBranch(content, force, document => IsEditing(document, culture, segment), document => document.TryPublishValues(culture, segment), userId); } /// @@ -1217,7 +1222,7 @@ namespace Umbraco.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { var deleteEventArgs = new DeleteEventArgs(content, evtMsgs); - if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs)) + if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs, nameof(Deleting))) { scope.Complete(); return OperationResult.Cancel(evtMsgs); @@ -1229,7 +1234,7 @@ namespace Umbraco.Core.Services.Implement // but... UnPublishing event makes no sense (not going to cancel?) and no need to save // just raise the event if (content.Trashed == false && content.Published) - scope.Events.Dispatch(UnPublished, this, new PublishEventArgs(content, false, false), "UnPublished"); + scope.Events.Dispatch(UnPublished, this, new PublishEventArgs(content, false, false), nameof(UnPublished)); DeleteLocked(scope, content); @@ -1265,7 +1270,7 @@ namespace Umbraco.Core.Services.Implement _documentRepository.Delete(c); var args = new DeleteEventArgs(c, false); // raise event & get flagged files - scope.Events.Dispatch(Deleted, this, args); + scope.Events.Dispatch(Deleted, this, args, nameof(Deleted)); // fixme not going to work, do it differently _mediaFileSystem.DeleteFiles(args.MediaFilesToDelete, // remove flagged files @@ -2149,7 +2154,7 @@ namespace Umbraco.Core.Services.Implement var query = Query().WhereIn(x => x.ContentTypeId, contentTypeIdsA); var contents = _documentRepository.Get(query).ToArray(); - if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs(contents))) + if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs(contents), nameof(Deleting))) { scope.Complete(); return; @@ -2296,7 +2301,7 @@ namespace Umbraco.Core.Services.Implement { scope.WriteLock(Constants.Locks.ContentTree); _documentBlueprintRepository.Delete(content); - scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(content), "DeletedBlueprint"); + scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(content), nameof(DeletedBlueprint)); scope.Complete(); } } @@ -2357,7 +2362,7 @@ namespace Umbraco.Core.Services.Implement _documentBlueprintRepository.Delete(blueprint); } - scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(blueprints), "DeletedBlueprint"); + scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(blueprints), nameof(DeletedBlueprint)); scope.Complete(); } } diff --git a/src/Umbraco.Core/Services/Implement/FileService.cs b/src/Umbraco.Core/Services/Implement/FileService.cs index 23a4cddc55..09bd096f8e 100644 --- a/src/Umbraco.Core/Services/Implement/FileService.cs +++ b/src/Umbraco.Core/Services/Implement/FileService.cs @@ -24,16 +24,15 @@ namespace Umbraco.Core.Services.Implement private readonly ITemplateRepository _templateRepository; private readonly IPartialViewRepository _partialViewRepository; private readonly IPartialViewMacroRepository _partialViewMacroRepository; - private readonly IXsltFileRepository _xsltRepository; private readonly IAuditRepository _auditRepository; - private const string PartialViewHeader = "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; + private const string PartialViewHeader = "@inherits Umbraco.Web.Mvc.UmbracoViewPage"; private const string PartialViewMacroHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; public FileService(IScopeProvider uowProvider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IStylesheetRepository stylesheetRepository, IScriptRepository scriptRepository, ITemplateRepository templateRepository, IPartialViewRepository partialViewRepository, IPartialViewMacroRepository partialViewMacroRepository, - IXsltFileRepository xsltRepository, IAuditRepository auditRepository) + IAuditRepository auditRepository) : base(uowProvider, logger, eventMessagesFactory) { _stylesheetRepository = stylesheetRepository; @@ -41,7 +40,6 @@ namespace Umbraco.Core.Services.Implement _templateRepository = templateRepository; _partialViewRepository = partialViewRepository; _partialViewMacroRepository = partialViewMacroRepository; - _xsltRepository = xsltRepository; _auditRepository = auditRepository; } @@ -724,22 +722,6 @@ namespace Umbraco.Core.Services.Implement } } - public IXsltFile GetXsltFile(string path) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _xsltRepository.Get(path); - } - } - - public IEnumerable GetXsltFiles(params string[] names) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _xsltRepository.GetMany(names).OrderBy(x => x.Name); - } - } - public Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = 0) { return CreatePartialViewMacro(partialView, PartialViewType.PartialView, snippetName, userId); @@ -1051,36 +1033,7 @@ namespace Umbraco.Core.Services.Implement } #endregion - - #region Xslt - - public Stream GetXsltFileContentStream(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _xsltRepository.GetFileContentStream(filepath); - } - } - - public void SetXsltFileContent(string filepath, Stream content) - { - using (var scope = ScopeProvider.CreateScope()) - { - _xsltRepository.SetFileContent(filepath, content); - scope.Complete(); - } - } - - public long GetXsltFileSize(string filepath) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _xsltRepository.GetFileSize(filepath); - } - } - - #endregion - + private void Audit(AuditType type, string message, int userId, int objectId) { _auditRepository.Save(new AuditItem(objectId, message, type, userId)); @@ -1182,4 +1135,4 @@ namespace Umbraco.Core.Services.Implement #endregion } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index bf01605416..663ecf586c 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -290,19 +290,6 @@ namespace Umbraco.Core.Services.Implement } } - /// - /// Gets a by its culture code - /// - /// Culture Name - also refered to as the Friendly name - /// - public ILanguage GetLanguageByCultureCode(string cultureName) - { - using (var scope = ScopeProvider.CreateScope(autoComplete: true)) - { - return _languageRepository.GetByCultureName(cultureName); - } - } - /// /// Gets a by its iso code /// @@ -317,7 +304,7 @@ namespace Umbraco.Core.Services.Implement } /// - public int GetLanguageIdByIsoCode(string isoCode) + public int? GetLanguageIdByIsoCode(string isoCode) { using (ScopeProvider.CreateScope(autoComplete: true)) { @@ -325,6 +312,33 @@ namespace Umbraco.Core.Services.Implement } } + /// + public string GetLanguageIsoCodeById(int id) + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetIsoCodeById(id); + } + } + + /// + public string GetDefaultLanguageIsoCode() + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetDefaultIsoCode(); + } + } + + /// + public int? GetDefaultLanguageId() + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetDefaultId(); + } + } + /// /// Gets all available languages /// diff --git a/src/Umbraco.Core/Services/Implement/MacroService.cs b/src/Umbraco.Core/Services/Implement/MacroService.cs index a28bb7ca50..fdcc8e2ee0 100644 --- a/src/Umbraco.Core/Services/Implement/MacroService.cs +++ b/src/Umbraco.Core/Services/Implement/MacroService.cs @@ -25,24 +25,6 @@ namespace Umbraco.Core.Services.Implement _auditRepository = auditRepository; } - /// - /// Returns an enum based on the properties on the Macro - /// - /// - internal static MacroTypes GetMacroType(IMacro macro) - { - if (string.IsNullOrEmpty(macro.XsltPath) == false) - return MacroTypes.Xslt; - - if (string.IsNullOrEmpty(macro.ScriptPath) == false) - return MacroTypes.PartialView; - - if (string.IsNullOrEmpty(macro.ControlType) == false && macro.ControlType.InvariantContains(".ascx")) - return MacroTypes.UserControl; - - return MacroTypes.Unknown; - } - /// /// Gets an object by its alias /// diff --git a/src/Umbraco.Core/Services/Implement/MemberService.cs b/src/Umbraco.Core/Services/Implement/MemberService.cs index f8245f18c2..ca2b39dee2 100644 --- a/src/Umbraco.Core/Services/Implement/MemberService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberService.cs @@ -186,8 +186,9 @@ namespace Umbraco.Core.Services.Implement /// Email of the to create /// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database /// Alias of the Type + /// /// - IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved = true) + IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved) { return CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias, isApproved); } diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index b6ddc400d8..1e585053da 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -1270,10 +1270,8 @@ namespace Umbraco.Core.Services.Implement { var macroName = macroElement.Element("name").Value; var macroAlias = macroElement.Element("alias").Value; - var controlType = macroElement.Element("scriptType").Value; - var controlAssembly = macroElement.Element("scriptAssembly").Value; - var xsltPath = macroElement.Element("xslt").Value; - var scriptPath = macroElement.Element("scriptingFile").Value; + var macroType = Enum.Parse(macroElement.Element("macroType").Value); + var macroSource = macroElement.Element("macroSource").Value; //Following xml elements are treated as nullable properties var useInEditorElement = macroElement.Element("useInEditor"); @@ -1308,7 +1306,7 @@ namespace Umbraco.Core.Services.Implement } var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; - var macro = existingMacro ?? new Macro(macroAlias, macroName, controlType, controlAssembly, xsltPath, scriptPath, + var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); var properties = macroElement.Element("properties"); @@ -1450,13 +1448,11 @@ namespace Umbraco.Core.Services.Implement /// public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId) { - var packageRepo = UmbracoConfig.For.UmbracoSettings().PackageRepositories.GetDefault(); - using (var httpClient = new HttpClient()) using (var scope = _scopeProvider.CreateScope()) { //includeHidden = true because we don't care if it's hidden we want to get the file regardless - var url = $"{packageRepo.RestApiUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; + var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; byte[] bytes; try { @@ -1485,7 +1481,7 @@ namespace Umbraco.Core.Services.Implement } } - Audit(AuditType.PackagerInstall, $"Package {packageId} fetched from {packageRepo.Id}", userId, -1); + Audit(AuditType.PackagerInstall, $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}", userId, -1); return null; } } diff --git a/src/Umbraco.Core/Services/Implement/UserService.cs b/src/Umbraco.Core/Services/Implement/UserService.cs index 4b27fb5c51..96cac1d814 100644 --- a/src/Umbraco.Core/Services/Implement/UserService.cs +++ b/src/Umbraco.Core/Services/Implement/UserService.cs @@ -198,7 +198,7 @@ namespace Umbraco.Core.Services.Implement { return _userRepository.GetByUsername(username, includeSecurityData: true); } - catch (DbException ex) + catch (DbException) { // fixme - refactor users/upgrade // currently kinda accepting anything on upgrade, but that won't deal with all cases @@ -727,7 +727,7 @@ namespace Umbraco.Core.Services.Implement { return _userRepository.Get(id); } - catch (DbException ex) + catch (DbException) { // fixme - refactor users/upgrade // currently kinda accepting anything on upgrade, but that won't deal with all cases diff --git a/src/Umbraco.Core/Services/LocalizationServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizationServiceExtensions.cs deleted file mode 100644 index a597b64f3b..0000000000 --- a/src/Umbraco.Core/Services/LocalizationServiceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Services -{ - public static class LocalizationServiceExtensions - { - /// - /// Returns the configured default variant language - /// - /// - /// - public static ILanguage GetDefaultVariantLanguage(this ILocalizationService service) - { - var langs = service.GetAllLanguages().OrderBy(x => x.Id).ToList(); - - if (langs.Count == 0) return null; - - //if there's only one language, by default it is the default - if (langs.Count == 1) - { - langs[0].IsDefaultVariantLanguage = true; - langs[0].Mandatory = true; - return langs[0]; - } - - var foundDefault = langs.FirstOrDefault(x => x.IsDefaultVariantLanguage); - if (foundDefault != null) return foundDefault; - - //if no language has the default flag, then the defaul language is the one with the lowest id - langs[0].IsDefaultVariantLanguage = true; - langs[0].Mandatory = true; - return langs[0]; - } - } -} diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 8040e6397b..4369e8faac 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -164,7 +164,7 @@ namespace Umbraco.Core var obj = JsonConvert.DeserializeObject(input); return obj; } - catch (Exception ex) + catch (Exception) { return input; } @@ -622,7 +622,7 @@ namespace Umbraco.Core byte[] decodedBytes = UrlTokenDecode(input); return decodedBytes != null ? Encoding.UTF8.GetString(decodedBytes) : null; } - catch (FormatException ex) + catch (FormatException) { return null; } @@ -1051,7 +1051,14 @@ namespace Umbraco.Core /// The filtered string. public static string ReplaceMany(this string text, IDictionary replacements) { - return Current.ShortStringHelper.ReplaceMany(text, replacements); + if (text == null) throw new ArgumentNullException(nameof(text)); + if (replacements == null) throw new ArgumentNullException(nameof(replacements)); + + + foreach (KeyValuePair item in replacements) + text = text.Replace(item.Key, item.Value); + + return text; } /// @@ -1062,9 +1069,16 @@ namespace Umbraco.Core /// The replacement character. /// The filtered string. public static string ReplaceMany(this string text, char[] chars, char replacement) - { - return Current.ShortStringHelper.ReplaceMany(text, chars, replacement); - } + { + if (text == null) throw new ArgumentNullException(nameof(text)); + if (chars == null) throw new ArgumentNullException(nameof(chars)); + + + for (int i = 0; i < chars.Length; i++) + text = text.Replace(chars[i], replacement); + + return text; + } // FORMAT STRINGS @@ -1133,7 +1147,8 @@ namespace Umbraco.Core /// The text to filter. /// The safe url segment. public static string ToUrlSegment(this string text) - { + { + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("message", nameof(text)); return Current.ShortStringHelper.CleanStringForUrlSegment(text); } @@ -1144,7 +1159,10 @@ namespace Umbraco.Core /// The culture. /// The safe url segment. public static string ToUrlSegment(this string text, CultureInfo culture) - { + { + if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("message", nameof(text)); + if (culture == null) throw new ArgumentNullException(nameof(culture)); + return Current.ShortStringHelper.CleanStringForUrlSegment(text, culture); } @@ -1473,5 +1491,11 @@ namespace Umbraco.Core guid[left] = guid[right]; guid[right] = temp; } + + /// + /// Turns an null-or-whitespace string into a null string. + /// + public static string NullEmpty(this string text) + => string.IsNullOrWhiteSpace(text) ? null : text; } } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index 42a2d3b5d4..3a9d9433f4 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -670,45 +670,6 @@ function validateSafeAlias(input, value, immediate, callback) {{ #endregion - #region ReplaceMany - - /// - /// Returns a new string in which all occurences of specified strings are replaced by other specified strings. - /// - /// The string to filter. - /// The replacements definition. - /// The filtered string. - public virtual string ReplaceMany(string text, IDictionary replacements) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (replacements == null) throw new ArgumentNullException(nameof(replacements)); - - - foreach (KeyValuePair item in replacements) - text = text.Replace(item.Key, item.Value); - - return text; - } - - /// - /// Returns a new string in which all occurences of specified characters are replaced by a specified character. - /// - /// The string to filter. - /// The characters to replace. - /// The replacement character. - /// The filtered string. - public virtual string ReplaceMany(string text, char[] chars, char replacement) - { - if (text == null) throw new ArgumentNullException(nameof(text)); - if (chars == null) throw new ArgumentNullException(nameof(chars)); - - - for (int i = 0; i < chars.Length; i++) - text = text.Replace(chars[i], replacement); - - return text; - } - - #endregion + } } diff --git a/src/Umbraco.Core/Strings/IShortStringHelper.cs b/src/Umbraco.Core/Strings/IShortStringHelper.cs index f48d43aa4f..7232b0efe7 100644 --- a/src/Umbraco.Core/Strings/IShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/IShortStringHelper.cs @@ -77,23 +77,6 @@ namespace Umbraco.Core.Strings /// Supports Utf8 and Ascii strings, not Unicode strings. string SplitPascalCasing(string text, char separator); - /// - /// Returns a new string in which all occurences of specified strings are replaced by other specified strings. - /// - /// The string to filter. - /// The replacements definition. - /// The filtered string. - string ReplaceMany(string text, IDictionary replacements); - - /// - /// Returns a new string in which all occurences of specified characters are replaced by a specified character. - /// - /// The string to filter. - /// The characters to replace. - /// The replacement character. - /// The filtered string. - string ReplaceMany(string text, char[] chars, char replacement); - /// /// Cleans a string. /// diff --git a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs index 691a325eaa..4628271625 100644 --- a/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs +++ b/src/Umbraco.Core/Sync/ApplicationUrlHelper.cs @@ -29,9 +29,9 @@ namespace Umbraco.Core.Sync // FIXME need another way to do it, eg an interface, injected! public static Func ApplicationUrlProvider { get; set; } - internal static string GetApplicationUrl(ILogger logger, IGlobalSettings globalSettings, IUmbracoSettingsSection settings, HttpRequestBase request = null) + internal static string GetApplicationUrl(ILogger logger, IGlobalSettings globalSettings, IUmbracoSettingsSection settings, IServerRegistrar serverRegistrar, HttpRequestBase request = null) { - var umbracoApplicationUrl = TryGetApplicationUrl(settings, logger, globalSettings); + var umbracoApplicationUrl = TryGetApplicationUrl(settings, logger, globalSettings, serverRegistrar); if (umbracoApplicationUrl != null) return umbracoApplicationUrl; @@ -50,7 +50,7 @@ namespace Umbraco.Core.Sync return umbracoApplicationUrl; } - internal static string TryGetApplicationUrl(IUmbracoSettingsSection settings, ILogger logger, IGlobalSettings globalSettings) + internal static string TryGetApplicationUrl(IUmbracoSettingsSection settings, ILogger logger, IGlobalSettings globalSettings, IServerRegistrar serverRegistrar) { // try umbracoSettings:settings/web.routing/@umbracoApplicationUrl // which is assumed to: @@ -88,7 +88,7 @@ namespace Umbraco.Core.Sync // - contain a scheme // - end or not with a slash, it will be taken care of // eg "http://www.mysite.com/umbraco" - url = Current.ServerRegistrar.GetCurrentServerUmbracoApplicationUrl(); + url = serverRegistrar.GetCurrentServerUmbracoApplicationUrl(); if (url.IsNullOrWhiteSpace() == false) { var umbracoApplicationUrl = url.TrimEnd('/'); diff --git a/src/Umbraco.Core/Sync/BatchedWebServiceServerMessenger.cs b/src/Umbraco.Core/Sync/BatchedWebServiceServerMessenger.cs deleted file mode 100644 index a0357111d1..0000000000 --- a/src/Umbraco.Core/Sync/BatchedWebServiceServerMessenger.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Cache; - -namespace Umbraco.Core.Sync -{ - /// - /// An that works by messaging servers via web services. - /// - /// - /// Abstract because it needs to be inherited by a class that will - /// - implement ProcessBatch() - /// - trigger FlushBatch() when appropriate - /// - internal abstract class BatchedWebServiceServerMessenger : WebServiceServerMessenger - { - internal BatchedWebServiceServerMessenger() - { - } - - internal BatchedWebServiceServerMessenger(string login, string password) - : base(login, password) - { - } - - internal BatchedWebServiceServerMessenger(string login, string password, bool useDistributedCalls) - : base(login, password, useDistributedCalls) - { - } - - protected BatchedWebServiceServerMessenger(Func> getLoginAndPassword) - : base(getLoginAndPassword) - { - } - - protected abstract ICollection GetBatch(bool ensureHttpContext); - - protected void FlushBatch() - { - var batch = GetBatch(false); - if (batch == null) return; - - var batcha = batch.ToArray(); - batch.Clear(); - if (batcha.Length == 0) return; - - ProcessBatch(batcha); - } - - // needs to be overriden to actually do something - protected abstract void ProcessBatch(RefreshInstructionEnvelope[] batch); - - protected override void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) - { - var idsA = ids == null ? null : ids.ToArray(); - - Type arrayType; - if (GetArrayType(idsA, out arrayType) == false) - throw new ArgumentException("All items must be of the same type, either int or Guid.", "ids"); - - BatchMessage(servers, refresher, messageType, idsA, arrayType, json); - } - - protected void BatchMessage( - IEnumerable servers, - ICacheRefresher refresher, - MessageType messageType, - IEnumerable ids = null, - Type idType = null, - string json = null) - { - var batch = GetBatch(true); - if (batch == null) - throw new Exception("Failed to get a batch."); - - batch.Add(new RefreshInstructionEnvelope(servers, refresher, - RefreshInstruction.GetInstructions(refresher, messageType, ids, idType, json))); - } - } -} diff --git a/src/Umbraco.Core/Sync/ConfigServerAddress.cs b/src/Umbraco.Core/Sync/ConfigServerAddress.cs deleted file mode 100644 index be00544344..0000000000 --- a/src/Umbraco.Core/Sync/ConfigServerAddress.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; - -namespace Umbraco.Core.Sync -{ - /// - /// Provides the address of a server based on the Xml configuration. - /// - internal class ConfigServerAddress : IServerAddress - { - public ConfigServerAddress(IServer n, IGlobalSettings globalSettings) - { - var webServicesUrl = IOHelper.ResolveUrl(SystemDirectories.WebServices); - - var protocol = globalSettings.UseHttps ? "https" : "http"; - if (n.ForceProtocol.IsNullOrWhiteSpace() == false) - protocol = n.ForceProtocol; - var domain = n.ServerAddress; - if (n.ForcePortnumber.IsNullOrWhiteSpace() == false) - domain += $":{n.ForcePortnumber}"; - ServerAddress = $"{protocol}://{domain}{webServicesUrl}/cacheRefresher.asmx"; - } - - public string ServerAddress { get; private set; } - - public override string ToString() - { - return ServerAddress; - } - } -} diff --git a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs b/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs deleted file mode 100644 index 83e085b324..0000000000 --- a/src/Umbraco.Core/Sync/ConfigServerRegistrar.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.Sync -{ - /// - /// Provides server registrations to the distributed cache by reading the legacy Xml configuration - /// in umbracoSettings to get the list of (manually) configured server nodes. - /// - internal class ConfigServerRegistrar : IServerRegistrar - { - private readonly List _addresses; - private readonly ServerRole _serverRole; - private readonly string _umbracoApplicationUrl; - - public ConfigServerRegistrar(IUmbracoSettingsSection settings, ILogger logger, IGlobalSettings globalSettings) - : this(settings.DistributedCall, logger, globalSettings) - { } - - // for tests - internal ConfigServerRegistrar(IDistributedCallSection settings, ILogger logger, IGlobalSettings globalSettings) - { - if (settings.Enabled == false) - { - _addresses = new List(); - _serverRole = ServerRole.Single; - _umbracoApplicationUrl = null; // unspecified - return; - } - - var serversA = settings.Servers.ToArray(); - - _addresses = serversA - .Select(x => new ConfigServerAddress(x, globalSettings)) - .Cast() - .ToList(); - - if (serversA.Length == 0) - { - _serverRole = ServerRole.Unknown; // config error, actually - logger.Debug("Server Role Unknown: DistributedCalls are enabled but no servers are listed."); - } - else - { - var master = serversA[0]; // first one is master - var appId = master.AppId; - var serverName = master.ServerName; - - if (appId.IsNullOrWhiteSpace() && serverName.IsNullOrWhiteSpace()) - { - _serverRole = ServerRole.Unknown; // config error, actually - logger.Debug("Server Role Unknown: Server Name or AppId missing from Server configuration in DistributedCalls settings."); - } - else - { - _serverRole = IsCurrentServer(appId, serverName) - ? ServerRole.Master - : ServerRole.Slave; - } - } - - var currentServer = serversA.FirstOrDefault(x => IsCurrentServer(x.AppId, x.ServerName)); - if (currentServer != null) - { - // match, use the configured url - // ReSharper disable once UseStringInterpolation - _umbracoApplicationUrl = string.Format("{0}://{1}:{2}/{3}", - currentServer.ForceProtocol.IsNullOrWhiteSpace() ? "http" : currentServer.ForceProtocol, - currentServer.ServerAddress, - currentServer.ForcePortnumber.IsNullOrWhiteSpace() ? "80" : currentServer.ForcePortnumber, - IOHelper.ResolveUrl(SystemDirectories.Umbraco).TrimStart('/')); - } - } - - private static bool IsCurrentServer(string appId, string serverName) - { - // match by appId or computer name - return (appId.IsNullOrWhiteSpace() == false && appId.Trim().InvariantEquals(HttpRuntime.AppDomainAppId)) - || (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName)); - } - - public IEnumerable Registrations => _addresses; - - public ServerRole GetCurrentServerRole() => _serverRole; - - public string GetCurrentServerUmbracoApplicationUrl() => _umbracoApplicationUrl; - } -} diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 0fcb7036f7..a1b89e58bc 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -71,7 +71,7 @@ namespace Umbraco.Core.Sync #region Messenger - protected override bool RequiresDistributed(IEnumerable servers, ICacheRefresher refresher, MessageType dispatchType) + protected override bool RequiresDistributed(ICacheRefresher refresher, MessageType dispatchType) { // we don't care if there's servers listed or not, // if distributed call is enabled we will make the call @@ -79,7 +79,6 @@ namespace Umbraco.Core.Sync } protected override void DeliverRemote( - IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, diff --git a/src/Umbraco.Core/Sync/DatabaseServerRegistrarOptions.cs b/src/Umbraco.Core/Sync/DatabaseServerRegistrarOptions.cs index 6f019e7104..58b66ca8e6 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerRegistrarOptions.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerRegistrarOptions.cs @@ -14,14 +14,9 @@ namespace Umbraco.Core.Sync public DatabaseServerRegistrarOptions() { StaleServerTimeout = TimeSpan.FromMinutes(2); // 2 minutes - ThrottleSeconds = 30; // 30 seconds RecurringSeconds = 60; // do it every minute } - - [Obsolete("This is no longer used")] - [EditorBrowsable(EditorBrowsableState.Never)] - public int ThrottleSeconds { get; set; } - + /// /// The amount of seconds to wait between calls to the database on the background thread /// diff --git a/src/Umbraco.Core/Sync/IServerMessenger.cs b/src/Umbraco.Core/Sync/IServerMessenger.cs index 1df4ce9710..b3e5ef862d 100644 --- a/src/Umbraco.Core/Sync/IServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IServerMessenger.cs @@ -13,78 +13,69 @@ namespace Umbraco.Core.Sync /// /// Notifies the distributed cache, for a specified . /// - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// The notification content. - void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, TPayload[] payload); + void PerformRefresh(ICacheRefresher refresher, TPayload[] payload); /// /// Notifies the distributed cache, for a specified . /// - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// The notification content. - void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload); + void PerformRefresh(ICacheRefresher refresher, string jsonPayload); /// /// Notifies the distributed cache of specifieds item invalidation, for a specified . /// /// The type of the invalidated items. - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// A function returning the unique identifier of items. /// The invalidated items. - void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances); + void PerformRefresh(ICacheRefresher refresher, Func getNumericId, params T[] instances); /// /// Notifies the distributed cache of specifieds item invalidation, for a specified . /// /// The type of the invalidated items. - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// A function returning the unique identifier of items. /// The invalidated items. - void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances); + void PerformRefresh(ICacheRefresher refresher, Func getGuidId, params T[] instances); /// /// Notifies all servers of specified items removal, for a specified . /// /// The type of the removed items. - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// A function returning the unique identifier of items. /// The removed items. - void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances); + void PerformRemove(ICacheRefresher refresher, Func getNumericId, params T[] instances); /// /// Notifies all servers of specified items removal, for a specified . /// - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// The unique identifiers of the removed items. - void PerformRemove(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds); + void PerformRemove(ICacheRefresher refresher, params int[] numericIds); /// /// Notifies all servers of specified items invalidation, for a specified . /// - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// The unique identifiers of the invalidated items. - void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds); + void PerformRefresh(ICacheRefresher refresher, params int[] numericIds); /// /// Notifies all servers of specified items invalidation, for a specified . /// - /// The servers that compose the load balanced environment. /// The ICacheRefresher. /// The unique identifiers of the invalidated items. - void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params Guid[] guidIds); + void PerformRefresh(ICacheRefresher refresher, params Guid[] guidIds); /// /// Notifies all servers of a global invalidation for a specified . /// - /// The servers that compose the load balanced environment. /// The ICacheRefresher. - void PerformRefreshAll(IEnumerable servers, ICacheRefresher refresher); + void PerformRefreshAll(ICacheRefresher refresher); } } diff --git a/src/Umbraco.Core/Sync/RefreshInstructionEnvelope.cs b/src/Umbraco.Core/Sync/RefreshInstructionEnvelope.cs index ac51e14a30..9cd442da2d 100644 --- a/src/Umbraco.Core/Sync/RefreshInstructionEnvelope.cs +++ b/src/Umbraco.Core/Sync/RefreshInstructionEnvelope.cs @@ -9,14 +9,12 @@ namespace Umbraco.Core.Sync /// public sealed class RefreshInstructionEnvelope { - public RefreshInstructionEnvelope(IEnumerable servers, ICacheRefresher refresher, IEnumerable instructions) + public RefreshInstructionEnvelope(ICacheRefresher refresher, IEnumerable instructions) { - Servers = servers; Refresher = refresher; Instructions = instructions; } - public IEnumerable Servers { get; set; } public ICacheRefresher Refresher { get; set; } public IEnumerable Instructions { get; set; } } diff --git a/src/Umbraco.Core/Sync/ServerMessengerBase.cs b/src/Umbraco.Core/Sync/ServerMessengerBase.cs index d16c88eca3..68223a40e6 100644 --- a/src/Umbraco.Core/Sync/ServerMessengerBase.cs +++ b/src/Umbraco.Core/Sync/ServerMessengerBase.cs @@ -27,9 +27,9 @@ namespace Umbraco.Core.Sync /// The cache refresher. /// The message type. /// true if distributed calls are required; otherwise, false, all we have is the local server. - protected virtual bool RequiresDistributed(IEnumerable servers, ICacheRefresher refresher, MessageType messageType) + protected virtual bool RequiresDistributed(ICacheRefresher refresher, MessageType messageType) { - return DistributedEnabled && servers.Any(); + return DistributedEnabled; } // ensures that all items in the enumerable are of the same type, either int or Guid. @@ -56,98 +56,97 @@ namespace Umbraco.Core.Sync #region IServerMessenger - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, TPayload[] payload) + public void PerformRefresh(ICacheRefresher refresher, TPayload[] payload) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (payload == null) throw new ArgumentNullException(nameof(payload)); - Deliver(servers, refresher, payload); + Deliver(refresher, payload); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) + public void PerformRefresh(ICacheRefresher refresher, string jsonPayload) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (jsonPayload == null) throw new ArgumentNullException(nameof(jsonPayload)); - Deliver(servers, refresher, MessageType.RefreshByJson, json: jsonPayload); + Deliver(refresher, MessageType.RefreshByJson, json: jsonPayload); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) + public void PerformRefresh(ICacheRefresher refresher, Func getNumericId, params T[] instances) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (getNumericId == null) throw new ArgumentNullException(nameof(getNumericId)); if (instances == null || instances.Length == 0) return; Func getId = x => getNumericId(x); - Deliver(servers, refresher, MessageType.RefreshByInstance, getId, instances); + Deliver(refresher, MessageType.RefreshByInstance, getId, instances); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances) + public void PerformRefresh(ICacheRefresher refresher, Func getGuidId, params T[] instances) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (getGuidId == null) throw new ArgumentNullException(nameof(getGuidId)); if (instances == null || instances.Length == 0) return; Func getId = x => getGuidId(x); - Deliver(servers, refresher, MessageType.RefreshByInstance, getId, instances); + Deliver(refresher, MessageType.RefreshByInstance, getId, instances); } - public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) + public void PerformRemove(ICacheRefresher refresher, Func getNumericId, params T[] instances) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (getNumericId == null) throw new ArgumentNullException(nameof(getNumericId)); if (instances == null || instances.Length == 0) return; Func getId = x => getNumericId(x); - Deliver(servers, refresher, MessageType.RemoveByInstance, getId, instances); + Deliver(refresher, MessageType.RemoveByInstance, getId, instances); } - public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds) + public void PerformRemove(ICacheRefresher refresher, params int[] numericIds) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (numericIds == null || numericIds.Length == 0) return; - Deliver(servers, refresher, MessageType.RemoveById, numericIds.Cast()); + Deliver(refresher, MessageType.RemoveById, numericIds.Cast()); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds) + public void PerformRefresh(ICacheRefresher refresher, params int[] numericIds) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (numericIds == null || numericIds.Length == 0) return; - Deliver(servers, refresher, MessageType.RefreshById, numericIds.Cast()); + Deliver(refresher, MessageType.RefreshById, numericIds.Cast()); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params Guid[] guidIds) + public void PerformRefresh(ICacheRefresher refresher, params Guid[] guidIds) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); if (guidIds == null || guidIds.Length == 0) return; - Deliver(servers, refresher, MessageType.RefreshById, guidIds.Cast()); + Deliver(refresher, MessageType.RefreshById, guidIds.Cast()); } - public void PerformRefreshAll(IEnumerable servers, ICacheRefresher refresher) + public void PerformRefreshAll(ICacheRefresher refresher) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - Deliver(servers, refresher, MessageType.RefreshAll); + Deliver(refresher, MessageType.RefreshAll); } - //public void PerformNotify(IEnumerable servers, ICacheRefresher refresher, object payload) + //public void PerformNotify(ICacheRefresher refresher, object payload) //{ // if (servers == null) throw new ArgumentNullException("servers"); // if (refresher == null) throw new ArgumentNullException("refresher"); - // Deliver(servers, refresher, payload); + // Deliver(refresher, payload); //} #endregion @@ -283,61 +282,57 @@ namespace Umbraco.Core.Sync // refresher.Notify(payload); //} - protected abstract void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null); + protected abstract void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null); - //protected abstract void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, object payload); + //protected abstract void DeliverRemote(ICacheRefresher refresher, object payload); - protected virtual void Deliver(IEnumerable servers, ICacheRefresher refresher, TPayload[] payload) + protected virtual void Deliver(ICacheRefresher refresher, TPayload[] payload) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - var serversA = servers.ToArray(); - // deliver local DeliverLocal(refresher, payload); // distribute? - if (RequiresDistributed(serversA, refresher, MessageType.RefreshByJson) == false) + if (RequiresDistributed(refresher, MessageType.RefreshByJson) == false) return; // deliver remote var json = JsonConvert.SerializeObject(payload); - DeliverRemote(serversA, refresher, MessageType.RefreshByJson, null, json); + DeliverRemote(refresher, MessageType.RefreshByJson, null, json); } - protected virtual void Deliver(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) + protected virtual void Deliver(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - var serversA = servers.ToArray(); var idsA = ids?.ToArray(); // deliver local DeliverLocal(refresher, messageType, idsA, json); // distribute? - if (RequiresDistributed(serversA, refresher, messageType) == false) + if (RequiresDistributed(refresher, messageType) == false) return; // deliver remote - DeliverRemote(serversA, refresher, messageType, idsA, json); + DeliverRemote(refresher, messageType, idsA, json); } - protected virtual void Deliver(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, Func getId, IEnumerable instances) + protected virtual void Deliver(ICacheRefresher refresher, MessageType messageType, Func getId, IEnumerable instances) { - if (servers == null) throw new ArgumentNullException(nameof(servers)); + if (refresher == null) throw new ArgumentNullException(nameof(refresher)); - var serversA = servers.ToArray(); var instancesA = instances.ToArray(); // deliver local DeliverLocal(refresher, messageType, getId, instancesA); // distribute? - if (RequiresDistributed(serversA, refresher, messageType) == false) + if (RequiresDistributed(refresher, messageType) == false) return; // deliver remote @@ -349,10 +344,10 @@ namespace Umbraco.Core.Sync // convert instances to identifiers var idsA = instancesA.Select(getId).ToArray(); - DeliverRemote(serversA, refresher, messageType, idsA); + DeliverRemote(refresher, messageType, idsA); } - //protected virtual void Deliver(IEnumerable servers, ICacheRefresher refresher, object payload) + //protected virtual void Deliver(ICacheRefresher refresher, object payload) //{ // if (servers == null) throw new ArgumentNullException("servers"); // if (refresher == null) throw new ArgumentNullException("refresher"); diff --git a/src/Umbraco.Core/Sync/WebServiceServerMessenger.cs b/src/Umbraco.Core/Sync/WebServiceServerMessenger.cs deleted file mode 100644 index dff839fe6f..0000000000 --- a/src/Umbraco.Core/Sync/WebServiceServerMessenger.cs +++ /dev/null @@ -1,383 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Threading; -using Newtonsoft.Json; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Cache; -using Umbraco.Core.Composing; - -namespace Umbraco.Core.Sync -{ - /// - /// An that works by messaging servers via web services. - /// - /// - /// this messenger sends ALL instructions to ALL servers, including the local server. - /// the CacheRefresher web service will run ALL instructions, so there may be duplicated, - /// except for "bulk" refresh, where it excludes those coming from the local server - /// - // - // TODO see Message() method: stop sending to local server! - // just need to figure out WebServerUtility permissions issues, if any - // - internal class WebServiceServerMessenger : ServerMessengerBase - { - private readonly Func> _getLoginAndPassword; - private volatile bool _hasLoginAndPassword; - private readonly object _locker = new object(); - - protected string Login { get; private set; } - protected string Password{ get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// Distribution is disabled. - internal WebServiceServerMessenger() - : base(false) - { } - - /// - /// Initializes a new instance of the class with a login and a password. - /// - /// The login. - /// The password. - /// Distribution will be enabled based on the umbraco config setting. - internal WebServiceServerMessenger(string login, string password) - : this(login, password, UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled) - { - } - - /// - /// Initializes a new instance of the class with a login and a password - /// and a value indicating whether distribution is enabled. - /// - /// The login. - /// The password. - /// A value indicating whether distribution is enabled. - internal WebServiceServerMessenger(string login, string password, bool distributedEnabled) - : base(distributedEnabled) - { - if (login == null) throw new ArgumentNullException("login"); - if (password == null) throw new ArgumentNullException("password"); - - Login = login; - Password = password; - } - - /// - /// Initializes a new instance of the with a function providing - /// a login and a password. - /// - /// A function providing a login and a password. - /// Distribution will be enabled based on the umbraco config setting. - public WebServiceServerMessenger(Func> getLoginAndPassword) - : base(false) // value will be overriden by EnsureUserAndPassword - { - _getLoginAndPassword = getLoginAndPassword; - } - - // lazy-get the login, password, and distributed setting - protected void EnsureLoginAndPassword() - { - if (_hasLoginAndPassword || _getLoginAndPassword == null) return; - - lock (_locker) - { - if (_hasLoginAndPassword) return; - _hasLoginAndPassword = true; - - try - { - var result = _getLoginAndPassword(); - if (result == null) - { - Login = null; - Password = null; - DistributedEnabled = false; - } - else - { - Login = result.Item1; - Password = result.Item2; - DistributedEnabled = UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled; - } - } - catch (Exception ex) - { - Current.Logger.Error("Could not resolve username/password delegate, server distribution will be disabled", ex); - Login = null; - Password = null; - DistributedEnabled = false; - } - } - } - - // this exists only for legacy reasons - we should just pass the server identity un-hashed - public static string GetCurrentServerHash() - { - if (SystemUtilities.GetCurrentTrustLevel() != System.Web.AspNetHostingPermissionLevel.Unrestricted) - throw new NotSupportedException("FullTrust ASP.NET permission level is required."); - return GetServerHash(NetworkHelper.MachineName, System.Web.HttpRuntime.AppDomainAppId); - } - - public static string GetServerHash(string machineName, string appDomainAppId) - { - using (var generator = new HashGenerator()) - { - generator.AddString(machineName); - generator.AddString(appDomainAppId); - return generator.GenerateHash(); - } - } - - protected override bool RequiresDistributed(IEnumerable servers, ICacheRefresher refresher, MessageType messageType) - { - EnsureLoginAndPassword(); - return base.RequiresDistributed(servers, refresher, messageType); - } - - protected override void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) - { - var idsA = ids == null ? null : ids.ToArray(); - - Type arrayType; - if (GetArrayType(idsA, out arrayType) == false) - throw new ArgumentException("All items must be of the same type, either int or Guid.", "ids"); - - Message(servers, refresher, messageType, idsA, arrayType, json); - } - - protected virtual void Message( - IEnumerable servers, - ICacheRefresher refresher, - MessageType messageType, - IEnumerable ids = null, - Type idArrayType = null, - string jsonPayload = null) - { - Current.Logger.Debug(() => - $"Performing distributed call for {refresher.GetType()}/{messageType} on servers ({string.Join(";", servers.Select(x => x.ToString()))}), ids: {(ids == null ? "" : string.Join(";", ids.Select(x => x.ToString())))}, json: {(jsonPayload ?? "")}"); - - try - { - // NOTE: we are messaging ALL servers including the local server - // at the moment, the web service, - // for bulk (batched) checks the origin and does NOT process the instructions again - // for anything else, processes the instructions again (but we don't use this anymore, batched is the default) - // TODO: see WebServerHelper, could remove local server from the list of servers - - // the default server messenger uses http requests - using (var client = new ServerSyncWebServiceClient()) - { - var asyncResults = new List(); - - LogStartDispatch(); - - // go through each configured node submitting a request asynchronously - // NOTE: 'asynchronously' in this case does not mean that it will continue while we give the page back to the user! - foreach (var n in servers) - { - // set the server address - client.Url = n.ServerAddress; - - // add the returned WaitHandle to the list for later checking - switch (messageType) - { - case MessageType.RefreshByJson: - asyncResults.Add(client.BeginRefreshByJson(refresher.RefresherUniqueId, jsonPayload, Login, Password, null, null)); - break; - - case MessageType.RefreshAll: - asyncResults.Add(client.BeginRefreshAll(refresher.RefresherUniqueId, Login, Password, null, null)); - break; - - case MessageType.RefreshById: - if (idArrayType == null) - throw new InvalidOperationException("Cannot refresh by id if the idArrayType is null."); - - if (idArrayType == typeof(int)) - { - // bulk of ints is supported - var json = JsonConvert.SerializeObject(ids.Cast().ToArray()); - var result = client.BeginRefreshByIds(refresher.RefresherUniqueId, json, Login, Password, null, null); - asyncResults.Add(result); - } - else // must be guids - { - // bulk of guids is not supported, iterate - asyncResults.AddRange(ids.Select(i => - client.BeginRefreshByGuid(refresher.RefresherUniqueId, (Guid)i, Login, Password, null, null))); - } - - break; - case MessageType.RemoveById: - if (idArrayType == null) - throw new InvalidOperationException("Cannot remove by id if the idArrayType is null."); - - // must be ints - asyncResults.AddRange(ids.Select(i => - client.BeginRemoveById(refresher.RefresherUniqueId, (int)i, Login, Password, null, null))); - break; - } - } - - // wait for all requests to complete - var waitHandles = asyncResults.Select(x => x.AsyncWaitHandle); - WaitHandle.WaitAll(waitHandles.ToArray()); - - // handle results - var errorCount = 0; - foreach (var asyncResult in asyncResults) - { - try - { - switch (messageType) - { - case MessageType.RefreshByJson: - client.EndRefreshByJson(asyncResult); - break; - - case MessageType.RefreshAll: - client.EndRefreshAll(asyncResult); - break; - - case MessageType.RefreshById: - if (idArrayType == typeof(int)) - client.EndRefreshById(asyncResult); - else - client.EndRefreshByGuid(asyncResult); - break; - - case MessageType.RemoveById: - client.EndRemoveById(asyncResult); - break; - } - } - catch (WebException ex) - { - LogDispatchNodeError(ex); - errorCount++; - } - catch (Exception ex) - { - LogDispatchNodeError(ex); - errorCount++; - } - } - - LogDispatchBatchResult(errorCount); - } - } - catch (Exception ee) - { - LogDispatchBatchError(ee); - } - } - - protected virtual void Message(IEnumerable envelopes) - { - var envelopesA = envelopes.ToArray(); - var servers = envelopesA.SelectMany(x => x.Servers).Distinct(); - - try - { - // NOTE: we are messaging ALL servers including the local server - // at the moment, the web service, - // for bulk (batched) checks the origin and does NOT process the instructions again - // for anything else, processes the instructions again (but we don't use this anymore, batched is the default) - // TODO: see WebServerHelper, could remove local server from the list of servers - - using (var client = new ServerSyncWebServiceClient()) - { - var asyncResults = new List(); - - LogStartDispatch(); - - // go through each configured node submitting a request asynchronously - // NOTE: 'asynchronously' in this case does not mean that it will continue while we give the page back to the user! - foreach (var server in servers) - { - // set the server address - client.Url = server.ServerAddress; - - var serverInstructions = envelopesA - .Where(x => x.Servers.Contains(server)) - .SelectMany(x => x.Instructions) - .Distinct() // only execute distinct instructions - no sense in running the same one. - .ToArray(); - - asyncResults.Add( - client.BeginBulkRefresh( - serverInstructions, - GetCurrentServerHash(), - Login, Password, null, null)); - } - - // wait for all requests to complete - var waitHandles = asyncResults.Select(x => x.AsyncWaitHandle).ToArray(); - WaitHandle.WaitAll(waitHandles.ToArray()); - - // handle results - var errorCount = 0; - foreach (var asyncResult in asyncResults) - { - try - { - client.EndBulkRefresh(asyncResult); - } - catch (WebException ex) - { - LogDispatchNodeError(ex); - errorCount++; - } - catch (Exception ex) - { - LogDispatchNodeError(ex); - errorCount++; - } - } - LogDispatchBatchResult(errorCount); - } - } - catch (Exception ee) - { - LogDispatchBatchError(ee); - } - } - - #region Logging - - private static void LogDispatchBatchError(Exception ee) - { - Current.Logger.Error("Error refreshing distributed list", ee); - } - - private static void LogDispatchBatchResult(int errorCount) - { - Current.Logger.Debug(string.Format("Distributed server push completed with {0} nodes reporting an error", errorCount == 0 ? "no" : errorCount.ToString(CultureInfo.InvariantCulture))); - } - - private static void LogDispatchNodeError(Exception ex) - { - Current.Logger.Error("Error refreshing a node in the distributed list", ex); - } - - private static void LogDispatchNodeError(WebException ex) - { - string url = (ex.Response != null) ? ex.Response.ResponseUri.ToString() : "invalid url (responseUri null)"; - Current.Logger.Error("Error refreshing a node in the distributed list, URI attempted: " + url, ex); - } - - private static void LogStartDispatch() - { - Current.Logger.Info("Submitting calls to distributed servers"); - } - - #endregion - } -} diff --git a/src/Umbraco.Core/UdiEntityType.cs b/src/Umbraco.Core/UdiEntityType.cs index 34bd26b537..07bde1ba93 100644 --- a/src/Umbraco.Core/UdiEntityType.cs +++ b/src/Umbraco.Core/UdiEntityType.cs @@ -54,8 +54,7 @@ namespace Umbraco.Core { Script, UdiType.StringUdi}, { PartialView, UdiType.StringUdi}, { PartialViewMacro, UdiType.StringUdi}, - { Stylesheet, UdiType.StringUdi}, - { Xslt, UdiType.StringUdi}, + { Stylesheet, UdiType.StringUdi} }; } @@ -108,7 +107,6 @@ namespace Umbraco.Core public const string Stylesheet = "stylesheet"; public const string PartialView = "partial-view"; public const string PartialViewMacro = "partial-view-macro"; - public const string Xslt = "xslt"; public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType) { diff --git a/src/Umbraco.Core/UdiGetterExtensions.cs b/src/Umbraco.Core/UdiGetterExtensions.cs index 3ba5fe6f65..5a5ccf5574 100644 --- a/src/Umbraco.Core/UdiGetterExtensions.cs +++ b/src/Umbraco.Core/UdiGetterExtensions.cs @@ -206,18 +206,7 @@ namespace Umbraco.Core return new StringUdi(entityType, entity.Path.TrimStart('/')).EnsureClosed(); } - - /// - /// Gets the entity identifier of the entity. - /// - /// The entity. - /// The entity identifier of the entity. - public static StringUdi GetUdi(this IXsltFile entity) - { - if (entity == null) throw new ArgumentNullException("entity"); - return new StringUdi(Constants.UdiEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed(); - } - + /// /// Gets the entity identifier of the entity. /// @@ -317,9 +306,6 @@ namespace Umbraco.Core var partialView = entity as IPartialView; if (partialView != null) return partialView.GetUdi(); - var xsltFile = entity as IXsltFile; - if (xsltFile != null) return xsltFile.GetUdi(); - var contentBase = entity as IContentBase; if (contentBase != null) return contentBase.GetUdi(); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 77b78f355d..42e532869b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -47,7 +47,6 @@ - @@ -74,7 +73,7 @@ - + @@ -137,6 +136,7 @@ + @@ -249,32 +249,24 @@ - - - - - - - - @@ -283,17 +275,11 @@ - - - - - - @@ -311,6 +297,7 @@ + @@ -319,6 +306,7 @@ + @@ -335,10 +323,12 @@ - + + + @@ -365,9 +355,11 @@ + + @@ -402,6 +394,7 @@ + @@ -552,7 +545,6 @@ - @@ -562,10 +554,6 @@ - - - - @@ -603,9 +591,7 @@ - - @@ -690,7 +676,6 @@ - @@ -710,7 +695,6 @@ - @@ -722,7 +706,6 @@ - @@ -810,8 +793,6 @@ - - @@ -848,7 +829,6 @@ - @@ -856,9 +836,7 @@ - - @@ -1182,7 +1160,6 @@ - @@ -1217,7 +1194,6 @@ - @@ -1287,7 +1263,6 @@ - @@ -1414,7 +1389,6 @@ - @@ -1466,9 +1440,6 @@ - - - @@ -1483,7 +1454,6 @@ - diff --git a/src/Umbraco.Core/WriteLock.cs b/src/Umbraco.Core/WriteLock.cs index b6a957234b..6ddc7e0bb3 100644 --- a/src/Umbraco.Core/WriteLock.cs +++ b/src/Umbraco.Core/WriteLock.cs @@ -10,9 +10,11 @@ namespace Umbraco.Core /// Provides a convenience methodology for implementing locked access to resources. /// /// - /// Intended as an infrastructure class. + /// Intended as an infrastructure class. + /// This is a very unefficient way to lock as it allocates one object each time we lock, + /// so it's OK to use this class for things that happen once, where it is convenient, but not + /// for performance-critical code! /// - [Obsolete("stop using, allocates")] public class WriteLock : IDisposable { private readonly ReaderWriterLockSlim _rwLock; diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 5dfa0d1285..4c33b16002 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -69,7 +69,6 @@ - @@ -157,24 +156,11 @@ Umbraco.Web - - -REM if not exist "$(TargetDir)x86" md "$(TargetDir)x86" -REM xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\x86\*.*" "$(TargetDir)x86" -REM if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64" -REM xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64" - - - REM xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /I /C /D -REM xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D - REM if not exist "$(TargetDir)x86" md "$(TargetDir)x86" -REM xcopy /s /y "$(NugetPackages)\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\x86\*.*" "$(TargetDir)x86" -REM if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64" -REM xcopy /s /y "$(NugetPackages)\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64" $(NuGetPackageFolders.Split(';')[0]) + diff --git a/src/Umbraco.Tests/Cache/CacheRefresherTests.cs b/src/Umbraco.Tests/Cache/CacheRefresherTests.cs deleted file mode 100644 index 8002cb9b4f..0000000000 --- a/src/Umbraco.Tests/Cache/CacheRefresherTests.cs +++ /dev/null @@ -1,29 +0,0 @@ -using NUnit.Framework; -using Umbraco.Core.Sync; - -namespace Umbraco.Tests.Cache -{ - [TestFixture] - public class CacheRefresherTests - { - [TestCase("", "123456", "testmachine", true)] //empty hash will continue - [TestCase("2e6deefea4444a69dbd15a01b4c2749d", "123456", "testmachine", false)] //match, don't continue - [TestCase("2e6deefea4444a69dbd15a01b4c2749d", "12345", "testmachine", true)] // no match, continue - [TestCase("2e6deefea4444a69dbd15a01b4c2749d", "123456", "testmachin", true)] // same - [TestCase("2e6deefea4444a69dbd15a01b4c2749", "123456", "testmachine", true)] // same - public void Continue_Refreshing_For_Request(string hash, string appDomainAppId, string machineName, bool expected) - { - if (expected) - Assert.IsTrue(Continue(hash, WebServiceServerMessenger.GetServerHash(appDomainAppId, machineName))); - else - Assert.IsFalse(Continue(hash, WebServiceServerMessenger.GetServerHash(appDomainAppId, machineName))); - } - - // that's what CacheRefresher.asmx.cs does... - private bool Continue(string hash1, string hash2) - { - if (string.IsNullOrEmpty(hash1)) return true; - return hash1 != hash2; - } - } -} diff --git a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs index ed28004477..dc67bb532f 100644 --- a/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCache/DistributedCacheTests.cs @@ -129,52 +129,52 @@ namespace Umbraco.Tests.Cache.DistributedCache public List PayloadsRefreshed = new List(); public int CountOfFullRefreshes = 0; - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, TPayload[] payload) + public void PerformRefresh(ICacheRefresher refresher, TPayload[] payload) { // doing nothing } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) + public void PerformRefresh(ICacheRefresher refresher, string jsonPayload) { PayloadsRefreshed.Add(jsonPayload); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) + public void PerformRefresh(ICacheRefresher refresher, Func getNumericId, params T[] instances) { IntIdsRefreshed.AddRange(instances.Select(getNumericId)); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, Func getGuidId, params T[] instances) + public void PerformRefresh(ICacheRefresher refresher, Func getGuidId, params T[] instances) { GuidIdsRefreshed.AddRange(instances.Select(getGuidId)); } - public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, string jsonPayload) + public void PerformRemove(ICacheRefresher refresher, string jsonPayload) { PayloadsRemoved.Add(jsonPayload); } - public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, Func getNumericId, params T[] instances) + public void PerformRemove(ICacheRefresher refresher, Func getNumericId, params T[] instances) { IntIdsRemoved.AddRange(instances.Select(getNumericId)); } - public void PerformRemove(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds) + public void PerformRemove(ICacheRefresher refresher, params int[] numericIds) { IntIdsRemoved.AddRange(numericIds); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params int[] numericIds) + public void PerformRefresh(ICacheRefresher refresher, params int[] numericIds) { IntIdsRefreshed.AddRange(numericIds); } - public void PerformRefresh(IEnumerable servers, ICacheRefresher refresher, params Guid[] guidIds) + public void PerformRefresh(ICacheRefresher refresher, params Guid[] guidIds) { GuidIdsRefreshed.AddRange(guidIds); } - public void PerformRefreshAll(IEnumerable servers, ICacheRefresher refresher) + public void PerformRefreshAll(ICacheRefresher refresher) { CountOfFullRefreshes++; } diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index f330c126d0..8212243e26 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -62,9 +62,9 @@ namespace Umbraco.Tests.Cache.PublishedCache _xml.LoadXml(GetXml()); var xmlStore = new XmlStore(() => _xml, null, null, null); var cacheProvider = new StaticCacheProvider(); - var domainCache = new DomainCache(ServiceContext.DomainService); - var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedShapshot( - new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, ContentTypesCache, null, null), + var domainCache = new DomainCache(ServiceContext.DomainService, SystemDefaultCultureProvider); + var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedSnapshot( + new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null), new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache), new PublishedMemberCache(null, cacheProvider, Current.Services.MemberService, ContentTypesCache), domainCache); @@ -77,7 +77,8 @@ namespace Umbraco.Tests.Cache.PublishedCache new WebSecurity(_httpContextFactory.HttpContext, Current.Services.UserService, globalSettings), umbracoSettings, Enumerable.Empty(), - globalSettings); + globalSettings, + ServiceContext.EntityService); _cache = _umbracoContext.ContentCache; } diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 29458deefb..b30312647e 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Composing Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = TypeFinder.FindClassesWithAttribute(new[] { typeof (UmbracoContext).Assembly }); - Assert.AreEqual(23, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] + Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree] } private static ProfilingLogger GetTestProfilingLogger() @@ -100,70 +100,6 @@ namespace Umbraco.Tests.Composing return new ProfilingLogger(logger, profiler); } - [Ignore("fixme - ignored test")] - [Test] - public void Benchmark_Original_Finder() - { - var profilingLogger = GetTestProfilingLogger(); - using (profilingLogger.TraceDuration("Starting test", "Finished test")) - { - using (profilingLogger.TraceDuration("Starting FindClassesOfType", "Finished FindClassesOfType")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinderOriginal.FindClassesOfType(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesOfTypeWithAttribute", "Finished FindClassesOfTypeWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinderOriginal.FindClassesOfTypeWithAttribute(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesWithAttribute", "Finished FindClassesWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinderOriginal.FindClassesWithAttribute(_assemblies).Count(), 0); - } - } - } - - } - - [Ignore("fixme - ignored test")] - [Test] - public void Benchmark_New_Finder() - { - var profilingLogger = GetTestProfilingLogger(); - using (profilingLogger.TraceDuration("Starting test", "Finished test")) - { - using (profilingLogger.TraceDuration("Starting FindClassesOfType", "Finished FindClassesOfType")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinder.FindClassesOfType(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesOfTypeWithAttribute", "Finished FindClassesOfTypeWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinder.FindClassesOfTypeWithAttribute(_assemblies).Count(), 0); - } - } - using (profilingLogger.TraceDuration("Starting FindClassesWithAttribute", "Finished FindClassesWithAttribute")) - { - for (var i = 0; i < 1000; i++) - { - Assert.Greater(TypeFinder.FindClassesWithAttribute(_assemblies).Count(), 0); - } - } - } - - } - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class MyTestAttribute : Attribute { @@ -374,7 +310,7 @@ namespace Umbraco.Tests.Composing using (new WriteLock(LocalFilteredAssemblyCacheLocker)) { var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter); - assemblies.ForEach(LocalFilteredAssemblyCache.Add); + foreach (var assembly in assemblies) LocalFilteredAssemblyCache.Add(assembly); } return LocalFilteredAssemblyCache; } diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 8965d60018..46f024429e 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -272,30 +272,23 @@ AnotherContentFinder public void Resolves_Actions() { var actions = _typeLoader.GetActions(); - Assert.AreEqual(37, actions.Count()); + Assert.AreEqual(35, actions.Count()); } [Test] public void Resolves_Trees() { var trees = _typeLoader.GetTrees(); - Assert.AreEqual(5, trees.Count()); + Assert.AreEqual(4, trees.Count()); } [Test] public void GetDataEditors() { var types = _typeLoader.GetDataEditors(); - Assert.AreEqual(42, types.Count()); + Assert.AreEqual(43, types.Count()); } - - [Test] - public void Resolves_XsltExtensions() - { - var types = _typeLoader.GetXsltExtensions(); - Assert.AreEqual(3, types.Count()); - } - + /// /// This demonstrates this issue: http://issues.umbraco.org/issue/U4-3505 - the TypeList was returning a list of assignable types /// not explicit types which is sort of ideal but is confusing so we'll do it the less confusing way. @@ -319,12 +312,6 @@ AnotherContentFinder Assert.IsNull(shouldNotFind); } - [XsltExtension("Blah.Blah")] - public class MyXsltExtension - { - - } - public interface IFindMe : IDiscoverable { diff --git a/src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs b/src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs deleted file mode 100644 index 399b1df7bb..0000000000 --- a/src/Umbraco.Tests/Composing/XsltExtensionCollectionTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; -using LightInject; -using NUnit.Framework; -using Umbraco.Core.Macros; -using Umbraco.Web; - -namespace Umbraco.Tests.Composing -{ - [TestFixture] - public class XsltExtensionCollectionTests : ComposingTestBase - { - [Test] - public void XsltExtensionsCollectionBuilderWorks() - { - var container = new ServiceContainer(); - var builder = new XsltExtensionCollectionBuilder(container); - builder.AddExtensionObjectProducer(() => TypeLoader.GetXsltExtensions()); - var extensions = builder.CreateCollection(); - - Assert.AreEqual(3, extensions.Count()); - - Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof (XsltEx1))); - Assert.IsTrue(extensions.Select(x => x.ExtensionObject.GetType()).Contains(typeof(XsltEx2))); - Assert.AreEqual("test1", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx1)).Namespace); - Assert.AreEqual("test2", extensions.Single(x => x.ExtensionObject.GetType() == typeof(XsltEx2)).Namespace); - } - - #region Test Objects - - [XsltExtension("test1")] - public class XsltEx1 - { } - - //test with legacy one - [umbraco.XsltExtension("test2")] - public class XsltEx2 - { } - - #endregion - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/DeveloperElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/DeveloperElementDefaultTests.cs deleted file mode 100644 index a3f8b79aa1..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/DeveloperElementDefaultTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class DeveloperElementDefaultTests : DeveloperElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/DeveloperElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/DeveloperElementTests.cs deleted file mode 100644 index aa79ffd965..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/DeveloperElementTests.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class DeveloperElementTests : UmbracoSettingsTests - { - [Test] - public void AppCodeFileExtensions() - { - Assert.IsTrue(SettingsSection.Developer.AppCodeFileExtensions.Count() == 2); - Assert.IsTrue(SettingsSection.Developer.AppCodeFileExtensions.All( - x => "cs,vb".Split(',').Contains(x.Extension))); - } - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs deleted file mode 100644 index 2c3a843af6..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementDefaultTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class DistributedCallElementDefaultTests : DistributedCallElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void Enabled() - { - Assert.IsTrue(SettingsSection.DistributedCall.Enabled == false); - - } - - [Test] - public override void Servers() - { - Assert.IsTrue(SettingsSection.DistributedCall.Servers.Any() == false); - } - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementTests.cs deleted file mode 100644 index c7796a3b0a..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/DistributedCallElementTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class DistributedCallElementTests : UmbracoSettingsTests - { - [Test] - public virtual void Enabled() - { - Assert.IsTrue(SettingsSection.DistributedCall.Enabled == true); - - } - [Test] - public void UserId() - { - Assert.IsTrue(SettingsSection.DistributedCall.UserId == 0); - - } - [Test] - public virtual void Servers() - { - Assert.IsTrue(SettingsSection.DistributedCall.Servers.Count() == 2); - Assert.IsTrue(SettingsSection.DistributedCall.Servers.ElementAt(0).ServerAddress == "127.0.0.1"); - Assert.IsTrue(SettingsSection.DistributedCall.Servers.ElementAt(1).ServerAddress == "127.0.0.2"); - Assert.IsTrue(SettingsSection.DistributedCall.Servers.ElementAt(1).ForceProtocol == "https"); - Assert.IsTrue(SettingsSection.DistributedCall.Servers.ElementAt(1).ForcePortnumber == "443"); - } - - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs deleted file mode 100644 index 4a96957e2f..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementDefaultTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class PackageRepositoriesElementDefaultTests : PackageRepositoriesElementTests - { - protected override bool TestingDefaults - { - get { return true; } - } - - [Test] - public override void Repositories() - { - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.Count() == 1); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Id == Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Name == "Umbraco package Repository"); - } - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs deleted file mode 100644 index 6b5d0e2d48..0000000000 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/PackageRepositoriesElementTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; - -namespace Umbraco.Tests.Configurations.UmbracoSettings -{ - [TestFixture] - public class PackageRepositoriesElementTests : UmbracoSettingsTests - { - [Test] - public virtual void Repositories() - { - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.Count() == 2); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Id == Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).Name == "Umbraco package Repository"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).HasCustomWebServiceUrl == false); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).WebServiceUrl == "/umbraco/webservices/api/repository.asmx"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).RepositoryUrl == "http://packages.umbraco.org"); - - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(1).Id == Guid.Parse("163245E0-CD22-44B6-841A-1B9B9D2E955F")); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(1).Name == "Test Repo"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(1).HasCustomWebServiceUrl == false); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).WebServiceUrl == "/umbraco/webservices/api/repository.asmx"); - Assert.IsTrue(SettingsSection.PackageRepositories.Repositories.ElementAt(0).RepositoryUrl == "http://packages.umbraco.org"); - } - } -} diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs index d419d9d320..429aa28bb7 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/SecurityElementTests.cs @@ -33,7 +33,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [Test] public void AuthCookieName() { - Assert.IsTrue(SettingsSection.Security.AuthCookieName == Constants.Web.AuthCookieName); + Assert.IsTrue(SettingsSection.Security.AuthCookieName == "UMB_UCONTEXT"); } } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/TemplateElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/TemplateElementTests.cs index bfc8f72c6f..3e5a6df65b 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/TemplateElementTests.cs +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/TemplateElementTests.cs @@ -6,25 +6,11 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings [TestFixture] public class TemplateElementTests : UmbracoSettingsTests { - [Test] - public void UseAspNetMasterPages() - { - Assert.IsTrue(SettingsSection.Templates.UseAspNetMasterPages == true); - } - [Test] - public void EnableSkinSupport() - { - Assert.IsTrue(SettingsSection.Templates.EnableSkinSupport); - } [Test] public void DefaultRenderingEngine() { Assert.IsTrue(SettingsSection.Templates.DefaultRenderingEngine == RenderingEngine.Mvc); } - [Test] - public void EnableTemplateFolders() - { - Assert.IsTrue(SettingsSection.Templates.EnableTemplateFolders == false); - } + } } diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config index c56ee45f55..fc59f62d12 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config @@ -147,19 +147,9 @@ - true - true Mvc - - - - cs - vb - - - false true @@ -179,26 +169,6 @@ - - - - - 0 - - - - 127.0.0.1 - 127.0.0.2 - - - - - - - - - - @@ -226,4 +196,4 @@ internalRedirectPreservesTemplate="false"> - \ No newline at end of file + diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config index 0848a893d6..ba10dbca78 100644 --- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config +++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.minimal.config @@ -42,25 +42,16 @@ Mvc - - - - - - - - - - \ No newline at end of file + diff --git a/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs b/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs index 9f83529d11..123b9c54c2 100644 --- a/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/AbstractFileSystemTests.cs @@ -123,12 +123,12 @@ namespace Umbraco.Tests.IO var created = _fileSystem.GetCreated("test.txt"); var modified = _fileSystem.GetLastModified("test.txt"); - Assert.AreEqual(DateTime.Today.Year, created.Year); - Assert.AreEqual(DateTime.Today.Month, created.Month); + Assert.AreEqual(DateTime.UtcNow.Year, created.Year); + Assert.AreEqual(DateTime.UtcNow.Month, created.Month); Assert.AreEqual(DateTime.UtcNow.Date, created.Date); - Assert.AreEqual(DateTime.Today.Year, modified.Year); - Assert.AreEqual(DateTime.Today.Month, modified.Month); + Assert.AreEqual(DateTime.UtcNow.Year, modified.Year); + Assert.AreEqual(DateTime.UtcNow.Month, modified.Month); Assert.AreEqual(DateTime.UtcNow.Date, modified.Date); _fileSystem.DeleteFile("test.txt"); diff --git a/src/Umbraco.Tests/IO/IoHelperTests.cs b/src/Umbraco.Tests/IO/IoHelperTests.cs index fb3283718e..07436eff1a 100644 --- a/src/Umbraco.Tests/IO/IoHelperTests.cs +++ b/src/Umbraco.Tests/IO/IoHelperTests.cs @@ -47,7 +47,6 @@ namespace Umbraco.Tests.IO Assert.AreEqual(IOHelper.MapPath(SystemDirectories.UmbracoClient, true), IOHelper.MapPath(SystemDirectories.UmbracoClient, false)); Assert.AreEqual(IOHelper.MapPath(SystemDirectories.UserControls, true), IOHelper.MapPath(SystemDirectories.UserControls, false)); Assert.AreEqual(IOHelper.MapPath(SystemDirectories.WebServices, true), IOHelper.MapPath(SystemDirectories.WebServices, false)); - Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Xslt, true), IOHelper.MapPath(SystemDirectories.Xslt, false)); } [Test] diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index 502178377e..246626f0a8 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -14,8 +14,10 @@ using Umbraco.Core.Sync; using Umbraco.Tests.Cache.DistributedCache; using Umbraco.Tests.Services; using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web.Cache; +using static Umbraco.Tests.Cache.DistributedCache.DistributedCacheTests; namespace Umbraco.Tests.Integration { @@ -50,8 +52,8 @@ namespace Umbraco.Tests.Integration { base.Compose(); - Container.Register(_ => new DistributedCacheTests.TestServerRegistrar()); // localhost-only - Container.Register(new PerContainerLifetime()); + Container.Register(_ => new TestServerRegistrar()); // localhost-only + Container.Register(new PerContainerLifetime()); Container.RegisterCollectionBuilder() .Add() @@ -106,17 +108,17 @@ namespace Umbraco.Tests.Integration private IContent CreateBranch() { var content1 = MockedContent.CreateSimpleContent(_contentType, "Content1"); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); // 2 (published) // .1 (published) // .2 (not published) var content2 = MockedContent.CreateSimpleContent(_contentType, "Content2", content1); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content21 = MockedContent.CreateSimpleContent(_contentType, "Content21", content2); - content21.PublishValues(); + content21.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content21); var content22 = MockedContent.CreateSimpleContent(_contentType, "Content22", content2); ServiceContext.ContentService.Save(content22); @@ -135,12 +137,12 @@ namespace Umbraco.Tests.Integration // .1 (published) // .2 (not published) var content4 = MockedContent.CreateSimpleContent(_contentType, "Content4", content1); - content4.PublishValues(); + content4.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content4); content4.Name = "Content4X"; ServiceContext.ContentService.Save(content4); var content41 = MockedContent.CreateSimpleContent(_contentType, "Content41", content4); - content41.PublishValues(); + content41.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content41); var content42 = MockedContent.CreateSimpleContent(_contentType, "Content42", content4); ServiceContext.ContentService.Save(content42); @@ -149,10 +151,10 @@ namespace Umbraco.Tests.Integration // .1 (published) // .2 (not published) var content5 = MockedContent.CreateSimpleContent(_contentType, "Content5", content1); - content5.PublishValues(); + content5.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content5); var content51 = MockedContent.CreateSimpleContent(_contentType, "Content51", content5); - content51.PublishValues(); + content51.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content51); var content52 = MockedContent.CreateSimpleContent(_contentType, "Content52", content5); ServiceContext.ContentService.Save(content52); @@ -439,7 +441,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -476,7 +478,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -513,7 +515,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -553,7 +555,7 @@ namespace Umbraco.Tests.Integration ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -574,12 +576,12 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -606,7 +608,7 @@ namespace Umbraco.Tests.Integration ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -630,12 +632,12 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); content.Name = "changed"; - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.AreEqual(2, _msgCount); @@ -656,7 +658,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -680,7 +682,7 @@ namespace Umbraco.Tests.Integration var content = ServiceContext.ContentService.GetRootContent().FirstOrDefault(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); content.Name = "changed"; ServiceContext.ContentService.Save(content); @@ -741,7 +743,7 @@ namespace Umbraco.Tests.Integration ServiceContext.ContentService.Unpublish(content1); ResetEvents(); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); Assert.AreEqual(2, _msgCount); @@ -984,7 +986,7 @@ namespace Umbraco.Tests.Integration var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -1008,7 +1010,7 @@ namespace Umbraco.Tests.Integration var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ServiceContext.ContentService.MoveToRecycleBin(content); @@ -1033,7 +1035,7 @@ namespace Umbraco.Tests.Integration var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); content.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content); @@ -1204,7 +1206,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -1224,7 +1226,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); content.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content); @@ -1246,11 +1248,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); @@ -1332,7 +1334,7 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); @@ -1354,7 +1356,7 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content1); @@ -1380,7 +1382,7 @@ namespace Umbraco.Tests.Integration Assert.IsNotNull(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1402,11 +1404,11 @@ namespace Umbraco.Tests.Integration Assert.IsNotNull(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1427,11 +1429,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1451,15 +1453,15 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1480,13 +1482,13 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1506,17 +1508,17 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); content1.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content1); var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1537,16 +1539,16 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ResetEvents(); @@ -1566,20 +1568,20 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); var content4 = CreateContent(content3.Id); Assert.IsNotNull(content4); - content4.PublishValues(); + content4.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content4); ServiceContext.ContentService.Unpublish(content3); @@ -1600,18 +1602,18 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ResetEvents(); @@ -1631,22 +1633,22 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); var content4 = CreateContent(content3.Id); Assert.IsNotNull(content4); - content4.PublishValues(); + content4.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content4); ServiceContext.ContentService.Unpublish(content3); @@ -1667,11 +1669,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Unpublish(content1); var content3 = CreateContent(); @@ -1694,11 +1696,11 @@ namespace Umbraco.Tests.Integration { var content1 = CreateContent(); Assert.IsNotNull(content1); - content1.PublishValues(); + content1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content1); var content2 = CreateContent(content1.Id); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); content2.Properties.First().SetValue("changed"); ServiceContext.ContentService.Save(content2); @@ -1778,7 +1780,7 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ResetEvents(); @@ -1832,11 +1834,11 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -1891,7 +1893,7 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); ServiceContext.ContentService.Move(content1, content2.Id); @@ -2001,11 +2003,11 @@ namespace Umbraco.Tests.Integration var content2 = CreateContent(); Assert.IsNotNull(content2); - content2.PublishValues(); + content2.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content2); var content3 = CreateContent(content2.Id); Assert.IsNotNull(content3); - content3.PublishValues(); + content3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content3); ServiceContext.ContentService.Unpublish(content2); @@ -2081,7 +2083,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -2101,7 +2103,7 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var content2 = CreateContent(); Assert.IsNotNull(content2); @@ -2124,7 +2126,7 @@ namespace Umbraco.Tests.Integration { var content = CreateBranch(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); ResetEvents(); @@ -2166,17 +2168,17 @@ namespace Umbraco.Tests.Integration { var content = CreateContent(); Assert.IsNotNull(content); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var v1 = content.VersionId; content.Properties.First().SetValue("changed"); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var v2 = content.VersionId; content.Properties.First().SetValue("again"); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); var v3 = content.VersionId; @@ -2210,12 +2212,12 @@ namespace Umbraco.Tests.Integration Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsFalse(content.WasPropertyDirty("Published")); - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsTrue(content.WasPropertyDirty("Published")); // has just been published - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); Assert.IsFalse(content.IsPropertyDirty("Published")); Assert.IsFalse(content.WasPropertyDirty("Published")); // was published already @@ -2234,5 +2236,15 @@ namespace Umbraco.Tests.Integration // all content type events #endregion + + public class LocalServerMessenger : ServerMessengerBase + { + public LocalServerMessenger() : base(false) + { } + + protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) + { + } + } } } diff --git a/src/Umbraco.Tests/Integration/GetCultureTests.cs b/src/Umbraco.Tests/Integration/GetCultureTests.cs deleted file mode 100644 index 5441024570..0000000000 --- a/src/Umbraco.Tests/Integration/GetCultureTests.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Tests.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Tests.Testing; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Integration -{ - [TestFixture] - [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class GetCultureTests : TestWithSomeContentBase - { - protected override void Compose() - { - base.Compose(); - Container.Register(); - } - - [Test] - public void GetCulture() - { - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type"); - contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - - var c1 = contentService.CreateAndSave("content", -1, "umbBlah"); - var c2 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c3 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c4 = contentService.CreateAndSave("content", c3, "umbBlah"); - - foreach (var l in ServiceContext.LocalizationService.GetAllLanguages().Where(x => x.CultureName != "en-US").ToArray()) - ServiceContext.LocalizationService.Delete(l); - - var l0 = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); - var l1 = new Core.Models.Language("fr-FR"); - var l2 = new Core.Models.Language("de-DE"); - ServiceContext.LocalizationService.Save(l1); - ServiceContext.LocalizationService.Save(l2); - - foreach (var d in ServiceContext.DomainService.GetAll(true).ToArray()) - ServiceContext.DomainService.Delete(d); - - ServiceContext.DomainService.Save(new UmbracoDomain("domain1.com") {DomainName="domain1.com", RootContentId = c1.Id, LanguageId = l0.Id}); - ServiceContext.DomainService.Save(new UmbracoDomain("domain1.fr") { DomainName = "domain1.fr", RootContentId = c1.Id, LanguageId = l1.Id }); - ServiceContext.DomainService.Save(new UmbracoDomain("*100112") { DomainName = "*100112", RootContentId = c3.Id, LanguageId = l2.Id }); - - var content = c2; - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - content.Id, content.Path, new Uri("http://domain1.com/")); - Assert.AreEqual("en-US", culture.Name); - - content = c2; - culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("fr-FR", culture.Name); - - content = c4; - culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("de-DE", culture.Name); - } - - [Test] - public void GetCultureWithWildcard() - { - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type"); - contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - - var c1 = contentService.CreateAndSave("content", -1, "umbBlah"); - var c2 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c3 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c4 = contentService.CreateAndSave("content", c3, "umbBlah"); - - foreach (var l in ServiceContext.LocalizationService.GetAllLanguages().Where(x => x.CultureName != "en-US").ToArray()) - ServiceContext.LocalizationService.Delete(l); - - var l0 = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); - var l1 = new Core.Models.Language("fr-FR"); - var l2 = new Core.Models.Language("de-DE"); - ServiceContext.LocalizationService.Save(l1); - ServiceContext.LocalizationService.Save(l2); - - foreach (var d in ServiceContext.DomainService.GetAll(true).ToArray()) - ServiceContext.DomainService.Delete(d); - - ServiceContext.DomainService.Save(new UmbracoDomain("*0000") { DomainName = "*0000", RootContentId = c1.Id, LanguageId = l2.Id }); - ServiceContext.DomainService.Save(new UmbracoDomain("*0001") { DomainName = "*0001", RootContentId = c3.Id, LanguageId = l1.Id }); - //ServiceContext.DomainService.Save(new UmbracoDomain("*100112") { DomainName = "*100112", RootContentId = c3.Id, LanguageId = l2.Id }); - - var content = c2; - var culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - content.Id, content.Path, new Uri("http://domain1.com/")); - Assert.AreEqual("de-DE", culture.Name); - - content = c4; - culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("fr-FR", culture.Name); - } - } -} diff --git a/src/Umbraco.Tests/Macros/MacroTests.cs b/src/Umbraco.Tests/Macros/MacroTests.cs index e3a4db5390..29d81e773e 100644 --- a/src/Umbraco.Tests/Macros/MacroTests.cs +++ b/src/Umbraco.Tests/Macros/MacroTests.cs @@ -53,7 +53,7 @@ namespace Umbraco.Tests.Macros { Name = "test", Alias = "test", - ScriptName = "~/usercontrols/menu.ascx" + MacroSource = "~/usercontrols/menu.ascx" }; macroModel.Properties.Add(new MacroPropertyModel(macroPropName, val)); @@ -68,7 +68,6 @@ namespace Umbraco.Tests.Macros Assert.AreEqual(converted.Result, prop.GetValue(ctrl)); } - [TestCase("Xslt", true)] [TestCase("PartialView", true)] [TestCase("UserControl", true)] [TestCase("Unknown", false)] @@ -78,9 +77,7 @@ namespace Umbraco.Tests.Macros var model = new MacroModel { MacroType = macroType, - Xslt = "anything", - ScriptName = "anything", - TypeName = "anything" + MacroSource = "anything" }; var filename = MacroRenderer.GetMacroFileName(model); if (expectedNonNull) diff --git a/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs b/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs index bb134d7e35..9c0560351b 100644 --- a/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs +++ b/src/Umbraco.Tests/Misc/ApplicationUrlHelperTests.cs @@ -11,25 +11,18 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Sync; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Cache.DistributedCache; +using Umbraco.Tests.TestHelpers.Stubs; + namespace Umbraco.Tests.Misc { [TestFixture] public class ApplicationUrlHelperTests { - private IServerRegistrar _registrar; - // note: in tests, read appContext._umbracoApplicationUrl and not the property, // because reading the property does run some code, as long as the field is null. - private void Initialize(IUmbracoSettingsSection settings, IGlobalSettings globalSettings) - { - _registrar = new ConfigServerRegistrar(settings.DistributedCall, Mock.Of(), globalSettings); - var container = new ServiceContainer(); - container.ConfigureUmbracoCore(); - container.Register(_ => _registrar); - } - [TearDown] public void Reset() { @@ -39,18 +32,42 @@ namespace Umbraco.Tests.Misc [Test] public void NoApplicationUrlByDefault() { - var state = new RuntimeState(Mock.Of(), new Lazy(Mock.Of), new Lazy(Mock.Of)); + var state = new RuntimeState(Mock.Of(), new Lazy(Mock.Of), new Lazy(Mock.Of), Mock.Of(), Mock.Of()); Assert.IsNull(state.ApplicationUrl); } + [Test] + public void SetApplicationUrlViaServerRegistrar() + { + // no applicable settings, but a provider + + var settings = Mock.Of(section => + section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) + && section.ScheduledTasks == Mock.Of()); + + var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); + globalConfig.Setup(x => x.UseHttps).Returns(true); + + var registrar = new Mock(); + registrar.Setup(x => x.GetCurrentServerUmbracoApplicationUrl()).Returns("http://server1.com/umbraco"); + + var state = new RuntimeState( + Mock.Of(), + new Lazy(() => registrar.Object), + new Lazy(Mock.Of), settings, globalConfig.Object); + + state.EnsureApplicationUrl(); + + Assert.AreEqual("http://server1.com/umbraco", state.ApplicationUrl.ToString()); + } + [Test] public void SetApplicationUrlViaProvider() { // no applicable settings, but a provider var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) + section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) && section.ScheduledTasks == Mock.Of()); var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); @@ -58,11 +75,11 @@ namespace Umbraco.Tests.Misc ApplicationUrlHelper.ApplicationUrlProvider = request => "http://server1.com/umbraco"; - Initialize(settings, globalConfig.Object); + - var state = new RuntimeState(Mock.Of(), new Lazy(Mock.Of), new Lazy(Mock.Of)); + var state = new RuntimeState(Mock.Of(), new Lazy(Mock.Of), new Lazy(Mock.Of), settings, globalConfig.Object); - state.EnsureApplicationUrl(settings, globalConfig.Object); + state.EnsureApplicationUrl(); Assert.AreEqual("http://server1.com/umbraco", state.ApplicationUrl.ToString()); } @@ -73,176 +90,31 @@ namespace Umbraco.Tests.Misc // no applicable settings, cannot set url var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) + section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) && section.ScheduledTasks == Mock.Of()); var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(true); - Initialize(settings, globalConfig.Object); - - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); // still NOT set Assert.IsNull(url); } - - [Test] - public void SetApplicationUrlFromDcSettingsSsl1() - { - // set from distributed call settings - // first server is master server - - var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Enabled && callSection.Servers == new[] - { - Mock.Of(server => server.ServerName == NetworkHelper.MachineName && server.ServerAddress == "server1.com"), - Mock.Of(server => server.ServerName == "ANOTHERNAME" && server.ServerAddress == "server2.com"), - }) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == (string)null)); - - var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - globalConfig.Setup(x => x.UseHttps).Returns(true); - - Initialize(settings, globalConfig.Object); - - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); - - Assert.AreEqual("http://server1.com:80/umbraco", url); - - var role = _registrar.GetCurrentServerRole(); - Assert.AreEqual(ServerRole.Master, role); - } - - [Test] - public void SetApplicationUrlFromDcSettingsSsl2() - { - // set from distributed call settings - // other servers are slave servers - - var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Enabled && callSection.Servers == new[] - { - Mock.Of(server => server.ServerName == "ANOTHERNAME" && server.ServerAddress == "server2.com"), - Mock.Of(server => server.ServerName == NetworkHelper.MachineName && server.ServerAddress == "server1.com"), - }) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == (string)null)); - - var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - globalConfig.Setup(x => x.UseHttps).Returns(true); - - Initialize(settings, globalConfig.Object); - - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); - - Assert.AreEqual("http://server1.com:80/umbraco", url); - - var role = _registrar.GetCurrentServerRole(); - Assert.AreEqual(ServerRole.Slave, role); - } - - [Test] - public void SetApplicationUrlFromDcSettingsSsl3() - { - // set from distributed call settings - // cannot set if not enabled - - var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Enabled == false && callSection.Servers == new[] - { - Mock.Of(server => server.ServerName == "ANOTHERNAME" && server.ServerAddress == "server2.com"), - Mock.Of(server => server.ServerName == NetworkHelper.MachineName && server.ServerAddress == "server1.com"), - }) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == (string)null)); - - var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - globalConfig.Setup(x => x.UseHttps).Returns(true); - - Initialize(settings, globalConfig.Object); - - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); - - Assert.IsNull(url); - - var role = _registrar.GetCurrentServerRole(); - Assert.AreEqual(ServerRole.Single, role); - } - - [Test] - public void ServerRoleSingle() - { - // distributed call settings disabled, single server - - var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Enabled == false && callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == (string)null)); - - var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - - Initialize(settings, globalConfig.Object); - - var role = _registrar.GetCurrentServerRole(); - Assert.AreEqual(ServerRole.Single, role); - } - - [Test] - public void ServerRoleUnknown1() - { - // distributed call enabled but missing servers, unknown server - - var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Enabled && callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == (string)null)); - - var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - - Initialize(settings, globalConfig.Object); - - var role = _registrar.GetCurrentServerRole(); - Assert.AreEqual(ServerRole.Unknown, role); - } - - [Test] - public void ServerRoleUnknown2() - { - // distributed call enabled, cannot find server, assume it's an undeclared slave - - var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Enabled && callSection.Servers == new[] - { - Mock.Of(server => server.ServerName == "ANOTHERNAME" && server.ServerAddress == "server2.com"), - }) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string)null) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == (string)null)); - - var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); - - Initialize(settings, globalConfig.Object); - - var role = _registrar.GetCurrentServerRole(); - Assert.AreEqual(ServerRole.Slave, role); - } - + [Test] public void SetApplicationUrlFromStSettingsNoSsl() { var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) + section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/umbraco")); var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(false); - Initialize(settings, globalConfig.Object); - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); + + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); Assert.AreEqual("http://mycoolhost.com/umbraco", url); } @@ -251,16 +123,15 @@ namespace Umbraco.Tests.Misc public void SetApplicationUrlFromStSettingsSsl() { var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) + section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/umbraco/")); var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(true); - Initialize(settings, globalConfig.Object); - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); + + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); Assert.AreEqual("https://mycoolhost.com/umbraco", url); } @@ -269,18 +140,19 @@ namespace Umbraco.Tests.Misc public void SetApplicationUrlFromWrSettingsSsl() { var settings = Mock.Of(section => - section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == "httpx://whatever.com/umbraco/") + section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == "httpx://whatever.com/umbraco/") && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/umbraco")); var globalConfig = Mock.Get(SettingsForTests.GenerateMockGlobalSettings()); globalConfig.Setup(x => x.UseHttps).Returns(true); - Initialize(settings, globalConfig.Object); + - var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object); + var url = ApplicationUrlHelper.TryGetApplicationUrl(settings, Mock.Of(), globalConfig.Object, Mock.Of()); Assert.AreEqual("httpx://whatever.com/umbraco", url); } + + } } diff --git a/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs b/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs index e7b7d4a9bc..4b52289e2a 100644 --- a/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs +++ b/src/Umbraco.Tests/Models/Collections/SimpleOrder.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Models.Collections internal void Reset(IEnumerable properties) { Clear(); - properties.ForEach(Add); + foreach (var property in properties) Add(property); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } diff --git a/src/Umbraco.Tests/Models/MacroTests.cs b/src/Umbraco.Tests/Models/MacroTests.cs index 052c42942b..b4060134bd 100644 --- a/src/Umbraco.Tests/Models/MacroTests.cs +++ b/src/Umbraco.Tests/Models/MacroTests.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Deep_Clone() { - var macro = new Macro(1, Guid.NewGuid(), true, 3, "test", "Test", "blah", "blah", "xslt", false, true, true, "script"); + var macro = new Macro(1, Guid.NewGuid(), true, 3, "test", "Test", false, true, true, "~/script.cshtml", MacroTypes.PartialView); macro.Properties.Add(new MacroProperty(6, Guid.NewGuid(), "rewq", "REWQ", 1, "asdfasdf")); var clone = (Macro)macro.DeepClone(); diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 3b293621d5..bc0891e397 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -65,6 +65,8 @@ namespace Umbraco.Tests.Models var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" }; var prop = new Property(propertyType); + const string langFr = "fr-FR"; + // can set value // and get edited and published value // because non-publishing @@ -84,8 +86,8 @@ namespace Umbraco.Tests.Models Assert.IsNull(prop.GetValue(published: true)); // cannot set non-supported variation value - Assert.Throws(() => prop.SetValue("x", 1)); - Assert.IsNull(prop.GetValue(1)); + Assert.Throws(() => prop.SetValue("x", langFr)); + Assert.IsNull(prop.GetValue(langFr)); // can publish value // and get edited and published values @@ -109,41 +111,41 @@ namespace Umbraco.Tests.Models // can set value // and get values - prop.SetValue("c", 1); + prop.SetValue("c", langFr); Assert.AreEqual("b", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.IsNull(prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.IsNull(prop.GetValue(langFr, published: true)); // can publish value // and get edited and published values - prop.PublishValue(1); + prop.PublishValue(langFr); Assert.AreEqual("b", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.AreEqual("c", prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.AreEqual("c", prop.GetValue(langFr, published: true)); // can clear all prop.ClearPublishedAllValues(); Assert.AreEqual("b", prop.GetValue()); Assert.IsNull(prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.IsNull(prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.IsNull(prop.GetValue(langFr, published: true)); // can publish all prop.PublishAllValues(); Assert.AreEqual("b", prop.GetValue()); Assert.AreEqual("b", prop.GetValue(published: true)); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.AreEqual("c", prop.GetValue(1, published: true)); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.AreEqual("c", prop.GetValue(langFr, published: true)); // same for culture - prop.ClearPublishedCultureValues(1); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.IsNull(prop.GetValue(1, published: true)); - prop.PublishCultureValues(1); - Assert.AreEqual("c", prop.GetValue(1)); - Assert.AreEqual("c", prop.GetValue(1, published: true)); + prop.ClearPublishedCultureValues(langFr); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.IsNull(prop.GetValue(langFr, published: true)); + prop.PublishCultureValues(langFr); + Assert.AreEqual("c", prop.GetValue(langFr)); + Assert.AreEqual("c", prop.GetValue(langFr, published: true)); prop.ClearPublishedCultureValues(); Assert.AreEqual("b", prop.GetValue()); @@ -159,25 +161,29 @@ namespace Umbraco.Tests.Models var contentType = new ContentType(-1) { Alias = "contentType" }; var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; - const int langFr = 1; - const int langUk = 2; + const string langFr = "fr-FR"; + const string langUk = "en-UK"; + // throws if the content type does not support the variation Assert.Throws(() => content.SetName(langFr, "name-fr")); + // now it will work contentType.Variations = ContentVariation.CultureNeutral; + // invariant name works content.Name = "name"; Assert.AreEqual("name", content.GetName(null)); content.SetName(null, "name2"); Assert.AreEqual("name2", content.Name); Assert.AreEqual("name2", content.GetName(null)); + // variant names work content.SetName(langFr, "name-fr"); content.SetName(langUk, "name-uk"); - Assert.AreEqual("name-fr", content.GetName(langFr)); Assert.AreEqual("name-uk", content.GetName(langUk)); + // variant dictionary of names work Assert.AreEqual(2, content.Names.Count); Assert.IsTrue(content.Names.ContainsKey(langFr)); Assert.AreEqual("name-fr", content.Names[langFr]); @@ -186,8 +192,10 @@ namespace Umbraco.Tests.Models } [Test] - public void ContentTests() + public void ContentPublishValues() { + const string langFr = "fr-FR"; + var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" }; var contentType = new ContentType(-1) { Alias = "contentType" }; contentType.AddPropertyType(propertyType); @@ -202,12 +210,12 @@ namespace Umbraco.Tests.Models Assert.IsNull(content.GetValue("prop", published: true)); // cannot set non-supported variation value - Assert.Throws(() => content.SetValue("prop", "x", 1)); - Assert.IsNull(content.GetValue("prop", 1)); + Assert.Throws(() => content.SetValue("prop", "x", langFr)); + Assert.IsNull(content.GetValue("prop", langFr)); // can publish value // and get edited and published values - content.PublishValues(); + content.TryPublishValues(); Assert.AreEqual("a", content.GetValue("prop")); Assert.AreEqual("a", content.GetValue("prop", published: true)); @@ -228,41 +236,43 @@ namespace Umbraco.Tests.Models // can set value // and get values - content.SetValue("prop", "c", 1); + content.SetValue("prop", "c", langFr); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.IsNull(content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.IsNull(content.GetValue("prop", langFr, published: true)); // can publish value // and get edited and published values - content.PublishValues(1); + Assert.IsFalse(content.TryPublishValues(langFr)); // no name + content.SetName(langFr, "name-fr"); + content.TryPublishValues(langFr); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); // can clear all content.ClearAllPublishedValues(); Assert.AreEqual("b", content.GetValue("prop")); Assert.IsNull(content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.IsNull(content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.IsNull(content.GetValue("prop", langFr, published: true)); // can publish all - content.PublishAllValues(); + content.TryPublishAllValues(); Assert.AreEqual("b", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); // same for culture - content.ClearCulturePublishedValues(1); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.IsNull(content.GetValue("prop", 1, published: true)); - content.PublishCultureValues(1); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + content.ClearCulturePublishedValues(langFr); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.IsNull(content.GetValue("prop", langFr, published: true)); + content.PublishCultureValues(langFr); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); content.ClearCulturePublishedValues(); Assert.AreEqual("b", content.GetValue("prop")); @@ -273,21 +283,76 @@ namespace Umbraco.Tests.Models var other = new Content("other", -1, contentType) { Id = 2, VersionId = 1 }; other.SetValue("prop", "o"); - other.SetValue("prop", "o1", 1); + other.SetValue("prop", "o1", langFr); // can copy other's edited value content.CopyAllValues(other); Assert.AreEqual("o", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - Assert.AreEqual("o1", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("o1", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); // can copy self's published value content.CopyAllValues(content); Assert.AreEqual("b", content.GetValue("prop")); Assert.AreEqual("b", content.GetValue("prop", published: true)); - Assert.AreEqual("c", content.GetValue("prop", 1)); - Assert.AreEqual("c", content.GetValue("prop", 1, published: true)); + Assert.AreEqual("c", content.GetValue("prop", langFr)); + Assert.AreEqual("c", content.GetValue("prop", langFr, published: true)); + } + + [Test] + public void ContentPublishVariations() + { + const string langFr = "fr-FR"; + const string langUk = "en-UK"; + const string langEs = "es-ES"; + + var propertyType = new PropertyType("editor", ValueStorageType.Nvarchar) { Alias = "prop" }; + var contentType = new ContentType(-1) { Alias = "contentType" }; + contentType.AddPropertyType(propertyType); + + var content = new Content("content", -1, contentType) { Id = 1, VersionId = 1 }; + + contentType.Variations |= ContentVariation.CultureNeutral; + propertyType.Variations |= ContentVariation.CultureNeutral; + + content.SetValue("prop", "a"); + content.SetValue("prop", "a-fr", langFr); + content.SetValue("prop", "a-uk", langUk); + content.SetValue("prop", "a-es", langEs); + + // cannot publish without a name + Assert.IsFalse(content.TryPublishValues(langFr)); + + // works with a name + // and then FR is available, and published + content.SetName(langFr, "name-fr"); + content.TryPublishValues(langFr); + + // now UK is available too + content.SetName(langUk, "name-uk"); + + // test available, published + Assert.IsTrue(content.IsCultureAvailable(langFr)); + Assert.IsTrue(content.IsCulturePublished(langFr)); + Assert.AreEqual("name-fr", content.GetPublishName(langFr)); + Assert.AreNotEqual(DateTime.MinValue, content.GetDateCulturePublished(langFr)); + Assert.IsFalse(content.IsCultureEdited(langFr)); // once published, edited is *wrong* until saved + + Assert.IsTrue(content.IsCultureAvailable(langUk)); + Assert.IsFalse(content.IsCulturePublished(langUk)); + Assert.IsNull(content.GetPublishName(langUk)); + Assert.Throws(() => content.GetDateCulturePublished(langUk)); // not published! + Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited + + Assert.IsFalse(content.IsCultureAvailable(langEs)); + Assert.IsFalse(content.IsCulturePublished(langEs)); + Assert.IsNull(content.GetPublishName(langEs)); + Assert.Throws(() => content.GetDateCulturePublished(langEs)); // not published! + Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited + + // cannot test IsCultureEdited here - as that requires the content service and repository + // see: ContentServiceTests.Can_SaveRead_Variations } [Test] diff --git a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs index 3bc419ea95..0afc581f83 100644 --- a/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ b/src/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs @@ -187,13 +187,13 @@ namespace Umbraco.Tests.Persistence.NPocoTests contentTypeService.Save(contentType); var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); id2 = content1.Id; var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); id3 = content2.Id; diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index f01c59df03..035c857e35 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -83,9 +83,9 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository, cacheHelper: realCache); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, cacheHelper: realCache); - var udb = (UmbracoDatabase) scope.Database; + var udb = (UmbracoDatabase)scope.Database; udb.EnableSqlCount = false; @@ -124,7 +124,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository, out DataTypeRepository _); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, out DataTypeRepository _); var versions = new List(); var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); @@ -134,22 +134,22 @@ namespace Umbraco.Tests.Persistence.Repositories // save = create the initial version contentTypeRepository.Save(hasPropertiesContentType); - repository.Save(content1); - + repository.Save(content1); + versions.Add(content1.VersionId); // the first version // publish = new edit version content1.SetValue("title", "title"); - ((Content) content1).PublishValues(); - ((Content) content1).PublishedState = PublishedState.Publishing; - repository.Save(content1); - + ((Content)content1).TryPublishValues(); + ((Content)content1).PublishedState = PublishedState.Publishing; + repository.Save(content1); + versions.Add(content1.VersionId); // NEW VERSION // new edit version has been created Assert.AreNotEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); - Assert.AreEqual(PublishedState.Published, ((Content) content1).PublishedState); + Assert.AreEqual(PublishedState.Published, ((Content)content1).PublishedState); Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks @@ -159,8 +159,8 @@ namespace Umbraco.Tests.Persistence.Repositories // save = update the current (draft) version content1.Name = "name-1"; content1.SetValue("title", "title-1"); - repository.Save(content1); - + repository.Save(content1); + versions.Add(content1.VersionId); // the same version // no new version has been created @@ -172,15 +172,15 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(true, scope.Database.ExecuteScalar($"SELECT published FROM {Constants.DatabaseSchema.Tables.Document} WHERE nodeId=@id", new { id = content1.Id })); // unpublish = no impact on versions - ((Content) content1).PublishedState = PublishedState.Unpublishing; - repository.Save(content1); - + ((Content)content1).PublishedState = PublishedState.Unpublishing; + repository.Save(content1); + versions.Add(content1.VersionId); // the same version // no new version has been created Assert.AreEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsFalse(content1.Published); - Assert.AreEqual(PublishedState.Unpublished, ((Content) content1).PublishedState); + Assert.AreEqual(PublishedState.Unpublished, ((Content)content1).PublishedState); Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks @@ -190,8 +190,8 @@ namespace Umbraco.Tests.Persistence.Repositories // save = update the current (draft) version content1.Name = "name-2"; content1.SetValue("title", "title-2"); - repository.Save(content1); - + repository.Save(content1); + versions.Add(content1.VersionId); // the same version // no new version has been created @@ -202,16 +202,16 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(false, scope.Database.ExecuteScalar($"SELECT published FROM {Constants.DatabaseSchema.Tables.Document} WHERE nodeId=@id", new { id = content1.Id })); // publish = version - ((Content) content1).PublishValues(); - ((Content) content1).PublishedState = PublishedState.Publishing; - repository.Save(content1); - + ((Content)content1).TryPublishValues(); + ((Content)content1).PublishedState = PublishedState.Publishing; + repository.Save(content1); + versions.Add(content1.VersionId); // NEW VERSION // new version has been created Assert.AreNotEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); - Assert.AreEqual(PublishedState.Published, ((Content) content1).PublishedState); + Assert.AreEqual(PublishedState.Published, ((Content)content1).PublishedState); Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks @@ -224,8 +224,8 @@ namespace Umbraco.Tests.Persistence.Repositories //Thread.Sleep(2000); // force date change - repository.Save(content1); - + repository.Save(content1); + versions.Add(content1.VersionId); // the same version // no new version has been created @@ -238,16 +238,16 @@ namespace Umbraco.Tests.Persistence.Repositories // publish = new version content1.Name = "name-4"; content1.SetValue("title", "title-4"); - ((Content) content1).PublishValues(); - ((Content) content1).PublishedState = PublishedState.Publishing; - repository.Save(content1); - + ((Content)content1).TryPublishValues(); + ((Content)content1).PublishedState = PublishedState.Publishing; + repository.Save(content1); + versions.Add(content1.VersionId); // NEW VERSION // a new version has been created Assert.AreNotEqual(versions[versions.Count - 2], versions[versions.Count - 1]); Assert.IsTrue(content1.Published); - Assert.AreEqual(PublishedState.Published, ((Content) content1).PublishedState); + Assert.AreEqual(PublishedState.Published, ((Content)content1).PublishedState); Assert.AreEqual(versions[versions.Count - 1], repository.Get(content1.Id).VersionId); // misc checks @@ -261,7 +261,7 @@ namespace Umbraco.Tests.Persistence.Repositories Console.WriteLine(); foreach (var v in allVersions) { - var c = (Content) v; + var c = (Content)v; Console.WriteLine($"{c.Id} {c.VersionId} {(c.Published ? "+" : "-")}pub pk={c.VersionId} ppk={c.PublishedVersionId} name=\"{c.Name}\" pname=\"{c.PublishName}\""); } @@ -297,7 +297,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository, out DataTypeRepository _); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, out DataTypeRepository _); var emptyContentType = MockedContentTypes.CreateBasicContentType(); var hasPropertiesContentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); @@ -310,11 +310,11 @@ namespace Umbraco.Tests.Persistence.Repositories contentTypeRepository.Save(hasPropertiesContentType); repository.Save(content1); repository.Save(content2); - repository.Save(content3); - - - // this will cause the GetPropertyCollection to execute and we need to ensure that - // all of the properties and property types are all correct + repository.Save(content3); + + + // this will cause the GetPropertyCollection to execute and we need to ensure that + // all of the properties and property types are all correct var result = repository.GetMany(content1.Id, content2.Id, content3.Id).ToArray(); var n1 = result[0]; var n2 = result[1]; @@ -347,7 +347,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository, out DataTypeRepository dataTypeDefinitionRepository); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, out DataTypeRepository dataTypeDefinitionRepository); var editor = new DecimalPropertyEditor(Logger); var dtd = new DataType(editor) { Name = "test", DatabaseType = ValueStorageType.Decimal }; @@ -394,7 +394,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! IContent textpage = MockedContent.CreateSimpleContent(contentType); @@ -414,21 +414,21 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository, out TemplateRepository templateRepository); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository, out TemplateRepository templateRepository); var template = new Template("hello", "hello"); - templateRepository.Save(template); - - + templateRepository.Save(template); + + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); contentType.AllowedTemplates = Enumerable.Empty(); // because CreateSimpleContentType assigns one already contentType.SetDefaultTemplate(template); var textpage = MockedContent.CreateSimpleContent(contentType); contentTypeRepository.Save(contentType); - repository.Save(textpage); - - + repository.Save(textpage); + + var fetched = repository.Get(textpage.Id); Assert.NotNull(textpage.Template); @@ -448,20 +448,20 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! - contentTypeRepository.Save(contentType); - - + contentTypeRepository.Save(contentType); + + var textpage = MockedContent.CreateSimpleContent(contentType, "test@umbraco.org"); var anotherTextpage = MockedContent.CreateSimpleContent(contentType, "@lightgiants"); repository.Save(textpage); - repository.Save(anotherTextpage); - - - + repository.Save(anotherTextpage); + + + Assert.That(contentType.HasIdentity, Is.True); Assert.That(textpage.HasIdentity, Is.True); @@ -481,19 +481,19 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage"); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! var textpage = MockedContent.CreateSimpleContent(contentType); contentTypeRepository.Save(contentType); - repository.Save(textpage); - - + repository.Save(textpage); + + var subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id); - repository.Save(subpage); - - + repository.Save(subpage); + + Assert.That(contentType.HasIdentity, Is.True); Assert.That(textpage.HasIdentity, Is.True); Assert.That(subpage.HasIdentity, Is.True); @@ -509,10 +509,10 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var content = repository.Get(NodeDto.NodeIdSeed + 3); - var dirty = ((Content) content).IsDirty(); + var dirty = ((Content)content).IsDirty(); Assert.That(dirty, Is.False); } @@ -524,12 +524,12 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var content = repository.Get(NodeDto.NodeIdSeed + 2); content.Name = "About 2"; - repository.Save(content); - + repository.Save(content); + var updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); Assert.AreEqual(content.Id, updatedContent.Id); @@ -538,8 +538,8 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual(content.GetValue("title"), "Welcome to our Home page"); content.SetValue("title", "toot"); - repository.Save(content); - + repository.Save(content); + updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); Assert.AreEqual("toot", updatedContent.GetValue("title")); @@ -553,12 +553,12 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var content = repository.Get(NodeDto.NodeIdSeed + 2); content.Template = null; - repository.Save(content); - + repository.Save(content); + var updatedContent = repository.Get(NodeDto.NodeIdSeed + 2); Assert.IsNull(updatedContent.Template); @@ -572,19 +572,19 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out var contentTypeRepository); + var repository = CreateRepository((IScopeAccessor)provider, out var contentTypeRepository); var contentType = contentTypeRepository.Get(NodeDto.NodeIdSeed + 1); var content = new Content("Textpage 2 Child Node", NodeDto.NodeIdSeed + 4, contentType); content.CreatorId = 0; content.WriterId = 0; - repository.Save(content); - + repository.Save(content); + var id = content.Id; - repository.Delete(content); - - + repository.Delete(content); + + var content1 = repository.Get(id); Assert.IsNull(content1); } @@ -596,7 +596,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var content = repository.Get(NodeDto.NodeIdSeed + 4); Assert.AreEqual(NodeDto.NodeIdSeed + 4, content.Id); @@ -617,7 +617,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); var result = repository.Get(query); @@ -634,7 +634,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); result = repository.GetMany().ToArray(); // save them all @@ -642,24 +642,24 @@ namespace Umbraco.Tests.Persistence.Repositories { content.SetValue("title", content.GetValue("title") + "x"); repository.Save(content); - } - - - // publish them all + } + + + // publish them all foreach (var content in result) { - content.PublishValues(); + content.TryPublishValues(); repository.Save(content); - } - - + } + + scope.Complete(); } // get them all again using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var result2 = repository.GetMany().ToArray(); Assert.AreEqual(result.Length, result2.Length); @@ -679,6 +679,100 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.AreEqual("[table].[column2]", matches[1].Groups[1].Value); Assert.AreEqual("[alias2]", matches[1].Groups[2].Value); } + + [Test] + public void GetPagedResultsByQuery_With_Variant_Names() + { + //2x content types, one invariant, one variant + + var invariantCT = MockedContentTypes.CreateSimpleContentType("umbInvariantTextpage", "Invariant Textpage"); + invariantCT.Variations = ContentVariation.InvariantNeutral; + foreach (var p in invariantCT.PropertyTypes) p.Variations = ContentVariation.InvariantNeutral; + ServiceContext.FileService.SaveTemplate(invariantCT.DefaultTemplate); // else, FK violation on contentType! + ServiceContext.ContentTypeService.Save(invariantCT); + + var variantCT = MockedContentTypes.CreateSimpleContentType("umbVariantTextpage", "Variant Textpage"); + variantCT.Variations = ContentVariation.CultureNeutral; + var propTypes = variantCT.PropertyTypes.ToList(); + for (int i = 0; i < propTypes.Count; i++) + { + var p = propTypes[i]; + //every 2nd one is variant + p.Variations = i % 2 == 0 ? ContentVariation.CultureNeutral : ContentVariation.InvariantNeutral; + } + ServiceContext.FileService.SaveTemplate(variantCT.DefaultTemplate); // else, FK violation on contentType! + ServiceContext.ContentTypeService.Save(variantCT); + + invariantCT.AllowedContentTypes = new[] { new ContentTypeSort(invariantCT.Id, 0), new ContentTypeSort(variantCT.Id, 1) }; + ServiceContext.ContentTypeService.Save(invariantCT); + + //create content + + var root = MockedContent.CreateSimpleContent(invariantCT); + ServiceContext.ContentService.Save(root); + + for (int i = 0; i < 25; i++) + { + var isInvariant = i % 2 == 0; + var name = (isInvariant ? "INV" : "VAR") + "_" + Guid.NewGuid().ToString(); + var culture = isInvariant ? null : "en-US"; + + var child = MockedContent.CreateSimpleContent( + isInvariant ? invariantCT : variantCT, + name, root, + culture, + setPropertyValues: isInvariant); + + if (!isInvariant) + { + //manually set the property values since we have mixed variant/invariant property types + child.SetValue("title", name + " Subpage", culture: culture); + child.SetValue("bodyText", "This is a subpage", culture: null); //this one is invariant + child.SetValue("author", "John Doe", culture: culture); + } + + ServiceContext.ContentService.Save(child); + } + + var provider = TestObjects.GetScopeProvider(Logger); + using (var scope = provider.CreateScope()) + { + var repository = CreateRepository((IScopeAccessor)provider, out _); + + var query = scope.SqlContext.Query().Where(x => x.ParentId == root.Id); + + try + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = true; + scope.Database.AsUmbracoDatabase().EnableSqlCount = true; + + var result = repository.GetPage(query, 0, 20, out var totalRecords, "UpdateDate", Direction.Ascending, true); + + Assert.AreEqual(25, totalRecords); + foreach (var r in result) + { + var isInvariant = r.ContentType.Alias == "umbInvariantTextpage"; + var name = isInvariant ? r.Name : r.Names["en-US"]; + var namePrefix = (isInvariant ? "INV" : "VAR"); + + //ensure the correct name (invariant vs variant) is in the result + Assert.IsTrue(name.StartsWith(namePrefix)); + + foreach (var p in r.Properties) + { + //ensure there is a value for the correct variant/invariant property + var value = p.GetValue(p.PropertyType.Variations.Has(ContentVariation.InvariantNeutral) ? null : "en-US"); + Assert.IsNotNull(value); + } + } + } + finally + { + scope.Database.AsUmbracoDatabase().EnableSqlTrace = false; + scope.Database.AsUmbracoDatabase().EnableSqlCount = false; + } + } + } [Test] public void GetPagedResultsByQuery_CustomPropertySort() @@ -686,7 +780,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Name.Contains("Text")); @@ -718,7 +812,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); @@ -747,7 +841,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); var result = repository.GetPage(query, 1, 1, out var totalRecords, "Name", Direction.Ascending, true); @@ -764,7 +858,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); var result = repository.GetPage(query, 0, 2, out var totalRecords, "Name", Direction.Ascending, true); @@ -781,7 +875,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); var result = repository.GetPage(query, 0, 1, out var totalRecords, "Name", Direction.Descending, true); @@ -798,7 +892,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); @@ -817,7 +911,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); @@ -836,7 +930,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var contents = repository.GetMany(NodeDto.NodeIdSeed + 2, NodeDto.NodeIdSeed + 3); @@ -853,7 +947,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var contents = repository.GetMany(); @@ -879,7 +973,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var exists = repository.Exists(NodeDto.NodeIdSeed + 2); @@ -893,7 +987,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Level == 2); var result = repository.Count(query); @@ -908,7 +1002,7 @@ namespace Umbraco.Tests.Persistence.Repositories var provider = TestObjects.GetScopeProvider(Logger); using (var scope = provider.CreateScope()) { - var repository = CreateRepository((IScopeAccessor) provider, out _); + var repository = CreateRepository((IScopeAccessor)provider, out _); var query = scope.SqlContext.Query().Where(x => x.Key == new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0")); var content = repository.Get(query).SingleOrDefault(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs index 357015a9e5..3a7840dbf5 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Translations = new List { - new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"), "Hello world") + new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"), "Hello world") } }; @@ -70,7 +70,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Translations = new List { - new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"), "Hello world") + new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"), "Hello world") } }; @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Persistence.Repositories { Translations = new List { - new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"), "Hello world") + new DictionaryTranslation(ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"), "Hello world") } }; @@ -371,7 +371,7 @@ namespace Umbraco.Tests.Persistence.Repositories public void CreateTestData() { - var language = ServiceContext.LocalizationService.GetLanguageByCultureCode("en-US"); + var language = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); var languageDK = new Language("da-DK") { CultureName = "da-DK" }; ServiceContext.LocalizationService.Save(languageDK);//Id 2 diff --git a/src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs deleted file mode 100644 index 2357ba273a..0000000000 --- a/src/Umbraco.Tests/Persistence/Repositories/EntityRepositoryTests.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Tests.Persistence.Repositories -{ - class EntityRepositoryTests - { - } -} diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index fbf43a1889..8ef6416185 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -76,31 +76,6 @@ namespace Umbraco.Tests.Persistence.Repositories } } - [Test] - public void Can_Perform_Get_By_Culture_Name_On_LanguageRepository() - { - var provider = TestObjects.GetScopeProvider(Logger); - using (var scope = provider.CreateScope()) - { - var repository = CreateRepository(provider); - - var au = CultureInfo.GetCultureInfo("en-AU"); - var language = (ILanguage)new Language(au.Name) - { - CultureName = au.DisplayName - }; - repository.Save(language); - - //re-get - language = repository.GetByCultureName(au.DisplayName); - - // Assert - Assert.That(language, Is.Not.Null); - Assert.That(language.HasIdentity, Is.True); - Assert.That(language.CultureName, Is.EqualTo(au.DisplayName)); - Assert.That(language.IsoCode, Is.EqualTo(au.Name)); - } - } [Test] public void Get_When_Id_Doesnt_Exist_Returns_Null() diff --git a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs index de013426b7..a526864d6b 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MacroRepositoryTest.cs @@ -37,7 +37,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + var macro = new Macro("test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); ; Assert.Throws(() => repository.Save(macro)); @@ -94,14 +94,11 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(macro.Alias, Is.EqualTo("test1")); Assert.That(macro.CacheByPage, Is.EqualTo(false)); Assert.That(macro.CacheByMember, Is.EqualTo(false)); - Assert.That(macro.ControlAssembly, Is.EqualTo("MyAssembly1")); - Assert.That(macro.ControlType, Is.EqualTo("~/usercontrol/test1.ascx")); Assert.That(macro.DontRender, Is.EqualTo(true)); Assert.That(macro.Name, Is.EqualTo("Test1")); Assert.That(macro.CacheDuration, Is.EqualTo(0)); - Assert.That(macro.ScriptPath, Is.EqualTo("~/views/macropartials/test1.cshtml")); + Assert.That(macro.MacroSource, Is.EqualTo("~/views/macropartials/test1.cshtml")); Assert.That(macro.UseInEditor, Is.EqualTo(false)); - Assert.That(macro.XsltPath, Is.EqualTo("test1.xslt")); } @@ -171,7 +168,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); // Act - var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + var macro = new Macro("test", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); macro.Properties.Add(new MacroProperty("test", "Test", 0, "test")); repository.Save(macro); @@ -197,12 +194,9 @@ namespace Umbraco.Tests.Persistence.Repositories macro.CacheDuration = 1234; macro.CacheByPage = true; macro.CacheByMember = true; - macro.ControlAssembly = ""; - macro.ControlType = ""; macro.DontRender = false; - macro.ScriptPath = "~/newpath.cshtml"; + macro.MacroSource = "~/newpath.cshtml"; macro.UseInEditor = true; - macro.XsltPath = ""; repository.Save(macro); @@ -214,12 +208,9 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(macroUpdated.CacheDuration, Is.EqualTo(1234)); Assert.That(macroUpdated.CacheByPage, Is.EqualTo(true)); Assert.That(macroUpdated.CacheByMember, Is.EqualTo(true)); - Assert.That(macroUpdated.ControlAssembly, Is.EqualTo("")); - Assert.That(macroUpdated.ControlType, Is.EqualTo("")); Assert.That(macroUpdated.DontRender, Is.EqualTo(false)); - Assert.That(macroUpdated.ScriptPath, Is.EqualTo("~/newpath.cshtml")); + Assert.That(macroUpdated.MacroSource, Is.EqualTo("~/newpath.cshtml")); Assert.That(macroUpdated.UseInEditor, Is.EqualTo(true)); - Assert.That(macroUpdated.XsltPath, Is.EqualTo("")); } } @@ -299,7 +290,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var macro = new Macro("newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); repository.Save(macro); @@ -324,7 +315,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var macro = new Macro("newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); macro.Properties.Add(new MacroProperty("blah1", "New1", 4, "test.editor")); repository.Save(macro); @@ -348,7 +339,7 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - var macro = new Macro("newmacro", "A new macro", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"); + var macro = new Macro("newmacro", "A new macro", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView); var prop1 = new MacroProperty("blah1", "New1", 4, "test.editor"); var prop2 = new MacroProperty("blah2", "New2", 3, "test.editor"); @@ -434,9 +425,9 @@ namespace Umbraco.Tests.Persistence.Repositories { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - repository.Save(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); - repository.Save(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); - repository.Save(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); + repository.Save(new Macro("test1", "Test1", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView)); + repository.Save(new Macro("test2", "Test2", "~/views/macropartials/test2.cshtml", MacroTypes.PartialView)); + repository.Save(new Macro("test3", "Tet3", "~/views/macropartials/test3.cshtml", MacroTypes.PartialView)); scope.Complete(); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index 2e72a2e14c..53985b7897 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Persistence.Repositories mediaTypeRepository = new MediaTypeRepository(scopeAccessor, cacheHelper, Logger); var tagRepository = new TagRepository(scopeAccessor, cacheHelper, Logger); - var repository = new MediaRepository(scopeAccessor, cacheHelper, Logger, mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(scopeAccessor, cacheHelper, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 4f901935dc..d07d2dda9a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence.Repositories memberTypeRepository = new MemberTypeRepository(accessor, DisabledCache, Logger); memberGroupRepository = new MemberGroupRepository(accessor, DisabledCache, Logger); var tagRepo = new TagRepository(accessor, DisabledCache, Logger); - var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo); + var repository = new MemberRepository(accessor, DisabledCache, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index 216fb08ecd..90230e15c7 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; +using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; @@ -964,7 +965,7 @@ namespace Umbraco.Tests.Persistence.Repositories var accessor = (IScopeAccessor) provider; var tagRepository = new TagRepository(accessor, DisabledCache, Logger); mediaTypeRepository = new MediaTypeRepository(accessor, DisabledCache, Logger); - var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(accessor, DisabledCache, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index e288bc5eaf..39128eb60a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -178,7 +178,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(repository.Get("test"), Is.Not.Null); Assert.That(_viewsFileSystem.FileExists("test.cshtml"), Is.True); Assert.AreEqual( - @"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage @{ Layout = null;}".StripWhitespace(), + @"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null;}".StripWhitespace(), template.Content.StripWhitespace()); } @@ -208,7 +208,7 @@ namespace Umbraco.Tests.Persistence.Repositories Assert.That(repository.Get("test2"), Is.Not.Null); Assert.That(_viewsFileSystem.FileExists("test2.cshtml"), Is.True); Assert.AreEqual( - "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage @{ Layout = \"test.cshtml\";}".StripWhitespace(), + "@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = \"test.cshtml\";}".StripWhitespace(), template2.Content.StripWhitespace()); } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 56d5bfbc0c..2688629c5c 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories var accessor = (IScopeAccessor) provider; mediaTypeRepository = new MediaTypeRepository(accessor, CacheHelper, Mock.Of()); var tagRepository = new TagRepository(accessor, CacheHelper, Mock.Of()); - var repository = new MediaRepository(accessor, CacheHelper, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(accessor, CacheHelper, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs index 9d2c574743..f52bfdaa59 100644 --- a/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs @@ -415,35 +415,7 @@ namespace Umbraco.Tests.Persistence scope.Complete(); } } - - [Test] - public void Can_Create_cmsStylesheet_Table() - { - using (var scope = ScopeProvider.CreateScope()) - { - var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of()); - - helper.CreateTable(); - helper.CreateTable(); - - scope.Complete(); - } - } - - [Test] - public void Can_Create_cmsStylesheetProperty_Table() - { - using (var scope = ScopeProvider.CreateScope()) - { - var helper = new DatabaseSchemaCreator(scope.Database, Mock.Of()); - - helper.CreateTable(); - helper.CreateTable(); - - scope.Complete(); - } - } - + [Test] public void Can_Create_cmsTags_Table() { diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs new file mode 100644 index 0000000000..24f3c9a9fe --- /dev/null +++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs @@ -0,0 +1,393 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using Moq; +using NUnit.Framework; +using SqlCE4Umbraco; +using umbraco; +using umbraco.businesslogic; +using umbraco.cms.businesslogic; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Profiling; +using Umbraco.Core.PropertyEditors; +using umbraco.DataLayer; +using umbraco.editorControls; +using umbraco.interfaces; +using umbraco.MacroEngines; +using umbraco.uicontrols; +using Umbraco.Web; +using Umbraco.Web.PropertyEditors; + +namespace Umbraco.Tests.Plugins +{ + + [TestFixture] + public class PluginManagerTests + { + private PluginManager _manager; + [SetUp] + public void Initialize() + { + //this ensures its reset + _manager = new PluginManager(new ActivatorServiceProvider(), new NullCacheProvider(), + new ProfilingLogger(Mock.Of(), Mock.Of())); + + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + //TODO: Should probably update this so it only searches this assembly and add custom types to be found + _manager.AssembliesToScan = new[] + { + this.GetType().Assembly, + typeof(ApplicationStartupHandler).Assembly, + typeof(SqlCEHelper).Assembly, + typeof(CMSNode).Assembly, + typeof(System.Guid).Assembly, + typeof(NUnit.Framework.Assert).Assembly, + typeof(Microsoft.CSharp.CSharpCodeProvider).Assembly, + typeof(System.Xml.NameTable).Assembly, + typeof(System.Configuration.GenericEnumConverter).Assembly, + typeof(System.Web.SiteMap).Assembly, + typeof(TabPage).Assembly, + typeof(System.Web.Mvc.ActionResult).Assembly, + typeof(TypeFinder).Assembly, + typeof(ISqlHelper).Assembly, + typeof(ICultureDictionary).Assembly, + typeof(UmbracoContext).Assembly, + typeof(BaseDataType).Assembly + }; + } + + [TearDown] + public void TearDown() + { + _manager = null; + } + + private DirectoryInfo PrepareFolder() + { + var assDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory; + var dir = Directory.CreateDirectory(Path.Combine(assDir.FullName, "PluginManager", Guid.NewGuid().ToString("N"))); + foreach (var f in dir.GetFiles()) + { + f.Delete(); + } + return dir; + } + + //[Test] + //public void Scan_Vs_Load_Benchmark() + //{ + // var pluginManager = new PluginManager(false); + // var watch = new Stopwatch(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + // watch.Reset(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var refreshers = pluginManager.ResolveTypes(false); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); + //} + + ////NOTE: This test shows that Type.GetType is 100% faster than Assembly.Load(..).GetType(...) so we'll use that :) + //[Test] + //public void Load_Type_Benchmark() + //{ + // var watch = new Stopwatch(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + // watch.Reset(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.macroCacheRefresh"); + // var type3 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.templateCacheRefresh"); + // var type4 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.presentation.cache.MediaLibraryRefreshers"); + // var type5 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.presentation.cache.pageRefresher"); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); + // watch.Reset(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + //} + + [Test] + public void Detect_Legacy_Plugin_File_List() + { + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + + var filePath= Path.Combine(tempFolder, string.Format("umbraco-plugins.{0}.list", NetworkHelper.FileSafeMachineName)); + + File.WriteAllText(filePath, @" + + + + +"); + + Assert.IsEmpty(_manager.ReadCache()); // uber-legacy cannot be read + + File.Delete(filePath); + + File.WriteAllText(filePath, @" + + + + +"); + + Assert.IsEmpty(_manager.ReadCache()); // legacy cannot be read + + File.Delete(filePath); + + File.WriteAllText(filePath, @"IContentFinder + +MyContentFinder +AnotherContentFinder + +"); + + Assert.IsNotNull(_manager.ReadCache()); // works + } + + [Test] + public void Create_Cached_Plugin_File() + { + var types = new[] { typeof (PluginManager), typeof (PluginManagerTests), typeof (UmbracoContext) }; + + var typeList1 = new PluginManager.TypeList(typeof (object), null); + foreach (var type in types) typeList1.Add(type); + _manager.AddTypeList(typeList1); + _manager.WriteCache(); + + var plugins = _manager.TryGetCached(typeof (object), null); + var diffType = _manager.TryGetCached(typeof (object), typeof (ObsoleteAttribute)); + + Assert.IsTrue(plugins.Success); + //this will be false since there is no cache of that type resolution kind + Assert.IsFalse(diffType.Success); + + Assert.AreEqual(3, plugins.Result.Count()); + var shouldContain = types.Select(x => x.AssemblyQualifiedName); + //ensure they are all found + Assert.IsTrue(plugins.Result.ContainsAll(shouldContain)); + } + + [Test] + public void Get_Plugins_Hash() + { + //Arrange + var dir = PrepareFolder(); + var d1 = dir.CreateSubdirectory("1"); + var d2 = dir.CreateSubdirectory("2"); + var d3 = dir.CreateSubdirectory("3"); + var d4 = dir.CreateSubdirectory("4"); + var f1 = new FileInfo(Path.Combine(d1.FullName, "test1.dll")); + var f2 = new FileInfo(Path.Combine(d1.FullName, "test2.dll")); + var f3 = new FileInfo(Path.Combine(d2.FullName, "test1.dll")); + var f4 = new FileInfo(Path.Combine(d2.FullName, "test2.dll")); + var f5 = new FileInfo(Path.Combine(d3.FullName, "test1.dll")); + var f6 = new FileInfo(Path.Combine(d3.FullName, "test2.dll")); + var f7 = new FileInfo(Path.Combine(d4.FullName, "test1.dll")); + f1.CreateText().Close(); + f2.CreateText().Close(); + f3.CreateText().Close(); + f4.CreateText().Close(); + f5.CreateText().Close(); + f6.CreateText().Close(); + f7.CreateText().Close(); + var list1 = new[] { f1, f2, f3, f4, f5, f6 }; + var list2 = new[] { f1, f3, f5 }; + var list3 = new[] { f1, f3, f5, f7 }; + + //Act + var hash1 = PluginManager.GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of())); + var hash2 = PluginManager.GetFileHash(list2, new ProfilingLogger(Mock.Of(), Mock.Of())); + var hash3 = PluginManager.GetFileHash(list3, new ProfilingLogger(Mock.Of(), Mock.Of())); + + //Assert + Assert.AreNotEqual(hash1, hash2); + Assert.AreNotEqual(hash1, hash3); + Assert.AreNotEqual(hash2, hash3); + + Assert.AreEqual(hash1, PluginManager.GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of()))); + } + + [Test] + public void Ensure_Only_One_Type_List_Created() + { + var foundTypes1 = _manager.ResolveFindMeTypes(); + var foundTypes2 = _manager.ResolveFindMeTypes(); + Assert.AreEqual(1, _manager.TypeLists.Count(x => x.BaseType == typeof(IFindMe) && x.AttributeType == null)); + } + + [Test] + public void Resolves_Assigned_Mappers() + { + var foundTypes1 = _manager.ResolveAssignedMapperTypes(); + Assert.AreEqual(31, foundTypes1.Count()); + } + + [Test] + public void Resolves_Types() + { + var foundTypes1 = _manager.ResolveFindMeTypes(); + Assert.AreEqual(2, foundTypes1.Count()); + } + + [Test] + public void Resolves_Attributed_Trees() + { + var trees = _manager.ResolveAttributedTrees(); + // commit 6c5e35ec2cbfa31be6790d1228e0c2faf5f55bc8 brings the count down to 14 + Assert.AreEqual(6, trees.Count()); + } + + [Test] + public void Resolves_Actions() + { + var actions = _manager.ResolveActions(); + Assert.AreEqual(38, actions.Count()); + } + + [Test] + public void Resolves_Trees() + { + var trees = _manager.ResolveTrees(); + Assert.AreEqual(34, trees.Count()); + } + + [Test] + public void Resolves_Applications() + { + var apps = _manager.ResolveApplications(); + Assert.AreEqual(7, apps.Count()); + } + + [Test] + public void Resolves_DataTypes() + { + var types = _manager.ResolveDataTypes(); + Assert.AreEqual(35, types.Count()); + } + + [Test] + public void Resolves_RazorDataTypeModels() + { + var types = _manager.ResolveRazorDataTypeModels(); + Assert.AreEqual(2, types.Count()); + } + + [Test] + public void Resolves_RestExtensions() + { + var types = _manager.ResolveRestExtensions(); + Assert.AreEqual(3, types.Count()); + } + + [Test] + public void Resolves_XsltExtensions() + { + var types = _manager.ResolveXsltExtensions(); + Assert.AreEqual(3, types.Count()); + } + + /// + /// This demonstrates this issue: http://issues.umbraco.org/issue/U4-3505 - the TypeList was returning a list of assignable types + /// not explicit types which is sort of ideal but is confusing so we'll do it the less confusing way. + /// + [Test] + public void TypeList_Resolves_Explicit_Types() + { + var types = new HashSet(); + + var propEditors = new PluginManager.TypeList(typeof (PropertyEditor), null); + propEditors.Add(typeof(LabelPropertyEditor)); + types.Add(propEditors); + + var found = types.SingleOrDefault(x => x.BaseType == typeof (PropertyEditor) && x.AttributeType == null); + + Assert.IsNotNull(found); + + //This should not find a type list of this type + var shouldNotFind = types.SingleOrDefault(x => x.BaseType == typeof (IParameterEditor) && x.AttributeType == null); + + Assert.IsNull(shouldNotFind); + } + + [XsltExtension("Blah.Blah")] + public class MyXsltExtension + { + + } + + + [Umbraco.Web.BaseRest.RestExtension("Blah")] + public class MyRestExtesion + { + + } + + public interface IFindMe : IDiscoverable + { + + } + + public class FindMe1 : IFindMe + { + + } + + public class FindMe2 : IFindMe + { + + } + + } +} diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index bc17a7fb03..25933fdd9d 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -75,7 +76,7 @@ namespace Umbraco.Tests.Published var cacheMock = new Mock(); var cacheContent = new Dictionary(); cacheMock.Setup(x => x.GetById(It.IsAny())).Returns(id => cacheContent.TryGetValue(id, out IPublishedContent content) ? content : null); - var publishedSnapshotMock = new Mock(); + var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Content).Returns(cacheMock.Object); var publishedSnapshotAccessorMock = new Mock(); publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); @@ -99,7 +100,7 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", Array.Empty()); - var cnt1 = new PublishedSnapshotTestObjects.TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); + var cnt1 = new TestPublishedContent(cntType1, 1234, Guid.NewGuid(), new Dictionary(), false); cacheContent[cnt1.Id] = cnt1; Assert.AreSame(cnt1, element1.Value("prop1")); @@ -164,7 +165,7 @@ namespace Umbraco.Tests.Published var cacheMock = new Mock(); var cacheContent = new Dictionary(); cacheMock.Setup(x => x.GetById(It.IsAny())).Returns(id => cacheContent.TryGetValue(id, out IPublishedContent content) ? content : null); - var publishedSnapshotMock = new Mock(); + var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Content).Returns(cacheMock.Object); var publishedSnapshotAccessorMock = new Mock(); publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); @@ -200,8 +201,8 @@ namespace Umbraco.Tests.Published var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); - var cnt1 = new PublishedSnapshotTestObjects.TestPublishedContent(contentType1, 1003, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); - var cnt2 = new PublishedSnapshotTestObjects.TestPublishedContent(contentType2, 1004, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); + var cnt1 = new TestPublishedContent(contentType1, 1003, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); + var cnt2 = new TestPublishedContent(contentType2, 1004, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); cacheContent[cnt1.Id] = cnt1.CreateModel(); cacheContent[cnt2.Id] = cnt2.CreateModel(); diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index a916a2d51e..c806930704 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -104,7 +104,7 @@ namespace Umbraco.Tests.Published }); var contentCache = new Mock(); - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); // mocked published snapshot returns a content cache publishedSnapshot @@ -242,10 +242,10 @@ namespace Umbraco.Tests.Published _owner = owner; } - public override bool HasValue(int? languageId = null, string segment = null) => _hasValue; - public override object GetSourceValue(int? languageId = null, string segment = null) => _sourceValue; - public override object GetValue(int? languageId = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); - public override object GetXPathValue(int? languageId = null, string segment = null) => throw new WontImplementException(); + public override bool HasValue(string culture = null, string segment = null) => _hasValue; + public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; + public override object GetValue(string culture = null, string segment = null) => PropertyType.ConvertInterToObject(_owner, ReferenceCacheLevel, InterValue, _preview); + public override object GetXPathValue(string culture = null, string segment = null) => throw new WontImplementException(); } class TestPublishedContent : PublishedContentBase @@ -273,6 +273,7 @@ namespace Umbraco.Tests.Published public override int TemplateId { get; } public override int SortOrder { get; } public override string Name { get; } + public override IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public override string UrlName { get; } public override string DocumentTypeAlias { get; } public override int DocumentTypeId { get; } diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 49d1707567..950d81ee36 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -121,7 +121,7 @@ namespace Umbraco.Tests.Published var elementsCache = new DictionaryCacheProvider(); var snapshotCache = new DictionaryCacheProvider(); - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.SnapshotCache).Returns(snapshotCache); publishedSnapshot.Setup(x => x.ElementsCache).Returns(elementsCache); diff --git a/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs b/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs index 48495b2ae7..fcb462e5c5 100644 --- a/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs +++ b/src/Umbraco.Tests/Published/PublishedSnapshotTestObjects.cs @@ -8,8 +8,6 @@ namespace Umbraco.Tests.Published { public class PublishedSnapshotTestObjects { - #region Published models - [PublishedModel("element1")] public class TestElementModel1 : PublishedElementModel { @@ -49,65 +47,6 @@ namespace Umbraco.Tests.Published public IEnumerable Prop2 => this.Value>("prop2"); } - - #endregion - - #region Support classes - - internal class TestPublishedContent : PublishedElement, IPublishedContent - { - public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing) - : base(contentType, key, values, previewing) - { - Id = id; - } - - public int Id { get; } - public int TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public string UrlName { get; set; } - public string DocumentTypeAlias => ContentType.Alias; - public int DocumentTypeId { get; set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - public PublishedItemType ItemType => ContentType.ItemType; - public bool IsDraft { get; set; } - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - - // copied from PublishedContentBase - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - var firstNonNullProperty = property; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content?.GetProperty(alias); - if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; - } - - // if we find a content with the property with a value, return that property - // if we find no content with the property, return null - // if we find a content with the property without a value, return that property - // have to save that first property while we look further up, hence firstNonNullProperty - - return property != null && property.HasValue() ? property : firstNonNullProperty; - } - } - - #endregion + } } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index 6e7bae28a6..4668a86c78 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -200,7 +200,8 @@ namespace Umbraco.Tests.PublishedContent public Guid Key { get; set; } public int TemplateId { get; set; } public int SortOrder { get; set; } - public string Name { get; set; } + public string Name { get; set; } + public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); public string UrlName { get; set; } public string DocumentTypeAlias { get; set; } public int DocumentTypeId { get; set; } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 2713229a5e..61c37d6a51 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -74,7 +74,8 @@ namespace Umbraco.Tests.PublishedContent new WebSecurity(httpContext, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings); + globalSettings, + ServiceContext.EntityService); return umbracoContext; } @@ -196,13 +197,13 @@ namespace Umbraco.Tests.PublishedContent Assert.AreEqual(2, result[1].Id); } - private static SolidPublishedShapshot CreatePublishedSnapshot() + private static SolidPublishedSnapshot CreatePublishedSnapshot() { var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var caches = new SolidPublishedShapshot(); + var caches = new SolidPublishedSnapshot(); var cache = caches.InnerContentCache; var props = new[] diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 0e726064f4..04ed54d81c 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -356,7 +356,7 @@ namespace Umbraco.Tests.PublishedContent var result = doc.Ancestors().OrderBy(x => x.Level) .Single() .Descendants() - .FirstOrDefault(x => x.Value("selectedNodes", "").Split(',').Contains("1173")); + .FirstOrDefault(x => x.Value("selectedNodes", defaultValue: "").Split(',').Contains("1173")); Assert.IsNotNull(result); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs similarity index 91% rename from src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs rename to src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index 8f6ee5c2b1..4b7a131bd0 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -1,345 +1,347 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moq; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; -using Umbraco.Core.PropertyEditors; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; -using Umbraco.Web.PublishedCache; - -namespace Umbraco.Tests.PublishedContent -{ - class SolidPublishedShapshot : IPublishedShapshot - { - public readonly SolidPublishedContentCache InnerContentCache = new SolidPublishedContentCache(); - public readonly SolidPublishedContentCache InnerMediaCache = new SolidPublishedContentCache(); - - public IPublishedContentCache Content => InnerContentCache; - - public IPublishedMediaCache Media => InnerMediaCache; - - public IPublishedMemberCache Members => null; - - public IDomainCache Domains => null; - - public IDisposable ForcedPreview(bool forcedPreview, Action callback = null) - { - throw new NotImplementedException(); - } - - public void Resync() - { } - - public ICacheProvider SnapshotCache => null; - - public ICacheProvider ElementsCache => null; - } - - class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache - { - private readonly Dictionary _content = new Dictionary(); - - public SolidPublishedContentCache() - : base(false) - { } - - public void Add(SolidPublishedContent content) - { - _content[content.Id] = content.CreateModel(); - } - - public void Clear() - { - _content.Clear(); - } - - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null) - { - throw new NotImplementedException(); - } - - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null) - { - throw new NotImplementedException(); - } - - public string GetRouteById(bool preview, int contentId) - { - throw new NotImplementedException(); - } - - public string GetRouteById(int contentId) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetById(bool preview, int contentId) - { - return _content.ContainsKey(contentId) ? _content[contentId] : null; - } - - public override IPublishedContent GetById(bool preview, Guid contentId) - { - throw new NotImplementedException(); - } - - public override bool HasById(bool preview, int contentId) - { - return _content.ContainsKey(contentId); - } - - public override IEnumerable GetAtRoot(bool preview) - { - return _content.Values.Where(x => x.Parent == null); - } - - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetSingleByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override System.Xml.XPath.XPathNavigator CreateNavigator(bool preview) - { - throw new NotImplementedException(); - } - - public override System.Xml.XPath.XPathNavigator CreateNodeNavigator(int id, bool preview) - { - throw new NotImplementedException(); - } - - public override bool HasContent(bool preview) - { - return _content.Count > 0; - } - - public override PublishedContentType GetContentType(int id) - { - throw new NotImplementedException(); - } - - public override PublishedContentType GetContentType(string alias) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByContentType(PublishedContentType contentType) - { - throw new NotImplementedException(); - } - } - - class SolidPublishedContent : IPublishedContent - { - #region Constructor - - public SolidPublishedContent(PublishedContentType contentType) - { - // initialize boring stuff - TemplateId = 0; - WriterName = CreatorName = string.Empty; - WriterId = CreatorId = 0; - CreateDate = UpdateDate = DateTime.Now; - Version = Guid.Empty; - IsDraft = false; - - ContentType = contentType; - DocumentTypeAlias = contentType.Alias; - DocumentTypeId = contentType.Id; - } - - #endregion - - #region Content - - public int Id { get; set; } - public Guid Key { get; set; } - public int TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public string UrlName { get; set; } - public string DocumentTypeAlias { get; private set; } - public int DocumentTypeId { get; private set; } - public string WriterName { get; set; } - public string CreatorName { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - public string Url { get; set; } - - public PublishedItemType ItemType { get { return PublishedItemType.Content; } } - public bool IsDraft { get; set; } - - #endregion - - #region Tree - - public int ParentId { get; set; } - public IEnumerable ChildIds { get; set; } - - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - - #endregion - - #region ContentType - - public PublishedContentType ContentType { get; private set; } - - #endregion - - #region Properties - - public IEnumerable Properties { get; set; } - - public IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias)); - } - - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content == null ? null : content.GetProperty(alias); - } - - return property; - } - - public object this[string alias] - { - get - { - var property = GetProperty(alias); - return property == null || property.HasValue() == false ? null : property.GetValue(); - } - } - - #endregion - } - - class SolidPublishedProperty : IPublishedProperty - { - public string Alias { get; set; } - public object SolidSourceValue { get; set; } - public object SolidValue { get; set; } - public bool SolidHasValue { get; set; } - public object SolidXPathValue { get; set; } - - public object GetSourceValue(int? languageId = null, string segment = null) => SolidSourceValue; - public object GetValue(int? languageId = null, string segment = null) => SolidValue; - public object GetXPathValue(int? languageId = null, string segment = null) => SolidXPathValue; - public bool HasValue(int? languageId = null, string segment = null) => SolidHasValue; - } - - [PublishedModel("ContentType2")] - internal class ContentType2 : PublishedContentModel - { - #region Plumbing - - public ContentType2(IPublishedContent content) - : base(content) - { } - - #endregion - - public int Prop1 => this.Value("prop1"); - } - - [PublishedModel("ContentType2Sub")] - internal class ContentType2Sub : ContentType2 - { - #region Plumbing - - public ContentType2Sub(IPublishedContent content) - : base(content) - { } - - #endregion - } - - class PublishedContentStrong1 : PublishedContentModel - { - public PublishedContentStrong1(IPublishedContent content) - : base(content) - { } - - public int StrongValue => this.Value("strongValue"); - } - - class PublishedContentStrong1Sub : PublishedContentStrong1 - { - public PublishedContentStrong1Sub(IPublishedContent content) - : base(content) - { } - - public int AnotherValue => this.Value("anotherValue"); - } - - class PublishedContentStrong2 : PublishedContentModel - { - public PublishedContentStrong2(IPublishedContent content) - : base(content) - { } - - public int StrongValue => this.Value("strongValue"); - } - - class AutoPublishedContentType : PublishedContentType - { - private static readonly PublishedPropertyType Default; - - static AutoPublishedContentType() - { - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(Mock.Of())) { Id = 666 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - Default = factory.CreatePropertyType("*", 666); - } - - public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.InvariantNeutral) - { } - - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.InvariantNeutral) - { } - - public override PublishedPropertyType GetPropertyType(string alias) - { - var propertyType = base.GetPropertyType(alias); - return propertyType ?? Default; - } - } -} +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Moq; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.PublishedContent +{ + class SolidPublishedSnapshot : IPublishedSnapshot + { + public readonly SolidPublishedContentCache InnerContentCache = new SolidPublishedContentCache(); + public readonly SolidPublishedContentCache InnerMediaCache = new SolidPublishedContentCache(); + + public IPublishedContentCache Content => InnerContentCache; + + public IPublishedMediaCache Media => InnerMediaCache; + + public IPublishedMemberCache Members => null; + + public IDomainCache Domains => null; + + public IDisposable ForcedPreview(bool forcedPreview, Action callback = null) + { + throw new NotImplementedException(); + } + + public void Resync() + { } + + public ICacheProvider SnapshotCache => null; + + public ICacheProvider ElementsCache => null; + } + + class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache + { + private readonly Dictionary _content = new Dictionary(); + + public SolidPublishedContentCache() + : base(false) + { } + + public void Add(SolidPublishedContent content) + { + _content[content.Id] = content.CreateModel(); + } + + public void Clear() + { + _content.Clear(); + } + + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) + { + throw new NotImplementedException(); + } + + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) + { + throw new NotImplementedException(); + } + + public string GetRouteById(bool preview, int contentId, string culture = null) + { + throw new NotImplementedException(); + } + + public string GetRouteById(int contentId, string culture = null) + { + throw new NotImplementedException(); + } + + public override IPublishedContent GetById(bool preview, int contentId) + { + return _content.ContainsKey(contentId) ? _content[contentId] : null; + } + + public override IPublishedContent GetById(bool preview, Guid contentId) + { + throw new NotImplementedException(); + } + + public override bool HasById(bool preview, int contentId) + { + return _content.ContainsKey(contentId); + } + + public override IEnumerable GetAtRoot(bool preview) + { + return _content.Values.Where(x => x.Parent == null); + } + + public override IPublishedContent GetSingleByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override IPublishedContent GetSingleByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetByXPath(bool preview, string xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, Core.Xml.XPathVariable[] vars) + { + throw new NotImplementedException(); + } + + public override System.Xml.XPath.XPathNavigator CreateNavigator(bool preview) + { + throw new NotImplementedException(); + } + + public override System.Xml.XPath.XPathNavigator CreateNodeNavigator(int id, bool preview) + { + throw new NotImplementedException(); + } + + public override bool HasContent(bool preview) + { + return _content.Count > 0; + } + + public override PublishedContentType GetContentType(int id) + { + throw new NotImplementedException(); + } + + public override PublishedContentType GetContentType(string alias) + { + throw new NotImplementedException(); + } + + public override IEnumerable GetByContentType(PublishedContentType contentType) + { + throw new NotImplementedException(); + } + } + + class SolidPublishedContent : IPublishedContent + { + #region Constructor + + public SolidPublishedContent(PublishedContentType contentType) + { + // initialize boring stuff + TemplateId = 0; + WriterName = CreatorName = string.Empty; + WriterId = CreatorId = 0; + CreateDate = UpdateDate = DateTime.Now; + Version = Guid.Empty; + IsDraft = false; + + ContentType = contentType; + DocumentTypeAlias = contentType.Alias; + DocumentTypeId = contentType.Id; + } + + #endregion + + #region Content + + public int Id { get; set; } + public Guid Key { get; set; } + public int TemplateId { get; set; } + public int SortOrder { get; set; } + public string Name { get; set; } + public IReadOnlyDictionary CultureNames => throw new NotSupportedException(); + public string UrlName { get; set; } + public string DocumentTypeAlias { get; private set; } + public int DocumentTypeId { get; private set; } + public string WriterName { get; set; } + public string CreatorName { get; set; } + public int WriterId { get; set; } + public int CreatorId { get; set; } + public string Path { get; set; } + public DateTime CreateDate { get; set; } + public DateTime UpdateDate { get; set; } + public Guid Version { get; set; } + public int Level { get; set; } + public string Url { get; set; } + + public PublishedItemType ItemType { get { return PublishedItemType.Content; } } + public bool IsDraft { get; set; } + + #endregion + + #region Tree + + public int ParentId { get; set; } + public IEnumerable ChildIds { get; set; } + + public IPublishedContent Parent { get; set; } + public IEnumerable Children { get; set; } + + #endregion + + #region ContentType + + public PublishedContentType ContentType { get; private set; } + + #endregion + + #region Properties + + public IEnumerable Properties { get; set; } + + public IPublishedProperty GetProperty(string alias) + { + return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias)); + } + + public IPublishedProperty GetProperty(string alias, bool recurse) + { + var property = GetProperty(alias); + if (recurse == false) return property; + + IPublishedContent content = this; + while (content != null && (property == null || property.HasValue() == false)) + { + content = content.Parent; + property = content == null ? null : content.GetProperty(alias); + } + + return property; + } + + public object this[string alias] + { + get + { + var property = GetProperty(alias); + return property == null || property.HasValue() == false ? null : property.GetValue(); + } + } + + #endregion + } + + class SolidPublishedProperty : IPublishedProperty + { + public string Alias { get; set; } + public object SolidSourceValue { get; set; } + public object SolidValue { get; set; } + public bool SolidHasValue { get; set; } + public object SolidXPathValue { get; set; } + + public object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; + public object GetValue(string culture = null, string segment = null) => SolidValue; + public object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; + public bool HasValue(string culture = null, string segment = null) => SolidHasValue; + } + + [PublishedModel("ContentType2")] + internal class ContentType2 : PublishedContentModel + { + #region Plumbing + + public ContentType2(IPublishedContent content) + : base(content) + { } + + #endregion + + public int Prop1 => this.Value("prop1"); + } + + [PublishedModel("ContentType2Sub")] + internal class ContentType2Sub : ContentType2 + { + #region Plumbing + + public ContentType2Sub(IPublishedContent content) + : base(content) + { } + + #endregion + } + + class PublishedContentStrong1 : PublishedContentModel + { + public PublishedContentStrong1(IPublishedContent content) + : base(content) + { } + + public int StrongValue => this.Value("strongValue"); + } + + class PublishedContentStrong1Sub : PublishedContentStrong1 + { + public PublishedContentStrong1Sub(IPublishedContent content) + : base(content) + { } + + public int AnotherValue => this.Value("anotherValue"); + } + + class PublishedContentStrong2 : PublishedContentModel + { + public PublishedContentStrong2(IPublishedContent content) + : base(content) + { } + + public int StrongValue => this.Value("strongValue"); + } + + class AutoPublishedContentType : PublishedContentType + { + private static readonly PublishedPropertyType Default; + + static AutoPublishedContentType() + { + var dataTypeService = new TestObjects.TestDataTypeService( + new DataType(new VoidEditor(Mock.Of())) { Id = 666 }); + + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); + Default = factory.CreatePropertyType("*", 666); + } + + public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) + : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.InvariantNeutral) + { } + + public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.InvariantNeutral) + { } + + public override PublishedPropertyType GetPropertyType(string alias) + { + var propertyType = base.GetPropertyType(alias); + return propertyType ?? Default; + } + } +} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs index 61c5b4aae9..2f2edf3964 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs @@ -1,6 +1,8 @@ using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Routing; +using LightInject; namespace Umbraco.Tests.Routing { @@ -15,7 +17,7 @@ namespace Umbraco.Tests.Routing var umbracoContext = GetUmbracoContext(urlAsString); var publishedRouter = CreatePublishedRouter(); var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByIdPath(Logger); + var lookup = new ContentFinderByIdPath(Container.GetInstance().WebRouting, Logger); var result = lookup.TryFindContent(frequest); diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs similarity index 89% rename from src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs rename to src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs index c58899d4d1..5080ab339d 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -1,50 +1,52 @@ -using Moq; -using NUnit.Framework; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Routing; -using Umbraco.Core.Models; -using Umbraco.Tests.Testing; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class ContentFinderByNiceUrlAndTemplateTests : BaseWebTest - { - Template CreateTemplate(string alias) - { - var template = new Template(alias, alias); - template.Content = ""; // else saving throws with a dirty internal error - Current.Services.FileService.SaveTemplate(template); - return template; - } - - [TestCase("/blah")] - [TestCase("/default.aspx/blah")] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/home/Sub1/blah")] - [TestCase("/Home/Sub1/Blah")] //different cases - [TestCase("/home/Sub1.aspx/blah")] - public void Match_Document_By_Url_With_Template(string urlAsString) - { - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); +using Moq; +using NUnit.Framework; +using LightInject; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Routing; +using Umbraco.Core.Models; +using Umbraco.Tests.Testing; +using Current = Umbraco.Web.Composing.Current; +using Umbraco.Core.Configuration.UmbracoSettings; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class ContentFinderByUrlAndTemplateTests : BaseWebTest + { + Template CreateTemplate(string alias) + { + var template = new Template(alias, alias); + template.Content = ""; // else saving throws with a dirty internal error + Current.Services.FileService.SaveTemplate(template); + return template; + } + + [TestCase("/blah")] + [TestCase("/default.aspx/blah")] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! + [TestCase("/home/Sub1/blah")] + [TestCase("/Home/Sub1/Blah")] //different cases + [TestCase("/home/Sub1.aspx/blah")] + public void Match_Document_By_Url_With_Template(string urlAsString) + { + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettings.Object); - - var template1 = CreateTemplate("test"); - var template2 = CreateTemplate("blah"); - var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrlAndTemplate(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.IsNotNull(frequest.PublishedContent); - Assert.IsNotNull(frequest.TemplateAlias); - Assert.AreEqual("blah".ToUpperInvariant(), frequest.TemplateAlias.ToUpperInvariant()); - } - } -} + + var template1 = CreateTemplate("test"); + var template2 = CreateTemplate("blah"); + var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings:globalSettings.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrlAndTemplate(Logger, ServiceContext.FileService); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.IsNotNull(frequest.PublishedContent); + Assert.IsNotNull(frequest.TemplateAlias); + Assert.AreEqual("blah".ToUpperInvariant(), frequest.TemplateAlias.ToUpperInvariant()); + } + } +} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs similarity index 93% rename from src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs rename to src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs index 66f5cc722a..8b591dfb84 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs @@ -1,166 +1,166 @@ -using System; -using System.Globalization; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Configuration; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class ContentFinderByNiceUrlTests : BaseWebTest - { - [TestCase("/", 1046)] - [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/Sub1", 1173)] - [TestCase("/sub1", 1173)] - [TestCase("/sub1.aspx", 1173)] - [TestCase("/home/sub1", -1)] // should fail - - // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so - // we've made it return "/test-page" => we have to support that url back in the lookup... - [TestCase("/home", 1046)] - [TestCase("/test-page", 1172)] - public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); +using System; +using System.Globalization; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class ContentFinderByUrlTests : BaseWebTest + { + [TestCase("/", 1046)] + [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! + [TestCase("/Sub1", 1173)] + [TestCase("/sub1", 1173)] + [TestCase("/sub1.aspx", 1173)] + [TestCase("/home/sub1", -1)] // should fail + + // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so + // we've made it return "/test-page" => we have to support that url back in the lookup... + [TestCase("/home", 1046)] + [TestCase("/test-page", 1172)] + public void Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrl(Logger); - - Assert.IsTrue(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); - - // fixme debugging - going further down, the routes cache is NOT empty?! - if (urlString == "/home/sub1") - System.Diagnostics.Debugger.Break(); - - var result = lookup.TryFindContent(frequest); - - if (expectedId > 0) - { - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - else - { - Assert.IsFalse(result); - } - } - - [TestCase("/", 1046)] - [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/home", 1046)] - [TestCase("/home/Sub1", 1173)] - [TestCase("/Home/Sub1", 1173)] //different cases - [TestCase("/home/Sub1.aspx", 1173)] - public void Match_Document_By_Url(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrl(Logger); + + Assert.IsTrue(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); + + // fixme debugging - going further down, the routes cache is NOT empty?! + if (urlString == "/home/sub1") + System.Diagnostics.Debugger.Break(); + + var result = lookup.TryFindContent(frequest); + + if (expectedId > 0) + { + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + else + { + Assert.IsFalse(result); + } + } + + [TestCase("/", 1046)] + [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! + [TestCase("/home", 1046)] + [TestCase("/home/Sub1", 1173)] + [TestCase("/Home/Sub1", 1173)] //different cases + [TestCase("/home/Sub1.aspx", 1173)] + public void Match_Document_By_Url(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrl(Logger); - - Assert.IsFalse(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - /// - /// This test handles requests with special characters in the URL. - /// - /// - /// - [TestCase("/", 1046)] - [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] - public void Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrl(Logger); + + Assert.IsFalse(UmbracoConfig.For.GlobalSettings().HideTopLevelNodeFromPath); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + /// + /// This test handles requests with special characters in the URL. + /// + /// + /// + [TestCase("/", 1046)] + [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] + public void Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - var lookup = new ContentFinderByNiceUrl(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - /// - /// This test handles requests with a hostname associated. - /// The logic for handling this goes through the DomainHelper and is a bit different - /// from what happens in a normal request - so it has a separate test with a mocked - /// hostname added. - /// - /// - /// - [TestCase("/", 1046)] - [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] - public void Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + var lookup = new ContentFinderByUrl(Logger); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + + /// + /// This test handles requests with a hostname associated. + /// The logic for handling this goes through the DomainHelper and is a bit different + /// from what happens in a normal request - so it has a separate test with a mocked + /// hostname added. + /// + /// + /// + [TestCase("/", 1046)] + [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] + public void Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/")); - var lookup = new ContentFinderByNiceUrl(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - /// - /// This test handles requests with a hostname with special characters associated. - /// The logic for handling this goes through the DomainHelper and is a bit different - /// from what happens in a normal request - so it has a separate test with a mocked - /// hostname added. - /// - /// - /// - [TestCase("/æøå/", 1046)] - [TestCase("/æøå/home/sub1", 1173)] - [TestCase("/æøå/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] - public void Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) - { - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/")); + var lookup = new ContentFinderByUrl(Logger); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + + /// + /// This test handles requests with a hostname with special characters associated. + /// The logic for handling this goes through the DomainHelper and is a bit different + /// from what happens in a normal request - so it has a separate test with a mocked + /// hostname added. + /// + /// + /// + [TestCase("/æøå/", 1046)] + [TestCase("/æøå/home/sub1", 1173)] + [TestCase("/æøå/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] + public void Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) + { + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(); - var frequest = publishedRouter.CreateRequest(umbracoContext); - frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/æøå")); - var lookup = new ContentFinderByNiceUrl(Logger); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - } -} + var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(); + var frequest = publishedRouter.CreateRequest(umbracoContext); + frequest.Domain = new DomainAndUri(new Domain(1, "mysite/æøå", -1, CultureInfo.CurrentCulture, false), new Uri("http://mysite/æøå")); + var lookup = new ContentFinderByUrl(Logger); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + } +} diff --git a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs similarity index 97% rename from src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs rename to src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs index 396697001d..949467a6fe 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByNiceUrlWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -1,188 +1,188 @@ -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Routing -{ - - [TestFixture] - public class ContentFinderByNiceUrlWithDomainsTests : UrlRoutingTestBase - { - void SetDomains3() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"} - }); - - } - - void SetDomains4() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - - } - - protected override string GetXmlContent(int templateId) - { - return @" - - -]> - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - -"; - } - - [TestCase("http://domain1.com/", 1001)] - [TestCase("http://domain1.com/1001-1", 10011)] - [TestCase("http://domain1.com/1001-2/1001-2-1", 100121)] - - public void Lookup_SingleDomain(string url, int expectedId) - { - SetDomains3(); - - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + + [TestFixture] + public class ContentFinderByUrlWithDomainsTests : UrlRoutingTestBase + { + void SetDomains3() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"} + }); + + } + + void SetDomains4() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); + + } + + protected override string GetXmlContent(int templateId) + { + return @" + + +]> + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + +"; + } + + [TestCase("http://domain1.com/", 1001)] + [TestCase("http://domain1.com/1001-1", 10011)] + [TestCase("http://domain1.com/1001-2/1001-2-1", 100121)] + + public void Lookup_SingleDomain(string url, int expectedId) + { + SetDomains3(); + + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - - var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(Container); - var frequest = publishedRouter.CreateRequest(umbracoContext); - - // must lookup domain else lookup by url fails - publishedRouter.FindDomain(frequest); - - var lookup = new ContentFinderByNiceUrl(Logger); - var result = lookup.TryFindContent(frequest); - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - [TestCase("http://domain1.com/", 1001, "en-US")] - [TestCase("http://domain1.com/en", 10011, "en-US")] - [TestCase("http://domain1.com/en/1001-1-1", 100111, "en-US")] - [TestCase("http://domain1.com/fr", 10012, "fr-FR")] - [TestCase("http://domain1.com/fr/1001-2-1", 100121, "fr-FR")] - [TestCase("http://domain1.com/1001-3", 10013, "en-US")] - - [TestCase("http://domain2.com/1002", 1002, null)] - - [TestCase("http://domain3.com/", 1003, "en-US")] - [TestCase("http://domain3.com/en", 10031, "en-US")] - [TestCase("http://domain3.com/en/1003-1-1", 100311, "en-US")] - [TestCase("http://domain3.com/fr", 10032, "fr-FR")] - [TestCase("http://domain3.com/fr/1003-2-1", 100321, "fr-FR")] - [TestCase("http://domain3.com/1003-3", 10033, "en-US")] - - [TestCase("https://domain1.com/", 1001, "en-US")] - [TestCase("https://domain3.com/", 1001, null)] // because domain3 is explicitely set on http - - public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) - { - SetDomains4(); - - // defaults depend on test environment - expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; - - var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettingsMock.Object); - - var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); - var publishedRouter = CreatePublishedRouter(Container); - var frequest = publishedRouter.CreateRequest(umbracoContext); - - // must lookup domain else lookup by url fails - publishedRouter.FindDomain(frequest); - Assert.AreEqual(expectedCulture, frequest.Culture.Name); - - var lookup = new ContentFinderByNiceUrl(Logger); - var result = lookup.TryFindContent(frequest); - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - } -} + SettingsForTests.ConfigureSettings(globalSettingsMock.Object); + + var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(Container); + var frequest = publishedRouter.CreateRequest(umbracoContext); + + // must lookup domain else lookup by url fails + publishedRouter.FindDomain(frequest); + + var lookup = new ContentFinderByUrl(Logger); + var result = lookup.TryFindContent(frequest); + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + + [TestCase("http://domain1.com/", 1001, "en-US")] + [TestCase("http://domain1.com/en", 10011, "en-US")] + [TestCase("http://domain1.com/en/1001-1-1", 100111, "en-US")] + [TestCase("http://domain1.com/fr", 10012, "fr-FR")] + [TestCase("http://domain1.com/fr/1001-2-1", 100121, "fr-FR")] + [TestCase("http://domain1.com/1001-3", 10013, "en-US")] + + [TestCase("http://domain2.com/1002", 1002, null)] + + [TestCase("http://domain3.com/", 1003, "en-US")] + [TestCase("http://domain3.com/en", 10031, "en-US")] + [TestCase("http://domain3.com/en/1003-1-1", 100311, "en-US")] + [TestCase("http://domain3.com/fr", 10032, "fr-FR")] + [TestCase("http://domain3.com/fr/1003-2-1", 100321, "fr-FR")] + [TestCase("http://domain3.com/1003-3", 10033, "en-US")] + + [TestCase("https://domain1.com/", 1001, "en-US")] + [TestCase("https://domain3.com/", 1001, null)] // because domain3 is explicitely set on http + + public void Lookup_NestedDomains(string url, int expectedId, string expectedCulture) + { + SetDomains4(); + + // defaults depend on test environment + expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; + + var globalSettingsMock = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettingsMock.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); + SettingsForTests.ConfigureSettings(globalSettingsMock.Object); + + var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettingsMock.Object); + var publishedRouter = CreatePublishedRouter(Container); + var frequest = publishedRouter.CreateRequest(umbracoContext); + + // must lookup domain else lookup by url fails + publishedRouter.FindDomain(frequest); + Assert.AreEqual(expectedCulture, frequest.Culture.Name); + + var lookup = new ContentFinderByUrl(Logger); + var result = lookup.TryFindContent(frequest); + Assert.IsTrue(result); + Assert.AreEqual(expectedId, frequest.PublishedContent.Id); + } + } +} diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index b1d024cc8f..e4fcfc46f8 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -277,7 +277,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(expectedCulture, frequest.Culture.Name); - var finder = new ContentFinderByNiceUrl(Logger); + var finder = new ContentFinderByUrl(Logger); var result = finder.TryFindContent(frequest); Assert.IsTrue(result); @@ -326,7 +326,7 @@ namespace Umbraco.Tests.Routing publishedRouter.FindDomain(frequest); // find document - var finder = new ContentFinderByNiceUrl(Logger); + var finder = new ContentFinderByUrl(Logger); var result = finder.TryFindContent(frequest); // apply wildcard domain @@ -338,48 +338,6 @@ namespace Umbraco.Tests.Routing } - - #region Cases - [TestCase(10011, "http://domain1.com/", "en-US")] - [TestCase(100111, "http://domain1.com/", "en-US")] - [TestCase(10011, "http://domain1.fr/", "fr-FR")] - [TestCase(100111, "http://domain1.fr/", "fr-FR")] - [TestCase(1001121, "http://domain1.fr/", "de-DE")] - #endregion - public void GetCulture(int nodeId, string currentUrl, string expectedCulture) - { - var domainService = SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") - { - Id = 1, - LanguageId = LangEngId, - RootContentId = 1001, - LanguageIsoCode = "en-US" - }, - new UmbracoDomain("domain1.fr/") - { - Id = 1, - LanguageId = LangFrId, - RootContentId = 1001, - LanguageIsoCode = "fr-FR" - }, - new UmbracoDomain("*100112") - { - Id = 1, - LanguageId = LangDeId, - RootContentId = 100112, - LanguageIsoCode = "de-DE" - } - }); - - var umbracoContext = GetUmbracoContext("http://anything/"); - - var content = umbracoContext.ContentCache.GetById(nodeId); - Assert.IsNotNull(content); - - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(umbracoContext, domainService, ServiceContext.LocalizationService, null, content.Id, content.Path, new Uri(currentUrl)); - Assert.AreEqual(expectedCulture, culture.Name); - } + } } diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs deleted file mode 100644 index 246a9beb8a..0000000000 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using System.Collections.Generic; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; -using Umbraco.Web.PublishedCache.XmlPublishedCache; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class NiceUrlProviderTests : BaseWebTest - { - protected override void Compose() - { - base.Compose(); - Container.Register(); - } - - private IUmbracoSettingsSection _umbracoSettings; - - public override void SetUp() - { - base.SetUp(); - - //generate new mock settings and assign so we can configure in individual tests - _umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); - SettingsForTests.ConfigureSettings(_umbracoSettings); - } - - /// - /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap - /// and that they are all cached correctly. - /// - [Test] - public void Ensure_Cache_Is_Correct() - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new [] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); - requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) - - var samples = new Dictionary { - { 1046, "/home" }, - { 1173, "/home/sub1" }, - { 1174, "/home/sub1/sub2" }, - { 1176, "/home/sub1/sub-3" }, - { 1177, "/home/sub1/custom-sub-1" }, - { 1178, "/home/sub1/custom-sub-2" }, - { 1175, "/home/sub-2" }, - { 1172, "/test-page" } - }; - - foreach (var sample in samples) - { - var result = umbracoContext.UrlProvider.GetUrl(sample.Key); - Assert.AreEqual(sample.Value, result); - } - - var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); - for (int i = 0; i < 5; i++) - { - var result = umbracoContext.UrlProvider.GetUrl(randomSample.Key); - Assert.AreEqual(randomSample.Value, result); - } - - var cache = umbracoContext.ContentCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(8, cachedRoutes.Count); - - foreach (var sample in samples) - { - Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); - Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); - } - - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); - } - - // test hideTopLevelNodeFromPath false - [TestCase(1046, "/home/")] - [TestCase(1173, "/home/sub1/")] - [TestCase(1174, "/home/sub1/sub2/")] - [TestCase(1176, "/home/sub1/sub-3/")] - [TestCase(1177, "/home/sub1/custom-sub-1/")] - [TestCase(1178, "/home/sub1/custom-sub-2/")] - [TestCase(1175, "/home/sub-2/")] - [TestCase(1172, "/test-page/")] - public void Get_Nice_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var result = umbracoContext.UrlProvider.GetUrl(nodeId); - Assert.AreEqual(niceUrlMatch, result); - } - - // no need for umbracoUseDirectoryUrls test = should be handled by UriUtilityTests - - // test hideTopLevelNodeFromPath true - [TestCase(1046, "/")] - [TestCase(1173, "/sub1/")] - [TestCase(1174, "/sub1/sub2/")] - [TestCase(1176, "/sub1/sub-3/")] - [TestCase(1177, "/sub1/custom-sub-1/")] - [TestCase(1178, "/sub1/custom-sub-2/")] - [TestCase(1175, "/sub-2/")] - [TestCase(1172, "/test-page/")] // not hidden because not first root - public void Get_Nice_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); - SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var result = umbracoContext.UrlProvider.GetUrl(nodeId); - Assert.AreEqual(niceUrlMatch, result); - } - - [Test] - public void Get_Nice_Url_Relative_Or_Absolute() - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - - - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; - Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); - } - - [Test] - public void Get_Nice_Url_Unpublished() - { - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); - SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] - { - new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - //mock the Umbraco settings that we need - var requestMock = Mock.Get(_umbracoSettings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; - - Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); - } - } -} diff --git a/src/Umbraco.Tests/Routing/RoutesCacheTests.cs b/src/Umbraco.Tests/Routing/RoutesCacheTests.cs index 4b04ebb50d..68e80076dd 100644 --- a/src/Umbraco.Tests/Routing/RoutesCacheTests.cs +++ b/src/Umbraco.Tests/Routing/RoutesCacheTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Routing { //var routingContext = GetRoutingContext("/test", 1111); var umbracoContext = GetUmbracoContext("/test", 0); - var cache = umbracoContext.PublishedShapshot.Content as PublishedContentCache; + var cache = umbracoContext.PublishedSnapshot.Content as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); // fixme not sure? diff --git a/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs b/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs index 3b149280bd..d9760f58c1 100644 --- a/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs +++ b/src/Umbraco.Tests/Routing/SiteDomainHelperTests.cs @@ -21,6 +21,9 @@ namespace Umbraco.Tests.Routing SiteDomainHelper.Clear(); // assuming this works! } + private static CultureInfo CultureFr = CultureInfo.GetCultureInfo("fr-fr"); + private static CultureInfo CultureUk = CultureInfo.GetCultureInfo("en-uk"); + [Test] public void AddSites() { @@ -188,38 +191,38 @@ namespace Umbraco.Tests.Routing var current = new Uri("https://www.domain1.com/foo/bar"); var domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), + new Domain(1, "domain2.com", -1, CultureFr, false), + new Domain(1, "domain1.com", -1, CultureUk, false), }); - var output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + var output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); // will pick it all right current = new Uri("https://domain1.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "https://domain2.com", -1, CultureInfo.CurrentCulture, false) + new Domain(1, "https://domain1.com", -1, CultureFr, false), + new Domain(1, "https://domain2.com", -1, CultureUk, false) }); - output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); current = new Uri("https://domain1.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false) + new Domain(1, "https://domain1.com", -1, CultureFr, false), + new Domain(1, "https://domain4.com", -1, CultureUk, false) }); - output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain1.com/", output); current = new Uri("https://domain4.com/foo/bar"); domainAndUris = DomainAndUris(current, new[] { - new Domain(1, "https://domain1.com", -1, CultureInfo.CurrentCulture, false), - new Domain(1, "https://domain4.com", -1, CultureInfo.CurrentCulture, false) + new Domain(1, "https://domain1.com", -1, CultureFr, false), + new Domain(1, "https://domain4.com", -1, CultureUk, false) }); - output = helper.MapDomain(current, domainAndUris).Uri.ToString(); + output = helper.MapDomain(domainAndUris, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("https://domain4.com/", output); } @@ -241,22 +244,22 @@ namespace Umbraco.Tests.Routing // so we'll get current // var current = new Uri("http://domain1.com/foo/bar"); - var output = helper.MapDomain(current, new[] + var output = helper.MapDomain(new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), current), - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), - }).Uri.ToString(); + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureFr, false), current), + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), + }, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("http://domain1.com/", output); // current is a site1 uri, domains do not contain current // so we'll get the corresponding site1 domain // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomain(current, new[] + output = helper.MapDomain(new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current), - new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false), current) - }).Uri.ToString(); + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureFr, false), current), + new DomainAndUri(new Domain(1, "domain2.net", -1, CultureUk, false), current) + }, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); // current is a site1 uri, domains do not contain current @@ -264,11 +267,11 @@ namespace Umbraco.Tests.Routing // order does not matter // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomain(current, new[] + output = helper.MapDomain(new[] { - new DomainAndUri(new Domain(1, "domain2.net", -1, CultureInfo.CurrentCulture, false), current), - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current) - }).Uri.ToString(); + new DomainAndUri(new Domain(1, "domain2.net", -1, CultureFr, false), current), + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureUk, false), current) + }, current, CultureFr.Name, CultureFr.Name).Uri.ToString(); Assert.AreEqual("http://domain1.net/", output); } @@ -291,14 +294,14 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains contains current // var current = new Uri("http://domain1.com/foo/bar"); - var output = helper.MapDomains(current, new[] + var output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), current), // no: current + what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureFr, false), current), // no: current + what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(1, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -306,14 +309,14 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains does not contain current // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomains(current, new[] + output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current), // no: what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureFr, false), current), // no: what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(1, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -324,15 +327,15 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains contains current // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomains(current, new[] + output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.com", -1, CultureInfo.CurrentCulture, false), current), // no: current + what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.com", -1, CultureFr, false), current), // no: current + what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain3.org", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(3, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); @@ -342,15 +345,15 @@ namespace Umbraco.Tests.Routing // current is a site1 uri, domains does not contain current // current = new Uri("http://domain1.com/foo/bar"); - output = helper.MapDomains(current, new[] + output = helper.MapDomains(new[] { - new DomainAndUri(new Domain(1, "domain1.net", -1, CultureInfo.CurrentCulture, false), current), // no: what MapDomain would pick - new DomainAndUri(new Domain(1, "domain2.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain3.com", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain3.org", -1, CultureInfo.CurrentCulture, false), current), // yes: bound site - new DomainAndUri(new Domain(1, "domain4.com", -1, CultureInfo.CurrentCulture, false), current), // no: not same site - new DomainAndUri(new Domain(1, "domain1.org", -1, CultureInfo.CurrentCulture, false), current), // yes: same site (though bogus setup) - }, true).ToArray(); + new DomainAndUri(new Domain(1, "domain1.net", -1, CultureFr, false), current), // no: what MapDomain would pick + new DomainAndUri(new Domain(1, "domain2.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain3.com", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain3.org", -1, CultureUk, false), current), // yes: bound site + new DomainAndUri(new Domain(1, "domain4.com", -1, CultureUk, false), current), // no: not same site + new DomainAndUri(new Domain(1, "domain1.org", -1, CultureUk, false), current), // yes: same site (though bogus setup) + }, current, true, CultureFr.Name, CultureFr.Name).ToArray(); Assert.AreEqual(3, output.Count()); Assert.Contains("http://domain1.org/", output.Select(d => d.Uri.ToString()).ToArray()); diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index 23a0773f71..703179b184 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -10,7 +10,8 @@ using Umbraco.Web; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Sync; - +using Umbraco.Core.Configuration.UmbracoSettings; + namespace Umbraco.Tests.Routing { [TestFixture] @@ -24,14 +25,16 @@ namespace Umbraco.Tests.Routing base.SetUp(); //create the module - _module = new UmbracoModule(); - - // test - _module.Logger = Mock.Of(); - var runtime = new RuntimeState(_module.Logger, new Lazy(), new Lazy()); + _module = new UmbracoModule + { + GlobalSettings = TestObjects.GetGlobalSettings(), + Logger = Mock.Of() + }; + var runtime = new RuntimeState(_module.Logger, new Lazy(), new Lazy(), Mock.Of(), _module.GlobalSettings); + _module.Runtime = runtime; runtime.Level = RuntimeLevel.Run; - _module.GlobalSettings = TestObjects.GetGlobalSettings(); + //SettingsForTests.ReservedPaths = "~/umbraco,~/install/"; //SettingsForTests.ReservedUrls = "~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx,~/VSEnterpriseHelper.axd"; diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs new file mode 100644 index 0000000000..185812002d --- /dev/null +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -0,0 +1,358 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; +using Umbraco.Tests.Testing; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] + public class UrlProviderTests : BaseWebTest + { + protected override void Compose() + { + base.Compose(); + Container.Register(); + } + + private IUmbracoSettingsSection _umbracoSettings; + + public override void SetUp() + { + base.SetUp(); + + //generate new mock settings and assign so we can configure in individual tests + _umbracoSettings = SettingsForTests.GenerateMockUmbracoSettings(); + SettingsForTests.ConfigureSettings(_umbracoSettings); + } + + /// + /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap + /// and that they are all cached correctly. + /// + [Test] + public void Ensure_Cache_Is_Correct() + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings: globalSettings.Object); + + var requestHandlerMock = Mock.Get(_umbracoSettings.RequestHandler); + requestHandlerMock.Setup(x => x.AddTrailingSlash).Returns(false);// (cached routes have none) + + var samples = new Dictionary { + { 1046, "/home" }, + { 1173, "/home/sub1" }, + { 1174, "/home/sub1/sub2" }, + { 1176, "/home/sub1/sub-3" }, + { 1177, "/home/sub1/custom-sub-1" }, + { 1178, "/home/sub1/custom-sub-2" }, + { 1175, "/home/sub-2" }, + { 1172, "/test-page" } + }; + + foreach (var sample in samples) + { + var result = umbracoContext.UrlProvider.GetUrl(sample.Key); + Assert.AreEqual(sample.Value, result); + } + + var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); + for (int i = 0; i < 5; i++) + { + var result = umbracoContext.UrlProvider.GetUrl(randomSample.Key); + Assert.AreEqual(randomSample.Value, result); + } + + var cache = umbracoContext.ContentCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(8, cachedRoutes.Count); + + foreach (var sample in samples) + { + Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); + Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); + } + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + } + + // test hideTopLevelNodeFromPath false + [TestCase(1046, "/home/")] + [TestCase(1173, "/home/sub1/")] + [TestCase(1174, "/home/sub1/sub2/")] + [TestCase(1176, "/home/sub1/sub-3/")] + [TestCase(1177, "/home/sub1/custom-sub-1/")] + [TestCase(1178, "/home/sub1/custom-sub-2/")] + [TestCase(1175, "/home/sub-2/")] + [TestCase(1172, "/test-page/")] + public void Get_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings: globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var result = umbracoContext.UrlProvider.GetUrl(nodeId); + Assert.AreEqual(niceUrlMatch, result); + } + + // no need for umbracoUseDirectoryUrls test = should be handled by UriUtilityTests + + // test hideTopLevelNodeFromPath true + [TestCase(1046, "/")] + [TestCase(1173, "/sub1/")] + [TestCase(1174, "/sub1/sub2/")] + [TestCase(1176, "/sub1/sub-3/")] + [TestCase(1177, "/sub1/custom-sub-1/")] + [TestCase(1178, "/sub1/custom-sub-2/")] + [TestCase(1175, "/sub-2/")] + [TestCase(1172, "/test-page/")] // not hidden because not first root + public void Get_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(true); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoContext = GetUmbracoContext("/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings: globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var result = umbracoContext.UrlProvider.GetUrl(nodeId); + Assert.AreEqual(niceUrlMatch, result); + } + + [Test] + public void Get_Url_For_Culture_Variant_Without_Domains_Non_Current_Url() + { + const string currentUri = "http://example.us/test"; + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var publishedContentCache = new Mock(); + publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) + .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + + var domainCache = new Mock(); + domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) + .Returns((int contentId, bool includeWildcards) => Enumerable.Empty()); + + var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); + + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) + .Returns(snapshot); + + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + urlProviders: new[] { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, + globalSettings: globalSettings.Object, + snapshotService: snapshotService.Object); + + //even though we are asking for a specific culture URL, there are no domains assigned so all that can be returned is a normal relative url. + var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + + Assert.AreEqual("/home/test-fr/", url); + } + + /// + /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is the culture specific domain + /// + [Test] + public void Get_Url_For_Culture_Variant_With_Current_Url() + { + const string currentUri = "http://example.fr/test"; + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var publishedContentCache = new Mock(); + publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) + .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + + var domainCache = new Mock(); + domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) + .Returns((int contentId, bool includeWildcards) => + { + if (contentId != 9876) return Enumerable.Empty(); + return new[] + { + new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false), //default + new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false) + }; + }); + domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); + + var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); + + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) + .Returns(snapshot); + + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + urlProviders: new[] { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, + globalSettings: globalSettings.Object, + snapshotService: snapshotService.Object); + + + var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + + Assert.AreEqual("/home/test-fr/", url); + } + + /// + /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is not the culture specific domain + /// + [Test] + public void Get_Url_For_Culture_Variant_Non_Current_Url() + { + const string currentUri = "http://example.us/test"; + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var publishedContentCache = new Mock(); + publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) + .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard + + var domainCache = new Mock(); + domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) + .Returns((int contentId, bool includeWildcards) => + { + if (contentId != 9876) return Enumerable.Empty(); + return new[] + { + new Domain(2, "example.us", 9876, CultureInfo.GetCultureInfo("en-US"), false), //default + new Domain(3, "example.fr", 9876, CultureInfo.GetCultureInfo("fr-FR"), false) + }; + }); + domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); + + var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); + + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) + .Returns(snapshot); + + var umbracoContext = GetUmbracoContext(currentUri, umbracoSettings: _umbracoSettings, + urlProviders: new[] { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, + globalSettings: globalSettings.Object, + snapshotService: snapshotService.Object); + + + var url = umbracoContext.UrlProvider.GetUrl(1234, "fr-FR"); + + //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain + Assert.AreEqual("http://example.fr/home/test-fr/", url); + } + + [Test] + public void Get_Url_Relative_Or_Absolute() + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings: globalSettings.Object); + + Assert.AreEqual("/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", umbracoContext.UrlProvider.GetUrl(1177)); + } + + [Test] + public void Get_Url_Unpublished() + { + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); + SettingsForTests.ConfigureSettings(globalSettings.Object); + + var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, urlProviders: new[] + { + new DefaultUrlProvider(_umbracoSettings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings: globalSettings.Object); + + //mock the Umbraco settings that we need + var requestMock = Mock.Get(_umbracoSettings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + + Assert.AreEqual("#", umbracoContext.UrlProvider.GetUrl(999999)); + } + } +} diff --git a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs similarity index 99% rename from src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs rename to src/Umbraco.Tests/Routing/UrlRoutesTests.cs index 2c44a26117..9b291249c9 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutesTests.cs @@ -13,7 +13,7 @@ namespace Umbraco.Tests.Routing // the quirks due to hideTopLevelFromPath and backward compatibility. [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class NiceUrlRoutesTests : TestWithDatabaseBase + public class UrlRoutesTests : TestWithDatabaseBase { #region Test Setup diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs similarity index 95% rename from src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs rename to src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs index fa223420f2..b064e9685c 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs @@ -1,423 +1,423 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moq; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.PublishedCache.XmlPublishedCache; -using Umbraco.Web.Routing; -using Umbraco.Core.Composing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - public class NiceUrlsProviderWithDomainsTests : UrlRoutingTestBase - { - protected override void Compose() - { - base.Compose(); - - Container.RegisterSingleton(_ => Mock.Of()); - Container.Register(); - } - - void SetDomains1() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains2() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains3() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains4() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - } - - void SetDomains5() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - } - - protected override string GetXmlContent(int templateId) - { - return @" - - -]> - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - This is some content]]> - - - - - - - - - - - - - - - - -"; - } - - // with one simple domain "domain1.com" - // basic tests - [TestCase(1001, "http://domain1.com", false, "/")] - [TestCase(10011, "http://domain1.com", false, "/1001-1/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - // absolute tests - [TestCase(1001, "http://domain1.com", true, "http://domain1.com/")] - [TestCase(10011, "http://domain1.com", true, "http://domain1.com/1001-1/")] - // different current tests - [TestCase(1001, "http://domain2.com", false, "http://domain1.com/")] - [TestCase(10011, "http://domain2.com", false, "http://domain1.com/1001-1/")] - [TestCase(1001, "https://domain1.com", false, "/")] - [TestCase(10011, "https://domain1.com", false, "/1001-1/")] - public void Get_Nice_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Core.Composing; + +namespace Umbraco.Tests.Routing +{ + [TestFixture] + public class UrlsProviderWithDomainsTests : UrlRoutingTestBase + { + protected override void Compose() + { + base.Compose(); + + Container.RegisterSingleton(_ => Mock.Of()); + Container.Register(); + } + + void SetDomains1() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains2() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains3() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains4() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); + } + + void SetDomains5() + { + SetupDomainServiceMock(new[] + { + new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); + } + + protected override string GetXmlContent(int templateId) + { + return @" + + +]> + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + This is some content]]> + + + + + + + + + + + + + + + + +"; + } + + // with one simple domain "domain1.com" + // basic tests + [TestCase(1001, "http://domain1.com", false, "/")] + [TestCase(10011, "http://domain1.com", false, "/1001-1/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + // absolute tests + [TestCase(1001, "http://domain1.com", true, "http://domain1.com/")] + [TestCase(10011, "http://domain1.com", true, "http://domain1.com/1001-1/")] + // different current tests + [TestCase(1001, "http://domain2.com", false, "http://domain1.com/")] + [TestCase(10011, "http://domain2.com", false, "http://domain1.com/1001-1/")] + [TestCase(1001, "https://domain1.com", false, "/")] + [TestCase(10011, "https://domain1.com", false, "/1001-1/")] + public void Get_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains1(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - // with one complete domain "http://domain1.com/foo" - // basic tests - [TestCase(1001, "http://domain1.com", false, "/foo/")] - [TestCase(10011, "http://domain1.com", false, "/foo/1001-1/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - // absolute tests - [TestCase(1001, "http://domain1.com", true, "http://domain1.com/foo/")] - [TestCase(10011, "http://domain1.com", true, "http://domain1.com/foo/1001-1/")] - // different current tests - [TestCase(1001, "http://domain2.com", false, "http://domain1.com/foo/")] - [TestCase(10011, "http://domain2.com", false, "http://domain1.com/foo/1001-1/")] - [TestCase(1001, "https://domain1.com", false, "http://domain1.com/foo/")] - [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] - public void Get_Nice_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains1(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + // with one complete domain "http://domain1.com/foo" + // basic tests + [TestCase(1001, "http://domain1.com", false, "/foo/")] + [TestCase(10011, "http://domain1.com", false, "/foo/1001-1/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + // absolute tests + [TestCase(1001, "http://domain1.com", true, "http://domain1.com/foo/")] + [TestCase(10011, "http://domain1.com", true, "http://domain1.com/foo/1001-1/")] + // different current tests + [TestCase(1001, "http://domain2.com", false, "http://domain1.com/foo/")] + [TestCase(10011, "http://domain2.com", false, "http://domain1.com/foo/1001-1/")] + [TestCase(1001, "https://domain1.com", false, "http://domain1.com/foo/")] + [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] + public void Get_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains2(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - // with one domain, not at root - [TestCase(1001, "http://domain1.com", false, "/1001/")] - [TestCase(10011, "http://domain1.com", false, "/")] - [TestCase(100111, "http://domain1.com", false, "/1001-1-1/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - public void Get_Nice_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains2(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + // with one domain, not at root + [TestCase(1001, "http://domain1.com", false, "/1001/")] + [TestCase(10011, "http://domain1.com", false, "/")] + [TestCase(100111, "http://domain1.com", false, "/1001-1-1/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + public void Get_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains3(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - // with nested domains - [TestCase(1001, "http://domain1.com", false, "/")] - [TestCase(10011, "http://domain1.com", false, "/en/")] - [TestCase(100111, "http://domain1.com", false, "/en/1001-1-1/")] - [TestCase(10012, "http://domain1.com", false, "/fr/")] - [TestCase(100121, "http://domain1.com", false, "/fr/1001-2-1/")] - [TestCase(10013, "http://domain1.com", false, "/1001-3/")] - [TestCase(1002, "http://domain1.com", false, "/1002/")] - [TestCase(1003, "http://domain3.com", false, "/")] - [TestCase(10031, "http://domain3.com", false, "/en/")] - [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] - public void Get_Nice_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains3(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + // with nested domains + [TestCase(1001, "http://domain1.com", false, "/")] + [TestCase(10011, "http://domain1.com", false, "/en/")] + [TestCase(100111, "http://domain1.com", false, "/en/1001-1-1/")] + [TestCase(10012, "http://domain1.com", false, "/fr/")] + [TestCase(100121, "http://domain1.com", false, "/fr/1001-2-1/")] + [TestCase(10013, "http://domain1.com", false, "/1001-3/")] + [TestCase(1002, "http://domain1.com", false, "/1002/")] + [TestCase(1003, "http://domain3.com", false, "/")] + [TestCase(10031, "http://domain3.com", false, "/en/")] + [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] + public void Get_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains4(); - - var currentUri = new Uri(currentUrl); - var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); - Assert.AreEqual(expected, result); - } - - [Test] - public void Get_Nice_Url_DomainsAndCache() - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var request = Mock.Get(settings.RequestHandler); - request.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains4(); + + var currentUri = new Uri(currentUrl); + var result = umbracoContext.UrlProvider.GetUrl(nodeId, currentUri, absolute); + Assert.AreEqual(expected, result); + } + + [Test] + public void Get_Url_DomainsAndCache() + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var request = Mock.Get(settings.RequestHandler); + request.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains4(); - - string ignore; - ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false); - ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); - - var cache = umbracoContext.ContentCache as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(7, cachedRoutes.Count); - - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); - - CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); - CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); - CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); - CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); - CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); - CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); - CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); - - // use the cache - Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false)); - Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false)); - - Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false)); - } - - private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) - { - Assert.IsTrue(routes.ContainsKey(id)); - Assert.AreEqual(route, routes[id]); - Assert.IsFalse(ids.ContainsKey(route)); - } - - [Test] - public void Get_Nice_Url_Relative_Or_Absolute() - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - var requestMock = Mock.Get(settings.RequestHandler); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains4(); + + string ignore; + ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain2.com"), false); + ignore = umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain2.com"), false); + + var cache = umbracoContext.ContentCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(7, cachedRoutes.Count); + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + + CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); + CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); + CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); + CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); + CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); + CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); + CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); + + // use the cache + Assert.AreEqual("/", umbracoContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/en/", umbracoContext.UrlProvider.GetUrl(10011, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/fr/", umbracoContext.UrlProvider.GetUrl(10012, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/1001-3/", umbracoContext.UrlProvider.GetUrl(10013, new Uri("http://domain1.com"), false)); + Assert.AreEqual("/1002/", umbracoContext.UrlProvider.GetUrl(1002, new Uri("http://domain1.com"), false)); + + Assert.AreEqual("http://domain1.com/fr/1001-2-1/", umbracoContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false)); + } + + private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) + { + Assert.IsTrue(routes.ContainsKey(id)); + Assert.AreEqual(route, routes[id]); + Assert.IsFalse(ids.ContainsKey(route)); + } + + [Test] + public void Get_Url_Relative_Or_Absolute() + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + var requestMock = Mock.Get(settings.RequestHandler); + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains4(); - - Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - - Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - - requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); - umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; - - Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); - } - - [Test] - public void Get_Nice_Url_Alternate() - { - var settings = SettingsForTests.GenerateMockUmbracoSettings(); - - var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container - globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); - globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains + + var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains4(); + + Assert.AreEqual("/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + + Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + + requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); + umbracoContext.UrlProvider.Mode = UrlProviderMode.Absolute; + + Assert.AreEqual("http://domain1.com/en/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", umbracoContext.UrlProvider.GetUrl(100311)); + } + + [Test] + public void Get_Url_Alternate() + { + var settings = SettingsForTests.GenerateMockUmbracoSettings(); + + var globalSettings = Mock.Get(TestObjects.GetGlobalSettings()); //this will modify the IGlobalSettings instance stored in the container + globalSettings.Setup(x => x.UseDirectoryUrls).Returns(true); + globalSettings.Setup(x => x.HideTopLevelNodeFromPath).Returns(false); // ignored w/domains SettingsForTests.ConfigureSettings(globalSettings.Object); - - var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] - { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) - }, globalSettings:globalSettings.Object); - - SetDomains5(); - - var url = umbracoContext.UrlProvider.GetUrl(100111, true); - Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); - - var result = umbracoContext.UrlProvider.GetOtherUrls(100111).ToArray(); - - Assert.AreEqual(2, result.Count()); - Assert.IsTrue(result.Contains("http://domain1a.com/en/1001-1-1/")); - Assert.IsTrue(result.Contains("http://domain1b.com/en/1001-1-1/")); - } - } -} + + var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, umbracoSettings: settings, urlProviders: new[] + { + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) + }, globalSettings:globalSettings.Object); + + SetDomains5(); + + var url = umbracoContext.UrlProvider.GetUrl(100111, true); + Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); + + var result = umbracoContext.UrlProvider.GetOtherUrls(100111).ToArray(); + + Assert.AreEqual(2, result.Count()); + Assert.IsTrue(result.Contains("http://domain1a.com/en/1001-1-1/")); + Assert.IsTrue(result.Contains("http://domain1b.com/en/1001-1-1/")); + } + } +} diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 41a7bf730d..0996aaab03 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -44,7 +44,7 @@ namespace Umbraco.Tests.Routing // get the nice url for 100111 var umbracoContext = GetUmbracoContext(url, 9999, umbracoSettings: settings, urlProviders: new [] { - new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object) + new DefaultUrlProvider(settings.RequestHandler, Logger, globalSettings.Object, new SiteDomainHelper()) }, globalSettings:globalSettings.Object); Assert.AreEqual("http://domain2.com/1001-1-1/", umbracoContext.UrlProvider.GetUrl(100111, true)); @@ -62,7 +62,7 @@ namespace Umbraco.Tests.Routing Assert.IsTrue(frequest.HasDomain); // check that it's been routed - var lookup = new ContentFinderByNiceUrl(Logger); + var lookup = new ContentFinderByUrl(Logger); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(100111, frequest.PublishedContent.Id); diff --git a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs index e1c856297d..38990405c4 100644 --- a/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs +++ b/src/Umbraco.Tests/Scheduling/BackgroundTaskRunnerTests.cs @@ -441,7 +441,7 @@ namespace Umbraco.Tests.Scheduling tManager.TaskCompleted += (sender, task) => tasks[task.Task].Set(); //execute first batch - tasks.ForEach(t => tManager.Add(t.Key)); + foreach (var t in tasks) tManager.Add(t.Key); //wait for all ITasks to complete WaitHandle.WaitAll(tasks.Values.Select(x => (WaitHandle)x).ToArray()); @@ -455,7 +455,7 @@ namespace Umbraco.Tests.Scheduling Thread.Sleep(2000); tasks = getTasks(); - tasks.ForEach(t => tManager.Add(t.Key)); + foreach (var t in tasks) tManager.Add(t.Key); //wait for all ITasks to complete WaitHandle.WaitAll(tasks.Values.Select(x => (WaitHandle)x).ToArray()); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 585a943416..cc9813cdbd 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -90,7 +90,9 @@ namespace Umbraco.Tests.Scoping publishedSnapshotAccessor, Logger, ScopeProvider, - documentRepository, mediaRepository, memberRepository, Container.GetInstance()); + documentRepository, mediaRepository, memberRepository, + SystemDefaultCultureProvider, + Container.GetInstance(), new SiteDomainHelper()); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) @@ -100,13 +102,15 @@ namespace Umbraco.Tests.Scoping var httpContext = GetHttpContextFactory(url, routeData).HttpContext; + var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( httpContext, service, - new WebSecurity(httpContext, Current.Services.UserService, TestObjects.GetGlobalSettings()), + new WebSecurity(httpContext, Current.Services.UserService, globalSettings), umbracoSettings ?? SettingsForTests.GetDefaultUmbracoSettings(), urlProviders ?? Enumerable.Empty(), - TestObjects.GetGlobalSettings()); + globalSettings, + Mock.Of()); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; @@ -144,7 +148,7 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); scope.Complete(); } @@ -159,7 +163,7 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { item.Name = "changed"; - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); if (complete) diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index c2ec892770..48bbdb1e22 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -334,7 +334,7 @@ namespace Umbraco.Tests.Scoping : base(false) { } - protected override void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) + protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) { throw new NotImplementedException(); } diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 258056f198..9de2012dce 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -123,10 +123,10 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); // should create an xml clone item.Name = "changed"; - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); // should re-use the xml clone // this should never change @@ -230,13 +230,13 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - item.PublishValues(); + item.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(item); for (var i = 0; i < count; i++) { var temp = new Content("content_" + i, -1, contentType); - temp.PublishValues(); + temp.TryPublishValues(); Current.Services.ContentService.SaveAndPublish(temp); ids[i] = temp.Id; } @@ -299,7 +299,7 @@ namespace Umbraco.Tests.Scoping : base(false) { } - protected override void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) + protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) { throw new NotImplementedException(); } diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 1b182024c3..51d6a7fa7d 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Core.Services; using Umbraco.Tests.Testing; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -26,11 +27,13 @@ namespace Umbraco.Tests.Security //should force app ctx to show not-configured ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); + var globalSettings = TestObjects.GetGlobalSettings(); var umbracoContext = new UmbracoContext( Mock.Of(), Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), new List(),TestObjects.GetGlobalSettings()); + new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), + TestObjects.GetUmbracoSettings(), new List(),globalSettings, + Mock.Of()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Install); var mgr = new BackOfficeCookieManager( @@ -44,11 +47,13 @@ namespace Umbraco.Tests.Security [Test] public void ShouldAuthenticateRequest_When_Configured() { + var globalSettings = TestObjects.GetGlobalSettings(); var umbCtx = new UmbracoContext( Mock.Of(), Mock.Of(), - new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), - TestObjects.GetUmbracoSettings(), new List(), TestObjects.GetGlobalSettings()); + new WebSecurity(Mock.Of(), Current.Services.UserService, globalSettings), + TestObjects.GetUmbracoSettings(), new List(), globalSettings, + Mock.Of()); var runtime = Mock.Of(x => x.Level == RuntimeLevel.Run); var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.UmbracoContext == umbCtx), runtime, TestObjects.GetGlobalSettings()); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 79929ff29d..8bb5f1bc9e 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -182,16 +182,16 @@ namespace Umbraco.Tests.Services { var contentService = ServiceContext.ContentService; var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); - root.PublishValues(); + root.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(root).Success); var content = contentService.CreateAndSave("Test", -1, "umbTextpage", 0); - content.PublishValues(); + content.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(content).Success); var hierarchy = CreateContentHierarchy().OrderBy(x => x.Level).ToArray(); contentService.Save(hierarchy, 0); foreach (var c in hierarchy) { - c.PublishValues(); + c.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(c).Success); } @@ -241,7 +241,7 @@ namespace Umbraco.Tests.Services // Assert - content.PublishValues(); + content.TryPublishValues(); Assert.IsTrue(contentService.SaveAndPublish(content).Success); } @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Services for (var i = 0; i < 20; i++) { content.SetValue("bodyText", "hello world " + Guid.NewGuid()); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); } @@ -395,12 +395,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "plus" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // verify @@ -436,7 +436,7 @@ namespace Umbraco.Tests.Services allTags = tagService.GetAllContentTags(); Assert.AreEqual(4, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); Assert.IsTrue(content1.Published); @@ -466,12 +466,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // verify @@ -507,9 +507,9 @@ namespace Umbraco.Tests.Services allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // tags are back @@ -538,12 +538,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "plus" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", content1.Id); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); // verify @@ -578,7 +578,7 @@ namespace Umbraco.Tests.Services allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); Assert.IsTrue(content1.Published); @@ -616,12 +616,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); contentService.Unpublish(content1); @@ -637,7 +637,7 @@ namespace Umbraco.Tests.Services var allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); tags = tagService.GetTagsForEntity(content2.Id); @@ -663,12 +663,12 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "hello", "world", "some", "tags", "bam" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", content1); content2.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); contentService.Unpublish(content1); @@ -684,7 +684,7 @@ namespace Umbraco.Tests.Services var allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); tags = tagService.GetTagsForEntity(content2.Id); @@ -768,7 +768,7 @@ namespace Umbraco.Tests.Services // create a content with tags and publish var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // edit tags and save @@ -808,7 +808,7 @@ namespace Umbraco.Tests.Services // Act content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Assert @@ -839,12 +839,12 @@ namespace Umbraco.Tests.Services contentTypeService.Save(contentType); var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Act content.AssignTags("tags", new[] { "another", "world" }, merge: true); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Assert @@ -875,12 +875,12 @@ namespace Umbraco.Tests.Services contentTypeService.Save(contentType); var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.AssignTags("tags", new[] { "hello", "world", "some", "tags" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Act content.RemoveTags("tags", new[] { "some", "world" }); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Assert @@ -1063,7 +1063,7 @@ namespace Umbraco.Tests.Services var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2); Assert.IsFalse(parent.Published); - parent.PublishValues(); + parent.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(parent); // publishing parent, so Text Page 2 can be updated. var content = contentService.GetById(NodeDto.NodeIdSeed + 4); @@ -1076,7 +1076,7 @@ namespace Umbraco.Tests.Services content.Name = "Text Page 2 Updated"; content.SetValue("author", "Jane Doe"); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // publishes the current version, creates a version var version2 = content.VersionId; @@ -1084,7 +1084,7 @@ namespace Umbraco.Tests.Services content.Name = "Text Page 2 ReUpdated"; content.SetValue("author", "Bob Hope"); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // publishes again, creates a version var version3 = content.VersionId; @@ -1151,11 +1151,11 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var root = contentService.GetById(NodeDto.NodeIdSeed + 2); - root.PublishValues(); + root.TryPublishValues(); contentService.SaveAndPublish(root); var content = contentService.GetById(NodeDto.NodeIdSeed + 4); content.ExpireDate = DateTime.Now.AddSeconds(1); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // Act @@ -1205,7 +1205,7 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var content = contentService.GetById(NodeDto.NodeIdSeed + 2); - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); using (var scope = ScopeProvider.CreateScope()) @@ -1235,7 +1235,7 @@ namespace Umbraco.Tests.Services var content = contentService.GetById(NodeDto.NodeIdSeed + 2); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1249,7 +1249,7 @@ namespace Umbraco.Tests.Services // Arrange var contentService = ServiceContext.ContentService; var parent = contentService.Create("parent", -1, "umbTextpage"); - parent.PublishValues(); + parent.TryPublishValues(); contentService.SaveAndPublish(parent); var content = contentService.Create("child", parent, "umbTextpage"); contentService.Save(content); @@ -1275,7 +1275,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual("Home", content.Name); content.Name = "foo"; - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); Assert.That(published.Success, Is.True); @@ -1316,7 +1316,7 @@ namespace Umbraco.Tests.Services var parent = contentService.GetById(parentId); - var parentCanPublishValues = parent.PublishValues(); + var parentCanPublishValues = parent.TryPublishValues(); var parentPublished = contentService.SaveAndPublish(parent); // parent can publish values @@ -1325,7 +1325,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(parentPublished.Success); Assert.IsTrue(parent.Published); - var contentCanPublishValues = content.PublishValues(); + var contentCanPublishValues = content.TryPublishValues(); var contentPublished = contentService.SaveAndPublish(content); // content cannot publish values because they are invalid @@ -1401,11 +1401,11 @@ namespace Umbraco.Tests.Services contentService.Save(content); var parent = contentService.GetById(NodeDto.NodeIdSeed + 2); - parent.PublishValues(); + parent.TryPublishValues(); var parentPublished = contentService.SaveAndPublish(parent, 0);//Publish root Home node to enable publishing of 'NodeDto.NodeIdSeed + 3' // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1424,11 +1424,11 @@ namespace Umbraco.Tests.Services contentService.Save(content, 0); var parent = contentService.GetById(NodeDto.NodeIdSeed + 2); - parent.PublishValues(); + parent.TryPublishValues(); var parentPublished = contentService.SaveAndPublish(parent, 0);//Publish root Home node to enable publishing of 'NodeDto.NodeIdSeed + 3' // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1461,7 +1461,7 @@ namespace Umbraco.Tests.Services var content = contentService.GetById(NodeDto.NodeIdSeed + 5); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1479,7 +1479,7 @@ namespace Umbraco.Tests.Services content.SetValue("author", "Barack Obama"); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); // Assert @@ -1504,14 +1504,14 @@ namespace Umbraco.Tests.Services content.SetValue("author", "Barack Obama"); // Act - content.PublishValues(); + content.TryPublishValues(); var published = contentService.SaveAndPublish(content, 0); var childContent = contentService.Create("Child", content.Id, "umbTextpage", 0); // Reset all identity properties childContent.Id = 0; childContent.Path = null; ((Content)childContent).ResetIdentity(); - childContent.PublishValues(); + childContent.TryPublishValues(); var childPublished = contentService.SaveAndPublish(childContent, 0); // Assert @@ -1530,12 +1530,12 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var root = contentService.GetById(NodeDto.NodeIdSeed + 2); - root.PublishValues(); + root.TryPublishValues(); var rootPublished = contentService.SaveAndPublish(root); var content = contentService.GetById(NodeDto.NodeIdSeed + 4); content.Properties["title"].SetValue(content.Properties["title"].GetValue() + " Published"); - content.PublishValues(); + content.TryPublishValues(); var contentPublished = contentService.SaveAndPublish(content); var publishedVersion = content.VersionId; @@ -1835,13 +1835,13 @@ namespace Umbraco.Tests.Services content1.PropertyValues(obj); content1.ResetDirtyProperties(false); ServiceContext.ContentService.Save(content1, 0); - content1.PublishValues(); + content1.TryPublishValues(); Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content1, 0).Success); var content2 = MockedContent.CreateBasicContent(contentType); content2.PropertyValues(obj); content2.ResetDirtyProperties(false); ServiceContext.ContentService.Save(content2, 0); - content2.PublishValues(); + content2.TryPublishValues(); Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content2, 0).Success); var editorGroup = ServiceContext.UserService.GetUserGroupByAlias("editor"); @@ -1999,7 +1999,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual(0, contentTags.Length); // publish - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // now tags have been set (published) @@ -2016,7 +2016,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual(0, copiedTags.Length); // publish - copy.PublishValues(); + copy.TryPublishValues(); contentService.SaveAndPublish(copy); // now tags have been set (published) @@ -2035,7 +2035,7 @@ namespace Umbraco.Tests.Services var parent = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 2); Assert.IsFalse(parent.Published); - parent.PublishValues(); + parent.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(parent); // publishing parent, so Text Page 2 can be updated. var content = contentService.GetById(NodeDto.NodeIdSeed + 4); @@ -2052,7 +2052,7 @@ namespace Umbraco.Tests.Services // non published = edited Assert.IsTrue(content.Edited); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // new version var version2 = content.VersionId; Assert.AreNotEqual(version1, version2); @@ -2077,7 +2077,7 @@ namespace Umbraco.Tests.Services content.Name = "Text Page 2 ReReUpdated"; - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); // new version var version3 = content.VersionId; Assert.AreNotEqual(version2, version3); @@ -2133,7 +2133,7 @@ namespace Umbraco.Tests.Services content = contentService.GetById(content.Id); Assert.AreEqual("Text Page 2 ReReUpdated", content.Name); Assert.AreEqual("Jane Doe", content.GetValue("author")); - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); Assert.IsFalse(content.Edited); content.Name = "Xxx"; @@ -2251,7 +2251,7 @@ namespace Umbraco.Tests.Services Assert.IsFalse(scope.Database.Exists(content.Id)); } - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); using (var scope = ScopeProvider.CreateScope()) @@ -2393,7 +2393,7 @@ namespace Umbraco.Tests.Services // becomes Published, !Edited // creates a new version // can get published property values - content.PublishValues(); + content.TryPublishValues(); contentService.SaveAndPublish(content); Assert.IsTrue(content.Published); @@ -2475,210 +2475,274 @@ namespace Umbraco.Tests.Services } [Test] - public void Can_SaveAndRead_Names() + public void Can_SaveRead_Variations() { var languageService = ServiceContext.LocalizationService; var langFr = new Language("fr-FR"); var langUk = new Language("en-UK"); - var langDe = new Language("de-DE"); + var langDe = new Language("de-DE"); + languageService.Save(langFr); languageService.Save(langUk); - languageService.Save(langDe); + languageService.Save(langDe); var contentTypeService = ServiceContext.ContentTypeService; - - // fixme - // contentType.Variations is InvariantNeutral | CultureNeutral - // propertyType.Variations can only be a subset of contentType.Variations - ie cannot *add* anything - // (at least, we should validate this) - // but then, - // if the contentType supports InvariantNeutral | CultureNeutral, - // the propertyType should support InvariantNeutral, or both, but not solely CultureNeutral? - // but does this mean that CultureNeutral implies InvariantNeutral? - // can a contentType *not* support InvariantNeutral? - - var contentType = contentTypeService.Get("umbTextpage"); + + // fixme + // contentType.Variations is InvariantNeutral | CultureNeutral + // propertyType.Variations can only be a subset of contentType.Variations - ie cannot *add* anything + // (at least, we should validate this) + // but then, + // if the contentType supports InvariantNeutral | CultureNeutral, + // the propertyType should support InvariantNeutral, or both, but not solely CultureNeutral? + // but does this mean that CultureNeutral implies InvariantNeutral? + // can a contentType *not* support InvariantNeutral? + + var contentType = contentTypeService.Get("umbTextpage"); contentType.Variations = ContentVariation.InvariantNeutral | ContentVariation.CultureNeutral; contentType.AddPropertyType(new PropertyType(Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar, "prop") { Variations = ContentVariation.CultureNeutral }); - contentTypeService.Save(contentType); - + contentTypeService.Save(contentType); + var contentService = ServiceContext.ContentService; - var content = contentService.Create("Home US", - 1, "umbTextpage"); - - // act + var content = contentService.Create("Home US", - 1, "umbTextpage"); - content.SetValue("author", "Barack Obama"); - content.SetValue("prop", "value-fr1", langFr.Id); - content.SetValue("prop", "value-uk1", langUk.Id); - content.SetName(langFr.Id, "name-fr"); - content.SetName(langUk.Id, "name-uk"); + // act + + content.SetValue("author", "Barack Obama"); + content.SetValue("prop", "value-fr1", langFr.IsoCode); + content.SetValue("prop", "value-uk1", langUk.IsoCode); + content.SetName(langFr.IsoCode, "name-fr"); + content.SetName(langUk.IsoCode, "name-uk"); contentService.Save(content); - - // content has been saved, - // it has names, but no publishNames, and no published cultures - var content2 = contentService.GetById(content.Id); - - Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetName(langUk.Id)); - - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.IsNull(content2.GetValue("prop", langUk.Id, published: true)); + // content has been saved, + // it has names, but no publishNames, and no published cultures + + var content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US", content2.Name); + Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); + + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.IsNull(content2.GetValue("prop", langUk.IsoCode, published: true)); Assert.IsNull(content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.IsNull(content2.GetPublishName(langUk.Id)); - - Assert.IsTrue(content.IsCultureAvailable(langFr.Id)); - Assert.IsTrue(content.IsCultureAvailable(langUk.Id)); - Assert.IsFalse(content.IsCultureAvailable(langDe.Id)); - - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsFalse(content.IsCulturePublished(langUk.Id)); - - // act + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.IsNull(content2.GetPublishName(langUk.IsoCode)); - content.PublishValues(langFr.Id); - content.PublishValues(langUk.Id); - contentService.SaveAndPublish(content); - - // both FR and UK have been published, - // and content has been published, + // only fr and uk have a name, and are available + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // nothing has been published yet + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false)); + + // not published => must be edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + // act + + content.TryPublishValues(langFr.IsoCode); + content.TryPublishValues(langUk.IsoCode); + contentService.SaveAndPublish(content); + + // both FR and UK have been published, + // and content has been published, // it has names, publishNames, and published cultures content2 = contentService.GetById(content.Id); Assert.AreEqual("Home US", content2.Name); - Assert.AreEqual("name-fr", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetName(langUk.Id)); + Assert.AreEqual("name-fr", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetName(langUk.IsoCode)); Assert.IsNull(content2.PublishName); // we haven't published InvariantNeutral - Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr and uk have been published now + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr and uk, published without changes, not edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); - Assert.IsTrue(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + // note that content and content2 culture published dates might be slightly different due to roundtrip to database + + // act - content.PublishValues(); - contentService.SaveAndPublish(content); - + content.TryPublishValues(); + contentService.SaveAndPublish(content); + // now it has publish name for invariant neutral content2 = contentService.GetById(content.Id); + + Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("Home US", content2.PublishName); - - // act - + + // act + content.SetName(null, "Home US2"); - content.SetName(langFr.Id, "name-fr2"); - content.SetName(langUk.Id, "name-uk2"); + content.SetName(langFr.IsoCode, "name-fr2"); + content.SetName(langUk.IsoCode, "name-uk2"); content.SetValue("author", "Barack Obama2"); - content.SetValue("prop", "value-fr2", langFr.Id); - content.SetValue("prop", "value-uk2", langUk.Id); - contentService.Save(content); - - // content has been saved, + content.SetValue("prop", "value-fr2", langFr.IsoCode); + content.SetValue("prop", "value-uk2", langUk.IsoCode); + contentService.Save(content); + + // content has been saved, // it has updated names, unchanged publishNames, and published cultures content2 = contentService.GetById(content.Id); Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); Assert.AreEqual("Home US", content2.PublishName); - Assert.AreEqual("name-fr", content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); - + Assert.AreEqual("name-fr", content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); + Assert.AreEqual("Barack Obama2", content2.GetValue("author")); Assert.AreEqual("Barack Obama", content2.GetValue("author", published: true)); - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.AreEqual("value-fr1", content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); - Assert.IsTrue(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + // we have changed values so now fr and uk are edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw + // act // cannot just 'save' since we are changing what's published! - - content.ClearPublishedValues(langFr.Id); + + content.ClearPublishedValues(langFr.IsoCode); contentService.SaveAndPublish(content); - - // content has been published, - // the french culture is gone + + // content has been published, + // the french culture is gone content2 = contentService.GetById(content.Id); Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); Assert.AreEqual("Home US", content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode)); + Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr is not published anymore + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // and so, fr has to be edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - // act - - contentService.Unpublish(content); - // content has been unpublished, - // but properties, names, etc. retain their 'published' values so the content - // can be re-published in its exact original state (before being unpublished) - // - // BEWARE! - // in order for a content to be unpublished as a whole, and then republished in - // its exact previous state, properties and names etc. retain their published - // values even though the content is not published - hence many things being - // non-null or true below - always check against content.Published to be sure + // act + + contentService.Unpublish(content); + + // content has been unpublished, + // but properties, names, etc. retain their 'published' values so the content + // can be re-published in its exact original state (before being unpublished) + // + // BEWARE! + // in order for a content to be unpublished as a whole, and then republished in + // its exact previous state, properties and names etc. retain their published + // values even though the content is not published - hence many things being + // non-null or true below - always check against content.Published to be sure + + content2 = contentService.GetById(content.Id); - content2 = contentService.GetById(content.Id); - Assert.IsFalse(content2.Published); Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); Assert.AreEqual("Home US", content2.PublishName); // not null, see note above - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); // not null, see note above + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); // not null, see note above - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); // has value, see note above - - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); // still true, see note above - - // act + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); // has value, see note above - contentService.SaveAndPublish(content); + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // fr is not published anymore - uk still is, see note above + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // and so, fr has to be edited - uk still is + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - // content has been re-published, + + // act + + contentService.SaveAndPublish(content); + + // content has been re-published, // everything is back to what it was before being unpublished content2 = contentService.GetById(content.Id); @@ -2686,20 +2750,76 @@ namespace Umbraco.Tests.Services Assert.IsTrue(content2.Published); Assert.AreEqual("Home US2", content2.Name); - Assert.AreEqual("name-fr2", content2.GetName(langFr.Id)); - Assert.AreEqual("name-uk2", content2.GetName(langUk.Id)); + Assert.AreEqual("name-fr2", content2.GetName(langFr.IsoCode)); + Assert.AreEqual("name-uk2", content2.GetName(langUk.IsoCode)); Assert.AreEqual("Home US", content2.PublishName); - Assert.IsNull(content2.GetPublishName(langFr.Id)); - Assert.AreEqual("name-uk", content2.GetPublishName(langUk.Id)); + Assert.IsNull(content2.GetPublishName(langFr.IsoCode)); + Assert.AreEqual("name-uk", content2.GetPublishName(langUk.IsoCode)); - Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.Id)); - Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.Id)); - Assert.IsNull(content2.GetValue("prop", langFr.Id, published: true)); - Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.Id, published: true)); + Assert.AreEqual("value-fr2", content2.GetValue("prop", langFr.IsoCode)); + Assert.AreEqual("value-uk2", content2.GetValue("prop", langUk.IsoCode)); + Assert.IsNull(content2.GetValue("prop", langFr.IsoCode, published: true)); + Assert.AreEqual("value-uk1", content2.GetValue("prop", langUk.IsoCode, published: true)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // no change, back to published + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // no change, back to published + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw - Assert.IsFalse(content.IsCulturePublished(langFr.Id)); - Assert.IsTrue(content.IsCulturePublished(langUk.Id)); + + // act + + content.TryPublishValues(langUk.IsoCode); + contentService.SaveAndPublish(content); + + content2 = contentService.GetById(content.Id); + + // no change + AssertPerCulture(content, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureAvailable(c), (langFr, true), (langUk, true), (langDe, false)); + + // no change + AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); + + // now, uk is no more edited + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); + + AssertPerCulture(content, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + AssertPerCulture(content2, (x, c) => x.GetDateCulturePublished(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw + + + // act + + content.SetName(langUk.IsoCode, "name-uk3"); + contentService.Save(content); + + content2 = contentService.GetById(content.Id); + + // changing the name = edited! + Assert.IsTrue(content.IsCultureEdited(langUk.IsoCode)); + Assert.IsTrue(content2.IsCultureEdited(langUk.IsoCode)); + } + + private void AssertPerCulture(IContent item, Func getter, params (ILanguage Language, bool Result)[] testCases) + { + foreach (var testCase in testCases) + { + var value = getter(item, testCase.Language.IsoCode); + Assert.AreEqual(testCase.Result, value, $"Expected {testCase.Result} and got {value} for culture {testCase.Language.IsoCode}."); + } } private IEnumerable CreateContentHierarchy() diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 1944516164..c9f8ba52ad 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -88,7 +88,7 @@ namespace Umbraco.Tests.Services var contentType = contentTypes[index]; var contentItem = MockedContent.CreateSimpleContent(contentType, "MyName_" + index + "_" + i, parentId); ServiceContext.ContentService.Save(contentItem); - contentItem.PublishValues(); + contentItem.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(contentItem); parentId = contentItem.Id; @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Services var contentType = contentTypes[index]; var contentItem = MockedContent.CreateSimpleContent(contentType, "MyName_" + index + "_" + i, parentId); ServiceContext.ContentService.Save(contentItem); - contentItem.PublishValues(); + contentItem.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(contentItem); parentId = contentItem.Id; } @@ -225,19 +225,19 @@ namespace Umbraco.Tests.Services var root = MockedContent.CreateSimpleContent(contentType1, "Root", -1); ServiceContext.ContentService.Save(root); - root.PublishValues(); + root.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(root); var level1 = MockedContent.CreateSimpleContent(contentType2, "L1", root.Id); ServiceContext.ContentService.Save(level1); - level1.PublishValues(); + level1.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(level1); for (int i = 0; i < 2; i++) { var level3 = MockedContent.CreateSimpleContent(contentType3, "L2" + i, level1.Id); ServiceContext.ContentService.Save(level3); - level3.PublishValues(); + level3.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(level3); } @@ -267,7 +267,7 @@ namespace Umbraco.Tests.Services ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContent contentItem = MockedContent.CreateTextpageContent(contentType1, "Testing", -1); - contentItem.PublishValues(); + contentItem.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(contentItem); var initProps = contentItem.Properties.Count; var initPropTypes = contentItem.PropertyTypes.Count(); @@ -297,14 +297,14 @@ namespace Umbraco.Tests.Services var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); foreach (var x in contentItems1) { - x.PublishValues(); + x.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(x); } var contentItems2 = MockedContent.CreateTextpageContent(contentType2, -1, 5).ToArray(); foreach (var x in contentItems2) { - x.PublishValues(); + x.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(x); } @@ -362,7 +362,7 @@ namespace Umbraco.Tests.Services var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); foreach (var x in contentItems1) { - x.PublishValues(); + x.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(x); } var alias = contentType1.PropertyTypes.First().Alias; @@ -496,7 +496,7 @@ namespace Umbraco.Tests.Services // Act var homeDoc = cs.Create("Home Page", -1, contentTypeAlias); - homeDoc.PublishValues(); + homeDoc.TryPublishValues(); cs.SaveAndPublish(homeDoc); // Assert diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index b8d24d940c..0546eb3c7b 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -19,7 +19,23 @@ namespace Umbraco.Tests.Services [Apartment(ApartmentState.STA)] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] public class EntityServiceTests : TestWithSomeContentBase - { + { + private Language _langFr; + private Language _langEs; + + public override void SetUp() + { + base.SetUp(); + + if (_langFr == null && _langEs == null) + { + _langFr = new Language("fr-FR"); + _langEs = new Language("es-ES"); + ServiceContext.LocalizationService.Save(_langFr); + ServiceContext.LocalizationService.Save(_langEs); + } + } + [Test] public void EntityService_Can_Get_Paged_Content_Children() { @@ -427,6 +443,79 @@ namespace Umbraco.Tests.Services Assert.That(entities.Any(), Is.True); Assert.That(entities.Length, Is.EqualTo(1)); Assert.That(entities.Any(x => x.Trashed), Is.False); + } + + [Test] + public void EntityService_Can_Get_Content_By_UmbracoObjectType_With_Variant_Names() + { + var service = ServiceContext.EntityService; + + + var alias = "test" + Guid.NewGuid(); + var contentType = MockedContentTypes.CreateSimpleContentType(alias, alias, false); + contentType.Variations = ContentVariation.CultureNeutral; + ServiceContext.ContentTypeService.Save(contentType); + + var c1 = MockedContent.CreateSimpleContent(contentType, "Test", -1); + c1.SetName(_langFr.IsoCode, "Test - FR"); + c1.SetName(_langEs.IsoCode, "Test - ES"); + ServiceContext.ContentService.Save(c1); + + var result = service.Get(c1.Id, UmbracoObjectTypes.Document); + Assert.AreEqual("Test", result.Name); + Assert.IsTrue(result.AdditionalData.ContainsKey("CultureNames")); + var cultureNames = (IDictionary)result.AdditionalData["CultureNames"]; + Assert.AreEqual("Test - FR", cultureNames[_langFr.Id]); + Assert.AreEqual("Test - ES", cultureNames[_langEs.Id]); + } + + [Test] + public void EntityService_Can_Get_Child_Content_By_ParentId_And_UmbracoObjectType_With_Variant_Names() + { + var service = ServiceContext.EntityService; + + var contentType = MockedContentTypes.CreateSimpleContentType("test1", "Test1", false); + contentType.Variations = ContentVariation.CultureNeutral; + ServiceContext.ContentTypeService.Save(contentType); + + var root = MockedContent.CreateSimpleContent(contentType); + ServiceContext.ContentService.Save(root); + + for (int i = 0; i < 10; i++) + { + var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); + if (i % 2 == 0) + { + c1.SetName(_langFr.IsoCode, "Test " + i + " - FR"); + c1.SetName(_langEs.IsoCode, "Test " + i + " - ES"); + } + ServiceContext.ContentService.Save(c1); + } + + var entities = service.GetChildren(root.Id, UmbracoObjectTypes.Document).ToArray(); + + Assert.AreEqual(10, entities.Length); + + for (int i = 0; i < entities.Length; i++) + { + if (i % 2 == 0) + { + Assert.AreEqual(1, entities[i].AdditionalData.Count); + Assert.AreEqual("CultureNames", entities[i].AdditionalData.Keys.First()); + var variantInfo = entities[i].AdditionalData.First().Value as IDictionary; + Assert.IsNotNull(variantInfo); + var keys = variantInfo.Keys.ToList(); + var vals = variantInfo.Values.ToList(); + Assert.AreEqual(_langFr.Id, keys[0]); + Assert.AreEqual("Test " + i + " - FR", vals[0]); + Assert.AreEqual(_langEs.Id, keys[1]); + Assert.AreEqual("Test " + i + " - ES", vals[1]); + } + else + { + Assert.AreEqual(0, entities[i].AdditionalData.Count); + } + } } [Test] @@ -460,7 +549,6 @@ namespace Umbraco.Tests.Services var objectTypeId = Constants.ObjectTypes.ContentItem; Assert.Throws(() => service.GetAll()); - Assert.Throws(() => service.GetAll(UmbracoObjectTypes.ContentItem)); Assert.Throws(() => service.GetAll(objectTypeId)); } @@ -518,7 +606,7 @@ namespace Umbraco.Tests.Services } Assert.That(entities.Any(x => - x.AdditionalData.Any(y => y.Value is EntitySlim.PropertySlim && ((EntitySlim.PropertySlim) y.Value).PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField)), Is.True); + x.AdditionalData.Any(y => y.Value is EntitySlim.PropertySlim && ((EntitySlim.PropertySlim)y.Value).PropertyEditorAlias == Constants.PropertyEditors.Aliases.UploadField)), Is.True); } [Test] diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index 1333ca9f57..a810d4c644 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Services.Importing Assert.That(templates.Count(), Is.EqualTo(numberOfTemplates)); Assert.AreEqual(init + numberOfTemplates, allTemplates.Count()); - Assert.IsTrue(allTemplates.All(x => x.Content.Contains("UmbracoTemplatePage"))); + Assert.IsTrue(allTemplates.All(x => x.Content.Contains("UmbracoViewPage"))); } [Test] diff --git a/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml b/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml index 9a776805e9..2b2c8da067 100644 --- a/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml +++ b/src/Umbraco.Tests/Services/Importing/StandardMvc-Package.xml @@ -1099,7 +1099,7 @@ Google Maps - A map macro that you can use within Rich Text Areas Articles SW_Master - ClientAreas SW_Master - x.IsVisible() && x.TemplateId > 0 && Umbraco.MemberHasAccess(x.Id, x.Path)); @@ -1238,7 +1238,7 @@ Google Maps - A map macro that you can use within Rich Text Areas Contact SW_Master - Home SW_Master - Login SW_Master - Search SW_Master - Sitemap SW_Master - StandardPage SW_Master - SW_Master SW_Master - /usercontrols/StandardWebsiteInstall.ascx - \ No newline at end of file + diff --git a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs index b2394200a1..c62f14a4b0 100644 --- a/src/Umbraco.Tests/Services/LocalizationServiceTests.cs +++ b/src/Umbraco.Tests/Services/LocalizationServiceTests.cs @@ -144,16 +144,7 @@ namespace Umbraco.Tests.Services //there's a call or two to get languages, so apart from that there should only be one call per level Assert.Less(scope.Database.AsUmbracoDatabase().SqlCount, 30); } - } - - [Test] - public void Can_Get_Language_By_Culture_Code() - { - var danish = ServiceContext.LocalizationService.GetLanguageByCultureCode("Danish"); - var english = ServiceContext.LocalizationService.GetLanguageByCultureCode("English"); - Assert.NotNull(danish); - Assert.NotNull(english); - } + } [Test] public void Can_GetLanguageById() diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs index c7e2978b19..ab8d18b249 100644 --- a/src/Umbraco.Tests/Services/MacroServiceTests.cs +++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs @@ -28,9 +28,9 @@ namespace Umbraco.Tests.Services { var repository = new MacroRepository((IScopeAccessor) provider, CacheHelper.CreateDisabledCacheHelper(), Mock.Of()); - repository.Save(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml")); - repository.Save(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml")); - repository.Save(new Macro("test3", "Tet3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml")); + repository.Save(new Macro("test1", "Test1", "~/views/macropartials/test1.cshtml", MacroTypes.PartialView)); + repository.Save(new Macro("test2", "Test2", "~/views/macropartials/test2.cshtml", MacroTypes.PartialView)); + repository.Save(new Macro("test3", "Tet3", "~/views/macropartials/test3.cshtml", MacroTypes.PartialView)); scope.Complete(); } } @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Services var macroService = ServiceContext.MacroService; // Act - var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + var macro = new Macro("test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); macroService.Save(macro); //assert @@ -85,13 +85,13 @@ namespace Umbraco.Tests.Services var result = macroService.GetById(macro.Id); Assert.AreEqual("test", result.Alias); Assert.AreEqual("Test", result.Name); - Assert.AreEqual("~/Views/MacroPartials/Test.cshtml", result.ScriptPath); + Assert.AreEqual("~/Views/MacroPartials/Test.cshtml", result.MacroSource); Assert.AreEqual(1234, result.CacheDuration); result = macroService.GetById(macro.Key); Assert.AreEqual("test", result.Alias); Assert.AreEqual("Test", result.Name); - Assert.AreEqual("~/Views/MacroPartials/Test.cshtml", result.ScriptPath); + Assert.AreEqual("~/Views/MacroPartials/Test.cshtml", result.MacroSource); Assert.AreEqual(1234, result.CacheDuration); } @@ -100,7 +100,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + var macro = new Macro("test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); macroService.Save(macro); // Act @@ -119,7 +119,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + IMacro macro = new Macro("test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); macroService.Save(macro); // Act @@ -143,7 +143,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + IMacro macro = new Macro("test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); macro.Properties.Add(new MacroProperty("blah", "Blah", 0, "blah")); macroService.Save(macro); @@ -174,7 +174,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - IMacro macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + IMacro macro = new Macro("test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); macro.Properties.Add(new MacroProperty("blah1", "Blah1", 0, "blah1")); macro.Properties.Add(new MacroProperty("blah2", "Blah2", 1, "blah2")); macro.Properties.Add(new MacroProperty("blah3", "Blah3", 2, "blah3")); @@ -218,7 +218,7 @@ namespace Umbraco.Tests.Services public void Can_Add_And_Remove_Properties() { var macroService = ServiceContext.MacroService; - var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + var macro = new Macro("test", "Test", "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); //adds some properties macro.Properties.Add(new MacroProperty("blah1", "Blah1", 0, "blah1")); @@ -253,7 +253,7 @@ namespace Umbraco.Tests.Services { // Arrange var macroService = ServiceContext.MacroService; - var macro = new Macro("test", string.Empty, scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + var macro = new Macro("test", string.Empty, "~/Views/MacroPartials/Test.cshtml", MacroTypes.PartialView, cacheDuration: 1234); // Act & Assert Assert.Throws(() => macroService.Save(macro)); diff --git a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs index d5c0eac8c9..b607290c5e 100644 --- a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs @@ -108,9 +108,9 @@ namespace Umbraco.Tests.Services ServiceContext.MemberTypeService.Save(contentType1); ServiceContext.MemberTypeService.Save(contentType2); var contentItems1 = MockedMember.CreateSimpleMember(contentType1, 10).ToArray(); - contentItems1.ForEach(x => ServiceContext.MemberService.Save(x)); + foreach (var x in contentItems1) ServiceContext.MemberService.Save(x); var contentItems2 = MockedMember.CreateSimpleMember(contentType2, 5).ToArray(); - contentItems2.ForEach(x => ServiceContext.MemberService.Save(x)); + foreach (var x in contentItems2) ServiceContext.MemberService.Save(x); //only update the contentType1 alias which will force an xml rebuild for all content of that type contentType1.Alias = "newAlias"; ServiceContext.MemberTypeService.Save(contentType1); @@ -141,7 +141,7 @@ namespace Umbraco.Tests.Services var contentType1 = MockedContentTypes.CreateSimpleMemberType("test1", "Test1"); ServiceContext.MemberTypeService.Save(contentType1); var contentItems1 = MockedMember.CreateSimpleMember(contentType1, 10).ToArray(); - contentItems1.ForEach(x => ServiceContext.MemberService.Save(x)); + foreach (var x in contentItems1) ServiceContext.MemberService.Save(x); var alias = contentType1.PropertyTypes.First(x => standardProps.ContainsKey(x.Alias) == false).Alias; var elementToMatch = "<" + alias + ">"; diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 17c065338d..5a7d7320e1 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -23,7 +23,7 @@ namespace Umbraco.Tests.Services public void PackagingService_Can_Export_Macro() { // Arrange - var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + var macro = new Macro("test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); ServiceContext.MacroService.Save(macro); // Act diff --git a/src/Umbraco.Tests/Services/PerformanceTests.cs b/src/Umbraco.Tests/Services/PerformanceTests.cs index b157eb669d..dd84c49ef0 100644 --- a/src/Umbraco.Tests/Services/PerformanceTests.cs +++ b/src/Umbraco.Tests/Services/PerformanceTests.cs @@ -249,7 +249,7 @@ namespace Umbraco.Tests.Services var result = new List(); ServiceContext.ContentTypeService.Save(contentType1); IContent lastParent = MockedContent.CreateSimpleContent(contentType1); - lastParent.PublishValues(); + lastParent.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(lastParent); result.Add(lastParent); //create 20 deep @@ -263,7 +263,7 @@ namespace Umbraco.Tests.Services //only publish evens if (j % 2 == 0) { - content.PublishValues(); + content.TryPublishValues(); ServiceContext.ContentService.SaveAndPublish(content); } else diff --git a/src/Umbraco.Tests/Services/TagServiceTests.cs b/src/Umbraco.Tests/Services/TagServiceTests.cs index 0db27e9f4c..c24c729bf8 100644 --- a/src/Umbraco.Tests/Services/TagServiceTests.cs +++ b/src/Umbraco.Tests/Services/TagServiceTests.cs @@ -36,21 +36,21 @@ namespace Umbraco.Tests.Services IContent content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "cow", "pig", "goat" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); // change content1.AssignTags("tags", new[] { "elephant" }, true); content1.RemoveTags("tags", new[] { "cow" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); // more changes content1.AssignTags("tags", new[] { "mouse" }, true); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); content1.RemoveTags("tags", new[] { "mouse" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); // get it back @@ -88,17 +88,17 @@ namespace Umbraco.Tests.Services var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); content1.AssignTags("tags", new[] { "cow", "pig", "goat" }); - content1.PublishValues(); + content1.TryPublishValues(); contentService.SaveAndPublish(content1); var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); content2.AssignTags("tags", new[] { "cow", "pig" }); - content2.PublishValues(); + content2.TryPublishValues(); contentService.SaveAndPublish(content2); var content3 = MockedContent.CreateSimpleContent(contentType, "Tagged content 3", -1); content3.AssignTags("tags", new[] { "cow" }); - content3.PublishValues(); + content3.TryPublishValues(); contentService.SaveAndPublish(content3); // Act diff --git a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs index 19cac22287..543b4133f9 100644 --- a/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/Strings/DefaultShortStringHelperTests.cs @@ -596,32 +596,7 @@ namespace Umbraco.Tests.Strings Assert.AreEqual(expected, output); } - [Test] // can't do cases with an IDictionary - public void ReplaceManyWithCharMap() - { - const string input = "télévisiön tzvâr ßup   pof"; - const string expected = "television tzvar ssup pof"; - IDictionary replacements = new Dictionary - { - { "é", "e" }, - { "ö", "o" }, - { "â", "a" }, - { "ß", "ss" }, - { " ", " " }, - }; - var output = _helper.ReplaceMany(input, replacements); - Assert.AreEqual(expected, output); - } - - #region Cases - [TestCase("val$id!ate|this|str'ing", "$!'", '-', "val-id-ate|this|str-ing")] - [TestCase("val$id!ate|this|str'ing", "$!'", '*', "val*id*ate|this|str*ing")] - #endregion - public void ReplaceManyByOneChar(string input, string toReplace, char replacement, string expected) - { - var output = _helper.ReplaceMany(input, toReplace.ToArray(), replacement); - Assert.AreEqual(expected, output); - } + #region Cases [TestCase("foo.txt", "foo.txt")] diff --git a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs index dfdb307502..964e1d7ad2 100644 --- a/src/Umbraco.Tests/Strings/MockShortStringHelper.cs +++ b/src/Umbraco.Tests/Strings/MockShortStringHelper.cs @@ -59,16 +59,6 @@ namespace Umbraco.Tests.Strings return "SPLIT-PASCAL-CASING::" + text; } - public string ReplaceMany(string text, IDictionary replacements) - { - return "REPLACE-MANY-A::" + text; - } - - public string ReplaceMany(string text, char[] chars, char replacement) - { - return "REPLACE-MANY-B::" + text; - } - public string CleanString(string text, CleanStringType stringType) { return "CLEAN-STRING-A::" + text; diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index be64ecbbd6..b297f27656 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Composing; @@ -232,18 +234,31 @@ namespace Umbraco.Tests.Strings Assert.AreEqual("SPLIT-PASCAL-CASING::JUST-ANYTHING", output); } - [Test] + [Test] // can't do cases with an IDictionary public void ReplaceManyWithCharMap() { - var output = "JUST-ANYTHING".ReplaceMany(null); - Assert.AreEqual("REPLACE-MANY-A::JUST-ANYTHING", output); + const string input = "télévisiön tzvâr ßup   pof"; + const string expected = "television tzvar ssup pof"; + IDictionary replacements = new Dictionary + { + { "é", "e" }, + { "ö", "o" }, + { "â", "a" }, + { "ß", "ss" }, + { " ", " " }, + }; + var output = input.ReplaceMany(replacements); + Assert.AreEqual(expected, output); } - [Test] - public void ReplaceManyByOneChar() + #region Cases + [TestCase("val$id!ate|this|str'ing", "$!'", '-', "val-id-ate|this|str-ing")] + [TestCase("val$id!ate|this|str'ing", "$!'", '*', "val*id*ate|this|str*ing")] + #endregion + public void ReplaceManyByOneChar(string input, string toReplace, char replacement, string expected) { - var output = "JUST-ANYTHING".ReplaceMany(new char[] {}, '*'); - Assert.AreEqual("REPLACE-MANY-B::JUST-ANYTHING", output); + var output = input.ReplaceMany(toReplace.ToArray(), replacement); + Assert.AreEqual(expected, output); } } } diff --git a/src/Umbraco.Tests/Templates/ViewHelperTests.cs b/src/Umbraco.Tests/Templates/ViewHelperTests.cs index 5bd7733920..ee7264eaec 100644 --- a/src/Umbraco.Tests/Templates/ViewHelperTests.cs +++ b/src/Umbraco.Tests/Templates/ViewHelperTests.cs @@ -10,7 +10,7 @@ namespace Umbraco.Tests.Templates public void NoOptions() { var view = ViewHelper.GetDefaultFileContent(); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; }"), FixView(view)); @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Templates public void Layout() { var view = ViewHelper.GetDefaultFileContent(layoutPageAlias: "Dharznoik"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = ""Dharznoik.cshtml""; }"), FixView(view)); @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Templates public void ClassName() { var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; }"), FixView(view)); @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Templates public void Namespace() { var view = ViewHelper.GetDefaultFileContent(modelNamespace: "Models"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @{ Layout = null; }"), FixView(view)); @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Templates public void ClassNameAndNamespace() { var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @using ContentModels = My.Models; @{ Layout = null; @@ -61,7 +61,7 @@ namespace Umbraco.Tests.Templates public void ClassNameAndNamespaceAndAlias() { var view = ViewHelper.GetDefaultFileContent(modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @using MyModels = My.Models; @{ Layout = null; @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Templates public void Combined() { var view = ViewHelper.GetDefaultFileContent(layoutPageAlias: "Dharznoik", modelClassName: "ClassName", modelNamespace: "My.Models", modelNamespaceAlias: "MyModels"); - Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage + Assert.AreEqual(FixView(@"@inherits Umbraco.Web.Mvc.UmbracoViewPage @using MyModels = My.Models; @{ Layout = ""Dharznoik.cshtml""; diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index 0b3e5d6efd..97d88a680b 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -28,6 +28,7 @@ using Umbraco.Web.Routing; using Umbraco.Web.Security; using Umbraco.Web.WebApi; using LightInject; +using System.Globalization; namespace Umbraco.Tests.TestHelpers.ControllerTesting { @@ -130,7 +131,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting webSecurity.Setup(x => x.UserHasSectionAccess(It.IsAny(), It.IsAny())) .Returns(() => true); - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); @@ -148,10 +149,11 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())), Enumerable.Empty(), globalSettings, + mockedEntityService, true); //replace it var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var membershipHelper = new MembershipHelper(umbCtx, Mock.Of(), Mock.Of()); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index 5e69eff83a..14d914be1d 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.TestHelpers.Entities return content; } - public static Content CreateSimpleContent(IContentType contentType, string name, int parentId = -1) + public static Content CreateSimpleContent(IContentType contentType, string name, int parentId = -1, string culture = null, string segment = null) { var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 }; object obj = @@ -46,25 +46,29 @@ namespace Umbraco.Tests.TestHelpers.Entities author = "John Doe" }; - content.PropertyValues(obj); + content.PropertyValues(obj, culture, segment); content.ResetDirtyProperties(false); return content; } - public static Content CreateSimpleContent(IContentType contentType, string name, IContent parent) + public static Content CreateSimpleContent(IContentType contentType, string name, IContent parent, string culture = null, string segment = null, bool setPropertyValues = true) { - var content = new Content(name, parent, contentType) { CreatorId = 0, WriterId = 0 }; - object obj = + var content = new Content(name, parent, contentType, culture) { CreatorId = 0, WriterId = 0 }; + + if (setPropertyValues) + { + object obj = new { title = name + " Subpage", bodyText = "This is a subpage", author = "John Doe" - }; - - content.PropertyValues(obj); + }; + + content.PropertyValues(obj, culture, segment); + } content.ResetDirtyProperties(false); diff --git a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs index 5d47c82dff..35d3d72183 100644 --- a/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs +++ b/src/Umbraco.Tests/TestHelpers/SettingsForTests.cs @@ -56,11 +56,8 @@ namespace Umbraco.Tests.TestHelpers var security = new Mock(); var requestHandler = new Mock(); var templates = new Mock(); - var dev = new Mock(); var logging = new Mock(); var tasks = new Mock(); - var distCall = new Mock(); - var repos = new Mock(); var providers = new Mock(); var routing = new Mock(); @@ -68,11 +65,8 @@ namespace Umbraco.Tests.TestHelpers settings.Setup(x => x.Security).Returns(security.Object); settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object); settings.Setup(x => x.Templates).Returns(templates.Object); - settings.Setup(x => x.Developer).Returns(dev.Object); settings.Setup(x => x.Logging).Returns(logging.Object); settings.Setup(x => x.ScheduledTasks).Returns(tasks.Object); - settings.Setup(x => x.DistributedCall).Returns(distCall.Object); - settings.Setup(x => x.PackageRepositories).Returns(repos.Object); settings.Setup(x => x.Providers).Returns(providers.Object); settings.Setup(x => x.WebRouting).Returns(routing.Object); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs new file mode 100644 index 0000000000..7d9bbec855 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedContent.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.TestHelpers.Stubs +{ + internal class TestPublishedContent : PublishedElement, IPublishedContent + { + public TestPublishedContent(PublishedContentType contentType, int id, Guid key, Dictionary values, bool previewing, Dictionary cultureNames = null) + : base(contentType, key, values, previewing) + { + Id = id; + CultureNames = cultureNames; + } + + public int Id { get; } + public int TemplateId { get; set; } + public int SortOrder { get; set; } + public string Name { get; set; } + public IReadOnlyDictionary CultureNames { get; set; } + public string UrlName { get; set; } + public string DocumentTypeAlias => ContentType.Alias; + public int DocumentTypeId { get; set; } + public string WriterName { get; set; } + public string CreatorName { get; set; } + public int WriterId { get; set; } + public int CreatorId { get; set; } + public string Path { get; set; } + public DateTime CreateDate { get; set; } + public DateTime UpdateDate { get; set; } + public Guid Version { get; set; } + public int Level { get; set; } + public string Url { get; set; } + public PublishedItemType ItemType => ContentType.ItemType; + public bool IsDraft { get; set; } + public IPublishedContent Parent { get; set; } + public IEnumerable Children { get; set; } + + // copied from PublishedContentBase + public IPublishedProperty GetProperty(string alias, bool recurse) + { + var property = GetProperty(alias); + if (recurse == false) return property; + + IPublishedContent content = this; + var firstNonNullProperty = property; + while (content != null && (property == null || property.HasValue() == false)) + { + content = content.Parent; + property = content?.GetProperty(alias); + if (firstNonNullProperty == null && property != null) firstNonNullProperty = property; + } + + // if we find a content with the property with a value, return that property + // if we find no content with the property, return null + // if we find a content with the property without a value, return that property + // have to save that first property while we look further up, hence firstNonNullProperty + + return property != null && property.HasValue() ? property : firstNonNullProperty; + } + } +} diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs index 10c8739029..3768803de2 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestPublishedSnapshotAccessor.cs @@ -4,6 +4,6 @@ namespace Umbraco.Tests.TestHelpers.Stubs { public class TestPublishedSnapshotAccessor : IPublishedSnapshotAccessor { - public IPublishedShapshot PublishedSnapshot { get; set; } + public IPublishedSnapshot PublishedSnapshot { get; set; } } } diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 47a847ad43..5380a747f5 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -84,8 +84,9 @@ namespace Umbraco.Tests.TestHelpers { var directoryInfo = new DirectoryInfo(IOHelper.MapPath(directory)); var preserve = preserves.ContainsKey(directory) ? preserves[directory] : null; - if (directoryInfo.Exists) - directoryInfo.GetFiles().Where(x => preserve == null || preserve.Contains(x.Name) == false).ForEach(x => x.Delete()); + if (directoryInfo.Exists) + foreach (var x in directoryInfo.GetFiles().Where(x => preserve == null || preserve.Contains(x.Name) == false)) + x.Delete(); } } @@ -140,8 +141,8 @@ namespace Umbraco.Tests.TestHelpers { // compare values var actualProperty = (Property) actual; - var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.LanguageId).ThenBy(x => x.Segment).ToArray(); - var actualPropertyValues = actualProperty.Values.OrderBy(x => x.LanguageId).ThenBy(x => x.Segment).ToArray(); + var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray(); + var actualPropertyValues = actualProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray(); if (expectedPropertyValues.Length != actualPropertyValues.Length) Assert.Fail($"{property.DeclaringType.Name}.{property.Name}: Expected {expectedPropertyValues.Length} but got {actualPropertyValues.Length}."); for (var i = 0; i < expectedPropertyValues.Length; i++) diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 50cb115eae..ee0dd38c45 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -106,7 +106,7 @@ namespace Umbraco.Tests.TestHelpers { var httpContext = Mock.Of(); - var publishedSnapshotMock = new Mock(); + var publishedSnapshotMock = new Mock(); publishedSnapshotMock.Setup(x => x.Members).Returns(Mock.Of()); var publishedSnapshot = publishedSnapshotMock.Object; var publishedSnapshotServiceMock = new Mock(); @@ -119,7 +119,7 @@ namespace Umbraco.Tests.TestHelpers var urlProviders = Enumerable.Empty(); if (accessor == null) accessor = new TestUmbracoContextAccessor(); - return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, true); + return UmbracoContext.EnsureContext(accessor, httpContext, publishedSnapshotService, webSecurity, umbracoSettings, urlProviders, globalSettings, Mock.Of(), true); } public IUmbracoSettingsSection GetUmbracoSettings() diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index b0676f2eb9..91002739d1 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -169,7 +169,7 @@ namespace Umbraco.Tests.TestHelpers var mediaService = GetLazyService(container, c => new MediaService(scopeProvider, mediaFileSystem, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); var contentTypeService = GetLazyService(container, c => new ContentTypeService(scopeProvider, logger, eventMessagesFactory, contentService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); var mediaTypeService = GetLazyService(container, c => new MediaTypeService(scopeProvider, logger, eventMessagesFactory, mediaService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); - var fileService = GetLazyService(container, c => new FileService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); + var fileService = GetLazyService(container, c => new FileService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c))); var localizationService = GetLazyService(container, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c))); var memberTypeService = GetLazyService(container, c => new MemberTypeService(scopeProvider, logger, eventMessagesFactory, memberService.Value, GetRepo(c), GetRepo(c), GetRepo(c))); diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 9a2828fd9f..0fcd3c9295 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -32,6 +32,7 @@ using LightInject; using Umbraco.Core.Migrations.Install; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; namespace Umbraco.Tests.TestHelpers { @@ -77,6 +78,7 @@ namespace Umbraco.Tests.TestHelpers Container.Register(); Container.Register(factory => PublishedSnapshotService); + Container.Register(factory => SystemDefaultCultureProvider); Container.GetInstance() .Clear() @@ -229,12 +231,16 @@ namespace Umbraco.Tests.TestHelpers } } + protected ISystemDefaultCultureProvider SystemDefaultCultureProvider { get; set; } + protected IPublishedSnapshotService PublishedSnapshotService { get; set; } protected override void Initialize() // fixme - should NOT be here! { base.Initialize(); + SystemDefaultCultureProvider = new TestSystemDefaultCultureProvider(); + CreateAndInitializeDatabase(); // ensure we have a PublishedSnapshotService @@ -264,8 +270,9 @@ namespace Umbraco.Tests.TestHelpers ScopeProvider, cache, publishedSnapshotAccessor, Container.GetInstance(), Container.GetInstance(), Container.GetInstance(), + SystemDefaultCultureProvider, Logger, - Container.GetInstance(), + Container.GetInstance(), new SiteDomainHelper(), ContentTypesCache, null, true, Options.PublishedRepositoryEvents); @@ -343,30 +350,35 @@ namespace Umbraco.Tests.TestHelpers } } - protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IGlobalSettings globalSettings = null) + protected UmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) { // ensure we have a PublishedCachesService - var service = PublishedSnapshotService as PublishedSnapshotService; + var service = snapshotService ?? PublishedSnapshotService as PublishedSnapshotService; if (service == null) throw new Exception("Not a proper XmlPublishedCache.PublishedCachesService."); - // re-initialize PublishedCacheService content with an Xml source with proper template id - service.XmlStore.GetXmlDocument = () => + if (service is PublishedSnapshotService) { - var doc = new XmlDocument(); - doc.LoadXml(GetXmlContent(templateId)); - return doc; - }; + // re-initialize PublishedCacheService content with an Xml source with proper template id + ((PublishedSnapshotService)service).XmlStore.GetXmlDocument = () => + { + var doc = new XmlDocument(); + doc.LoadXml(GetXmlContent(templateId)); + return doc; + }; + } var httpContext = GetHttpContextFactory(url, routeData).HttpContext; var umbracoContext = new UmbracoContext( httpContext, service, - new WebSecurity(httpContext, Container.GetInstance(), Container.GetInstance()), + new WebSecurity(httpContext, Container.GetInstance(), + Container.GetInstance()), umbracoSettings ?? Container.GetInstance(), urlProviders ?? Enumerable.Empty(), - globalSettings ?? Container.GetInstance()); + globalSettings ?? Container.GetInstance(), + ServiceContext.EntityService); if (setSingleton) Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; diff --git a/src/Umbraco.Tests/Testing/ContentBaseExtensions.cs b/src/Umbraco.Tests/Testing/ContentBaseExtensions.cs index 1a94b8770a..58d4dfbd7f 100644 --- a/src/Umbraco.Tests/Testing/ContentBaseExtensions.cs +++ b/src/Umbraco.Tests/Testing/ContentBaseExtensions.cs @@ -10,7 +10,7 @@ namespace Umbraco.Tests.Testing /// Set property values by alias with an annonymous object. /// /// Does not support variants. - public static void PropertyValues(this IContentBase content, object value) + public static void PropertyValues(this IContentBase content, object value, string culture = null, string segment = null) { if (value == null) throw new Exception("No properties has been passed in"); @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Testing var item = content.Properties.FirstOrDefault(x => x.Alias == propertyInfo.Name); if (item != null) { - item.SetValue(propertyInfo.GetValue(value, null)); + item.SetValue(propertyInfo.GetValue(value, null), culture, segment); //Update item with newly added value content.Properties.Add(item); } @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Testing { //Create new Property to add to collection var property = propertyType.CreateProperty(); - property.SetValue(propertyInfo.GetValue(value, null)); + property.SetValue(propertyInfo.GetValue(value, null), culture, segment); content.Properties.Add(property); } } diff --git a/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs b/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs new file mode 100644 index 0000000000..f7e5484500 --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/AccessorsAndProviders/TestSystemDefaultCultureProvider.cs @@ -0,0 +1,9 @@ +using Umbraco.Web.PublishedCache; + +namespace Umbraco.Tests.Testing.Objects.AccessorsAndProviders +{ + public class TestSystemDefaultCultureProvider : ISystemDefaultCultureProvider + { + public string DefaultCulture { get; set; } + } +} diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index b6a5d982c4..59bc24ed10 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Web.Security; using Moq; using NUnit.Framework; @@ -73,7 +74,7 @@ namespace Umbraco.Tests.Testing.TestingTests var umbracoContext = TestObjects.GetUmbracoContextMock(); var urlProviderMock = new Mock(); - urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + urlProviderMock.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns("/hello/world/1234"); var urlProvider = urlProviderMock.Object; diff --git a/src/Umbraco.Tests/UI/LegacyDialogTests.cs b/src/Umbraco.Tests/UI/LegacyDialogTests.cs index 5a978fb418..bcc3805c7b 100644 --- a/src/Umbraco.Tests/UI/LegacyDialogTests.cs +++ b/src/Umbraco.Tests/UI/LegacyDialogTests.cs @@ -3,6 +3,7 @@ using NUnit.Framework; using umbraco; using Umbraco.Core; using Umbraco.Core.Composing; +using Umbraco.Web; using Umbraco.Web._Legacy.UI; namespace Umbraco.Tests.UI @@ -21,14 +22,10 @@ namespace Umbraco.Tests.UI Assert.IsTrue(TypeHelper.IsTypeAssignableFrom(t), "The type " + t + " is not of type " + typeof(LegacyDialogTask)); } } - - [TestCase(typeof(XsltTasks), Constants.Applications.Developer)] - [TestCase(typeof(StylesheetTasks), Constants.Applications.Settings)] - [TestCase(typeof(stylesheetPropertyTasks), Constants.Applications.Settings)] + [TestCase(typeof(MemberGroupTasks), Constants.Applications.Members)] [TestCase(typeof(dictionaryTasks), Constants.Applications.Settings)] [TestCase(typeof(macroTasks), Constants.Applications.Developer)] - [TestCase(typeof(languageTasks), Constants.Applications.Settings)] [TestCase(typeof(CreatedPackageTasks), Constants.Applications.Developer)] public void Check_Assigned_Apps_For_Tasks(Type taskType, string app) { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f630194a41..827bfef4b7 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -128,6 +128,7 @@ + @@ -143,11 +144,15 @@ - - + + + + + + @@ -173,7 +178,9 @@ + + @@ -199,7 +206,6 @@ - @@ -233,7 +239,6 @@ - @@ -276,14 +281,8 @@ - - - - - - @@ -331,7 +330,6 @@ - @@ -372,7 +370,6 @@ - @@ -427,9 +424,7 @@ - - @@ -453,10 +448,7 @@ - - - @@ -596,9 +588,7 @@ - - - + @@ -606,11 +596,9 @@ $(NuGetPackageFolders.Split(';')[0]) - - - + @@ -624,5 +612,4 @@ - \ No newline at end of file diff --git a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs index 1a61882949..1f5fe1a6e3 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderIndexActionSelectorAttributeTests.cs @@ -71,7 +71,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesDefaultIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -94,7 +95,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesOverriddenIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -117,7 +119,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesCustomIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); @@ -140,7 +143,8 @@ namespace Umbraco.Tests.Web.Mvc new Mock(null, null, globalSettings).Object, TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - globalSettings, + globalSettings, + Mock.Of(), true); var ctrl = new MatchesAsyncIndexController { UmbracoContext = umbCtx }; var controllerCtx = new ControllerContext(req, ctrl); diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index d9a12751c0..931cc57493 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -45,6 +45,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -66,6 +67,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var ctrl = new TestSurfaceController { UmbracoContext = umbCtx }; @@ -85,6 +87,7 @@ namespace Umbraco.Tests.Web.Mvc TestObjects.GetUmbracoSettings(), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var controller = new TestSurfaceController { UmbracoContext = umbracoContext }; @@ -97,7 +100,7 @@ namespace Umbraco.Tests.Web.Mvc [Test] public void Can_Lookup_Content() { - var publishedSnapshot = new Mock(); + var publishedSnapshot = new Mock(); publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); var publishedSnapshotService = new Mock(); publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); @@ -111,6 +114,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "AutoLegacy")), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var helper = new UmbracoHelper( @@ -148,6 +152,7 @@ namespace Umbraco.Tests.Web.Mvc Mock.Of(section => section.WebRouting == webRoutingSettings), Enumerable.Empty(), globalSettings, + Mock.Of(), true); var content = Mock.Of(publishedContent => publishedContent.Id == 12345); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 053a1f7c5d..295f42fee2 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -19,6 +19,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; +using Umbraco.Tests.Testing.Objects.AccessorsAndProviders; using Umbraco.Web; using Umbraco.Web.Models; using Umbraco.Web.Mvc; @@ -424,18 +425,22 @@ namespace Umbraco.Tests.Web.Mvc var scopeProvider = TestObjects.GetScopeProvider(Mock.Of()); var factory = Mock.Of(); _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), null, - null, null, null, - Current.Logger, TestObjects.GetGlobalSettings(), null, true, false); // no events + null, null, null, + new TestSystemDefaultCultureProvider(), + Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; + + var globalSettings = TestObjects.GetGlobalSettings(); var ctx = new UmbracoContext( - GetHttpContextFactory(url, routeData).HttpContext, + http, _service, - new WebSecurity(http, Current.Services.UserService, TestObjects.GetGlobalSettings()), + new WebSecurity(http, Current.Services.UserService, globalSettings), TestObjects.GetUmbracoSettings(), Enumerable.Empty(), - TestObjects.GetGlobalSettings()); + globalSettings, + Mock.Of()); //if (setSingleton) //{ diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 8b52ce1893..d35b4e5823 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Web; using LightInject; using Moq; @@ -72,8 +73,8 @@ namespace Umbraco.Tests.Web //setup a mock url provider which we'll use fo rtesting var testUrlProvider = new Mock(); - testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode) => + testUrlProvider.Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((UmbracoContext umbCtx, int id, Uri url, UrlProviderMode mode, string culture) => { return "/my-test-url"; }); @@ -90,6 +91,7 @@ namespace Umbraco.Tests.Web //pass in the custom url provider new[]{ testUrlProvider.Object }, globalSettings, + entityService.Object, true)) { var output = TemplateUtilities.ParseInternalLinks(input, umbCtx.UrlProvider); diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index 86339c309f..cc97633cde 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -5,6 +5,7 @@ using System.Web.Mvc; using System.Web.Routing; using Moq; using NUnit.Framework; +using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Tests.Testing; using Umbraco.Web; @@ -29,7 +30,8 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), - TestObjects.GetGlobalSettings()); + TestObjects.GetGlobalSettings(), + Mock.Of()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -46,7 +48,8 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), - TestObjects.GetGlobalSettings()); + TestObjects.GetGlobalSettings(), + Mock.Of()); var r1 = new RouteData(); r1.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbCtx); @@ -73,7 +76,8 @@ namespace Umbraco.Tests.Web new WebSecurity(Mock.Of(), Current.Services.UserService, TestObjects.GetGlobalSettings()), TestObjects.GetUmbracoSettings(), new List(), - TestObjects.GetGlobalSettings()); + TestObjects.GetGlobalSettings(), + Mock.Of()); var httpContext = Mock.Of(); diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index 17105a7d7a..07defe12c9 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -84,27 +84,21 @@ }; } - if (!String.prototype.trimStart) { - - /** trims the start of the string*/ - String.prototype.trimStart = function (str) { - if (this.startsWith(str)) { - return this.substring(str.length); - } - return this; - }; - } - - if (!String.prototype.trimEnd) { + /** trims the start of the string*/ + String.prototype.trimStart = function (str) { + if (this.startsWith(str)) { + return this.substring(str.length); + } + return this; + }; - /** trims the end of the string*/ - String.prototype.trimEnd = function (str) { - if (this.endsWith(str)) { - return this.substring(0, this.length - str.length); - } - return this; - }; - } + /** trims the end of the string*/ + String.prototype.trimEnd = function (str) { + if (this.endsWith(str)) { + return this.substring(0, this.length - str.length); + } + return this; + }; if (!String.prototype.utf8Encode) { @@ -330,19 +324,19 @@ return false; }; } - + if (!Object.toBoolean) { /** Converts a string/integer/bool to true/false */ - Object.toBoolean = function (obj) { - if ((typeof obj) === "boolean") { + Object.toBoolean = function (obj) { + if ((typeof obj) === "boolean") { return obj; - } - if (obj === "1" || obj === 1 || obj === "true") { + } + if (obj === "1" || obj === 1 || obj === "true") { return true; - } + } return false; }; } -})(); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js index 0896f986e6..23664ed842 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js @@ -38,7 +38,7 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se calculateWidth(); }); } - + function calculateWidth(){ $timeout(function(){ //total width minus room for avatar, search, and help icon @@ -119,9 +119,10 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se } else { var lastAccessed = historyService.getLastAccessedItemForSection(section.alias); - var path = lastAccessed != null ? lastAccessed.link : section.alias; - $location.path(path).search(''); - } + var path = lastAccessed != null ? lastAccessed.link : section.alias; + $location.path(path); + } + navigationService.clearSearch(); }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index e1c1592a51..124d971b28 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -20,7 +20,7 @@ $scope.page.listViewPath = null; $scope.page.isNew = $scope.isNew ? true : false; $scope.page.buttonGroupState = "init"; - $scope.page.languageId = $scope.languageId; + $scope.page.culture = $scope.culture; $scope.allowOpen = true; // add all editors to an editors array to support split view @@ -121,14 +121,14 @@ /** * This does the content loading and initializes everything, called on load and changing variants - * @param {any} languageId + * @param {any} culture */ - function getNode(languageId) { + function getNode(culture) { $scope.page.loading = true; //we are editing so get the content item from the server - $scope.getMethod()($scope.contentId, languageId) + $scope.getMethod()($scope.contentId, culture) .then(function (data) { $scope.content = data; @@ -257,7 +257,7 @@ else { //Browse content nodes based on the selected tree language variant - $scope.page.languageId ? getNode($scope.page.languageId) : getNode(); + $scope.page.culture ? getNode($scope.page.culture) : getNode(); } @@ -533,7 +533,7 @@ saveMethod: "&", getMethod: "&", getScaffoldMethod: "&?", - languageId: "=?", + culture: "=?", model: "=?" } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index 6be9889999..1b99cd958e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -218,6 +218,7 @@ Use this directive to construct a header inside the main editor window. } function setCurrentVariant(variants) { + angular.forEach(variants, function (variant) { if(variant.current) { scope.vm.currentVariant = variant; @@ -225,30 +226,35 @@ Use this directive to construct a header inside the main editor window. }); } + //TODO: This doesn't really affect any UI currently, need some feedback from mads function setVariantStatusColor(variants) { angular.forEach(variants, function (variant) { - angular.forEach(variant.states, function(state){ - switch (state.name) { - case "Published": - case "Published +": - state.stateColor = "success"; - break; - default: - state.stateColor = "gray"; - } - }); + + //TODO: What about variant.exists? If we are applying colors/styles, this should be one of them + + switch (variant.state) { + case "Published": + variant.stateColor = "success"; + break; + case "Unpublished": + //TODO: Not sure if these statuses will ever bubble up to the UI? + case "Publishing": + case "Unpublishing": + default: + variant.stateColor = "gray"; + } }); } - scope.goBack = function() { - if(scope.onBack) { + scope.goBack = function () { + if (scope.onBack) { scope.onBack(); } }; scope.selectVariant = function (event, variant) { scope.vm.dropdownOpen = false; - $location.search({ languageId: variant.language.id }); + $location.search("cculture", variant.language.culture); }; scope.openIconPicker = function() { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js index 1bb805b139..3d12b4ff7d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbgroupsbuilder.directive.js @@ -2,7 +2,7 @@ 'use strict'; function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService, editorService) { - + function link(scope, el, attr, ctrl) { var validationTranslated = ""; @@ -498,6 +498,7 @@ property: property, contentType: scope.contentType, contentTypeName: scope.model.name, + contentTypeAllowCultureVariant: scope.model.allowCultureVariant, view: "views/common/infiniteeditors/propertysettings/propertysettings.html", size: "small", submit: function(model) { diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js index 9ad97899e5..92cc0cb2bb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js @@ -160,7 +160,6 @@ angular.module('umbraco.mocks'). { name: "Data types", childNodesUrl: dataTypeChildrenUrl, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: dataTypeMenuUrl, metaData: { treeAlias: "dataTypes" } }, { name: "Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "macros" } }, { name: "Packages", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "packager" } }, - { name: "XSLT Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "xslt" } }, { name: "Partial View Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeAlias: "partialViewMacros" } } ], expanded: true, @@ -240,4 +239,4 @@ angular.module('umbraco.mocks'). } }; - }]); \ No newline at end of file + }]); diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index bf9a94a446..153f638ce1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -260,8 +260,6 @@ angular.module('umbraco.mocks'). "errors_stylesNoStylesOnPage": "No active styles available", "errors_tableColMergeLeft": "Please place cursor at the left of the two cells you wish to merge", "errors_tableSplitNotSplittable": "You cannot split a cell that hasn't been merged.", - "errors_xsltErrorHeader": "Error in XSLT source", - "errors_xsltErrorText": "The XSLT has not been saved, because it contained error(s)", "general_about": "About", "general_action": "Action", "general_add": "Add", @@ -598,11 +596,6 @@ angular.module('umbraco.mocks'). "speechBubbles_templateErrorText": "Please make sure that you do not have 2 templates with the same alias", "speechBubbles_templateSavedHeader": "Template saved", "speechBubbles_templateSavedText": "Template saved without any errors!", - "speechBubbles_xsltErrorHeader": "XSLT not saved", - "speechBubbles_xsltErrorText": "XSLT contained an error", - "speechBubbles_xsltPermissionErrorText": "XSLT could not be saved, check file permissions", - "speechBubbles_xsltSavedHeader": "XSLT saved", - "speechBubbles_xsltSavedText": "No errors in XSLT", "speechBubbles_contentUnpublished": "Content unpublished", "speechBubbles_partialViewSavedHeader": "Partial view saved", "speechBubbles_partialViewSavedText": "Partial view saved without any errors!", @@ -703,7 +696,6 @@ angular.module('umbraco.mocks'). "treeHeaders_scripts": "Scripts", "treeHeaders_stylesheets": "Stylesheets", "treeHeaders_templates": "Templates", - "treeHeaders_xslt": "XSLT Files", "update_updateAvailable": "New update ready", "update_updateDownloadText": "%0% is ready, click here for download", "update_updateNoServer": "No connection to server", @@ -760,4 +752,4 @@ angular.module('umbraco.mocks'). .respond(getLanguageResource); } }; - }]); \ No newline at end of file + }]); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 7d560d7b43..1f2a611fe5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -314,17 +314,17 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { * * * @param {Int} id id of content item to return - * @param {Int} languageId optional ID of the language to retrieve the item in + * @param {Int} culture optional culture to retrieve the item in * @returns {Promise} resourcePromise object containing the content item. * */ - getById: function (id, languageId) { + getById: function (id, culture) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "contentApiBaseUrl", "GetById", - { id: id, languageId: languageId })), + { id: id, culture: culture })), 'Failed to retrieve data for content id ' + id); }, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 36a184cd95..e8e10c5d63 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -5,7 +5,7 @@ * @description A helper service for most editors, some methods are specific to content/media/member model types but most are used by * all editors to share logic and reduce the amount of replicated code among editors. **/ -function contentEditingHelper(fileManager, $q, $location, $routeParams, notificationsService, localizationService, serverValidationManager, dialogService, formHelper, appState) { +function contentEditingHelper(fileManager, $q, $location, $routeParams, notificationsService, navigationService, localizationService, serverValidationManager, dialogService, formHelper, appState) { function isValidIdentifier(id){ //empty id <= 0 @@ -596,7 +596,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica // /belle/#/content/edit/9876 (where 9876 is the new id) //clear the query strings - $location.search(""); + navigationService.clearSearch(); //change to new path $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id); @@ -617,9 +617,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica * For some editors like scripts or entites that have names as ids, these names can change and we need to redirect * to their new paths, this is helper method to do that. */ - redirectToRenamedContent: function (id) { + redirectToRenamedContent: function (id) { //clear the query strings - $location.search(""); + navigationService.clearSearch(); //change to new path $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id); //don't add a browser history for this diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 257d36af31..9a19304288 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -102,6 +102,25 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#clearSearch + * @methodOf umbraco.services.navigationService + * + * @description + * utility to clear the querystring/search params while maintaining a known list of parameters that should be maintained throughout the app + */ + clearSearch: function () { + var retainKeys = ["mculture"]; + var currentSearch = $location.search(); + $location.search(''); + _.each(retainKeys, function (k) { + if (currentSearch[k]) { + $location.search(k, currentSearch[k]); + } + }); + }, + /** * @ngdoc method * @name umbraco.services.navigationService#load diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index 439720e461..70004900f9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -37,7 +37,7 @@ var saveModel = _.pick(displayModel, 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', - 'key', 'parentId', 'alias', 'path'); + 'key', 'parentId', 'alias', 'path', 'allowCultureVariant'); //TODO: Map these saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); @@ -56,7 +56,7 @@ }); var saveProperties = _.map(realProperties, function (p) { - var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData'); + var saveProperty = _.pick(p, 'id', 'alias', 'description', 'validation', 'label', 'sortOrder', 'dataTypeId', 'groupId', 'memberCanEdit', 'showOnMemberProfile', 'isSensitiveData', 'allowCultureVariant'); return saveProperty; }); @@ -326,19 +326,23 @@ //get the selected variant and build the additional published variants saveModel.publishVariations = []; - _.each(displayModel.variants, - function (d) { - //set the selected variant if this is current - if (d.current === true) { - saveModel.languageId = d.language.id; - } - if (d.publish === true) { - saveModel.publishVariations.push({ - languageId: d.language.id, - segment: d.segment - }); - } - }); + + //if there's any variants than we need to set the language and include the variants to publish + if (displayModel.variants.length > 0) { + _.each(displayModel.variants, + function (d) { + //set the selected variant if this is current + if (d.current === true) { + saveModel.culture = d.language.culture; + } + if (d.publish === true) { + saveModel.publishVariations.push({ + culture: d.language.culture, + segment: d.segment + }); + } + }); + } var propExpireDate = displayModel.removeDate; var propReleaseDate = displayModel.releaseDate; diff --git a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js index b1810ca683..00458aa6b4 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/navigation.controller.js @@ -96,10 +96,12 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar appState.setMenuState("currentNode", args.node); //not legacy, lets just set the route value and clear the query string if there is one. - $location.path(n.routePath).search(""); + $location.path(n.routePath); + navigationService.clearSearch(); } else if (n.section) { - $location.path(n.section).search(""); + $location.path(n.section); + navigationService.clearSearch(); } navigationService.hideNavigation(); @@ -236,24 +238,37 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar languageResource.getAll().then(function(languages) { $scope.languages = languages; - // make the default language selected - $scope.languages.forEach(function (language) { - if (language.isDefault) { - $scope.selectedLanguage = language; + if ($scope.languages.length > 1) { + var defaultLang = _.find($scope.languages, function (l) { + return l.isDefault; + }); + if (defaultLang) { + //set the route param + $location.search("mculture", defaultLang.culture); } - }); - }); + } + init(); + }); })); - /** - * Updates the tree's query parameters - */ - function initTree() { + function init() { + //select the current language if set in the query string + var mainCulture = $location.search().mculture; + if (mainCulture && $scope.languages && $scope.languages.length > 1) { + var found = _.find($scope.languages, function (l) { + return l.culture === mainCulture; + }); + if (found) { + //set the route param + $scope.selectedLanguage = found; + } + } + //create the custom query string param for this tree var queryParams = {}; - if ($scope.selectedLanguage && $scope.selectedLanguage.id) { - queryParams["languageId"] = $scope.selectedLanguage.id; + if ($scope.selectedLanguage && $scope.selectedLanguage.culture) { + queryParams["culture"] = $scope.selectedLanguage.culture; } var queryString = $.param(queryParams); //create the query string from the params object @@ -266,6 +281,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } } + function nodeExpandedHandler(args) { //store the reference to the expanded node path if (args.node) { @@ -274,11 +290,15 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } $scope.selectLanguage = function(language) { - $scope.selectedLanguage = language; + + $location.search("mculture", language.culture); + + //$scope.selectedLanguage = language; + // close the language selector $scope.page.languageSelectorIsOpen = false; - initTree(); //this will reset the tree params and the tree directive will pick up the changes in a $watch + init(); //re-bind language to the query string and update the tree params //execute after next digest because the internal watch on the customtreeparams needs to be bound now that we've changed it $timeout(function () { diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 5f660cf212..e4f9852a11 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -104,7 +104,6 @@ app.config(function ($routeProvider) { resolve: doLogout() }) .when('/:section', { - //This allows us to dynamically change the template for this route since you cannot inject services into the templateUrl method. template: "
", //This controller will execute for this route, then we can execute some code in order to set the template Url diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js index 964c95c43f..3830833086 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js @@ -58,6 +58,11 @@ }); function activate() { + //make the default the same as the content type + if (!$scope.model.property.id) { + $scope.model.property.allowCultureVariant = $scope.model.contentTypeAllowCultureVariant; + } + matchValidationType(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index d8e07edc31..8d3a85baec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -21,16 +21,16 @@
Required label
@@ -38,99 +38,105 @@
- +
- +
- - + - -
- + +
+
- + - + - - - + + +
- +
+ +
Property Type Variation
+ + + +
+
- +
- + - + - + - +
-
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.controller.js index c9f3cc347c..eb2b0e5930 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.controller.js @@ -1,6 +1,6 @@ //used for the media picker dialog angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeController", - function ($scope, $location, $log, notificationsService) { + function ($scope, $location, $log, notificationsService, navigationService) { $scope.discard = function(not){ @@ -13,7 +13,7 @@ angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeCo // when no callback is added run the normal functionality of the discard button not.args.listener(); - $location.search(""); + navigationService.clearSearch(); //we need to break the path up into path and query var parts = not.args.path.split("?"); @@ -33,4 +33,4 @@ angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeCo notificationsService.remove(not); }; - }); \ No newline at end of file + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js index a489e9927d..6601efef89 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.controller.js @@ -1,23 +1,27 @@ (function () { "use strict"; - function PublishController($scope, $timeout) { + function PublishController($scope) { var vm = this; - vm.variants = $scope.model.variants; + var variants = $scope.model.variants; vm.changeSelection = changeSelection; + vm.loading = true; + + vm.dirtyVariants = []; + vm.pristineVariants = []; //watch this model, if it's reset, then re init - $scope.$watch(function() { - return $scope.model.variants; - }, - function(newVal, oldVal) { + $scope.$watch(function () { + return $scope.model.variants; + }, + function (newVal, oldVal) { vm.variants = newVal; if (oldVal && oldVal.length) { //re-bind the selections for (var i = 0; i < oldVal.length; i++) { - var found = _.find(vm.variants, function(v) { - return v.language.id == oldVal[i].language.id; + var found = _.find(variants, function (v) { + return v.language.id === oldVal[i].language.id; }); if (found) { found.publish = oldVal[i].publish; @@ -28,24 +32,42 @@ }); function changeSelection(variant) { - var firstSelected = _.find(vm.variants, function(v) { + var firstSelected = _.find(variants, function (v) { return v.publish; }); $scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected } function onInit() { - _.each(vm.variants, - function (v) { - v.compositeId = v.language.id + "_" + (v.segment ? v.segment : ""); - v.htmlId = "publish_variant_" + v.compositeId; + _.each(variants, + function (variant) { + variant.compositeId = variant.language.id + "_" + (variant.segment ? variant.segment : ""); + variant.htmlId = "publish_variant_" + variant.compositeId; + + //separate "pristine" and "dirty" variants + if (variant.isEdited === true) { + vm.dirtyVariants.push(variant); + } else if (variant.isEdited === true || + variant.isEdited === false && variant.state === "Unpublished") { + vm.dirtyVariants.push(variant); + } else { + vm.pristineVariants.push(variant); + } }); - //now sort it so that the current one is at the top - vm.variants = _.sortBy(vm.variants, function(v) { - return v.current ? 0 : 1; - }); - //ensure that the current one is selected - vm.variants[0].publish = true; + + if (vm.dirtyVariants.length !== 0) { + //now sort it so that the current one is at the top + vm.dirtyVariants = _.sortBy(vm.dirtyVariants, function (v) { + return v.current ? 0 : 1; + }); + //ensure that the current one is selected + vm.dirtyVariants[0].publish = true; + } else { + //disable Publish button if we have nothing to publish + $scope.model.disableSubmitButton = true; + } + + vm.loading = false; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html index ed7f32fc25..cfb205eb25 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/publish/publish.html @@ -1,34 +1,52 @@ -
- +
+
-

What languages would you like to publish?

+

+

-
-
+
+ +
+
+
+ +
+
+

+
+ +
+
+
{{ variant.language.name }}
+
{{ variant.state }}
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js index 700b18b518..e8b1bb0f68 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.controller.js @@ -36,7 +36,7 @@ angular.module("umbraco") //perform the path change, if it is successful then the promise will resolve otherwise it will fail $scope.model.close(); - $location.path("/logout"); + $location.path("/logout").search(''); }; $scope.gotoHistory = function (link) { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index dfae5d8877..8964aa1b97 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -50,7 +50,7 @@ server-validation-field="Alias"> - + {{vm.currentVariant.language.name}}   @@ -59,9 +59,14 @@ {{variant.language.name}} - {{variant.state}} + + + {{variant.state}} + + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index e8a384c2a9..1bd924e9f0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -21,7 +21,7 @@ function ContentEditController($scope, $routeParams, contentResource) { $scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty; $scope.page = $routeParams.page; $scope.isNew = $routeParams.create; - $scope.languageId = $routeParams.languageId; + $scope.culture = $routeParams.cculture; } angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/content/edit.html index d9801da179..288fb74294 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/edit.html @@ -7,7 +7,7 @@ get-scaffold-method="getScaffoldMethod" tree-alias="content" is-new="isNew" - language-id="languageId" + culture="culture" model="model">
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/listview/listview.html index c27d885483..7d435adc6d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/listview/listview.html @@ -1,19 +1,18 @@ -
- -
-
- -
- -
- - - - -
- -
+ + +
+
+
+ +
+
+ + +
+
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index d9c7d185da..606ae1d9b1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -1,47 +1,65 @@
-
+ + -
-
- -
-
- -
+
-
+
+
+ +
+
+ +
-
+
-
-
- -
+
-
- - +
+
+ +
- - +
+ + -
+ + -
+
+
+ +
+ +
+
Content Type Variation
+ Define the rules for how this content type's properties can be varied +
+
+ +
+ +
+ + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html index 0cfe76cd7a..c3bc28a145 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/templates/templates.html @@ -1,23 +1,27 @@ -
+
+ + +
-
-
- -
- -
- - - - -
+
+
+ +
+ +
+ + +
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/listview/listview.html index 1461341d6d..21d29b2161 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/listview/listview.html @@ -1,19 +1,18 @@ -
- -
-
- -
- -
- - - - -
- -
+ + +
+
+
+ +
+
+ + +
+
+
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html index 7c9811e37a..d34b31c8ea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/views/permissions/permissions.html @@ -1,49 +1,51 @@
+ + +
-
- -
-
- -
-
- -
- -
- -
- -
-
- -
- -
- - - - - - - - -
- -
- +
+
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+ + + + + + + + +
+ +
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html index 8f093a5924..d692c6fd9e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html @@ -19,50 +19,53 @@ + + -
+
-
- - - -
- -
- -
-
- +
+
+ + diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html index 6ecc2b2d89..3bbf735199 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html @@ -20,49 +20,54 @@ -
+ + + +
+ +
+ -
- - -
-
- -
-
- +
+
+ + + diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html index 9b03935efc..2e747fd5a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html @@ -19,14 +19,16 @@ - -
-
- + + +
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 279cdc538d..25cfb7ea61 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -7,14 +7,12 @@ novalidate val-form-manager> - -
+ + -
+
-
- - +
- - - +
+ + + + + + + +
+ +
+ +
+ + + + + + +
- -
- -
- - -
- -
-
+ + diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index 67f204aa2f..5aded20cfa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -165,196 +165,200 @@
-
+ - -
- + - - + +
+ - - + + - + + +
+ +
+ +
- +
+ + +
-
- - -
- -
- - -
- -
- - -
- -
- - -
- - - - - - - - +
+ + +
+ label="Change password" + label-key="general_changePassword" + state="changePasswordButtonState" + ng-if="model.changePasswordModel.isChanging === false" + size="s"> -
+ + + + + + + + + + +
+


Password reset to value: {{model.user.resetPasswordValue}}

+
-
-


Password reset to value: {{model.user.resetPasswordValue}}

-
+ +
+
+ Status: +
+
+ + {{model.user.userDisplayState.name}} + +
+
- -
-
- Status: +
+
+ Last login: +
+
+ {{ model.user.formattedLastLogin }} + {{ model.user.name | umbWordLimit:1 }} has not logged in yet +
-
- - {{model.user.userDisplayState.name}} - -
-
-
-
- Last login: +
+
+ Failed login attempts: +
+
+ {{ model.user.failedPasswordAttempts }} +
-
- {{ model.user.formattedLastLogin }} - {{ model.user.name | umbWordLimit:1 }} has not logged in yet -
-
-
-
- Failed login attempts: +
+
+ Last lockout date: +
+
+ + {{ model.user.name | umbWordLimit:1 }} hasn't been locked out + + {{ model.user.formattedLastLockoutDate }} +
-
- {{ model.user.failedPasswordAttempts }} -
-
-
-
- Last lockout date: +
+
+ Password is last changed: +
+
+ + The password hasn't been changed + + {{ model.user.formattedLastPasswordChangeDate }} +
-
- - {{ model.user.name | umbWordLimit:1 }} hasn't been locked out - - {{ model.user.formattedLastLockoutDate }} -
-
-
-
- Password is last changed: +
+
+ User is created: +
+
+ {{ model.user.formattedCreateDate }} +
-
- - The password hasn't been changed - - {{ model.user.formattedLastPasswordChangeDate }} -
-
-
-
- User is created: +
+
+ User is last updated: +
+
+ {{ model.user.formattedUpdateDate }} +
-
- {{ model.user.formattedCreateDate }} -
-
-
-
- User is last updated: -
-
- {{ model.user.formattedUpdateDate }} -
-
+ -
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 0d8d4f48d0..63348ecb9a 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -121,7 +121,7 @@ - + @@ -173,13 +173,6 @@ create.aspx - - xslt.ascx - ASPXCodeBehind - - - xslt.ascx - UserControlProxy.aspx ASPXCodeBehind @@ -235,20 +228,6 @@ umbracoPage.Master - - editstylesheet.aspx - ASPXCodeBehind - - - editstylesheet.aspx - - - EditStyleSheetProperty.aspx - ASPXCodeBehind - - - EditStyleSheetProperty.aspx - treeInit.aspx ASPXCodeBehind @@ -295,10 +274,6 @@ - - - - @@ -401,6 +376,7 @@ + @@ -418,25 +394,8 @@ - - - - - - - - - - - - - - - - - @@ -448,10 +407,8 @@ - - @@ -468,7 +425,6 @@ - @@ -565,26 +521,12 @@ - - - - - UserControl - - - UserControl - - - - - - @@ -592,37 +534,13 @@ Form - - - - - Form - - - - - - - - - - - - - - - - - - - @@ -738,11 +656,12 @@ - + + - + diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/dictionaryItem.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/dictionaryItem.gif deleted file mode 100644 index e9dc737967..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/dictionaryItem.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/help.png b/src/Umbraco.Web.UI/Umbraco/Images/editor/help.png deleted file mode 100644 index 4e559d0636..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/help.png and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insChildTemplateNew.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insChildTemplateNew.gif deleted file mode 100644 index 1463e9de5e..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insChildTemplateNew.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insField.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insField.gif deleted file mode 100644 index 3a3721dd35..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insField.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByLevel.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByLevel.gif deleted file mode 100644 index 2b80b7a43a..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByLevel.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByTree.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByTree.gif deleted file mode 100644 index 8b7eed666b..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insFieldByTree.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacro.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacro.gif deleted file mode 100644 index eeb3cdb444..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacro.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacroSB.png b/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacroSB.png deleted file mode 100644 index 2f54d14d37..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMacroSB.png and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMemberItem.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/insMemberItem.gif deleted file mode 100644 index 5eb1d4452e..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insMemberItem.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/insRazorMacro.png b/src/Umbraco.Web.UI/Umbraco/Images/editor/insRazorMacro.png deleted file mode 100644 index 2ad7831026..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/insRazorMacro.png and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/inshtml.GIF b/src/Umbraco.Web.UI/Umbraco/Images/editor/inshtml.GIF deleted file mode 100644 index 3442c48cd4..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/inshtml.GIF and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpageContent.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpageContent.gif deleted file mode 100644 index ad99338234..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpageContent.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpagePlaceHolder.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpagePlaceHolder.gif deleted file mode 100644 index 00de9bed9a..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/masterpagePlaceHolder.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/Images/editor/xslVisualize.gif b/src/Umbraco.Web.UI/Umbraco/Images/editor/xslVisualize.gif deleted file mode 100644 index b8dfba19b6..0000000000 Binary files a/src/Umbraco.Web.UI/Umbraco/Images/editor/xslVisualize.gif and /dev/null differ diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml index d8c9f8570f..f9d7fc616e 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml @@ -321,8 +321,6 @@ Žádne aktivní styly nejsou dostupné Umístěte, prosím, kurzor nalevo od těch dvou buňek, které chcete sloučit Nemužete rozdělit buňku, která nebyla sloučená. - Chyba ve zdroji XSLT - Soubor XSLT nebyl uložen, protože obsahoval chyby V nastavení datového typu použitého pro tuto vlastnost je chyba, zkontrolujte, prosím, datový typ @@ -817,11 +815,6 @@ Ujistěte se, prosím, že nemáte 2 šablony se stejným aliasem Šablona byla uložena Šablona byla uložena bez chyb! - XSLT nebyl uložen - XSLT obsahoval chybu - XSLT nemohl být uložen, zkontrolujte oprávnění k souboru - XSLT byl uložen - V XSLT nejsou žádné chyby Publikování obsahu bylo zrušeno Částečný pohled byl uložen Částečný pohled byl uložen bez chyb! @@ -990,7 +983,6 @@ Skripty Stylopisy Šablony - XSLT soubory Oprávnění Uživatele Typy Uživatelů Uživatelé @@ -1041,4 +1033,4 @@ Vaše nedávná historie Relace vyprší za - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml index dee52cff5c..e3b58903d1 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml @@ -337,8 +337,6 @@ Det er ingen aktive stiler eller formateringer på denne siden Sett markøren til venstre i de 2 cellene du ønsker å slå sammen Du kan ikke dele en celle som allerede er delt. - Feil i XSLT kode - XSLT ble ikke lagret på grunn av feil i koden Det er et problem dem datatypen som brukes til denne egenskapen. Kontroller innstillingene og prøv igjen. @@ -795,11 +793,6 @@ Vennlig hilsen Umbraco roboten Vennligst forviss deg om at du ikke har to maler med samme alias Malen ble lagret Malen ble lagret uten feil! - XSLT-koden ble ikke lagret - XSLT-koden inneholdt en feil - XSLT-koden ble ikke lagret, sjekk filrettigheter - XSLT lagret - Ingen feil i XSLT! Innhold avpublisert Delmal lagret Delmal lagret uten feil @@ -974,7 +967,6 @@ Vennlig hilsen Umbraco roboten Skript Stiler Maler - XSLT Filer Analytics Brukertillatelser Brukertyper typer @@ -1028,4 +1020,4 @@ Vennlig hilsen Umbraco roboten Din historikk Sesjonen utløper om - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml index b1997a1c0a..37c3dec60e 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml @@ -378,8 +378,6 @@ 讀取使用者控制項 %0% 錯誤 讀取使用者控制項 %0% 錯誤(組件:%0%,類別:%1%) 讀取巨集引擎腳本錯誤(檔案:%0%) - 分析XSLT檔案錯誤:%0% - 讀取XSLT檔案錯誤:%0% 請輸入標題 請選擇類型 圖片尺寸大於原始尺寸不會提高圖片品質,您確定要把圖片尺寸變大嗎? @@ -390,8 +388,6 @@ 沒有可用的樣式 請把游標放在您要合併的兩個儲存格中的左邊儲存格 非合併儲存格不能分離。 - XSLT源碼出錯 - XSLT未保存,因為包含錯誤。 這是此屬性所使用的資料類別設定錯誤,請檢查資料類別 @@ -959,11 +955,6 @@ 範本別名相同 範本已保存 範本保存,無錯誤。 - XSLT未保存 - XSLT有錯誤 - XSLT無法保存,請檢查許可權。 - XSLT已保存 - XSLT無錯誤 內容已取消發佈 片段視圖已保存 片段視圖保存,無錯誤。 @@ -1186,7 +1177,6 @@ 腳本 樣式表 範本 - XSLT文件 統計 @@ -1350,4 +1340,4 @@ 轉址追蹤器已開啟。 啟動轉址追蹤器錯誤,更多資訊請參閱您的紀錄檔。 - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/trees.Release.config b/src/Umbraco.Web.UI/config/trees.Release.config index 5842800085..83b85f62f6 100644 --- a/src/Umbraco.Web.UI/config/trees.Release.config +++ b/src/Umbraco.Web.UI/config/trees.Release.config @@ -19,7 +19,6 @@ - @@ -35,4 +34,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index b647bcbcb6..3076936cd5 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -20,7 +20,6 @@ - @@ -39,4 +38,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 3f06c398f5..534a9e2640 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -90,50 +90,6 @@ - - - - - 0 - - - - - - - - - - - - true - - Mvc - - - - - cs - vb - - - - - - - - 0 - - - - - - - - - - - - - - - - - @@ -294,4 +229,4 @@ umbracoApplicationUrl=""> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml index 1a082ab916..69983469ae 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml @@ -59,13 +59,6 @@ - -
XSLT file
- /create/xslt.ascx - - - -
member
/create/member.ascx @@ -110,20 +103,6 @@
- -
Language
- /create/language.ascx - - - -
- -
Language
- /create/language.ascx - - - -
diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml index 46c93c6ede..a1bddce17e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.xml @@ -68,13 +68,6 @@ - -
XSLT file
- /create/xslt.ascx - - - -
membergroup
/create/simple.ascx @@ -105,20 +98,6 @@
- -
Language
- /create/language.ascx - - - -
- -
Language
- /create/language.ascx - - - -
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 8491af027a..40f16ede1f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -488,8 +488,6 @@ Der er ingen aktive styles eller formatteringer på denne side Du skal stå til venstre for de 2 celler du ønsker at samle! Du kan ikke opdele en celle, som ikke allerede er delt. - Fejl i XSLT kode - Din XSLT er ikke opdateret, da det indeholdt en fejl Der er et problem med den datatype, der bruges til denn egenskab. Kontroller konfigurationen og prøv igen. @@ -1058,11 +1056,6 @@ Mange hilsner fra Umbraco robotten Undgå venligst at du har 2 templates med samme alias Skabelon gemt Skabelon gemt uden fejl! - XSLT'en blev ikke gemt - XSLT'en indeholdt fejl - XSLT kunne ikke gemmes, check filrettigheder - XSLT gemt - Der var ingen fejl i din XSLT! Indhold fjernet fra udgivelse Partial view gemt Partial view gemt uden fejl! @@ -1313,7 +1306,6 @@ Mange hilsner fra Umbraco robotten Scripts Stylesheets Skabeloner - XSLT-filer Analytics Brugertilladelser Bruger Typer diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml index 454b122b7a..fb0106f519 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml @@ -342,8 +342,6 @@ Keine aktiven Stile vorhanden Bitte platzieren Sie den Mauszeiger in die erste der zusammenzuführenden Zellen Sie können keine Zelle trennen, die nicht zuvor aus mehreren zusammengeführt wurde. - Fehler im XSLT - Das XSLT ist fehlerhaft und wurde daher nicht gespeichert. Es liegt ein Konfigurationsfehler beim Datentyp dieser Eigenschaft vor. Bitte prüfen Sie den Datentyp bzw. die Eigenschaft. @@ -817,11 +815,6 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die Bitte prüfen Sie, ob möglicherweise zwei Vorlagen den gleichen Alias verwenden. Vorlage gespeichert Vorlage erfolgreich gespeichert! - XSLT nicht gespeichert - Das XSLT enthält Fehler - XSLT kann nicht gespeichert werden. Bitte überprüfen Sie die Schreibrechte auf Dateiebene. - XSLT gespeichert - Keine Fehler im XSLT Veröffentlichung des Inhalts aufgehoben Partielle Ansicht gespeichert Partielle Ansicht ohne Fehler gespeichert. @@ -982,7 +975,6 @@ Ihr freundlicher Umbraco-Robot Client-Skripte Stylesheets Vorlagen - XSLT-Dateien Auswertungen Berechtigungen Benutzertypen diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 993f845831..ab33d9af1b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -434,8 +434,6 @@ Error loading userControl '%0%' Error loading customControl (Assembly: %0%, Type: '%1%') Error loading MacroEngine script (file: %0%) - "Error parsing XSLT file: %0% - "Error reading XSLT file: %0% Please enter a title Please choose a type You're about to make the picture larger than the original size. Are you sure that you want to proceed? @@ -446,8 +444,6 @@ No active styles available Please place cursor at the left of the two cells you wish to merge You cannot split a cell that hasn't been merged. - Error in XSLT source - The XSLT has not been saved, because it contained error(s) There is a configuration error with the data type used for this property, please check the data type @@ -1262,11 +1258,6 @@ To manage your website, simply open the Umbraco back office and start adding con Please make sure that you do not have 2 templates with the same alias Template saved Template saved without any errors! - XSLT not saved - XSLT contained an error - XSLT could not be saved, check file permissions - XSLT saved - No errors in XSLT Content unpublished Partial view saved Partial view saved without any errors! @@ -1668,7 +1659,6 @@ To manage your website, simply open the Umbraco back office and start adding con Scripts Stylesheets Templates - XSLT Files Analytics Users diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index d3326c57b3..b8df84ad25 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -201,6 +201,7 @@ Publish Published Published (pending changes) + Unpublished (pending changes) Publication Status Publish at Unpublish at @@ -231,6 +232,10 @@ Content root This value is hidden. If you need access to view this value please contact your website administrator. This value is hidden. + What languages would you like to publish? + No languages available to be published. + Published Languages. + Create a new Content Template from '%0%' @@ -503,8 +508,6 @@ Error loading userControl '%0%' Error loading customControl (Assembly: %0%, Type: '%1%') Error loading MacroEngine script (file: %0%) - "Error parsing XSLT file: %0% - "Error reading XSLT file: %0% Please enter a title Please choose a type You're about to make the picture larger than the original size. Are you sure that you want to proceed? @@ -515,8 +518,6 @@ No active styles available Please place cursor at the left of the two cells you wish to merge You cannot split a cell that hasn't been merged. - Error in XSLT source - The XSLT has not been saved, because it contained error(s) There is a configuration error with the data type used for this property, please check the data type @@ -1384,11 +1385,6 @@ To manage your website, simply open the Umbraco back office and start adding con Please make sure that you do not have 2 templates with the same alias Template saved Template saved without any errors! - XSLT not saved - XSLT contained an error - XSLT could not be saved, check file permissions - XSLT saved - No errors in XSLT Content unpublished Partial view saved Partial view saved without any errors! @@ -1796,7 +1792,6 @@ To manage your website, simply open the Umbraco back office and start adding con Scripts Stylesheets Templates - XSLT Files Analytics Users diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml index cf44e6e955..eea06e5a86 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml @@ -487,8 +487,6 @@ Error cargando userControl '%0%' Error cargandog customControl (Assembly: %0%, Type: '%1%') Error cargando MacroEngine script (file: %0%) - "Error analizando archivo XSLT: %0% - "Error leyendo archivo XSLT: %0% @@ -511,11 +509,6 @@ - - - - El XSLT no se ha guardado, porque contenía un error (s) Hay un error en la configuración el tipo de datos usado para esta propiedad, por favor revisa el tipo de datos. @@ -1160,11 +1153,6 @@ Por favor, asegúrate de que no hay 2 plantillas con el mismo alias Plantilla guardada Plantilla guardada sin errores - El XSLT no se ha guardado - El XSLT tenía un error - El XSLT no se ha podido guardar, comprueba los permisos de los ficheros - XSLT guardado - No hay errores en el XSLT Contenido oculto Vista parcial guardada Vista parcial guardada sin errores @@ -1530,7 +1518,6 @@ Scripts Hojas de estilo Plantillas - Archivos XSLT Analíticas Usuarios diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml index 37a3795d12..64034eb9df 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml @@ -388,8 +388,6 @@ Erreur de chargement du userControl '%0%' Erreur de chargement d'un customControl (Assembly: %0%, Type: '%1%') Erreur de chargement d'un script du MacroEngine (fichier : %0%) - "Erreur de parsing d'un fichier XSLT : %0% - "Erreur de lecture d'un fichier XSLT : %0% Veuillez entrer un titre Veuillez choisir un type Vous allez définir une taille d'image supérieure à sa taille d'origine. Êtes-vous certain(e) de vouloir continuer? @@ -400,8 +398,6 @@ Aucun style actif disponible Veuillez placer le curseur à gauche des deux cellules que vous voulez fusionner Vous ne pouvez pas scinder une cellule qui n'a pas été fusionnée. - Erreur dans le code source XSLT - Le XSLT n'a pas été sauvegardé car il contient des erreurs Il y a une erreur de configuration du type de données utilisé pour cette propriété, veuillez vérifier le type de données. @@ -990,11 +986,6 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Assurez-vous de ne pas avoir 2 modèles avec le même alias. Modèle sauvegardé Modèle sauvegardé sans aucune erreurs ! - Le XSLT n'a pas été sauvegardé - Le XSLT contenait une erreur - Le XSLT n'a pas pu être sauvegardé, vérifiez les permissions de fichier - Le XSLT a été sauvegardé - Aucune erreur dans le XSLT Contenu publié Vue partielle sauvegardée Vue partielle sauvegardée sans erreurs ! @@ -1251,7 +1242,6 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Scripts Feuilles de style Modèles - Fichiers XSLT Permissions utilisateur Types d'utilisateurs Utilisateurs diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml index 3443e4ddf3..0697b4512f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml @@ -261,8 +261,6 @@ סגנונות עיצוב פעילים לא זמינים יש למקם את הסמן משמאל לשני התאים אותם תרצה למזג אין אפשרות לפצל תא שלא מוזג לפני כן. - שגיאה במקור XSLT - קובץ ה- XSLT לא נשמר, הקובץ מכיל שגיאות. אודות @@ -720,11 +718,6 @@ To manage your website, simply open the Umbraco back office and start adding con שים לב שאין 2 תבניות עם אותו השם/כינוי התבנית נשמרה התבנית נשמרה ללא שגיאות! - הקובץ XSLT לאנשמר - הקובץ XSLT מכיל שגיאה - אין אפשרות לשמור את ה- XSLT, בדוק הרשאות קובץ לפני - הקובץ XSLT נשמר - אין שגיאות ב- XSLT השתמש בסינטקס CSS לדוגמא: h1, .redHeader, .blueTex @@ -892,7 +885,6 @@ To manage your website, simply open the Umbraco back office and start adding con סקריפטים גיליונות סגנון תבניות - קבצי XSLT הרשאות משתמש משתמש מקליד משתמש diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml index f1f00bbafe..50f8417bed 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml @@ -262,8 +262,6 @@ - - Info @@ -707,11 +705,6 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i - - - - - @@ -882,7 +875,6 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i Scripts Fogli di stile Templates - Files XSLT Permessi Utente Tipi di Utente Utenti diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml index 36e294f1b8..282f092966 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml @@ -370,8 +370,6 @@ userControl の読み込みエラー '%0%' customControl の読み込みエラー (アセンブリ: %0%, タイプ: '%1%') MacroEngine スクリプトの読み込みエラー (ファイル: %0%) - XSLT ファイル解析エラー: %0% - XSLT ファイル読み込みエラー: %0% タイトルを入力してください 型を選択してください 元画像より大きくしようとしていますが、本当によろしいのですか? @@ -382,8 +380,6 @@ 有効なスタイルがありません 結合したい2つのセルの左側にカーソルを置いてください このセルは結合されたものではないので分離する事はできません。 - XSLTソースにエラーがあります - 1つ以上のエラーがあるのでこのXSLTは保存できませんでした このプロパティに使用されているデータタイプにエラーがあります @@ -941,11 +937,6 @@ Runwayをインストールして作られた新しいウェブサイトがど 2つのテンプレートで同じエイリアスを使用していないか確認してください テンプレートを保存しました エラーなくテンプレートを保存しました! - XSLTは未保存です - XSLTにエラーがあります - XSLTを保存できません。アクセス権を確認してください。 - XSLTを保存しました - XSLTにエラーはありません コンテンツは公開されていません 部分ビュー保存しました 部分ビューをエラーなしで保存しました! @@ -1190,7 +1181,6 @@ Runwayをインストールして作られた新しいウェブサイトがど スクリプト スタイルシート テンプレート - XSLT ファイル アナリティクス ユーザーの権限 ユーザータイプ diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml index 9cbe4a0ee2..08445b7a79 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml @@ -259,8 +259,6 @@ 사용할 수 있는 스타일이 없습니다. 합치기 원하시는 두셀의 왼쪽에 커서를 가져다놓으세요 병합되지 않은 셀을 분리할 수 없습니다. - XSLT 소스에러 - 에러를 포함하고 있어 XSLT가 저장되지 않았습니다. 정보 @@ -702,11 +700,6 @@ 2 템플릿에 동일한 별칭이 적용되지 않았는지 확인하시기 바랍니다. 템플릿 저장 탬플릿이 에러없이 저장되었습니다! - XSLT 저장되지 않음 - XSLT 에 에러가 포함됨 - XSLT가 저장되지 않았습니다. 권한을 확인하세요 - XSLT 저장 - XSLT 에러없음 CSS 태그를 사용하세요 예: h1, .redHeader, .blueTex @@ -875,7 +868,6 @@ 스크립트 스타일시트 템플릿 - XSLT 파일 사용자권한 사용자 유형 사용자 @@ -920,4 +912,4 @@ 사용자 타입 작성자 - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index d398a839ec..84922e8ae9 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -398,8 +398,6 @@ Error bij het laden van userControl '%0%' Error bij het laden van customControl (Assembly: %0%, Type: '%1%') Error bij het laden van MacroEngine script (file: %0%) - "Error bij het parsen van XSLT file: %0% - "Error bij het laden van XSLT file: %0% Vul een titel in Selecteer een type U wilt een afbeelding groter maken dan de originele afmetingen. Weet je zeker dat je wilt doorgaan? @@ -410,8 +408,6 @@ Geen actieve stijlen beschikbaar Plaats de cursor links van de twee cellen die je wilt samenvoegen Je kunt een cel die is samengevoegd niet delen - Fout in de XSLT bron - De XSLT is niet opgeslagen omdat deze fout(en) bevat Er is een configuratiefout bij het gegevenstype dat wordt gebruikt voor deze eigenschap. Controleer het gegevenstype @@ -1003,11 +999,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Controleer dat je geen 2 tamplates met dezelfde naam hebt Template opgeslagen Template opgeslagen zonder fouten! - XSLT niet opgeslagen - XSLT bevat een fout - XSLT kon niet worden opgeslagen, controleer de bestandsbeveiliging - XSLT opgeslagen - Geen fouten in de XSLT! Inhoud gedepubliceerd Partial view opgeslagen Partial view opgeslagen zonder fouten! @@ -1264,7 +1255,6 @@ Om een vertalingstaak te sluiten, ga aub naar het detailoverzicht en klik op de Scripts Stylesheets Sjablonen - XSLT Bestanden Analytics diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml index d47b06d10e..05dcf4d181 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml @@ -487,8 +487,6 @@ Wystąpił błąd podczas ładowania userControl '%0%' Wystąpił błąd podczas ładowania customControl (Assembly: %0%, Typ: '%1%') Wystąpił błąd podczas ładowania skryptu MacroEngine (plik: %0%) - "Wystąpił błąd podczas parsowania pliku XSLT: %0% - "Wystąpił błąd odczytu pliku XSLT: %0% Proszę podać tytuł Proszę wybrać typ Chcesz utworzyć obraz większy niż rozmiar oryginalny. Czy na pewno chcesz kontynuować? @@ -499,8 +497,6 @@ Brak dostępnych aktywnych stylów Proszę ustaw kursor po lewej stronie dwóch komórek, które chcesz połączyć Nie możesz podzielić komórki, która nie była wcześniej połączona. - Błąd w źródle XSLT - Plik XSLT nie został zapisany, ponieważ wystąpiły błędy Wystąpił błąd konfiguracji związany z typem danych użytych we właściwościach, proszę sprawdź typ danych @@ -1142,11 +1138,6 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb Proszę się upewnić że nie ma dwóch szablonów o tym samym aliasie Szablon został zapisany Szablon został zapisany bez żadnych błędów! - Nie zapisano XSLT - XSLT zawiera błędy - Nie można zapisać XSLT, sprawdź uprawnienia dostępu do pliku - Zapisano XSLT - XSLT nie zawiera błedów Cofnięto publikację treści Częściowy Widok został zapisany Częściowy Widok został zapisany bez błędów! @@ -1502,7 +1493,6 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb Skrypty Arkusze stylów Szablony - Pliki XSLT Analizy Częściowe Widoki Pliki Makro Częściowych Widoków diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml index 1f3d3d9120..e3ed402e52 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml @@ -256,8 +256,6 @@ Nenhum estilo ativo disponível Favor colocar o cursos à esquerda das duas células que deseja mesclar Você não pode dividir uma célula que não foi mesclada. - Erro na fonta XSLT - O XSLT não foi salvo porque contém erro(s) Sobre @@ -690,11 +688,6 @@ Você pode publicar esta página e todas suas sub-páginas ao selecionar pub Favor confirmar que não existem 2 modelos com o mesmo apelido Modelo salvo Modelo salvo sem nenhum erro! - XSLT não salvo - XSLT continha um erro - XSLT não pode ser salvo, cheque as permissões do arquivo - XSLT salvo - Nenhum erro no XSLT Use sintaxe CSS ex: h1, .redHeader, .blueTex @@ -858,7 +851,6 @@ Para fechar a tarefa de tradução vá até os detalhes e clique no botão "Fech Scripts Stylesheets Modelos - Arquivos XSLT Permissões de usuário Tipos de Usuários Usuários diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index 009fbdb683..27a67a2c5e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -512,8 +512,6 @@ Ошибка загрузки пользовательского элемента управления '%0%' Ошибка загрузки внешнего типа (сборка: %0%, тип: '%1%') Ошибка загрузки макроса (файл: %0%) - "Ошибка разбора кода XSLT в файле: %0% - "Ошибка чтения XSLT-файла: %0% Ошибка в конфигурации типа данных, используемого для свойства, проверьте тип данных Укажите заголовок Выберите тип @@ -525,8 +523,6 @@ Не определен ни один доступный стиль Поместите курсор в левую из двух ячеек, которые хотите объединить Нельзя разделить ячейку, которая не была до этого объединена - Ошибка в XSLT-документе - XSLT-документ не был сохранен, так как он содержит одну или несколько ошибок О системе @@ -1389,11 +1385,6 @@ Шаблон сохранен без ошибок Проверка значений Ошибки, найденные при проверке значений, должны быть исправлены, чтобы было возможно сохранить документ - XSLT-документ не сохранен - XSLT-документ содержит одну или несколько ошибок - XSLT-документ не может быть сохранен, проверьте установки файловых разрешений - XSLT-документ сохранен - Ошибок в XSLT-документе нет Удалено %0% групп пользователей '%0%' была удалена Активировано %0% пользователей @@ -1649,7 +1640,6 @@ Стили CSS Шаблоны Пользователи - Файлы XSLT Доступны обновления diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml index d1fcb46576..b71a702e70 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml @@ -326,8 +326,6 @@ Det finns inga tillgängliga stilar Placera markören i den vänstra av de två celler du vill slå ihop Du kan inte dela en cell som inte är ihopslagen. - Fel i XSLT-scriptet - XSLT-scriptet har inte sparats eftersom det innehåller ett eller flera fel Om @@ -756,11 +754,6 @@ Kontrollera att du inte har två sidmallar med samma alias Sidmallen har sparats Sidmallen sparades utan fel - XSLT-scriptet sparades inte - XSLT-scriptet innehöll ett fel - XSLT-scripet kunde inte sparas, kontrollera filrättigheterna - XSLT-scriptet har sparats - Inga fel i XSLT-scriptet Använder CSS-syntax, t ex: h1, .redHeader, .blueTex @@ -905,7 +898,6 @@ Skript Stilmallar Sidmallar - XSLT-filer Användarrättigheter Användartyper Användare @@ -959,4 +951,4 @@ Översättare Din profil - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml index 8436976f9e..a26459b23e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/tr.xml @@ -314,8 +314,6 @@ Error loading userControl '%0%' Error loading customControl (Assembly: %0%, Type: '%1%') Error loading MacroEngine script (Dosya: %0%) - "Error parsing XSLT file: %0% - "Error reading XSLT file: %0% Lütfen bir başlık girin Lütfen bir tür seçin Orijinal boyutundan daha resmi büyütmek üzereyiz. Devam etmek istediğinizden emin misiniz? @@ -326,8 +324,6 @@ Henüz aktif stilleri Birleştirmek istediğiniz iki hücre solundaki imleci Lütfen Sen birleştirilmiş henüz bir hücreyi bölemezsiniz. - XSLT kaynak hatae - O hatayı içerdiği XSLT, kaydedilmemiş (ler)) Bu özellik için kullanılan veri türüne sahip bir yapılandırma hatası var, veri türünü kontrol edin @@ -894,11 +890,6 @@ To manage your website, simply open the Umbraco back office and start adding con Aynı isim ile 2 template bulunmadığından emin olun Template kaydedildi Template sorunsuz kaydedildi! - XSLT kaydedilmedi - XSLT hata içeriyor - XSLT kaydedilemedi, dosya izinlerini kontrol edin - XSLT kaydedildi - XSLT'de hata içermiyor İçerik yayından kaldırıldı Partial view kaydedildi Partial view sorunsuz kaydedildi! @@ -1081,7 +1072,6 @@ To manage your website, simply open the Umbraco back office and start adding con Scriptler Stil dosyaları Şablonlar - XSLT Dosyaları Analitikler diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index fed9f9d057..502dfabc4c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -393,8 +393,6 @@ 加载 userControl 时出错 '%0%' 加载 customControl 时出错(程序集: %0%, 类型: '%1%') 加载 MacroEngine 脚本时出错 (文件: %0%) - "解析 xslt 文件时出错: %0% - "读取 xslt 文件时出错: %0% 请输入标题 请选择类型 图片尺寸大于原始尺寸不会提高图片质量,您确定要把图片尺寸变大吗? @@ -405,8 +403,6 @@ 没有可用的样式 请把光标放在您要合并的两个单元格中的左边单元格 非合并单元格不能分离。 - XSLT源码出错 - XSLT未保存,因为包含错误。 此属性使用的数据类型存在配置错误, 请检查数据类型 @@ -992,11 +988,6 @@ 模板别名相同 模板已保存 模板保存,无错误。 - XSLT未保存 - XSLT有错误 - XSLT无法保存,请检查权限。 - XSLT已保存 - XSLT无错误 未发布内容 片段视图已保存 片段视图保存,无错误。 @@ -1247,7 +1238,6 @@ 脚本 样式表 模板 - XSLT文件 用户权限 Users 分部视图 diff --git a/src/Umbraco.Web.UI/umbraco/controls/ProgressBar.ascx b/src/Umbraco.Web.UI/umbraco/controls/ProgressBar.ascx deleted file mode 100644 index 6331a3a293..0000000000 --- a/src/Umbraco.Web.UI/umbraco/controls/ProgressBar.ascx +++ /dev/null @@ -1,2 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ProgressBar.ascx.cs" Inherits="umbraco.presentation.umbraco.controls.ProgressBar" %> -<%#Services.TextService.Localize("publish/inProgress")%>
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx deleted file mode 100644 index 99932fc2fe..0000000000 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx +++ /dev/null @@ -1,38 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PartialViewMacro.ascx.cs" Inherits="Umbraco.Web.UI.Umbraco.Create.PartialViewMacro" %> -<%@ Import Namespace="umbraco" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - * - - Cannot end with '/' or '.' - - - - - - - - - - - - - - -
")); - bt_insert.Enabled = true; - } - else - PlaceHolderParamters.Controls.Clear(); - - - if (assemblies.SelectedValue != "") - { - methods.Items.Clear(); - methods.Items.Add(new ListItem("Choose method", "")); - methods.Attributes.Add("onChange", "document.forms[0].submit()"); - List methodList = ht[assemblies.SelectedValue]; - foreach (string method in methodList) - { - ListItem li = new ListItem(method); - if (method == selectedMethod) - li.Selected = true; - methods.Items.Add(li); - } - } - } - - /// - /// Gets the XSLT assemblies and their methods. - /// - /// A list of assembly names linked to a list of method signatures. - private SortedList> GetXsltAssembliesAndMethods() - { - SortedList> _tempAssemblies = new SortedList>(); - - // add all extensions definied by macro - foreach(KeyValuePair extension in Umbraco.Web.Macros.XsltMacroEngine.GetXsltExtensions()) - _tempAssemblies.Add(extension.Key, GetStaticMethods(extension.Value.GetType())); - - // add the Umbraco library (not included in macro extensions) - _tempAssemblies.Add("umbraco.library", GetStaticMethods(typeof(umbraco.library))); - - return _tempAssemblies; - - } - - /// - /// Gets the static methods of the specified type, alphabetically sorted. - /// - /// The type. - /// A sortd list with method signatures. - private List GetStaticMethods(Type type) - { - List methods = new List(); - foreach (MethodInfo method in type.GetMethods()) - { - if (method.IsStatic) - { - // add method name to signature - StringBuilder methodSignature = new StringBuilder(method.Name); - - // add parameters to signature - methodSignature.Append('('); - ParameterInfo[] parameters = method.GetParameters(); - for(int i=0; i - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs deleted file mode 100644 index 2b5efd94bf..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltChooseExtension.aspx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.developer { - - - public partial class xsltChooseExtension { - - /// - /// assemblies control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList assemblies; - - /// - /// methods control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList methods; - - /// - /// PlaceHolderParamters control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder PlaceHolderParamters; - - /// - /// bt_insert control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_insert; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx deleted file mode 100644 index 34142b2805..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx +++ /dev/null @@ -1,61 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoDialog.Master" Codebehind="xsltInsertValueOf.aspx.cs" AutoEventWireup="True" Inherits="umbraco.developer.xsltInsertValueOf" %> -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - - - - - - - - - - - - - - - - - -

- <%= Services.TextService.Localize("or") %> <%= Services.TextService.Localize("cancel") %> -

-
\ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs deleted file mode 100644 index 1a0c1d1c90..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections; -using System.Linq; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Web.UI.Pages; - -namespace umbraco.developer -{ - /// - /// Summary description for xsltInsertValueOf. - /// - [WebformsPageTreeAuthorize(Constants.Trees.Xslt)] - public partial class xsltInsertValueOf : UmbracoEnsuredPage - { - protected void Page_Load(object sender, System.EventArgs e) - { - ArrayList preValuesSource = new ArrayList(); - - // Attributes - string[] attributes = {"@id", "@parentID", "@level", "@writerID", "@nodeType", "@template", "@sortOrder", "@createDate", "@creatorName", "@updateDate", "@nodeName", "@urlName", "@writerName", "@nodeTypeAlias", "@path"}; - foreach (string att in attributes) - preValuesSource.Add(att); - - // generic properties - string existingGenProps = ","; - var exclude = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray(); - - var propertyTypes = Services.ContentTypeService.GetAllPropertyTypeAliases(); - - foreach (var ptAlias in propertyTypes.Where(x => exclude.Contains(x) == false)) - { - if (!existingGenProps.Contains("," + ptAlias + ",")) - { - preValuesSource.Add(ptAlias); - - - existingGenProps += ptAlias + ","; - } - } - - - preValuesSource.Sort(); - preValues.DataSource = preValuesSource; - preValues.DataBind(); - preValues.Items.Insert(0, new ListItem("Prevalues...", "")); - - preValues.Attributes.Add("onChange", "if (this.value != '') document.getElementById('" + valueOf.ClientID + "').value = this.value"); - - if(!String.IsNullOrEmpty(Request.QueryString["value"])) - valueOf.Text = Request.QueryString["value"]; - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs deleted file mode 100644 index d02006dd22..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltInsertValueOf.aspx.designer.cs +++ /dev/null @@ -1,51 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.developer { - - - public partial class xsltInsertValueOf { - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// valueOf control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox valueOf; - - /// - /// preValues control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList preValues; - - /// - /// disableOutputEscaping control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox disableOutputEscaping; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx deleted file mode 100644 index 74c40eca64..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx +++ /dev/null @@ -1,55 +0,0 @@ -<%@ Page Language="C#" MasterPageFile="../../masterpages/umbracoDialog.Master" AutoEventWireup="true" - CodeBehind="xsltVisualize.aspx.cs" ValidateRequest="false" Inherits="umbraco.presentation.umbraco.developer.Xslt.xsltVisualize" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> -<%@ Register TagPrefix="cc2" Namespace="umbraco.controls" Assembly="Umbraco.Web" %> - - - - - - - - - - - -

- -
-
- -

- -

- - - -
- -
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs deleted file mode 100644 index fe4bca93e5..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; - -using System.Text; -using System.Xml; -using System.IO; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.UI.Pages; - -namespace umbraco.presentation.umbraco.developer.Xslt -{ - [WebformsPageTreeAuthorize(Constants.Trees.Xslt)] - public partial class xsltVisualize : UmbracoEnsuredPage - { - private const string XsltVisualizeCookieName = "UMB_XSLTVISPG"; - - protected void Page_Load(object sender, EventArgs e) - { - if (!IsPostBack) - { - // Check if cookie exists in the current request. - // zb-00004 #29956 : refactor cookies names & handling - if (Request.HasCookieValue(XsltVisualizeCookieName)) - contentPicker.Value = Request.GetCookieValue(XsltVisualizeCookieName); - } - - } - - protected void visualizeDo_Click(object sender, EventArgs e) - { - // get xslt file - string xslt; - if (xsltSelection.Value.Contains("", xsltSelection.Value); - xslt = Umbraco.Web.Macros.XsltMacroEngine.AddXsltExtensionsToHeader(xslt); - } - - int pageId; - if (int.TryParse(contentPicker.Value, out pageId) == false) - pageId = -1; - - // transform - string xsltResult; - try - { - xsltResult = Umbraco.Web.Macros.XsltMacroEngine.TestXsltTransform(Current.ProfilingLogger, xslt, pageId); - } - catch (Exception ee) - { - xsltResult = string.Format( - "

Error parsing the XSLT:

{0}

", - ee.ToString()); - } - - visualizeContainer.Visible = true; - - // update output - visualizeArea.Text = !String.IsNullOrEmpty(xsltResult) ? "
" + xsltResult + "
" : "

The XSLT didn't generate any output

"; - - - // add cookie with current page - // zb-00004 #29956 : refactor cookies names & handling - Response.Cookies.Set(new HttpCookie(XsltVisualizeCookieName, contentPicker.Value) - { - Expires = DateTime.Now + TimeSpan.FromMinutes(20) - }); - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs deleted file mode 100644 index cfc124ef7a..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/xsltVisualize.aspx.designer.cs +++ /dev/null @@ -1,87 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.developer.Xslt { - - - public partial class xsltVisualize { - - /// - /// Pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel1; - - /// - /// xsltSelection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden xsltSelection; - - /// - /// contentPicker control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.controls.ContentPicker contentPicker; - - /// - /// visualizeDo control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button visualizeDo; - - /// - /// visualizeContainer control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane visualizeContainer; - - /// - /// visualizePanel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel visualizePanel; - - /// - /// visualizeArea control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal visualizeArea; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs index f1df183074..d5dd83f278 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs @@ -1,16 +1,12 @@ using System; using System.Linq; using System.Globalization; -using System.Linq; using System.Web.UI; using System.Xml; using Umbraco.Core.IO; using Umbraco.Web; -using umbraco.cms.businesslogic; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.Services; -using Umbraco.Web.Composing; using Umbraco.Web.UI.Pages; using Umbraco.Web._Legacy.Actions; using Button = System.Web.UI.WebControls.Button; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx deleted file mode 100644 index 7feb7a60d2..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx +++ /dev/null @@ -1,48 +0,0 @@ -<%@ Page Language="C#" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="true" - CodeBehind="search.aspx.cs" Inherits="umbraco.presentation.dialogs.search" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %> - - - - - - -

Search

-

- -
-

- -

No results match

- -
-
- - - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs deleted file mode 100644 index d570b32d85..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Examine; -using System.Xml; -using Examine; -using Examine.LuceneEngine.SearchCriteria; -using System.Linq; -using Umbraco.Core; -using Umbraco.Core.Xml; - - -namespace umbraco.presentation.dialogs -{ - //fixme - is this even used anymore? - public partial class search : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - - protected override void OnInit(EventArgs e) - { - CurrentApp = IndexTypes.Content; - if (!string.IsNullOrEmpty(Request["app"])) - { - CurrentApp = Request["app"].ToLower(); - } - - base.OnInit(e); - } - - protected void Page_Load(object sender, EventArgs e) - { - Page.Form.DefaultButton = searchButton.UniqueID; - - if (!IsPostBack && Request["search"] != "") - { - keyword.Text = Request["search"]; - DoSearch(); - - } - } - - protected void search_Click(object sender, EventArgs e) - { - DoSearch(); - } - - private void DoSearch() - { - var txt = keyword.Text.ToLower(); - - int limit; - if (!int.TryParse(Request["limit"], out limit)) - { - limit = 100; - } - - //if it doesn't start with "*", then search only nodeName and nodeId - var internalSearcher = (CurrentApp == Constants.Applications.Members) - ? ExamineManager.Instance.GetSearcher(Constants.Examine.InternalMemberIndexer) - : ExamineManager.Instance.GetSearcher(Constants.Examine.InternalIndexer); - - //create some search criteria, make everything combined to be 'And' and only search the current app - var criteria = internalSearcher.CreateCriteria(CurrentApp, Examine.SearchCriteria.BooleanOperation.And); - - IEnumerable results; - if (txt.StartsWith("*")) - { - //if it starts with * then search all fields - results = internalSearcher.Search(txt.Substring(1), true); - } - else - { - var words = txt.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(w => w.ToLower().MultipleCharacterWildcard()).ToList(); - var operation = criteria.GroupedOr(new[] { "__nodeName", "__NodeId", "id" }, new[] { words[0] }); - words.RemoveAt(0); - foreach (var word in words) - operation = operation.And().GroupedOr(new[] { "__nodeName" }, new[] { word }); - - // ensure the user can only find nodes they are allowed to see - // fixme BORKED because we don't deal with all start nodes - if (Security.CurrentUser.StartContentIds.FirstOrDefault() > 0) - { - //TODO: This is not correct! This will not filter out seearches 'from' this node, this - // query is meant to search 'for' a specific node. - operation = operation.And().Id(Security.CurrentUser.StartContentIds.FirstOrDefault()); - } - - results = internalSearcher.Search(operation.Compile()); - } - - nothingFound.Visible = !results.Any(); - - searchResult.XPathNavigator = ResultsAsXml(results).CreateNavigator(); - } - - private XmlDocument ResultsAsXml(IEnumerable results) - { - var result = new XmlDocument(); - result.LoadXml(""); - - foreach (var r in results) - { - var x = XmlHelper.AddTextNode(result, "result", ""); - x.Attributes.Append(XmlHelper.AddAttribute(result, "id", r.Id.ToString(CultureInfo.InvariantCulture))); - x.Attributes.Append(XmlHelper.AddAttribute(result, "title", r.Fields["nodeName"])); - x.Attributes.Append(XmlHelper.AddAttribute(result, "author", r.Fields["writerName"])); - x.Attributes.Append(XmlHelper.AddAttribute(result, "changeDate", r.Fields["updateDate"])); - x.Attributes.Append(XmlHelper.AddAttribute(result, "type", r.Fields["nodeTypeAlias"])); - result.DocumentElement.AppendChild(x); - } - - return result; - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.designer.cs deleted file mode 100644 index efc60d5bed..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/search.aspx.designer.cs +++ /dev/null @@ -1,60 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.dialogs { - - - public partial class search { - - /// - /// Wizard control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Wizard; - - /// - /// keyword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox keyword; - - /// - /// searchButton control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button searchButton; - - /// - /// nothingFound control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel nothingFound; - - /// - /// searchResult control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Xml searchResult; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs index 2d8b100e93..b28b037ddd 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sendToTranslation.aspx.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using System.Net.Mail; using System.Web; -using System.Linq; using System.Web.UI.WebControls; using Umbraco.Core; using Umbraco.Core.Services; @@ -13,7 +12,6 @@ using Umbraco.Core.Models.Membership; using Umbraco.Web; using Umbraco.Web.Composing; using Umbraco.Web.UI.Pages; -using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Entities; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs index 93877b34d7..33728a19c5 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/umbracoField.aspx.cs @@ -34,10 +34,7 @@ namespace umbraco.dialogs pp_casing.Text = Services.TextService.Localize("templateEditor/casing"); pp_encode.Text = Services.TextService.Localize("templateEditor/encoding"); - if (UmbracoConfig.For.UmbracoSettings().Templates.UseAspNetMasterPages) - { - tagName.Value = "umbraco:Item"; - } + tagName.Value = "umbraco:Item"; using (var scope = Current.ScopeProvider.CreateScope()) { diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/ping.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/ping.aspx.cs deleted file mode 100644 index 3d8f80ef97..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/ping.aspx.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Web; -using System.Web.SessionState; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; - -namespace umbraco.presentation -{ - - [Obsolete("This class will be removed in future versions.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public partial class ping : System.Web.UI.Page - { - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) - { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); - } - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - } - #endregion - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/modals/ShowUmbracoTags.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/modals/ShowUmbracoTags.aspx.cs deleted file mode 100644 index 3d23d12393..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/modals/ShowUmbracoTags.aspx.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Umbraco.Core; - -namespace umbraco.cms.presentation.settings.modal -{ - /// - /// Summary description for ShowUmbracoTags. - /// - public partial class ShowUmbracoTags : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - - public ShowUmbracoTags() - { - CurrentApp = Constants.Applications.Settings.ToString(); - } - - public static string alias = ""; - protected void Page_Load(object sender, EventArgs e) - { - alias = Request.QueryString["alias"].Replace(" ", "").Trim(); - // Put user code to initialize the page here - } - - /// - /// Pane7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected Umbraco.Web._Legacy.Controls.Pane Pane7; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs deleted file mode 100644 index 5551228665..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs +++ /dev/null @@ -1,151 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.IO; -using System.Web.UI; -using umbraco.cms.presentation.Trees; -using Umbraco.Web._Legacy.Controls; -using Umbraco.Core; -using Umbraco.Web.UI.Pages; - -namespace umbraco.cms.presentation.settings.stylesheet -{ - /// - /// Summary description for editstylesheet. - /// - public partial class editstylesheet : UmbracoEnsuredPage - { - protected MenuButton SaveButton; - - private string filename; - protected string TreeSyncPath { get; private set; } - - public editstylesheet() - { - CurrentApp = Constants.Applications.Settings.ToString(); - } - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - filename = Request.QueryString["id"].Replace('\\', '/').TrimStart('/'); - - var editor = Panel1.NewTabPage(Services.TextService.Localize("stylesheet")); - editor.Controls.Add(Pane7); - - var props = Panel1.NewTabPage(Services.TextService.Localize("properties")); - props.Controls.Add(Pane8); - - SaveButton = Panel1.Menu.NewButton(); - SaveButton.Text = Services.TextService.Localize("save"); - SaveButton.ButtonType = MenuButtonType.Primary; - SaveButton.ID = "save"; - SaveButton.CssClass = "client-side"; - } - - protected void Page_Load(object sender, EventArgs e) - { - Panel1.Text = Services.TextService.Localize("stylesheet/editstylesheet"); - pp_name.Text = Services.TextService.Localize("name"); - pp_path.Text = Services.TextService.Localize("path"); - - var stylesheet = Services.FileService.GetStylesheetByName(filename); - if (stylesheet == null) // not found - throw new FileNotFoundException("Could not find file '" + filename + "'."); - - lttPath.Text = "" + stylesheet.VirtualPath + ""; - editorSource.Text = stylesheet.Content; - TreeSyncPath = BaseTree.GetTreePathFromFilePath(filename); - - // name derives from path, without the .css extension, clean for xss - NameTxt.Text = stylesheet.Path.TrimEnd(".css").CleanForXss('\\', '/'); - - if (IsPostBack == false) - { - ClientTools - .SyncTree(TreeSyncPath, false); - } - } - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("../webservices/codeEditorSave.asmx")); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("../webservices/legacyAjaxCalls.asmx")); - } - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.TabView Panel1; - - /// - /// Pane7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane7; - protected global::Umbraco.Web._Legacy.Controls.Pane Pane8; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_name; - - /// - /// NameTxt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox NameTxt; - - /// - /// pp_path control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_path; - - /// - /// lttPath control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lttPath; - - /// - /// pp_source control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_source; - - /// - /// editorSource control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.CodeArea editorSource; - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs deleted file mode 100644 index f87889147d..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/property/EditStyleSheetProperty.aspx.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Linq; -using System.Web; -using System.Web.UI.WebControls; -using umbraco.cms.presentation.Trees; -using Umbraco.Core; -using Umbraco.Web.UI; -using Umbraco.Core.Services; - -namespace umbraco.cms.presentation.settings.stylesheet -{ - /// - /// Summary description for EditStyleSheetProperty. - /// - public partial class EditStyleSheetProperty : Umbraco.Web.UI.Pages.UmbracoEnsuredPage - { - public EditStyleSheetProperty() - { - CurrentApp = Constants.Applications.Settings.ToString(); - - } - - private Umbraco.Core.Models.StylesheetProperty _stylesheetproperty; - private Umbraco.Core.Models.Stylesheet _sheet; - - /// - /// Raises the event. - /// - /// The object that contains the event data. - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - _sheet = Services.FileService.GetStylesheetByName(Request.QueryString["id"]); - if (_sheet == null) throw new InvalidOperationException("No stylesheet found with name: " + Request.QueryString["id"]); - - var property = HttpUtility.UrlDecode(Request.QueryString["prop"]); - var propName = IsPostBack ? OriginalName.Value : property; - - _stylesheetproperty = _sheet.Properties.FirstOrDefault(x => x.Name.InvariantEquals(propName)); - if (_stylesheetproperty == null) throw new InvalidOperationException("No stylesheet property found with name: " + property); - - Panel1.Text = Services.TextService.Localize("stylesheet/editstylesheetproperty"); - - var bt = Panel1.Menu.NewButton(); - bt.Click += SaveClick; - bt.Text = Services.TextService.Localize("save"); - bt.ToolTip = Services.TextService.Localize("save"); - bt.ButtonType = Umbraco.Web._Legacy.Controls.MenuButtonType.Primary; - bt.ID = "save"; - } - - - protected override void OnPreRender(EventArgs e) - { - NameTxt.Text = _stylesheetproperty.Name; - Content.Text = _stylesheetproperty.Value; - AliasTxt.Text = _stylesheetproperty.Alias; - OriginalName.Value = _stylesheetproperty.Name; - - prStyles.Attributes["style"] = _stylesheetproperty.Value; - - var path = _sheet.Path.Replace('\\', '/'); - var nodePath = string.Format(BaseTree.GetTreePathFromFilePath(path) + - ",{0}_{1}", path, HttpUtility.UrlEncode(_stylesheetproperty.Name)); - - ClientTools.SyncTree(nodePath, IsPostBack); - - prStyles.Attributes["style"] = _stylesheetproperty.Value; - - base.OnPreRender(e); - } - - private void SaveClick(object sender, EventArgs e) - { - _stylesheetproperty.Value = Content.Text; - _stylesheetproperty.Alias = AliasTxt.Text; - - if (_stylesheetproperty.Name != NameTxt.Text) - { - //to change the name we actually have to remove the property and re-add it as a different one - _sheet.AddProperty(new Umbraco.Core.Models.StylesheetProperty(NameTxt.Text, _stylesheetproperty.Alias, _stylesheetproperty.Value)); - _sheet.RemoveProperty(_stylesheetproperty.Name); - //reset our variable - _stylesheetproperty = _sheet.Properties.Single(x => x.Name == NameTxt.Text); - } - - Services.FileService.SaveStylesheet(_sheet); - - ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Save, Services.TextService.Localize("speechBubbles/editStylesheetPropertySaved"), ""); - } - - - override protected void OnInit(EventArgs e) - { - base.OnInit(e); - Content.TextMode = TextBoxMode.MultiLine; - Content.Height = 250; - Content.Width = 300; - } - - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.UmbracoPanel Panel1; - - /// - /// Pane7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane7; - - protected global::System.Web.UI.WebControls.HiddenField OriginalName; - - /// - /// NameTxt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox NameTxt; - - /// - /// AliasTxt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox AliasTxt; - - /// - /// Content control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox Content; - - /// - /// prStyles control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl prStyles; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Image.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Image.cs deleted file mode 100644 index 020a555fbc..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Image.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Web.UI; -using System.Web.UI.HtmlControls; -using Umbraco.Core.Media; -using Umbraco.Web.Media; - -namespace umbraco.presentation.templateControls -{ - [Obsolete("This is no longer used and will be removed in future versions")] - public class Image : HtmlImage - { - public string NodeId { get; set; } - public string Field { get; set; } - public string Provider { get; set; } - public string Parameters { get; set; } - - protected override void Render(HtmlTextWriter writer) - { - int id; - bool hasid = int.TryParse(NodeId, out id); - int? nodeId = hasid ? id : (int?)null; - - string url; - bool imageFound = ImageUrl.TryGetImageUrl(Src, Field, Provider, Parameters, nodeId, out url); - Src = url; - if (imageFound) - { - base.Render(writer); - } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate deleted file mode 100644 index b26ca5da89..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/InlineXslt.xsltTemplate +++ /dev/null @@ -1,14 +0,0 @@ - - ]> - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs index b015fdd02a..0439cfe89d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Item.cs @@ -82,42 +82,7 @@ namespace umbraco.presentation.templateControls get { return (string)ViewState["TextIfEmpty"] ?? String.Empty; } set { ViewState["TextIfEmpty"] = value; } } - - /// - /// Gets or sets the XPath expression used for the inline XSLT transformation. - /// - /// - /// The XPath expression, or an empty string to disable XSLT transformation. - /// The code {0} is used as a placeholder for the rendered field contents. - /// - [Bindable(true)] - [Category("Umbraco")] - [DefaultValue("")] - [Localizable(true)] - public string Xslt - { - get { return (string)ViewState["Xslt"] ?? String.Empty; } - set { ViewState["Xslt"] = value; } - } - - /// - /// Gets or sets a value indicating whether XML entity escaping of the XSLT transformation output is disabled. - /// - /// true HTML escaping is disabled; otherwise, false (default). - /// - /// This corresponds value to the disable-output-escaping parameter - /// of the XSLT value-of element. - /// - [Bindable(true)] - [Category("Umbraco")] - [DefaultValue(false)] - [Localizable(true)] - public bool XsltDisableEscaping - { - get { return ViewState["XsltEscape"] == null ? false : (bool)ViewState["XsltEscape"]; } - set { ViewState["XsltEscape"] = value; } - } - + [Bindable(true)] [Category("Umbraco")] [DefaultValue("")] diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs index 06c09225e2..13414c7648 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs @@ -56,12 +56,10 @@ namespace umbraco.presentation.templateControls // parse macros and execute the XSLT transformation on the result if not empty string renderOutput = renderOutputWriter.ToString(); - string xsltTransformedOutput = renderOutput.Trim().Length == 0 - ? String.Empty - : XsltTransform(item.Xslt, renderOutput, item.XsltDisableEscaping); + renderOutput = renderOutput.Trim().Length == 0 ? string.Empty : renderOutput; // handle text before/after - xsltTransformedOutput = AddBeforeAfterText(xsltTransformedOutput, helper.FindAttribute(item.LegacyAttributes, "insertTextBefore"), helper.FindAttribute(item.LegacyAttributes, "insertTextAfter")); - string finalResult = xsltTransformedOutput.Trim().Length > 0 ? xsltTransformedOutput : GetEmptyText(item); + renderOutput = AddBeforeAfterText(renderOutput, helper.FindAttribute(item.LegacyAttributes, "insertTextBefore"), helper.FindAttribute(item.LegacyAttributes, "insertTextAfter")); + string finalResult = renderOutput.Trim().Length > 0 ? renderOutput : GetEmptyText(item); //Don't parse urls if a content item is assigned since that is taken care // of with the value converters @@ -192,48 +190,6 @@ namespace umbraco.presentation.templateControls } - /// - /// Transforms the content using the XSLT attribute, if provided. - /// - /// The xpath expression. - /// The item's rendered content. - /// if set to true, escaping is disabled. - /// The transformed content if the XSLT attribute is present, otherwise the original content. - protected virtual string XsltTransform(string xpath, string itemData, bool disableEscaping) - { - if (!String.IsNullOrEmpty(xpath)) - { - // XML-encode the expression and add the itemData parameter to it - string xpathEscaped = xpath.Replace("<", "<").Replace(">", ">").Replace("\"", """); - string xpathExpression = string.Format(xpathEscaped, "$itemData"); - - // prepare support for XSLT extensions - StringBuilder namespaceList = new StringBuilder(); - StringBuilder namespaceDeclaractions = new StringBuilder(); - foreach (KeyValuePair extension in Umbraco.Web.Macros.XsltMacroEngine.GetXsltExtensions()) - { - namespaceList.Append(extension.Key).Append(' '); - namespaceDeclaractions.AppendFormat("xmlns:{0}=\"urn:{0}\" ", extension.Key); - } - - // add the XSLT expression into the full XSLT document, together with the needed parameters - string xslt = string.Format(Umbraco.Web.umbraco.presentation.umbraco.templateControls.Resources.InlineXslt, xpathExpression, disableEscaping ? "yes" : "no", - namespaceList, namespaceDeclaractions); - - // create the parameter - Dictionary parameters = new Dictionary(1); - parameters.Add("itemData", itemData); - - // apply the XSLT transformation - using (var xslReader = new XmlTextReader(new StringReader(xslt))) - { - var transform = Umbraco.Web.Macros.XsltMacroEngine.GetXsltTransform(xslReader, false); - return Umbraco.Web.Macros.XsltMacroEngine.ExecuteItemRenderer(Current.ProfilingLogger, transform, itemData); - } - } - return itemData; - } - protected string AddBeforeAfterText(string text, string before, string after) { if (!String.IsNullOrEmpty(text)) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs index 1847871ac5..d9da799f82 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Macro.cs @@ -150,7 +150,7 @@ namespace umbraco.presentation.templateControls if (extension.InvariantEndsWith(".cshtml") == false && extension.InvariantEndsWith(".vbhtml") == false) throw new NotSupportedException(""); - tempMacro.ScriptName = FileLocation; + tempMacro.MacroSource = FileLocation; tempMacro.MacroType = MacroTypes.PartialView; if (string.IsNullOrEmpty(Attributes["Cache"]) == false) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs deleted file mode 100644 index ab69886621..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.Designer.cs +++ /dev/null @@ -1,84 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.umbraco.presentation.umbraco.templateControls { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Web.umbraco.presentation.umbraco.templateControls.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8"?> - ///<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#x00A0;"> ]> - ///<xsl:stylesheet - /// version="1.0" - /// xmlns:xsl="http://www.w3.org/1999/XSL/Transform" - /// xmlns:msxml="urn:schemas-microsoft-com:xslt" - /// xmlns:umbraco.library="urn:umbraco.library" - /// {3} - /// exclude-result-prefixes="msxml umbraco.library {2}"> - ///<xsl:output method="xml" omit-xml-declaration="yes"/> - ///<xsl:param name="currentPage"/> - ///<xsl:param name="itemData"/> - ///<xsl:template match="/"><xsl:value-of select="{0}" disa [rest of string was truncated]";. - /// - internal static string InlineXslt { - get { - return ResourceManager.GetString("InlineXslt", resourceCulture); - } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx deleted file mode 100644 index 00bf9d42e1..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/Resources.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - inlinexslt.xsltTemplate;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx deleted file mode 100644 index 4347d8ede8..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebService Language="c#" Codebehind="CacheRefresher.asmx.cs" Class="umbraco.presentation.webservices.CacheRefresher" %> diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs deleted file mode 100644 index c10c35a6c0..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CacheRefresher.asmx.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Linq; -using System.Web; -using System.Web.Services; -using System.Xml; -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Identity.Owin; -using Newtonsoft.Json; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; -using Umbraco.Core.Sync; -using Umbraco.Core.Models.Identity; -using Umbraco.Core.Security; -using Umbraco.Core.Xml; -using Umbraco.Web.Security; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.presentation.webservices -{ - /// - /// CacheRefresher web service. - /// - [WebService(Namespace="http://umbraco.org/webservices/")] - public class CacheRefresher : WebService - { - #region Helpers - - // is the server originating from this server - ie are we self-messaging? - // in which case we should ignore the message because it's been processed locally already - internal static bool SelfMessage(string hash) - { - if (string.IsNullOrEmpty(hash)) return false; // no hash = don't know = not self - if (hash != WebServiceServerMessenger.GetCurrentServerHash()) return false; - - Current.Logger.Debug(() => - $"Ignoring self-message. (server: {NetworkHelper.MachineName}, appId: {HttpRuntime.AppDomainAppId}, hash: {hash})"); - - return true; - } - - private static ICacheRefresher GetRefresher(Guid id) - { - var refresher = Current.CacheRefreshers[id]; - if (refresher == null) - throw new InvalidOperationException("Cache refresher with ID \"" + id + "\" does not exist."); - return refresher; - } - - private static IJsonCacheRefresher GetJsonRefresher(Guid id) - { - return GetJsonRefresher(GetRefresher(id)); - } - - private static IJsonCacheRefresher GetJsonRefresher(ICacheRefresher refresher) - { - var jsonRefresher = refresher as IJsonCacheRefresher; - if (jsonRefresher == null) - throw new InvalidOperationException("Cache refresher with ID \"" + refresher.RefresherUniqueId + "\" does not implement " + typeof(IJsonCacheRefresher) + "."); - return jsonRefresher; - } - - private bool Authorized(string login, string rawPassword) - { - //TODO: This technique of passing the raw password in is a legacy idea and isn't really - // a very happy way to secure this webservice. To prevent brute force attacks, we need - // to ensure that the lockout policies are applied, though because we are not authenticating - // the user with their real password, we need to do this a bit manually. - - var userMgr = Context.GetOwinContext().GetBackOfficeUserManager(); - - var user = Current.Services.UserService.GetByUsername(login); - if (user == null) return false; - - var u = userMgr.FindById(user.Id); - if (u == null) return false; - - if (u.IsLockedOut) return false; - - if (user.RawPasswordValue != rawPassword) - { - //this performs the lockout and/or increments the access failed count - userMgr.AccessFailed(u.Id); - return false; - } - - return true; - } - - #endregion - - [WebMethod] - public void BulkRefresh(RefreshInstruction[] instructions, string appId, string login, string password) - { - if (Authorized(login, password) == false) return; - if (SelfMessage(appId)) return; // do not process self-messages - - // only execute distinct instructions - no sense in running the same one more than once - foreach (var instruction in instructions.Distinct()) - { - var refresher = GetRefresher(instruction.RefresherId); - switch (instruction.RefreshType) - { - case RefreshMethodType.RefreshAll: - refresher.RefreshAll(); - break; - case RefreshMethodType.RefreshByGuid: - refresher.Refresh(instruction.GuidId); - break; - case RefreshMethodType.RefreshById: - refresher.Refresh(instruction.IntId); - break; - case RefreshMethodType.RefreshByIds: // not directly supported by ICacheRefresher - foreach (var id in JsonConvert.DeserializeObject(instruction.JsonIds)) - refresher.Refresh(id); - break; - case RefreshMethodType.RefreshByJson: - GetJsonRefresher(refresher).Refresh(instruction.JsonPayload); - break; - case RefreshMethodType.RemoveById: - refresher.Remove(instruction.IntId); - break; - //case RefreshMethodType.RemoveByIds: // not directly supported by ICacheRefresher - // foreach (var id in JsonConvert.DeserializeObject(instruction.JsonIds)) - // refresher.Remove(id); - // break; - } - } - } - - [WebMethod] - public void RefreshAll(Guid uniqueIdentifier, string Login, string Password) - { - if (Authorized(Login, Password) == false) return; - GetRefresher(uniqueIdentifier).RefreshAll(); - } - - [WebMethod] - public void RefreshByGuid(Guid uniqueIdentifier, Guid Id, string Login, string Password) - { - if (Authorized(Login, Password) == false) return; - GetRefresher(uniqueIdentifier).Refresh(Id); - } - - [WebMethod] - public void RefreshById(Guid uniqueIdentifier, int Id, string Login, string Password) - { - if (Authorized(Login, Password) == false) return; - GetRefresher(uniqueIdentifier).Refresh(Id); - } - - [WebMethod] - public void RefreshByIds(Guid uniqueIdentifier, string jsonIds, string Login, string Password) - { - if (Authorized(Login, Password) == false) return; - var refresher = GetRefresher(uniqueIdentifier); - foreach (var id in JsonConvert.DeserializeObject(jsonIds)) - refresher.Refresh(id); - } - - [WebMethod] - public void RefreshByJson(Guid uniqueIdentifier, string jsonPayload, string Login, string Password) - { - if (Authorized(Login, Password) == false) return; - GetJsonRefresher(uniqueIdentifier).Refresh(jsonPayload); - } - - [WebMethod] - public void RemoveById(Guid uniqueIdentifier, int Id, string Login, string Password) - { - if (Authorized(Login, Password) == false) return; - GetRefresher(uniqueIdentifier).Remove(Id); - } - - [WebMethod] - public XmlDocument GetRefreshers(string Login, string Password) - { - if (Authorized(Login, Password) == false) return null; - - var xd = new XmlDocument(); - xd.LoadXml(""); - foreach (var cr in Current.CacheRefreshers) - { - var n = XmlHelper.AddTextNode(xd, "cacheRefresher", cr.Name); - n.Attributes.Append(XmlHelper.AddAttribute(xd, "uniqueIdentifier", cr.RefresherUniqueId.ToString())); - xd.DocumentElement.AppendChild(n); - } - return xd; - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx deleted file mode 100644 index 46767336e7..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebService Language="C#" CodeBehind="codeEditorSave.asmx.cs" Class="umbraco.presentation.webservices.codeEditorSave" %> diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs deleted file mode 100644 index f39dd051be..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/codeEditorSave.asmx.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Text.RegularExpressions; -using System.Web.Script.Services; -using System.Web.Services; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web.WebServices; -using Umbraco.Web.Macros; - -namespace umbraco.presentation.webservices -{ - /// - /// Summary description for codeEditorSave - /// - [WebService(Namespace = "http://tempuri.org/")] - [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - [ToolboxItem(false)] - [ScriptService] - public class codeEditorSave : UmbracoAuthorizedWebService - { - [WebMethod] - public string SaveXslt(string fileName, string oldName, string fileContents, bool ignoreDebugging) - { - if (AuthorizeRequest(Constants.Applications.Developer.ToString())) - { - IOHelper.EnsurePathExists(SystemDirectories.Xslt); - - // validate file - IOHelper.ValidateEditPath(IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName), - SystemDirectories.Xslt); - // validate extension - IOHelper.ValidateFileExtension(IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName), - new List() { "xsl", "xslt" }); - - StreamWriter SW; - string tempFileName = IOHelper.MapPath(SystemDirectories.Xslt + "/" + DateTime.Now.Ticks + "_temp.xslt"); - SW = File.CreateText(tempFileName); - SW.Write(fileContents); - SW.Close(); - - // Test the xslt - string errorMessage = ""; - - if (ignoreDebugging == false) - { - try - { - if (UmbracoContext.ContentCache.HasContent()) - XsltMacroEngine.TestXsltTransform(ProfilingLogger, fileContents); - - /* - // Check if there's any documents yet - string xpath = "/root/*"; - if (content.Instance.XmlContent.SelectNodes(xpath).Count > 0) - { - var macroXML = new XmlDocument(); - macroXML.LoadXml(""); - - var macroXSLT = new XslCompiledTransform(); - var umbPage = new page(content.Instance.XmlContent.SelectSingleNode("//* [@parentID = -1]")); - - var xslArgs = macro.AddMacroXsltExtensions(); - var lib = new library(umbPage); - xslArgs.AddExtensionObject("urn:umbraco.library", lib); - HttpContext.Current.Trace.Write("umbracoMacro", "After adding extensions"); - - // Add the current node - xslArgs.AddParam("currentPage", "", library.GetXmlNodeById(umbPage.PageID.ToString())); - - HttpContext.Current.Trace.Write("umbracoMacro", "Before performing transformation"); - - // Create reader and load XSL file - // We need to allow custom DTD's, useful for defining an ENTITY - var readerSettings = new XmlReaderSettings(); - readerSettings.ProhibitDtd = false; - using (var xmlReader = XmlReader.Create(tempFileName, readerSettings)) - { - var xslResolver = new XmlUrlResolver(); - xslResolver.Credentials = CredentialCache.DefaultCredentials; - macroXSLT.Load(xmlReader, XsltSettings.TrustedXslt, xslResolver); - xmlReader.Close(); - // Try to execute the transformation - var macroResult = new HtmlTextWriter(new StringWriter()); - macroXSLT.Transform(macroXML, xslArgs, macroResult); - macroResult.Close(); - - File.Delete(tempFileName); - } - } - */ - else - { - //errorMessage = Services.TextService.Localize("developer/xsltErrorNoNodesPublished"); - File.Delete(tempFileName); - //base.speechBubble(speechBubbleIcon.info, Services.TextService.Localize("errors/xsltErrorHeader"), "Unable to validate xslt as no published content nodes exist."); - } - } - catch (Exception errorXslt) - { - File.Delete(tempFileName); - - errorMessage = (errorXslt.InnerException ?? errorXslt).ToString(); - - // Full error message - errorMessage = errorMessage.Replace("\n", "
\n"); - //closeErrorMessage.Visible = true; - - // Find error - var m = Regex.Matches(errorMessage, @"\d*[^,],\d[^\)]", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - foreach (Match mm in m) - { - string[] errorLine = mm.Value.Split(','); - - if (errorLine.Length > 0) - { - var theErrorLine = int.Parse(errorLine[0]); - var theErrorChar = int.Parse(errorLine[1]); - - errorMessage = "Error in XSLT at line " + errorLine[0] + ", char " + errorLine[1] + - "
"; - errorMessage += ""; - - var xsltText = fileContents.Split("\n".ToCharArray()); - for (var i = 0; i < xsltText.Length; i++) - { - if (i >= theErrorLine - 3 && i <= theErrorLine + 1) - if (i + 1 == theErrorLine) - { - errorMessage += "" + (i + 1) + ": >>>  " + - Server.HtmlEncode(xsltText[i].Substring(0, theErrorChar)); - errorMessage += - "" + - Server.HtmlEncode( - xsltText[i].Substring(theErrorChar, - xsltText[i].Length - theErrorChar)). - Trim() + ""; - errorMessage += " <<<
"; - } - else - errorMessage += (i + 1) + ":      " + - Server.HtmlEncode(xsltText[i]) + "
"; - } - errorMessage += "
"; - } - } - } - } - - if (errorMessage == "" && fileName.ToLower().EndsWith(".xslt")) - { - //Hardcoded security-check... only allow saving files in xslt directory... - var savePath = IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName); - - if (savePath.StartsWith(IOHelper.MapPath(SystemDirectories.Xslt + "/"))) - { - //deletes the old xslt file - if (fileName != oldName) - { - - var p = IOHelper.MapPath(SystemDirectories.Xslt + "/" + oldName); - if (File.Exists(p)) - File.Delete(p); - } - - SW = File.CreateText(savePath); - SW.Write(fileContents); - SW.Close(); - errorMessage = "true"; - - - } - else - { - errorMessage = "Illegal path"; - } - } - - File.Delete(tempFileName); - - return errorMessage; - } - return "false"; - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs index ea22777168..13b37b21e8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs @@ -21,8 +21,6 @@ namespace umbraco.presentation.webservices [ScriptService] public class legacyAjaxCalls : UmbracoAuthorizedWebService { - private IUser _currentUser; - /// /// method to accept a string value for the node id. Used for tree's such as python /// and xslt since the file names are the node IDs