diff --git a/src/Umbraco.Compat7/Umbraco.Compat7.csproj b/src/Umbraco.Compat7/Umbraco.Compat7.csproj index 079cf0fac8..a95a929b34 100644 --- a/src/Umbraco.Compat7/Umbraco.Compat7.csproj +++ b/src/Umbraco.Compat7/Umbraco.Compat7.csproj @@ -31,8 +31,6 @@ - - diff --git a/src/Umbraco.Core/Events/EventDefinitionBase.cs b/src/Umbraco.Core/Events/EventDefinitionBase.cs index c0a061f80f..909868ac5b 100644 --- a/src/Umbraco.Core/Events/EventDefinitionBase.cs +++ b/src/Umbraco.Core/Events/EventDefinitionBase.cs @@ -7,27 +7,26 @@ namespace Umbraco.Core.Events { protected EventDefinitionBase(object sender, object args, string eventName = null) { - if (sender == null) throw new ArgumentNullException("sender"); - if (args == null) throw new ArgumentNullException("args"); - Sender = sender; - Args = args; + Sender = sender ?? throw new ArgumentNullException(nameof(sender)); + Args = args ?? throw new ArgumentNullException(nameof(args)); EventName = eventName; if (EventName.IsNullOrWhiteSpace()) { - var findResult = EventNameExtractor.FindEvent(sender, args, - //don't match "Ing" suffixed names - exclude:EventNameExtractor.MatchIngNames); + // don't match "Ing" suffixed names + var findResult = EventNameExtractor.FindEvent(sender, args, exclude:EventNameExtractor.MatchIngNames); if (findResult.Success == false) - throw new AmbiguousMatchException("Could not automatically find the event name, the event name will need to be explicitly registered for this event definition. Error: " + findResult.Result.Error); + throw new AmbiguousMatchException("Could not automatically find the event name, the event name will need to be explicitly registered for this event definition. " + + $"Sender: {sender.GetType()} Args: {args.GetType()}" + + " Error: " + findResult.Result.Error); EventName = findResult.Result.Name; } } - public object Sender { get; private set; } - public object Args { get; private set; } - public string EventName { get; private set; } + public object Sender { get; } + public object Args { get; } + public string EventName { get; } public abstract void RaiseEvent(); diff --git a/src/Umbraco.Core/Events/EventNameExtractor.cs b/src/Umbraco.Core/Events/EventNameExtractor.cs index 33137770d4..b46948af80 100644 --- a/src/Umbraco.Core/Events/EventNameExtractor.cs +++ b/src/Umbraco.Core/Events/EventNameExtractor.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Events { var events = CandidateEvents.GetOrAdd(senderType, t => { - return t.GetEvents(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + return t.GetEvents(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) //we can only look for events handlers with generic types because that is the only // way that we can try to find a matching event based on the arg type passed in .Where(x => x.EventHandlerType.IsGenericType) diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 20a474f71d..adf62397a8 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -343,6 +343,8 @@ namespace Umbraco.Core.IO internal ICompletable Shadow(Guid id) { + if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + var typed = _wrappers.ToArray(); var wrappers = new ShadowWrapper[typed.Length + 7]; var i = 0; diff --git a/src/Umbraco.Core/IO/ShadowFileSystems.cs b/src/Umbraco.Core/IO/ShadowFileSystems.cs index 1bfa2ebc18..f1ca0bffd9 100644 --- a/src/Umbraco.Core/IO/ShadowFileSystems.cs +++ b/src/Umbraco.Core/IO/ShadowFileSystems.cs @@ -13,6 +13,9 @@ namespace Umbraco.Core.IO // fixme - why are we managing logical call context here? should be bound // to the current scope, always => REFACTOR! but there should be something in // place (static?) to ensure we only have one concurrent shadow FS? + // + // => yes, that's _currentId - need to cleanup this entirely + // and, we probably need a way to stop shadowing entirely without cycling the app private const string ItemKey = "Umbraco.Core.IO.ShadowFileSystems"; diff --git a/src/Umbraco.Core/Models/PublicAccessEntry.cs b/src/Umbraco.Core/Models/PublicAccessEntry.cs index 5989f885cb..a458ce925a 100644 --- a/src/Umbraco.Core/Models/PublicAccessEntry.cs +++ b/src/Umbraco.Core/Models/PublicAccessEntry.cs @@ -27,6 +27,9 @@ namespace Umbraco.Core.Models _ruleCollection = new ObservableCollection(ruleCollection); _ruleCollection.CollectionChanged += _ruleCollection_CollectionChanged; + + foreach (var rule in _ruleCollection) + rule.AccessEntryId = Key; } public PublicAccessEntry(Guid id, int protectedNodeId, int loginNodeId, int noAccessNodeId, IEnumerable ruleCollection) @@ -40,6 +43,9 @@ namespace Umbraco.Core.Models _ruleCollection = new ObservableCollection(ruleCollection); _ruleCollection.CollectionChanged += _ruleCollection_CollectionChanged; + + foreach (var rule in _ruleCollection) + rule.AccessEntryId = Key; } void _ruleCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) diff --git a/src/Umbraco.Core/Persistence/FaultHandling/RetryDbConnection.cs b/src/Umbraco.Core/Persistence/FaultHandling/RetryDbConnection.cs index 24e7aeb495..ed438ccbc1 100644 --- a/src/Umbraco.Core/Persistence/FaultHandling/RetryDbConnection.cs +++ b/src/Umbraco.Core/Persistence/FaultHandling/RetryDbConnection.cs @@ -142,8 +142,8 @@ namespace Umbraco.Core.Persistence.FaultHandling protected override void Dispose(bool disposing) { - if (disposing && _inner != null) - _inner.Dispose(); + if (disposing) + _inner?.Dispose(); _inner = null; base.Dispose(disposing); } @@ -153,21 +153,30 @@ namespace Umbraco.Core.Persistence.FaultHandling _inner.Cancel(); } - public override string CommandText { get { return _inner.CommandText; } set { _inner.CommandText = value; } } + public override string CommandText + { + get => _inner.CommandText; + set => _inner.CommandText = value; + } - public override int CommandTimeout { get { return _inner.CommandTimeout; } set { _inner.CommandTimeout = value; } } + public override int CommandTimeout + { + get => _inner.CommandTimeout; + set => _inner.CommandTimeout = value; + } - public override CommandType CommandType { get { return _inner.CommandType; } set { _inner.CommandType = value; } } + public override CommandType CommandType + { + get => _inner.CommandType; + set => _inner.CommandType = value; + } protected override DbConnection DbConnection { - get - { - return _connection; - } + get => _connection; set { - if (value == null) throw new ArgumentNullException("value"); + if (value == null) throw new ArgumentNullException(nameof(value)); var connection = value as RetryDbConnection; if (connection == null) throw new ArgumentException("Value is not a FaultHandlingDbConnection instance."); if (_connection != null && _connection != connection) throw new Exception("Value is another FaultHandlingDbConnection instance."); @@ -181,12 +190,13 @@ namespace Umbraco.Core.Persistence.FaultHandling return _inner.CreateParameter(); } - protected override DbParameterCollection DbParameterCollection - { - get { return _inner.Parameters; } - } + protected override DbParameterCollection DbParameterCollection => _inner.Parameters; - protected override DbTransaction DbTransaction { get { return _inner.Transaction; } set { _inner.Transaction = value; } } + protected override DbTransaction DbTransaction + { + get => _inner.Transaction; + set => _inner.Transaction = value; + } public override bool DesignTimeVisible { get; set; } @@ -219,6 +229,10 @@ namespace Umbraco.Core.Persistence.FaultHandling _inner.Prepare(); } - public override UpdateRowSource UpdatedRowSource { get { return _inner.UpdatedRowSource; } set { _inner.UpdatedRowSource = value; } } + public override UpdateRowSource UpdatedRowSource + { + get => _inner.UpdatedRowSource; + set => _inner.UpdatedRowSource = value; + } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs index 6eb6b74e7c..1e652d829e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepositoryBase.cs @@ -1079,51 +1079,30 @@ AND umbracoNode.id <> @id", // query below is not safe + pointless if array is empty if (contentTypeIds.Length == 0) return; - // first part Gets all property groups including property type data even when no property type exists on the group - // second part Gets all property types including ones that are not on a group - // therefore the union of the two contains all of the property type and property group information we need - // NOTE: MySQL requires a SELECT * FROM the inner union in order to be able to sort . lame. + var sql = @"SELECT + pt.contentTypeId as contentTypeId, pt.uniqueID as ptUniqueID, pt.id as ptId, + pt.Alias as ptAlias, pt." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc, pt.mandatory as ptMandatory, + pt.Name as ptName, pt.sortOrder as ptSortOrder, pt.validationRegExp as ptRegExp, - var sqlBuilder = new StringBuilder(@"SELECT PG.contenttypeNodeId as contentTypeId, - PT.ptUniqueId as ptUniqueID, PT.ptId, PT.ptAlias, PT.ptDesc,PT.ptMandatory,PT.ptName,PT.ptSortOrder,PT.ptRegExp, - PT.dtId,PT.dtDbType,PT.dtPropEdAlias, - PG.id as pgId, PG.uniqueID as pgKey, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText - FROM cmsPropertyTypeGroup as PG - LEFT JOIN - ( - SELECT PT.uniqueID as ptUniqueId, PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc, - PT.mandatory as ptMandatory, PT.Name as ptName, PT.sortOrder as ptSortOrder, PT.validationRegExp as ptRegExp, - PT.propertyTypeGroupId as ptGroupId, - DT.dbType as dtDbType, DT.nodeId as dtId, DT.propertyEditorAlias as dtPropEdAlias - FROM cmsPropertyType as PT - INNER JOIN cmsDataType as DT - ON PT.dataTypeId = DT.nodeId - ) as PT - ON PT.ptGroupId = PG.id - WHERE (PG.contenttypeNodeId in (@contentTypeIds)) + dt.nodeId as dtId, dt.dbType as dtDbType, dt.propertyEditorAlias as dtPropEdAlias, - UNION + pg.id as pgId, pg.uniqueID as pgKey, pg.sortorder as pgSortOrder, pg." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText - SELECT PT.contentTypeId as contentTypeId, - PT.uniqueID as ptUniqueID, PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc, - PT.mandatory as ptMandatory, PT.Name as ptName, PT.sortOrder as ptSortOrder, PT.validationRegExp as ptRegExp, - DT.nodeId as dtId, DT.dbType as dtDbType, DT.propertyEditorAlias as dtPropEdAlias, - PG.id as pgId, PG.uniqueID as pgKey, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText - FROM cmsPropertyType as PT - INNER JOIN cmsDataType as DT - ON PT.dataTypeId = DT.nodeId - LEFT JOIN cmsPropertyTypeGroup as PG - ON PG.id = PT.propertyTypeGroupId - WHERE (PT.contentTypeId in (@contentTypeIds))"); +FROM cmsPropertyType as pt +INNER JOIN cmsDataType as dt ON pt.dataTypeId = dt.nodeId +LEFT JOIN cmsPropertyTypeGroup as pg ON pg.id = pt.propertyTypeGroupId - sqlBuilder.AppendLine(" ORDER BY (pgId)"); +WHERE pt.contentTypeId IN (@contentTypeIds) + +ORDER BY pgId +"; //NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count! // Since there are 2 groups of params, it will be half! if (((contentTypeIds.Length / 2) - 1) > 2000) throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters"); - var result = db.Fetch(sqlBuilder.ToString(), new { contentTypeIds = contentTypeIds }); + var result = db.Fetch(sql, new { contentTypeIds = contentTypeIds }); foreach (var contentTypeId in contentTypeIds) { diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index b3da57b98b..4a7a5fef7c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -296,11 +296,6 @@ AND umbracoNode.id <> @id", return GetCachedPreValueCollection(dataTypeId); } - internal static string GetCacheKeyRegex(int preValueId) - { - return CacheKeys.DataTypePreValuesCacheKey + @"[-\d]+-([\d]*,)*" + preValueId + @"(?!\d)[,\d$]*"; - } - public string GetPreValueAsString(int preValueId) { var collections = IsolatedCache.GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_"); diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index 687839c943..61c5c74481 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -101,6 +101,10 @@ namespace Umbraco.Core.Persistence.Repositories Database.Insert(rule); } + //update the id so HasEntity is correct + foreach (var rule in entity.Rules) + rule.Id = rule.Key.GetHashCode(); + entity.ResetDirtyProperties(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBaseOfTIdTEntity.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBaseOfTIdTEntity.cs index 933124e11c..161386b267 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBaseOfTIdTEntity.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBaseOfTIdTEntity.cs @@ -65,6 +65,8 @@ namespace Umbraco.Core.Persistence.Repositories case RepositoryCacheMode.Scoped: provider = scope.IsolatedRuntimeCache; break; + case RepositoryCacheMode.None: + return new NullCacheProvider(); // fixme cache instance default: throw new Exception("oops: cache mode."); } @@ -130,17 +132,21 @@ namespace Umbraco.Core.Persistence.Repositories return _cachePolicy = NoCacheRepositoryCachePolicy.Instance; // create the cache policy using IsolatedCache which is either global - // or scoped depending on the repository cache mode for the current scope - _cachePolicy = CreateCachePolicy(IsolatedCache); + // or scoped depending on the repository cache mode for the current scope var scope = UnitOfWork.Scope; switch (scope.RepositoryCacheMode) { case RepositoryCacheMode.Default: + _cachePolicy = CreateCachePolicy(IsolatedCache); break; case RepositoryCacheMode.Scoped: + _cachePolicy = CreateCachePolicy(IsolatedCache); var globalIsolatedCache = GetIsolatedCache(GlobalCache.IsolatedRuntimeCache); _cachePolicy = _cachePolicy.Scoped(globalIsolatedCache, scope); break; + case RepositoryCacheMode.None: + _cachePolicy = NoCacheRepositoryCachePolicy.Instance; + break; default: throw new Exception("oops: cache mode."); } diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 4e7aa45225..a7021bdd57 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -2,6 +2,7 @@ using System; using System.Data; using System.Data.Common; using System.Data.SqlClient; +using System.Linq; using System.Text; using NPoco; using StackExchange.Profiling; @@ -206,7 +207,7 @@ namespace Umbraco.Core.Persistence _logger.Error("Exception (" + InstanceId + ").", x); _logger.Debug("At:\r\n" + Environment.StackTrace); if (EnableSqlTrace == false) - _logger.Debug("Sql:\r\n" + CommandToString(_cmd)); + _logger.Debug("Sql:\r\n" + CommandToString(LastSQL, LastArgs)); base.OnException(x); } @@ -232,23 +233,28 @@ namespace Umbraco.Core.Persistence base.OnExecutingCommand(cmd); } - private static string CommandToString(DbCommand cmd) + private string CommandToString(DbCommand cmd) + { + return CommandToString(cmd.CommandText, cmd.Parameters.Cast().Select(x => x.Value).ToArray()); + } + + private string CommandToString(string sql, object[] args) { var sb = new StringBuilder(); #if DEBUG_DATABASES sb.Append(InstanceId); sb.Append(": "); #endif - sb.Append(cmd.CommandText); - if (cmd.Parameters.Count > 0) + sb.Append(sql); + if (args.Length > 0) sb.Append(" --"); var i = 0; - foreach (DbParameter p in cmd.Parameters) + foreach (var arg in args) { sb.Append(" @"); sb.Append(i++); sb.Append(":"); - sb.Append(p.Value); + sb.Append(arg); } return sb.ToString(); diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs index 5c268175a2..5a201a7dcc 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs @@ -52,6 +52,9 @@ namespace Umbraco.Core.Persistence.UnitOfWork #region IDatabaseContext + // fixme - stop using the actual Database here - it forces the creation of the DB + // should have a reference to a IDatabaseContext to use instead! + /// public ISqlSyntaxProvider SqlSyntax => Database.SqlSyntax; diff --git a/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs b/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs index 23cf94d327..8b8fc0cf35 100644 --- a/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs +++ b/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs @@ -22,6 +22,16 @@ /// Reads from, and writes to, a scope-local cache. /// Upon scope completion, clears the global L2 cache. /// - Scoped = 2 + Scoped = 2, + + /// + /// No cache. + /// + /// + /// Bypasses caches entirely. + /// Upon scope completion, clears the global L2 cache. + /// fixme - what about a L1 cache? + /// + None = 3 } } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index dbeb5f1d0d..0074af6ac7 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -65,7 +65,7 @@ namespace Umbraco.Core.Scoping #if DEBUG_SCOPES _scopeProvider.RegisterScope(this); - Console.WriteLine("create " + _instanceId.ToString("N").Substring(0, 8)); + Console.WriteLine("create " + InstanceId.ToString("N").Substring(0, 8)); #endif if (detachable) @@ -88,16 +88,19 @@ namespace Umbraco.Core.Scoping ParentScope = parent; // cannot specify a different mode! - if (repositoryCacheMode != RepositoryCacheMode.Unspecified && parent.RepositoryCacheMode != repositoryCacheMode) - throw new ArgumentException("Cannot be different from parent.", nameof(repositoryCacheMode)); + // fixme - means that it's OK to go from L2 to None for reading purposes, but writing would be BAD! + // this is for XmlStore that wants to bypass caches when rebuilding XML (same for NuCache) + if (repositoryCacheMode != RepositoryCacheMode.Unspecified && parent.RepositoryCacheMode > repositoryCacheMode) + throw new ArgumentException($"Value '{repositoryCacheMode}' cannot be lower than parent value '{parent.RepositoryCacheMode}'.", nameof(repositoryCacheMode)); // cannot specify a dispatcher! if (_eventDispatcher != null) - throw new ArgumentException("Cannot be specified on nested scope.", nameof(eventDispatcher)); + throw new ArgumentException("Value cannot be specified on nested scope.", nameof(eventDispatcher)); // cannot specify a different fs scope! + // fixme - in fact, can be 'true' only on outer scope, and false does not make much sense if (scopeFileSystems != null && parent._scopeFileSystem != scopeFileSystems) - throw new ArgumentException("Cannot be different from parent.", nameof(scopeFileSystems)); + throw new ArgumentException($"Value '{scopeFileSystems.Value}' be different from parent value '{parent._scopeFileSystem}'.", nameof(scopeFileSystems)); } else { @@ -334,8 +337,11 @@ namespace Umbraco.Core.Scoping _logger.Debug("Dispose error (" + (ambient == null ? "no" : "other") + " ambient)"); if (ambient == null) throw new InvalidOperationException("Not the ambient scope (no ambient scope)."); - var infos = _scopeProvider.GetScopeInfo(ambient); - throw new InvalidOperationException("Not the ambient scope (see current ambient ctor stack trace).\r\n" + infos.CtorStack); + var ambientInfos = _scopeProvider.GetScopeInfo(ambient); + var disposeInfos = _scopeProvider.GetScopeInfo(this); + throw new InvalidOperationException("Not the ambient scope (see ctor stack traces).\r\n" + + "- ambient ctor ->\r\n" + ambientInfos.CtorStack + "\r\n" + + "- dispose ctor ->\r\n" + disposeInfos.CtorStack + "\r\n"); #else throw new InvalidOperationException("Not the ambient scope."); #endif diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index 3318cf81e7..97e5ea5cc3 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Data; using System.Runtime.Remoting.Messaging; +using System.Text; using System.Web; using Umbraco.Core.Composing; using Umbraco.Core.Events; @@ -110,8 +111,8 @@ namespace Umbraco.Core.Scoping if (StaticCallContextObjects.TryGetValue(objectKey, out object callContextObject)) { #if DEBUG_SCOPES - Logging.LogHelper.Debug("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8)); - //Logging.LogHelper.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); + Current.Logger.Debug("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8)); + //_logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif return (T)callContextObject; } @@ -119,7 +120,7 @@ namespace Umbraco.Core.Scoping // hard to inject into a static method :( Current.Logger.Warn("Missed " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8)); #if DEBUG_SCOPES - //Logging.LogHelper.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); + //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif return null; } @@ -155,8 +156,8 @@ namespace Umbraco.Core.Scoping lock (StaticCallContextObjectsLock) { #if DEBUG_SCOPES - Logging.LogHelper.Debug("Remove Object " + objectKey.ToString("N").Substring(0, 8)); - //Logging.LogHelper.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); + Current.Logger.Debug("Remove Object " + objectKey.ToString("N").Substring(0, 8)); + //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif StaticCallContextObjects.Remove(objectKey); } @@ -170,8 +171,8 @@ namespace Umbraco.Core.Scoping lock (StaticCallContextObjectsLock) { #if DEBUG_SCOPES - Logging.LogHelper.Debug("AddObject " + objectKey.ToString("N").Substring(0, 8)); - //Logging.LogHelper.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); + Current.Logger.Debug("AddObject " + objectKey.ToString("N").Substring(0, 8)); + //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 24)); #endif StaticCallContextObjects.Add(objectKey, value); } @@ -466,7 +467,7 @@ namespace Umbraco.Core.Scoping lock (StaticScopeInfosLock) { if (StaticScopeInfos.ContainsKey(scope)) throw new Exception("oops: already registered."); - Logging.LogHelper.Debug("Register " + scope.InstanceId.ToString("N").Substring(0, 8)); + _logger.Debug("Register " + scope.InstanceId.ToString("N").Substring(0, 8)); StaticScopeInfos[scope] = new ScopeInfo(scope, Environment.StackTrace); } } @@ -490,12 +491,12 @@ namespace Umbraco.Core.Scoping { if (sb.Length > 0) sb.Append(" < "); sb.Append(s.InstanceId.ToString("N").Substring(0, 8)); - var ss = s as IScopeInternal; - s = ss == null ? null : ss.ParentScope; + var ss = s as Scope; + s = ss?.ParentScope; } - Logging.LogHelper.Debug("Register " + (context ?? "null") + " context " + sb); + Current.Logger.Debug("Register " + (context ?? "null") + " context " + sb); if (context == null) info.NullStack = Environment.StackTrace; - //Logging.LogHelper.Debug("At:\r\n" + Head(Environment.StackTrace, 16)); + //Current.Logger.Debug("At:\r\n" + Head(Environment.StackTrace, 16)); info.Context = context; } } @@ -522,7 +523,7 @@ namespace Umbraco.Core.Scoping // enable this by default //Console.WriteLine("unregister " + scope.InstanceId.ToString("N").Substring(0, 8)); StaticScopeInfos.Remove(scope); - Logging.LogHelper.Debug("Remove " + scope.InstanceId.ToString("N").Substring(0, 8)); + _logger.Debug("Remove " + scope.InstanceId.ToString("N").Substring(0, 8)); // instead, enable this to keep *all* scopes // beware, there can be a lot of scopes! @@ -544,16 +545,16 @@ namespace Umbraco.Core.Scoping CtorStack = ctorStack; } - public IScope Scope { get; private set; } // the scope itself + public IScope Scope { get; } // the scope itself // the scope's parent identifier - public Guid Parent { get { return (Scope is NoScope || ((Scope) Scope).ParentScope == null) ? Guid.Empty : ((Scope) Scope).ParentScope.InstanceId; } } + public Guid Parent => ((Scope) Scope).ParentScope == null ? Guid.Empty : ((Scope) Scope).ParentScope.InstanceId; - public DateTime Created { get; private set; } // the date time the scope was created + public DateTime Created { get; } // the date time the scope was created public bool Disposed { get; set; } // whether the scope has been disposed already public string Context { get; set; } // the current 'context' that contains the scope (null, "http" or "lcc") - public string CtorStack { get; private set; } // the stacktrace of the scope ctor + public string CtorStack { get; } // the stacktrace of the scope ctor public string DisposedStack { get; set; } // the stacktrace when disposed public string NullStack { get; set; } // the stacktrace when the 'context' that contains the scope went null } diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index b85f82ee24..7267d1d420 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -164,7 +164,11 @@ namespace Umbraco.Core.Services throw new ArgumentException("No content with that id.", nameof(parentId)); var content = new Content(name, parentId, contentType); - CreateContent(null, content, parent, userId, false); + using (var uow = UowProvider.CreateUnitOfWork()) + { + CreateContent(uow, content, parent, userId, false); + uow.Complete(); + } return content; } @@ -189,7 +193,11 @@ namespace Umbraco.Core.Services throw new ArgumentException("No content type with that alias.", nameof(contentTypeAlias)); var content = new Content(name, -1, contentType); - CreateContent(null, content, null, userId, false); + using (var uow = UowProvider.CreateUnitOfWork()) + { + CreateContent(uow, content, null, userId, false); + uow.Complete(); + } return content; } @@ -940,6 +948,11 @@ namespace Umbraco.Core.Services return OperationStatus.Attempt.Cancel(evtMsgs); } + if (string.IsNullOrWhiteSpace(content.Name)) + { + throw new ArgumentException("Cannot save content with empty name."); + } + var isNew = content.IsNewEntity(); uow.WriteLock(Constants.Locks.ContentTree); @@ -1533,6 +1546,7 @@ namespace Umbraco.Core.Services // because we want it now, we have to calculate it by ourselves //paths[content.Id] = content.Path; paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : "-1") : parent.Path) + "," + content.Id; + Console.WriteLine("path " + content.Id + " = " + paths[content.Id]); var descendants = GetDescendants(content); foreach (var descendant in descendants) @@ -1540,14 +1554,16 @@ namespace Umbraco.Core.Services moves.Add(Tuple.Create(descendant, descendant.Path)); // capture original path // update path and level since we do not update parentId + if (paths.ContainsKey(descendant.ParentId) == false) + Console.WriteLine("oops on " + descendant.ParentId + " for " + content.Path + " " + parent?.Path); descendant.Path = paths[descendant.Id] = paths[descendant.ParentId] + "," + descendant.Id; + Console.WriteLine("path " + descendant.Id + " = " + paths[descendant.Id]); descendant.Level += levelDelta; PerformMoveContentLocked(repository, descendant, userId, trash); } } - private static void PerformMoveContentLocked(IContentRepository repository, IContent content, int userId, - bool? trash) + private static void PerformMoveContentLocked(IContentRepository repository, IContent content, int userId, bool? trash) { if (trash.HasValue) ((ContentBase) content).Trashed = trash.Value; content.WriterId = userId; @@ -1868,7 +1884,7 @@ namespace Umbraco.Core.Services { uow.ReadLock(Constants.Locks.ContentTree); var repository = uow.CreateRepository(); - return GetPublishedDescendantsLocked(repository, content); + return GetPublishedDescendantsLocked(repository, content).ToArray(); // ToArray important in uow! } } @@ -2067,7 +2083,7 @@ namespace Umbraco.Core.Services { if (HasChildren(content.Id)) { - var descendants = GetPublishedDescendants(content).ToArray(); + var descendants = GetPublishedDescendantsLocked(repository, content).ToArray(); uow.Events.Dispatch(Published, this, new PublishEventArgs(descendants, false, false), "Published"); } changeType = TreeChangeTypes.RefreshBranch; // whole branch diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTItemTService.cs b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTItemTService.cs index 73a58b1671..7ecc0e361f 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTItemTService.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTItemTService.cs @@ -39,11 +39,12 @@ namespace Umbraco.Core.Services protected void OnChanged(IScopeUnitOfWork uow, ContentTypeChange.EventArgs args) { - uow.Events.Dispatch(Changed, This, args); + uow.Events.Dispatch(Changed, This, args, "Changed"); } protected void OnUowRefreshedEntity(ContentTypeChange.EventArgs args) { + // that one is always immediate (not dispatched, transactional) UowRefreshedEntity.RaiseEvent(args, This); } diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 0873670f87..88b4e5e2eb 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -391,6 +391,9 @@ namespace Umbraco.Core.Services return; } + if (string.IsNullOrWhiteSpace(item.Name)) + throw new ArgumentException("Cannot save item with empty name."); + var repo = uow.CreateRepository(); uow.WriteLock(WriteLockIds); @@ -780,7 +783,7 @@ namespace Umbraco.Core.Services var evtMsgs = EventMessagesFactory.Get(); var containerObjectType = ContainerObjectType; - if (container.ContainedObjectType != containerObjectType) + if (container.ContainerObjectType != containerObjectType) { var ex = new InvalidOperationException("Not a container of the proper type."); return OperationStatus.Attempt.Fail(evtMsgs, ex); diff --git a/src/Umbraco.Core/Services/MacroService.cs b/src/Umbraco.Core/Services/MacroService.cs index 27b4a91df5..96249512b3 100644 --- a/src/Umbraco.Core/Services/MacroService.cs +++ b/src/Umbraco.Core/Services/MacroService.cs @@ -132,6 +132,11 @@ namespace Umbraco.Core.Services return; } + if (string.IsNullOrWhiteSpace(macro.Name)) + { + throw new ArgumentException("Cannot save macro with empty name."); + } + var repository = uow.CreateRepository(); repository.AddOrUpdate(macro); diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 4866c7da05..acb2af404f 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -156,7 +156,6 @@ - @@ -176,10 +175,6 @@ - - - - diff --git a/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs b/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs index 66ec612b16..ece846e0e4 100644 --- a/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs +++ b/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs @@ -2,23 +2,31 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Umbraco.Core.Composing; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; using Umbraco.Web.Cache; namespace Umbraco.Tests.Cache { [TestFixture] - public class CacheRefresherEventHandlerTests + [UmbracoTest(WithApplication = true)] + public class CacheRefresherEventHandlerTests : UmbracoTestBase { [Test] public void Can_Find_All_Event_Handlers() { - var testObjects = new TestObjects(null); - var serviceContext = testObjects.GetServiceContextMock(); + // fixme - cannot work with mocks + // because the events are defined on actual static classes, not on the interfaces, so name matching fails + // we should really refactor events entirely - in the meantime, let it be an UmbracoTestBase ;( + //var testObjects = new TestObjects(null); + //var serviceContext = testObjects.GetServiceContextMock(); + var serviceContext = Current.Services; + var definitions = new IEventDefinition[] { //I would test these but they are legacy events and we don't need them for deploy, when we migrate to new/better events we can wire up the check @@ -58,8 +66,8 @@ namespace Umbraco.Tests.Cache new EventDefinition>(null, serviceContext.ContentTypeService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, serviceContext.ContentTypeService, new DeleteEventArgs(Enumerable.Empty())), - new EventDefinition>(null, serviceContext.ContentTypeService, new SaveEventArgs(Enumerable.Empty())), - new EventDefinition>(null, serviceContext.ContentTypeService, new DeleteEventArgs(Enumerable.Empty())), + new EventDefinition>(null, serviceContext.MediaTypeService, new SaveEventArgs(Enumerable.Empty())), + new EventDefinition>(null, serviceContext.MediaTypeService, new DeleteEventArgs(Enumerable.Empty())), new EventDefinition>(null, serviceContext.MemberTypeService, new SaveEventArgs(Enumerable.Empty())), new EventDefinition>(null, serviceContext.MemberTypeService, new DeleteEventArgs(Enumerable.Empty())), @@ -100,11 +108,17 @@ namespace Umbraco.Tests.Cache new EventDefinition>(null, serviceContext.RelationService, new DeleteEventArgs(Enumerable.Empty())), }; + var ok = true; foreach (var definition in definitions) { var found = CacheRefresherComponent.FindHandler(definition); - Assert.IsNotNull(found, "Couldn't find method for " + definition.EventName + " on " + definition.Sender.GetType()); + if (found == null) + { + Console.WriteLine("Couldn't find method for " + definition.EventName + " on " + definition.Sender.GetType()); + ok = false; + } } + Assert.IsTrue(ok, "see log for details"); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Components/ComponentTests.cs b/src/Umbraco.Tests/Components/ComponentTests.cs index 581e15cfdc..928a1354fc 100644 --- a/src/Umbraco.Tests/Components/ComponentTests.cs +++ b/src/Umbraco.Tests/Components/ComponentTests.cs @@ -6,9 +6,11 @@ using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Components; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; +using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; @@ -28,11 +30,14 @@ namespace Umbraco.Tests.Components var logger = Mock.Of(); var s = testObjects.GetDefaultSqlSyntaxProviders(logger); var f = new UmbracoDatabaseFactory(s, logger, new MapperCollection(Enumerable.Empty())); + var fs = new FileSystems(logger); + var p = new ScopeProvider(f, fs, logger); var mock = new Mock(); - mock.Setup(x => x.GetInstance()).Returns(logger); - mock.Setup(x => x.GetInstance()).Returns(new ProfilingLogger(Mock.Of(), Mock.Of())); - mock.Setup(x => x.GetInstance()).Returns(f); + mock.Setup(x => x.GetInstance(typeof (ILogger))).Returns(logger); + mock.Setup(x => x.GetInstance(typeof (ProfilingLogger))).Returns(new ProfilingLogger(Mock.Of(), Mock.Of())); + mock.Setup(x => x.GetInstance(typeof (IUmbracoDatabaseFactory))).Returns(f); + mock.Setup(x => x.GetInstance(typeof (IScopeProvider))).Returns(p); setup?.Invoke(mock); return mock.Object; } diff --git a/src/Umbraco.Tests/CoreThings/UdiTests.cs b/src/Umbraco.Tests/CoreThings/UdiTests.cs index 411803bdd1..debb7faaf9 100644 --- a/src/Umbraco.Tests/CoreThings/UdiTests.cs +++ b/src/Umbraco.Tests/CoreThings/UdiTests.cs @@ -1,8 +1,13 @@ using System; using System.Linq; +using LightInject; +using Moq; using Newtonsoft.Json; using NUnit.Framework; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Composing; +using Umbraco.Core.Logging; using Umbraco.Core.Serialization; namespace Umbraco.Tests.CoreThings @@ -10,6 +15,21 @@ namespace Umbraco.Tests.CoreThings [TestFixture] public class UdiTests { + [SetUp] + public void SetUp() + { + // fixme - bad in a unit test - but Udi has a static ctor that wants it?! + var container = new Mock(); + container.Setup(x => x.GetInstance(typeof (TypeLoader))).Returns(new TypeLoader(new NullCacheProvider(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + Current.Container = container.Object; + } + + [TearDown] + public void TearDown() + { + Current.Reset(); + } + [Test] public void StringEntityCtorTest() { diff --git a/src/Umbraco.Tests/Dependencies/NuGet.cs b/src/Umbraco.Tests/Dependencies/NuGet.cs index 9a07b488ef..7841210105 100644 --- a/src/Umbraco.Tests/Dependencies/NuGet.cs +++ b/src/Umbraco.Tests/Dependencies/NuGet.cs @@ -74,7 +74,7 @@ namespace Umbraco.Tests.Dependencies // guarantees that all packages have one version, solutionwide, so it's okay to take First() here if (dependencyMinimumVersion != matchingPackages.First().PackageVersion) { - Debug.WriteLine("NuSpec dependency '{0}' with minimum version {1} in doesn't match with version {2} in the solution", + Debug.WriteLine("NuSpec dependency '{0}' with minimum version {1} doesn't match with version {2} in the solution", dependency.Id, dependencyMinimumVersion, matchingPackages.First().PackageVersion); failTest = true; } diff --git a/src/Umbraco.Tests/Issues/U9560.cs b/src/Umbraco.Tests/Issues/U9560.cs index 0b8bbfe4d5..86c7218876 100644 --- a/src/Umbraco.Tests/Issues/U9560.cs +++ b/src/Umbraco.Tests/Issues/U9560.cs @@ -4,11 +4,13 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Tests.Testing; using LightInject; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Issues { - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class U9560 : UmbracoTestBase + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)] + public class U9560 : TestWithDatabaseBase { [Test] public void Test() diff --git a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs index c786a61996..429737c508 100644 --- a/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs +++ b/src/Umbraco.Tests/Membership/MembershipProviderBaseTests.cs @@ -8,16 +8,34 @@ using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Web.Security; +using LightInject; using Moq; using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Composing; using Umbraco.Core.Security; +using Umbraco.Core.Services; namespace Umbraco.Tests.Membership { [TestFixture] public class MembershipProviderBaseTests { - + [SetUp] + public void SetUp() + { + Current.Container = new ServiceContainer(); + + var mRuntimeState = new Mock(); + mRuntimeState.Setup(x => x.Level).Returns(() => RuntimeLevel.Run); + Current.Container.RegisterSingleton(f => mRuntimeState.Object); + } + + [TearDown] + public void TearDown() + { + Current.Reset(); + } [Test] public void Change_Password_Without_AllowManuallyChangingPassword_And_No_Pass_Validation() @@ -125,7 +143,12 @@ namespace Umbraco.Tests.Membership Assert.Throws(() => provider.GetPassword("test", "test")); } + // fixme + // in v7 this test relies on ApplicationContext.Current being null, which makes little + // sense, not going to port the weird code in MembershipProviderBase.ResetPassword, so + // what shall we do? [Test] + [Ignore("makes no sense?")] public void ResetPassword_Without_EnablePasswordReset() { var providerMock = new Mock() { CallBase = true }; diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index f58552ad10..e80325b9c2 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -69,7 +69,7 @@ namespace Umbraco.Tests.Persistence lock (locker) { acquired++; - if (acquired == 5) m2.Set(); + if (acquired == threadCount) m2.Set(); } m1.Wait(); lock (locker) @@ -99,7 +99,7 @@ namespace Umbraco.Tests.Persistence foreach (var thread in threads) thread.Join(); - Assert.AreEqual(5, maxAcquired); + Assert.AreEqual(threadCount, maxAcquired); Assert.AreEqual(0, acquired); for (var i = 0; i < threadCount; i++) @@ -115,8 +115,8 @@ namespace Umbraco.Tests.Persistence var locker = new object(); var acquired = 0; var entered = 0; - var ms = new AutoResetEvent[5]; - for (var i = 0; i < 5; i++) ms[i] = new AutoResetEvent(false); + var ms = new AutoResetEvent[threadCount]; + for (var i = 0; i < threadCount; i++) ms[i] = new AutoResetEvent(false); var m1 = new ManualResetEventSlim(false); for (var i = 0; i < threadCount; i++) @@ -131,7 +131,7 @@ namespace Umbraco.Tests.Persistence lock (locker) { entered++; - if (entered == 5) m1.Set(); + if (entered == threadCount) m1.Set(); } ms[ic].WaitOne(); scope.Database.AcquireLockNodeWriteLock(Constants.Locks.Servers); @@ -164,11 +164,11 @@ namespace Umbraco.Tests.Persistence // all threads have entered ms[0].Set(); // let 0 go Thread.Sleep(100); - for (var i = 1; i < 5; i++) ms[i].Set(); // let others go + for (var i = 1; i < threadCount; i++) ms[i].Set(); // let others go Thread.Sleep(500); // only 1 thread has locked Assert.AreEqual(1, acquired); - for (var i = 0; i < 5; i++) ms[i].Set(); // let all go + for (var i = 0; i < threadCount; i++) ms[i].Set(); // let all go foreach (var thread in threads) thread.Join(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index b1261bd21e..918213f25c 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -85,21 +85,23 @@ namespace Umbraco.Tests.Persistence.Repositories contentTypeRepository.AddOrUpdate(hasPropertiesContentType); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); + versions.Add(content1.Version); // the first version //publish version - content1.ChangePublishedState(PublishedState.Published); + content1.ChangePublishedState(PublishedState.Publishing); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); + versions.Add(content1.Version); // the first version got published, same id //change something and make a pending version content1.Name = "new name"; + if (content1.Published) + content1.ChangePublishedState(PublishedState.Saving); repository.AddOrUpdate(content1); unitOfWork.Flush(); - versions.Add(content1.Version); + versions.Add(content1.Version); // the second version - Assert.AreEqual(3, versions.Distinct().Count()); + Assert.AreEqual(2, versions.Distinct().Count()); var content = repository.GetByQuery(unitOfWork.Query().Where(c => c.Id == content1.Id)).ToArray()[0]; Assert.AreEqual(versions[2], content.Version); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 778ae12687..ae00e5dbe5 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -8,13 +8,9 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; -using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Tests.TestHelpers; @@ -761,6 +757,9 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(unitOfWork); var contentType = repository.Get(NodeDto.NodeIdSeed + 1); + Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(2)); + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(4)); + // Act var urlAlias = new PropertyType("test", DataTypeDatabaseType.Nvarchar, "urlAlias") { @@ -772,13 +771,17 @@ namespace Umbraco.Tests.Persistence.Repositories }; var addedPropertyType = contentType.AddPropertyType(urlAlias); + + Assert.That(contentType.PropertyGroups.Count, Is.EqualTo(2)); + Assert.That(contentType.PropertyTypes.Count(), Is.EqualTo(5)); + repository.AddOrUpdate(contentType); unitOfWork.Flush(); // Assert var updated = repository.Get(NodeDto.NodeIdSeed + 1); Assert.That(addedPropertyType, Is.True); - Assert.That(updated.PropertyGroups.Count(), Is.EqualTo(2)); + Assert.That(updated.PropertyGroups.Count, Is.EqualTo(2)); Assert.That(updated.PropertyTypes.Count(), Is.EqualTo(5)); Assert.That(updated.PropertyTypes.Any(x => x.Alias == "urlAlias"), Is.True); Assert.That(updated.PropertyTypes.First(x => x.Alias == "urlAlias").PropertyGroupId, Is.Null); diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 0f07ff3c57..1811b9e3b2 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -424,7 +424,7 @@ namespace Umbraco.Tests.Persistence.Repositories var repository = CreateRepository(unitOfWork); // Act - var exists = repository.Exists(1034); //Content picker + var exists = repository.Exists(1046); //Content picker var doesntExist = repository.Exists(-80); // Assert @@ -481,12 +481,12 @@ namespace Umbraco.Tests.Persistence.Repositories using (var unitOfWork = provider.CreateUnitOfWork()) { var repository = Container.GetInstance(unitOfWork); - dtd = new DataTypeDefinition(-1, Constants.PropertyEditors.RadioButtonListAlias) { Name = "test" }; + dtd = new DataTypeDefinition(-1, Constants.PropertyEditors.RadioButtonListAlias) { Name = "test" }; repository.AddOrUpdate(dtd); unitOfWork.Flush(); - unitOfWork.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtd.Id, SortOrder = 0, Value = "test1" }); - unitOfWork.Database.Insert(new DataTypePreValueDto() { DataTypeNodeId = dtd.Id, SortOrder = 1, Value = "test2" }); + unitOfWork.Database.Insert(new DataTypePreValueDto { DataTypeNodeId = dtd.Id, SortOrder = 0, Value = "test1" }); + unitOfWork.Database.Insert(new DataTypePreValueDto { DataTypeNodeId = dtd.Id, SortOrder = 1, Value = "test2" }); //this will cache the result var collection = repository.GetPreValuesCollectionByDataTypeId(dtd.Id); @@ -496,7 +496,7 @@ namespace Umbraco.Tests.Persistence.Repositories var cache = CacheHelper.IsolatedRuntimeCache.GetCache(); Assert.IsTrue(cache); var cached = cache.Result - .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-"); + .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id); Assert.IsNotNull(cached); Assert.AreEqual(1, cached.Count()); @@ -527,7 +527,7 @@ namespace Umbraco.Tests.Persistence.Repositories var cache = CacheHelper.IsolatedRuntimeCache.GetCache(); Assert.IsTrue(cache); var cached = cache.Result - .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-"); + .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id); Assert.IsNotNull(cached); Assert.AreEqual(1, cached.Count()); diff --git a/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs index 359ead000b..05b7122154 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -6,13 +6,14 @@ using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Composing; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Persistence.Repositories { [TestFixture] - [UmbracoTest(WithApplication = true)] - public class PartialViewRepositoryTests : UmbracoTestBase + [UmbracoTest(WithApplication = true, Database = UmbracoTestOptions.Database.NewEmptyPerFixture)] + public class PartialViewRepositoryTests : TestWithDatabaseBase { private IFileSystem _fileSystem; diff --git a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs index 289d4dfb03..e09253f996 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs @@ -231,6 +231,7 @@ namespace Umbraco.Tests.Persistence.Repositories //now remove a few rules from a few of the items and then add some more, this will put things 'out of order' which //we need to verify our sort order is working for the relator + // fixme - no "relator" in v8?! for (int i = 0; i < allEntries.Count; i++) { //all the even ones diff --git a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs index c5e5573e26..9d0d57fe98 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ScriptRepositoryTest.cs @@ -10,13 +10,14 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.PropertyEditors; +using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; namespace Umbraco.Tests.Persistence.Repositories { [TestFixture] - [UmbracoTest(WithApplication = true)] - public class ScriptRepositoryTest : UmbracoTestBase + [UmbracoTest(WithApplication = true, Database = UmbracoTestOptions.Database.NewEmptyPerFixture)] + public class ScriptRepositoryTest : TestWithDatabaseBase { private IFileSystem _fileSystem; diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs index f53d07eedd..5d3185fd16 100644 --- a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs +++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs @@ -147,6 +147,7 @@ namespace Umbraco.Tests.Plugins public void Detect_Legacy_Plugin_File_List() { var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + Directory.CreateDirectory(tempFolder); var filePath= Path.Combine(tempFolder, string.Format("umbraco-plugins.{0}.list", NetworkHelper.FileSafeMachineName)); diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index 71648fc480..863b2c7246 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -80,7 +80,8 @@ namespace Umbraco.Tests.PropertyEditors public void CanConvertDropdownListMultiplePropertyEditor(object value, IEnumerable expected) { var converter = new DropdownListMultipleValueConverter(); - var result = converter.ConvertInterToObject(null, PropertyCacheLevel.Unknown, value, false); + var inter = converter.ConvertSourceToInter(null, value, false); + var result = converter.ConvertInterToObject(null, PropertyCacheLevel.Unknown, inter, false); Assert.AreEqual(expected, result); } @@ -93,7 +94,8 @@ namespace Umbraco.Tests.PropertyEditors public void CanConvertDropdownListMultipleWithKeysPropertyEditor(object value, IEnumerable expected) { var converter = new DropdownListMultipleWithKeysValueConverter(); - var result = converter.ConvertInterToObject(null, PropertyCacheLevel.Unknown, value, false); + var inter = converter.ConvertSourceToInter(null, value, false); + var result = converter.ConvertInterToObject(null, PropertyCacheLevel.Unknown, inter, false); Assert.AreEqual(expected, result); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index f8c6e1f760..2a85d0f66f 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -23,6 +23,14 @@ namespace Umbraco.Tests.PublishedContent // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx + public override void SetUp() + { + base.SetUp(); + + var umbracoContext = GetUmbracoContext(); + Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; + } + protected override void Compose() { base.Compose(); @@ -42,7 +50,7 @@ namespace Umbraco.Tests.PublishedContent return pluginManager; } - private void InitializeUmbracoContext() + private UmbracoContext GetUmbracoContext() { RouteData routeData = null; @@ -59,7 +67,7 @@ namespace Umbraco.Tests.PublishedContent TestObjects.GetUmbracoSettings(), Enumerable.Empty()); - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; + return umbracoContext; } public override void TearDown() diff --git a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs b/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs index 411674dd02..343968419c 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs @@ -1,5 +1,6 @@ using System; using NUnit.Framework; +using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache.XmlPublishedCache; @@ -48,6 +49,14 @@ namespace Umbraco.Tests.Routing "; } + protected override void Initialize() + { + base.Initialize(); + + if (FirstTestInFixture) + ServiceContext.ContentTypeService.Save(new ContentType(-1) { Alias = "Doc", Name = "name" }); + } + #endregion /* @@ -181,6 +190,19 @@ DetermineRouteById(id): [TestCase(2004, false, "/x/b")] [TestCase(2005, false, "/x/b/c")] [TestCase(2006, false, "/x/b/e")] + public void GetRouteByIdNoHide(int id, bool hide, string expected) + { + var umbracoContext = GetUmbracoContext("/test", 0); + var cache = umbracoContext.ContentCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + + SettingsForTests.HideTopLevelNodeFromPath = hide; + + const bool preview = true; // make sure we don't cache + var route = cache.GetRouteById(preview, id); + Assert.AreEqual(expected, route); + } + [TestCase(1000, true, "/")] [TestCase(1001, true, "/b")] [TestCase(1002, true, "/b/c")] @@ -192,7 +214,7 @@ DetermineRouteById(id): [TestCase(2004, true, "/b")] // collision! [TestCase(2005, true, "/b/c")] // collision! [TestCase(2006, true, "/b/e")] // risky! - public void GetRouteById(int id, bool hide, string expected) + public void GetRouteByIdHide(int id, bool hide, string expected) { var umbracoContext = GetUmbracoContext("/test", 0); var cache = umbracoContext.ContentCache as PublishedContentCache; @@ -214,10 +236,7 @@ DetermineRouteById(id): SettingsForTests.HideTopLevelNodeFromPath = false; - // make sure we cache - const bool preview = false; - - var route = cache.GetRouteById(preview, 1000); + var route = cache.GetRouteById(false, 1000); Assert.AreEqual("/a", route); // GetRouteById registers a non-trusted route, which is cached for @@ -239,6 +258,27 @@ DetermineRouteById(id): [TestCase("/a/b/c", false, 1002)] [TestCase("/a/b/c/d", false, 1003)] [TestCase("/x", false, 2000)] + public void GetByRouteNoHide(string route, bool hide, int expected) + { + var umbracoContext = GetUmbracoContext("/test", 0); + var cache = umbracoContext.ContentCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + + SettingsForTests.HideTopLevelNodeFromPath = hide; + + const bool preview = false; // make sure we don't cache - but HOW? should be some sort of switch?! + var content = cache.GetByRoute(preview, route); + if (expected < 0) + { + Assert.IsNull(content); + } + else + { + Assert.IsNotNull(content); + Assert.AreEqual(expected, content.Id); + } + } + [TestCase("/", true, 1000)] [TestCase("/a", true, 2003)] [TestCase("/a/b", true, -1)] @@ -248,7 +288,7 @@ DetermineRouteById(id): [TestCase("/y/z", true, 2002)] [TestCase("/b", true, 1001)] // (hence the 2004 collision) [TestCase("/b/c", true, 1002)] // (hence the 2005 collision) - public void GetByRoute(string route, bool hide, int expected) + public void GetByRouteHide(string route, bool hide, int expected) { var umbracoContext = GetUmbracoContext("/test", 0); var cache = umbracoContext.ContentCache as PublishedContentCache; @@ -256,7 +296,7 @@ DetermineRouteById(id): SettingsForTests.HideTopLevelNodeFromPath = hide; - const bool preview = true; // make sure we don't cache + const bool preview = false; // make sure we don't cache - but HOW? should be some sort of switch?! var content = cache.GetByRoute(preview, route); if (expected < 0) { @@ -278,10 +318,7 @@ DetermineRouteById(id): SettingsForTests.HideTopLevelNodeFromPath = false; - // make sure we cache - const bool preview = false; - - var content = cache.GetByRoute(preview, "/a/b/c"); + var content = cache.GetByRoute(false, "/a/b/c"); Assert.IsNotNull(content); Assert.AreEqual(1002, content.Id); diff --git a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs index 64a5b48d55..a1ea644a45 100644 --- a/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs @@ -1,22 +1,25 @@ using System; -using System.Collections.Generic; using System.Linq; +using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.IO; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Core.Composing; +using Umbraco.Core.Persistence.Mappers; namespace Umbraco.Tests.Scoping { [TestFixture] public class ScopeEventDispatcherTests //: BaseUmbracoConfigurationTest { + private TestObjects _testObjects; + [SetUp] public void Setup() { @@ -24,6 +27,23 @@ namespace Umbraco.Tests.Scoping DoThing1 = null; DoThing2 = null; DoThing3 = null; + + Current.Container = new ServiceContainer(); + + _testObjects = new TestObjects(Current.Container); + Current.Container.RegisterSingleton(f => Current.Container); + Current.Container.RegisterSingleton(factory => new FileSystems(factory.TryGetInstance())); + Current.Container.RegisterCollectionBuilder(); + + SettingsForTests.Reset(); // ensure we have configuration + } + + [TearDown] + public void TearDown() + { + Current.Reset(); + + SettingsForTests.Reset(); } [TestCase(false, true, true)] @@ -42,7 +62,7 @@ namespace Umbraco.Tests.Scoping DoThing1 += (sender, args) => { counter1++; if (cancel) args.Cancel = true; }; DoThing2 += (sender, args) => { counter2++; }; - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: passive ? new PassiveEventDispatcher() : null)) { var cancelled = scope.Events.DispatchCancelable(DoThing1, this, new SaveEventArgs("test")); @@ -71,7 +91,7 @@ namespace Umbraco.Tests.Scoping DoThing2 += OnDoThingFail; DoThing3 += OnDoThingFail; - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) { scope.Events.Dispatch(DoThing1, this, new SaveEventArgs("test")); @@ -101,7 +121,7 @@ namespace Umbraco.Tests.Scoping DoDeleteForContent += OnDoThingFail; DoForTestArgs += OnDoThingFail; DoForTestArgs2 += OnDoThingFail; - + var contentType = MockedContentTypes.CreateBasicContentType(); var content1 = MockedContent.CreateBasicContent(contentType); @@ -113,10 +133,10 @@ namespace Umbraco.Tests.Scoping var content3 = MockedContent.CreateBasicContent(contentType); content3.Id = 789; - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) { - + //content1 will be filtered from the args scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(new[]{ content1 , content3})); scope.Events.Dispatch(DoDeleteForContent, this, new DeleteEventArgs(content1)); @@ -145,13 +165,13 @@ namespace Umbraco.Tests.Scoping /// /// This will test that when we track events that before we Get the events we normalize all of the - /// event entities to be the latest one (most current) found amongst the event so that there is + /// event entities to be the latest one (most current) found amongst the event so that there is /// no 'stale' entities in any of the args /// [Test] public void LatestEntities() { - DoSaveForContent += OnDoThingFail; + DoSaveForContent += OnDoThingFail; var now = DateTime.Now; var contentType = MockedContentTypes.CreateBasicContentType(); @@ -165,9 +185,9 @@ namespace Umbraco.Tests.Scoping content3.Id = 123; content3.UpdateDate = now.AddMinutes(3); - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) - { + { scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content2)); scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content3)); @@ -175,7 +195,7 @@ namespace Umbraco.Tests.Scoping // events have been queued var events = scope.Events.GetEvents(EventDefinitionFilter.All).ToArray(); Assert.AreEqual(3, events.Length); - + foreach (var t in events) { var args = (SaveEventArgs)t.Args; @@ -205,7 +225,7 @@ namespace Umbraco.Tests.Scoping content3.Id = 123; content1.UpdateDate = now.AddMinutes(3); - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) { scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); @@ -214,7 +234,7 @@ namespace Umbraco.Tests.Scoping // events have been queued var events = scope.Events.GetEvents(EventDefinitionFilter.FirstIn).ToArray(); - Assert.AreEqual(1, events.Length); + Assert.AreEqual(1, events.Length); Assert.AreEqual(content1, ((SaveEventArgs) events[0].Args).SavedEntities.First()); Assert.IsTrue(object.ReferenceEquals(content1, ((SaveEventArgs)events[0].Args).SavedEntities.First())); Assert.AreEqual(content1.UpdateDate, ((SaveEventArgs) events[0].Args).SavedEntities.First().UpdateDate); @@ -238,7 +258,7 @@ namespace Umbraco.Tests.Scoping content3.Id = 123; content3.UpdateDate = now.AddMinutes(3); - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) { scope.Events.Dispatch(DoSaveForContent, this, new SaveEventArgs(content1)); @@ -262,7 +282,7 @@ namespace Umbraco.Tests.Scoping DoThing2 += OnDoThingFail; DoThing3 += OnDoThingFail; - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()); using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher())) { scope.Events.Dispatch(DoThing1, this, new SaveEventArgs("test")); @@ -288,7 +308,7 @@ namespace Umbraco.Tests.Scoping ScopeContext ambientContext = null; Guid value = Guid.Empty; - var scopeProvider = new ScopeProvider(Mock.Of(), Mock.Of(), Mock.Of()); + var scopeProvider = _testObjects.GetScopeProvider(Mock.Of()) as ScopeProvider; DoThing1 += (sender, args) => { counter++; }; DoThing2 += (sender, args) => { counter++; }; diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index aa3bd3dd05..ae16ba484f 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -27,6 +27,7 @@ namespace Umbraco.Tests.Scoping { base.TearDown(); SafeCallContext.Clear(); + ShadowFileSystems.ResetId(); ClearFiles(); } diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index f3efe56a2c..8bce82e67c 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -8,6 +10,10 @@ using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web.Cache; +using LightInject; +using Moq; +using Umbraco.Core.Events; +using Umbraco.Core.Sync; namespace Umbraco.Tests.Scoping { @@ -17,6 +23,32 @@ namespace Umbraco.Tests.Scoping { private CacheRefresherComponent _cacheRefresher; + protected override void Compose() + { + base.Compose(); + + // the cache refresher component needs to trigger to refresh caches + // but then, it requires a lot of plumbing ;( + // fixme - and we cannot inject a DistributedCache yet + // so doing all this mess + Container.RegisterSingleton(); + Container.RegisterSingleton(f => Mock.Of()); + Container.RegisterCollectionBuilder() + .Add(f => f.TryGetInstance().GetCacheRefreshers()); + } + + protected override void ComposeCacheHelper() + { + // this is what's created core web runtime + var cacheHelper = new CacheHelper( + new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()), + new StaticCacheProvider(), + new NullCacheProvider(), + new IsolatedRuntimeCache(type => new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); + Container.RegisterSingleton(f => cacheHelper); + Container.RegisterSingleton(f => f.GetInstance().RuntimeCache); + } + [TearDown] public void Teardown() { @@ -42,13 +74,16 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(user.Id, globalCached.Id); Assert.AreEqual("name", globalCached.Name); + // get user again - else we'd modify the one that's in the cache + user = service.GetUserById(user.Id); + _cacheRefresher = new CacheRefresherComponent(true); _cacheRefresher.Initialize(); Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) { - Assert.IsInstanceOf(scope); + Assert.IsInstanceOf(scope); Assert.IsNotNull(scopeProvider.AmbientScope); Assert.AreSame(scope, scopeProvider.AmbientScope); @@ -76,7 +111,7 @@ namespace Umbraco.Tests.Scoping } Assert.IsNull(scopeProvider.AmbientScope); - globalCached = (IUser)globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); if (complete) { // global cache has been cleared @@ -129,7 +164,7 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) { - Assert.IsInstanceOf(scope); + Assert.IsInstanceOf(scope); Assert.IsNotNull(scopeProvider.AmbientScope); Assert.AreSame(scope, scopeProvider.AmbientScope); @@ -221,7 +256,7 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) { - Assert.IsInstanceOf(scope); + Assert.IsInstanceOf(scope); Assert.IsNotNull(scopeProvider.AmbientScope); Assert.AreSame(scope, scopeProvider.AmbientScope); @@ -281,5 +316,29 @@ namespace Umbraco.Tests.Scoping { return $"uRepo_{typeof (T).Name}_"; } + + public class PassiveEventDispatcher : QueuingEventDispatcherBase + { + public PassiveEventDispatcher() + : base(false) + { } + + protected override void ScopeExitCompleted() + { + // do nothing + } + } + + public class LocalServerMessenger : ServerMessengerBase + { + public LocalServerMessenger() + : base(false) + { } + + protected override void DeliverRemote(IEnumerable servers, 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 fc43b508c4..d2f1ee2684 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Scoping // need to rewrite these tests entirely using the XmlStore and such // need to create an equivalent test for NuCache once we understand it - Assert.Fail("oops"); + Assert.Fail("need to rewrite these tests entirely for XmlStore"); } //private CacheRefresherComponent _cacheRefresher; diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index f0bcecb4a0..725c6b6ca1 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -61,6 +61,11 @@ namespace Umbraco.Tests.Services /// Regression test: http://issues.umbraco.org/issue/U4-9336 /// [Test] + [Ignore("not applicable to v8")] + + // fixme - this test was imported from 7.6 BUT it makes no sense for v8 + // we should trust the PATH, full stop + public void Moving_Node_To_Recycle_Bin_With_Invalid_Path() { var contentService = ServiceContext.ContentService; @@ -917,10 +922,10 @@ namespace Umbraco.Tests.Services // to do it twice! subpage2.Name = "Text Page 2 Updated"; subpage2.SetValue("author", "Jane Doe"); - contentService.SaveAndPublishWithStatus(subpage2, 0); // publishes the current version + contentService.SaveAndPublishWithStatus(subpage2); // publishes the current version subpage2.Name = "Text Page 2 Updated1"; - subpage2.SetValue("author", "Bop Hope"); - contentService.SaveAndPublishWithStatus(subpage2, 0); // now creates a new version + subpage2.SetValue("author", "Bob Hope"); + contentService.SaveAndPublishWithStatus(subpage2); // now creates a new version versions = contentService.GetVersions(NodeDto.NodeIdSeed + 3).ToList(); Assert.AreEqual(2, versions.Count); @@ -1450,7 +1455,7 @@ namespace Umbraco.Tests.Services })); Assert.IsTrue(ServiceContext.PublicAccessService.AddRule(content1, "test2", "test2").Success); - Assert.IsNotNull(ServiceContext.NotificationService.CreateNotification(ServiceContext.UserService.GetUserById(0), content1, "test")); + Assert.IsNotNull(ServiceContext.NotificationService.CreateNotification(ServiceContext.UserService.GetUserById(0), content1, "T")); ServiceContext.ContentService.AssignContentPermission(content1, 'A', new[] { 0 }); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 2941b8d291..4f3f231122 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -525,7 +525,7 @@ namespace Umbraco.Tests.Services var deletedContent = cs.GetById(content.Id); var deletedChildContentType = cts.Get(childContentType.Id); var deletedContentType = cts.Get(contentType.Id); - + Assert.IsNull(deletedChildContentType); Assert.IsNull(deletedContent); Assert.IsNull(deletedContentType); @@ -1534,7 +1534,8 @@ namespace Umbraco.Tests.Services public void Can_Remove_PropertyGroup_Without_Removing_Property_Types() { var service = ServiceContext.ContentTypeService; - var basePage = (IContentType)MockedContentTypes.CreateBasicContentType(); + + var basePage = (IContentType) MockedContentTypes.CreateBasicContentType(); basePage.AddPropertyGroup("Content"); basePage.AddPropertyGroup("Meta"); service.Save(basePage); @@ -1547,7 +1548,8 @@ namespace Umbraco.Tests.Services SortOrder = 1, DataTypeDefinitionId = -88 }; - var authorAdded = basePage.AddPropertyType(authorPropertyType, "Content"); + Assert.IsTrue(basePage.AddPropertyType(authorPropertyType, "Content")); + var titlePropertyType = new PropertyType(Constants.PropertyEditors.TextboxAlias, DataTypeDatabaseType.Ntext, "title") { Name = "Title", @@ -1556,20 +1558,20 @@ namespace Umbraco.Tests.Services SortOrder = 1, DataTypeDefinitionId = -88 }; - var titleAdded = basePage.AddPropertyType(authorPropertyType, "Meta"); + Assert.IsTrue(basePage.AddPropertyType(titlePropertyType, "Meta")); service.Save(basePage); - basePage = service.Get(basePage.Id); - var totalPt = basePage.PropertyTypes.Count(); + var count = basePage.PropertyTypes.Count(); + Assert.AreEqual(2, count); basePage.RemovePropertyGroup("Content"); - service.Save(basePage); + service.Save(basePage); basePage = service.Get(basePage.Id); - Assert.AreEqual(totalPt, basePage.PropertyTypes.Count()); + Assert.AreEqual(count, basePage.PropertyTypes.Count()); } [Test] diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index 9b8741539b..fde28d54e8 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Security.Cryptography; using System.Text; using NUnit.Framework; +using Umbraco.Core.Exceptions; using Umbraco.Core.Models.Membership; using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Core.Persistence.Querying; @@ -484,7 +485,7 @@ namespace Umbraco.Tests.Services var userType = userService.GetUserTypeByAlias("admin"); // Act & Assert - Assert.Throws(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io", userType)); + Assert.Throws(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io", userType)); } [Test] diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 0106bebcc0..5707b8eb5b 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -350,10 +350,10 @@ namespace Umbraco.Tests.TestHelpers.Entities contentCollection.Add(new PropertyType(Constants.PropertyEditors.DateAlias, DataTypeDatabaseType.Date) { Alias = "date", Name = "Date", Mandatory = false, SortOrder = 13, DataTypeDefinitionId = -41 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.DropDownListAlias, DataTypeDatabaseType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeDefinitionId = -42 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.CheckBoxListAlias, DataTypeDatabaseType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeDefinitionId = -43 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.ContentPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeDefinitionId = 1034 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.MediaPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeDefinitionId = 1035 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.MemberPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeDefinitionId = 1036 }); - contentCollection.Add(new PropertyType(Constants.PropertyEditors.RelatedLinks2Alias, DataTypeDatabaseType.Ntext) { Alias = "relatedLinks", Name = "Related Links", Mandatory = false, SortOrder = 21, DataTypeDefinitionId = 1040 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.ContentPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeDefinitionId = 1046 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.MediaPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeDefinitionId = 1048 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.MemberPicker2Alias, DataTypeDatabaseType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeDefinitionId = 1047 }); + contentCollection.Add(new PropertyType(Constants.PropertyEditors.RelatedLinks2Alias, DataTypeDatabaseType.Ntext) { Alias = "relatedLinks", Name = "Related Links", Mandatory = false, SortOrder = 21, DataTypeDefinitionId = 1050 }); contentCollection.Add(new PropertyType(Constants.PropertyEditors.TagsAlias, DataTypeDatabaseType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeDefinitionId = 1041 }); //contentCollection.Add(new PropertyType(Constants.PropertyEditors.UltraSimpleEditorAlias, DataTypeDatabaseType.Ntext) { Alias = "simpleEditor", Name = "Ultra Simple Editor", Mandatory = false, SortOrder = 19, DataTypeDefinitionId = 1038 }); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 9f27f9b963..1bfde929cb 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -217,7 +217,7 @@ namespace Umbraco.Tests.TestHelpers return new Lazy(() => container?.TryGetInstance() ?? ctor()); } - public IScopeProvider GetScopeProvider(ILogger logger, IUmbracoDatabaseFactory databaseFactory = null) + public IScopeProvider GetScopeProvider(ILogger logger, FileSystems fileSystems = null, IUmbracoDatabaseFactory databaseFactory = null) { if (databaseFactory == null) { @@ -228,7 +228,7 @@ namespace Umbraco.Tests.TestHelpers databaseFactory = new UmbracoDatabaseFactory(Constants.System.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger, mappers); } - var fileSystems = new FileSystems(logger); + fileSystems = fileSystems ?? new FileSystems(logger); var scopeProvider = new ScopeProvider(databaseFactory, fileSystems, logger); return scopeProvider; } diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 285b45e281..69ebf7158f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -108,6 +108,7 @@ namespace Umbraco.Tests.TestHelpers var timer = profilingLogger?.TraceDuration("teardown"); // fixme move that one up try { + // fixme - should we first kill all scopes? if (Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest) RemoveDatabaseFile(); diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index c219f58717..b6a5d982c4 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -49,6 +49,7 @@ namespace Umbraco.Tests.Testing.TestingTests // unless we can inject them in MembershipHelper, we need need this Container.Register(_ => Mock.Of()); Container.Register(_ => Mock.Of()); + Container.Register(_ => Mock.Of()); Container.Register(_ => CacheHelper.CreateDisabledCacheHelper()); Container.Register(); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index bb84e0f2f9..06fba6fadc 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -22,6 +22,7 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; @@ -219,9 +220,7 @@ namespace Umbraco.Tests.Testing { Assembly.Load("Umbraco.Core"), Assembly.Load("umbraco"), - Assembly.Load("Umbraco.Tests"), - Assembly.Load("cms"), - Assembly.Load("controls"), + Assembly.Load("Umbraco.Tests") } }; } @@ -279,10 +278,15 @@ namespace Umbraco.Tests.Testing sqlSyntaxProviders, Logger, Mock.Of())); + Container.RegisterSingleton(f => f.TryGetInstance()); Container.RegisterCollectionBuilder(); // empty - Container.Register(factory - => TestObjects.GetScopeUnitOfWorkProvider(factory.GetInstance(), factory.TryGetInstance(), factory.TryGetInstance())); + Container.RegisterSingleton(factory => new FileSystems(factory.TryGetInstance())); + Container.RegisterSingleton(factory + => TestObjects.GetScopeProvider(factory.TryGetInstance(), factory.TryGetInstance(), factory.TryGetInstance())); + Container.RegisterSingleton(factory + => TestObjects.GetScopeUnitOfWorkProvider(factory.TryGetInstance(), factory.TryGetInstance(), + factory.TryGetInstance(), factory.TryGetInstance())); Container.RegisterFrom(); // composition root is doing weird things, fix @@ -348,6 +352,20 @@ namespace Umbraco.Tests.Testing protected virtual void Reset() { + // reset and dispose scopes + // ensures we don't leak an opened database connection + // which would lock eg SqlCe .sdf files + var scopeProvider = Container?.TryGetInstance() as ScopeProvider; + if (scopeProvider != null) + { + Core.Scoping.Scope scope; + while ((scope = scopeProvider.AmbientScope) != null) + { + scope.Reset(); + scope.Dispose(); + } + } + Current.Reset(); Container?.Dispose(); diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 7e606a45d5..95becdab8f 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,15 +1,17 @@ using System; using System.Web; +using LightInject; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Scoping; +using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web; using Umbraco.Web.PublishedCache; using Umbraco.Web.Routing; @@ -21,6 +23,30 @@ namespace Umbraco.Tests.Web [TestFixture] public class TemplateUtilitiesTests { + [SetUp] + public void SetUp() + { + // fixme - now UrlProvider depends on EntityService for GetUrl(guid) - this is bad + // should not depend on more than IdkMap maybe - fix this! + var entityService = new Mock(); + entityService.Setup(x => x.GetIdForKey(It.IsAny(), It.IsAny())).Returns(Attempt.Fail()); + var serviceContext = new ServiceContext(entityService: entityService.Object); + + // fixme - bad in a unit test - but Udi has a static ctor that wants it?! + var container = new Mock(); + container.Setup(x => x.GetInstance(typeof(TypeLoader))).Returns(new TypeLoader(new NullCacheProvider(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + container.Setup(x => x.GetInstance(typeof (ServiceContext))).Returns(serviceContext); + Current.Container = container.Object; + + Umbraco.Web.Composing.Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); + } + + [TearDown] + public void TearDown() + { + Current.Reset(); + } + [TestCase("", "")] [TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")] [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")] @@ -48,7 +74,7 @@ namespace Umbraco.Tests.Web }); using (var umbCtx = UmbracoContext.EnsureContext( - Mock.Of(), + Umbraco.Web.Composing.Current.UmbracoContextAccessor, Mock.Of(), Mock.Of(), new Mock(null, null).Object, diff --git a/src/Umbraco.Tests/media/1003/sample.txt b/src/Umbraco.Tests/media/1003/sample.txt deleted file mode 100644 index c7e057d919..0000000000 --- a/src/Umbraco.Tests/media/1003/sample.txt +++ /dev/null @@ -1 +0,0 @@ -TestContent \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index e9ff9e503b..c7d11bc552 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -15,7 +15,7 @@ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} OnBuildSuccess true - + 44319 enabled disabled false @@ -171,6 +171,8 @@ All --> + +