Merge remote-tracking branch 'origin/v8/dev' into netcore/dev

# Conflicts:
#	src/Umbraco.Core/Services/UserServiceExtensions.cs
#	src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs
#	src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs
#	src/Umbraco.Infrastructure/Search/ExamineComposer.cs
#	src/Umbraco.PublishedCache.NuCache/DataSource/DatabaseDataSource.cs
#	src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs
This commit is contained in:
Bjarke Berg
2020-04-24 08:33:17 +02:00
15 changed files with 254 additions and 78 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Models.Membership;
@@ -68,5 +69,17 @@ namespace Umbraco.Core.Services
userService.ReplaceUserGroupPermissions(groupId, new char[] { });
}
public static IEnumerable<IProfile> GetProfilesById(this IUserService userService, params int[] ids)
{
var fullUsers = userService.GetUsersById(ids);
return fullUsers.Select(user =>
{
var asProfile = user as IProfile;
return asProfile ?? new UserProfile(user.Id, user.Name);
});
}
}
}

View File

@@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
@@ -16,23 +18,41 @@ namespace Umbraco.Examine
{
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
private readonly IUserService _userService;
private readonly IScopeProvider _scopeProvider;
private readonly IShortStringHelper _shortStringHelper;
public ContentValueSetBuilder(PropertyEditorCollection propertyEditors,
UrlSegmentProviderCollection urlSegmentProviders,
IUserService userService,
IShortStringHelper shortStringHelper,
IScopeProvider scopeProvider,
bool publishedValuesOnly)
: base(propertyEditors, publishedValuesOnly)
{
_urlSegmentProviders = urlSegmentProviders;
_userService = userService;
_shortStringHelper = shortStringHelper;
_scopeProvider = scopeProvider;
}
/// <inheritdoc />
public override IEnumerable<ValueSet> GetValueSets(params IContent[] content)
{
Dictionary<int, IProfile> creatorIds;
Dictionary<int, IProfile> writerIds;
// We can lookup all of the creator/writer names at once which can save some
// processing below instead of one by one.
using (var scope = _scopeProvider.CreateScope())
{
creatorIds = _userService.GetProfilesById(content.Select(x => x.CreatorId).ToArray())
.ToDictionary(x => x.Id, x => x);
writerIds = _userService.GetProfilesById(content.Select(x => x.WriterId).ToArray())
.ToDictionary(x => x.Id, x => x);
scope.Complete();
}
// TODO: There is a lot of boxing going on here and ultimately all values will be boxed by Lucene anyways
// but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since
// Lucene will do it no matter what? One idea was to create a `FieldValue` struct which would contain `object`, `object[]`, `ValueType` and `ValueType[]`
@@ -61,8 +81,8 @@ namespace Umbraco.Examine
{"urlName", urlValue?.Yield() ?? Enumerable.Empty<string>()}, //Always add invariant urlName
{"path", c.Path?.Yield() ?? Enumerable.Empty<string>()},
{"nodeType", c.ContentType.Id.ToString().Yield() ?? Enumerable.Empty<string>()},
{"creatorName", (c.GetCreatorProfile(_userService)?.Name ?? "??").Yield() },
{"writerName",(c.GetWriterProfile(_userService)?.Name ?? "??").Yield() },
{"creatorName", (creatorIds.TryGetValue(c.CreatorId, out var creatorProfile) ? creatorProfile.Name : "??").Yield() },
{"writerName", (writerIds.TryGetValue(c.WriterId, out var writerProfile) ? writerProfile.Name : "??").Yield() },
{"writerID", new object[] {c.WriterId}},
{"templateID", new object[] {c.TemplateId ?? 0}},
{UmbracoExamineFieldNames.VariesByCultureFieldName, new object[] {"n"}},

View File

@@ -15,6 +15,48 @@ namespace Umbraco.Core.Persistence
/// </summary>
public static partial class NPocoDatabaseExtensions
{
/// <summary>
/// Iterates over the result of a paged data set with a db reader
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="database"></param>
/// <param name="pageSize">
/// The number of rows to load per page
/// </param>
/// <param name="sql"></param>
/// <returns></returns>
/// <remarks>
/// NPoco's normal Page returns a List{T} but sometimes we don't want all that in memory and instead want to
/// iterate over each row with a reader using Query vs Fetch.
/// </remarks>
public static IEnumerable<T> QueryPaged<T>(this IDatabase database, long pageSize, Sql sql)
{
var sqlString = sql.SQL;
var sqlArgs = sql.Arguments;
int? itemCount = null;
long pageIndex = 0;
do
{
// Get the paged queries
database.BuildPageQueries<T>(pageIndex * pageSize, pageSize, sqlString, ref sqlArgs, out var sqlCount, out var sqlPage);
// get the item count once
if (itemCount == null)
{
itemCount = database.ExecuteScalar<int>(sqlCount, sqlArgs);
}
pageIndex++;
// iterate over rows without allocating all items to memory (Query vs Fetch)
foreach (var row in database.Query<T>(sqlPage, sqlArgs))
{
yield return row;
}
} while ((pageIndex * pageSize) < itemCount);
}
// NOTE
//
// proper way to do it with TSQL and SQLCE

View File

@@ -43,6 +43,12 @@ namespace Umbraco.Core.Runtime
public async Task<bool> AcquireLockAsync(int millisecondsTimeout)
{
if (!_dbFactory.Configured)
{
// if we aren't configured, then we're in an install state, in which case we have no choice but to assume we can acquire
return true;
}
if (!(_dbFactory.SqlContext.SqlSyntax is SqlServerSyntaxProvider sqlServerSyntaxProvider))
throw new NotSupportedException("SqlMainDomLock is only supported for Sql Server");
@@ -129,6 +135,12 @@ namespace Umbraco.Core.Runtime
// poll every 1 second
Thread.Sleep(1000);
if (!_dbFactory.Configured)
{
// if we aren't configured, we just keep looping since we can't query the db
continue;
}
lock (_locker)
{
// If cancellation has been requested we will just exit. Depending on timing of the shutdown,
@@ -361,41 +373,44 @@ namespace Umbraco.Core.Runtime
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
var db = GetDatabase();
try
if (_dbFactory.Configured)
{
db.BeginTransaction(IsolationLevel.ReadCommitted);
// get a write lock
_sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom);
// When we are disposed, it means we have released the MainDom lock
// and called all MainDom release callbacks, in this case
// if another maindom is actually coming online we need
// to signal to the MainDom coming online that we have shutdown.
// To do that, we update the existing main dom DB record with a suffixed "_updated" string.
// Otherwise, if we are just shutting down, we want to just delete the row.
if (_mainDomChanging)
var db = GetDatabase();
try
{
_logger.Debug<SqlMainDomLock>("Releasing MainDom, updating row, new application is booting.");
db.Execute($"UPDATE umbracoKeyValue SET [value] = [value] + '{UpdatedSuffix}' WHERE [key] = @key", new { key = MainDomKey });
db.BeginTransaction(IsolationLevel.ReadCommitted);
// get a write lock
_sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom);
// When we are disposed, it means we have released the MainDom lock
// and called all MainDom release callbacks, in this case
// if another maindom is actually coming online we need
// to signal to the MainDom coming online that we have shutdown.
// To do that, we update the existing main dom DB record with a suffixed "_updated" string.
// Otherwise, if we are just shutting down, we want to just delete the row.
if (_mainDomChanging)
{
_logger.Debug<SqlMainDomLock>("Releasing MainDom, updating row, new application is booting.");
db.Execute($"UPDATE umbracoKeyValue SET [value] = [value] + '{UpdatedSuffix}' WHERE [key] = @key", new { key = MainDomKey });
}
else
{
_logger.Debug<SqlMainDomLock>("Releasing MainDom, deleting row, application is shutting down.");
db.Execute("DELETE FROM umbracoKeyValue WHERE [key] = @key", new { key = MainDomKey });
}
}
else
catch (Exception ex)
{
_logger.Debug<SqlMainDomLock>("Releasing MainDom, deleting row, application is shutting down.");
db.Execute("DELETE FROM umbracoKeyValue WHERE [key] = @key", new { key = MainDomKey });
ResetDatabase();
_logger.Error<SqlMainDomLock>(ex, "Unexpected error during dipsose.");
_hasError = true;
}
finally
{
db?.CompleteTransaction();
ResetDatabase();
}
}
catch (Exception ex)
{
ResetDatabase();
_logger.Error<SqlMainDomLock>(ex, "Unexpected error during dipsose.");
_hasError = true;
}
finally
{
db?.CompleteTransaction();
ResetDatabase();
}
}
}

View File

@@ -4,6 +4,7 @@ using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Examine;
@@ -30,13 +31,14 @@ namespace Umbraco.Web.Search
composition.Register<IndexRebuilder>(Lifetime.Singleton);
composition.RegisterUnique<IUmbracoIndexConfig, UmbracoIndexConfig>();
composition.RegisterUnique<IIndexDiagnosticsFactory, IndexDiagnosticsFactory>();
composition.RegisterUnique<IIndexDiagnosticsFactory, IndexDiagnosticsFactory>();
composition.RegisterUnique<IPublishedContentValueSetBuilder>(factory =>
new ContentValueSetBuilder(
factory.GetInstance<PropertyEditorCollection>(),
factory.GetInstance<UrlSegmentProviderCollection>(),
factory.GetInstance<IUserService>(),
factory.GetInstance<IShortStringHelper>(),
factory.GetInstance<IScopeProvider>(),
true));
composition.RegisterUnique<IContentValueSetBuilder>(factory =>
new ContentValueSetBuilder(
@@ -44,6 +46,7 @@ namespace Umbraco.Web.Search
factory.GetInstance<UrlSegmentProviderCollection>(),
factory.GetInstance<IUserService>(),
factory.GetInstance<IShortStringHelper>(),
factory.GetInstance<IScopeProvider>(),
false));
composition.RegisterUnique<IValueSetBuilder<IMedia>, MediaValueSetBuilder>();
composition.RegisterUnique<IValueSetBuilder<IMember>, MemberValueSetBuilder>();

View File

@@ -19,6 +19,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
// provides efficient database access for NuCache
internal class DatabaseDataSource : IDataSource
{
private const int PageSize = 500;
private readonly ILogger _logger;
public DatabaseDataSource(ILogger logger)
@@ -85,33 +87,43 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Document && !x.Trashed)
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
return scope.Database.Query<ContentSourceDto>(sql).Select(CreateContentNodeKit);
// We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout.
// We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that.
foreach (var row in scope.Database.QueryPaged<ContentSourceDto>(PageSize, sql))
yield return CreateContentNodeKit(row);
}
public IEnumerable<ContentNodeKit> GetBranchContentSources(IScope scope, int id)
{
var syntax = scope.SqlContext.SqlSyntax;
var sql = ContentSourcesSelect(scope, s => s
var sql = ContentSourcesSelect(scope,
s => s.InnerJoin<NodeDto>("x").On<NodeDto, NodeDto>((left, right) => left.NodeId == right.NodeId || SqlText<bool>(left.Path, right.Path, (lp, rp) => $"({lp} LIKE {syntax.GetConcat(rp, "',%'")})"), aliasRight: "x"))
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Document && !x.Trashed)
.Where<NodeDto>(x => x.NodeId == id, "x")
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
.InnerJoin<NodeDto>("x").On<NodeDto, NodeDto>((left, right) => left.NodeId == right.NodeId || SqlText<bool>(left.Path, right.Path, (lp, rp) => $"({lp} LIKE {syntax.GetConcat(rp, "',%'")})"), aliasRight: "x"))
// We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout.
// We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that.
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Document && !x.Trashed)
.Where<NodeDto>(x => x.NodeId == id, "x")
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
return scope.Database.Query<ContentSourceDto>(sql).Select(CreateContentNodeKit);
foreach (var row in scope.Database.QueryPaged<ContentSourceDto>(PageSize, sql))
yield return CreateContentNodeKit(row);
}
public IEnumerable<ContentNodeKit> GetTypeContentSources(IScope scope, IEnumerable<int> ids)
{
if (!ids.Any()) return Enumerable.Empty<ContentNodeKit>();
if (!ids.Any()) yield break;
var sql = ContentSourcesSelect(scope)
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Document && !x.Trashed)
.WhereIn<ContentDto>(x => x.ContentTypeId, ids)
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
return scope.Database.Query<ContentSourceDto>(sql).Select(CreateContentNodeKit);
// We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout.
// We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that.
foreach (var row in scope.Database.QueryPaged<ContentSourceDto>(PageSize, sql))
yield return CreateContentNodeKit(row);
}
private Sql<ISqlContext> MediaSourcesSelect(IScope scope, Func<Sql<ISqlContext>, Sql<ISqlContext>> joins = null)
@@ -122,11 +134,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
x => Alias(x.Level, "Level"), x => Alias(x.Path, "Path"), x => Alias(x.SortOrder, "SortOrder"), x => Alias(x.ParentId, "ParentId"),
x => Alias(x.CreateDate, "CreateDate"), x => Alias(x.UserId, "CreatorId"))
.AndSelect<ContentDto>(x => Alias(x.ContentTypeId, "ContentTypeId"))
.AndSelect<ContentVersionDto>(x => Alias(x.Id, "VersionId"), x => Alias(x.Text, "EditName"), x => Alias(x.VersionDate, "EditVersionDate"), x => Alias(x.UserId, "EditWriterId"))
.AndSelect<ContentNuDto>("nuEdit", x => Alias(x.Data, "EditData"))
.From<NodeDto>();
if (joins != null)
@@ -134,9 +143,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
sql = sql
.InnerJoin<ContentDto>().On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId)
.InnerJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current)
.LeftJoin<ContentNuDto>("nuEdit").On<NodeDto, ContentNuDto>((left, right) => left.NodeId == right.NodeId && !right.Published, aliasRight: "nuEdit");
return sql;
@@ -158,33 +165,43 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed)
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
return scope.Database.Query<ContentSourceDto>(sql).Select(CreateMediaNodeKit);
// We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout.
// We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that.
foreach (var row in scope.Database.QueryPaged<ContentSourceDto>(PageSize, sql))
yield return CreateMediaNodeKit(row);
}
public IEnumerable<ContentNodeKit> GetBranchMediaSources(IScope scope, int id)
{
var syntax = scope.SqlContext.SqlSyntax;
var sql = MediaSourcesSelect(scope, s => s
.InnerJoin<NodeDto>("x").On<NodeDto, NodeDto>((left, right) => left.NodeId == right.NodeId || SqlText<bool>(left.Path, right.Path, (lp, rp) => $"({lp} LIKE {syntax.GetConcat(rp, "',%'")})"), aliasRight: "x"))
var sql = MediaSourcesSelect(scope,
s => s.InnerJoin<NodeDto>("x").On<NodeDto, NodeDto>((left, right) => left.NodeId == right.NodeId || SqlText<bool>(left.Path, right.Path, (lp, rp) => $"({lp} LIKE {syntax.GetConcat(rp, "',%'")})"), aliasRight: "x"))
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed)
.Where<NodeDto>(x => x.NodeId == id, "x")
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
return scope.Database.Query<ContentSourceDto>(sql).Select(CreateMediaNodeKit);
// We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout.
// We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that.
foreach (var row in scope.Database.QueryPaged<ContentSourceDto>(PageSize, sql))
yield return CreateMediaNodeKit(row);
}
public IEnumerable<ContentNodeKit> GetTypeMediaSources(IScope scope, IEnumerable<int> ids)
{
if (!ids.Any()) return Enumerable.Empty<ContentNodeKit>();
if (!ids.Any()) yield break;
var sql = MediaSourcesSelect(scope)
.Where<NodeDto>(x => x.NodeObjectType == Constants.ObjectTypes.Media && !x.Trashed)
.WhereIn<ContentDto>(x => x.ContentTypeId, ids)
.OrderBy<NodeDto>(x => x.Level, x => x.ParentId, x => x.SortOrder);
return scope.Database.Query<ContentSourceDto>(sql).Select(CreateMediaNodeKit);
// We need to page here. We don't want to iterate over every single row in one connection cuz this can cause an SQL Timeout.
// We also want to read with a db reader and not load everything into memory, QueryPaged lets us do that.
foreach (var row in scope.Database.QueryPaged<ContentSourceDto>(PageSize, sql))
yield return CreateMediaNodeKit(row);
}
private ContentNodeKit CreateContentNodeKit(ContentSourceDto dto)

View File

@@ -4,6 +4,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CSharpTest.Net.Collections;
using Newtonsoft.Json;
using Umbraco.Core;
@@ -885,12 +886,37 @@ namespace Umbraco.Web.PublishedCache.NuCache
//into a new DLL for the application which includes both content types and media types.
//Since the models in the cache are based on these actual classes, all of the objects in the cache need to be updated
//to use the newest version of the class.
using (_contentStore.GetScopedWriteLock(_scopeProvider))
using (_mediaStore.GetScopedWriteLock(_scopeProvider))
{
NotifyLocked(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out var draftChanged, out var publishedChanged);
NotifyLocked(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out var anythingChanged);
}
// NOTE: Ideally this can be run on background threads here which would prevent blocking the UI
// as is the case when saving a content type. Intially one would think that it won't be any different
// between running this here or in another background thread immediately after with regards to how the
// UI will respond because we already know between calling `WithSafeLiveFactoryReset` to reset the PureLive models
// and this code here, that many front-end requests could be attempted to be processed. If that is the case, those pages are going to get a
// model binding error and our ModelBindingExceptionFilter is going to to its magic to reload those pages so the end user is none the wiser.
// So whether or not this executes 'here' or on a background thread immediately wouldn't seem to make any difference except that we can return
// execution to the UI sooner.
// BUT!... there is a difference IIRC. There is still execution logic that continues after this call on this thread with the cache refreshers
// and those cache refreshers need to have the up-to-date data since other user cache refreshers will be expecting the data to be 'live'. If
// we ran this on a background thread then those cache refreshers are going to not get 'live' data when they query the content cache which
// they require.
// These can be run side by side in parallel.
Parallel.Invoke(
() =>
{
using (_contentStore.GetScopedWriteLock(_scopeProvider))
{
NotifyLocked(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _);
}
},
() =>
{
using (_mediaStore.GetScopedWriteLock(_scopeProvider))
{
NotifyLocked(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _);
}
});
}
((PublishedSnapshot)CurrentPublishedSnapshot)?.Resync();

View File

@@ -924,6 +924,24 @@ namespace Umbraco.Tests.Services
Assert.AreEqual(user.Id, profile.Id);
}
[Test]
public void Get_By_Profile_Id_Must_return_null_if_user_not_exists()
{
var profile = ServiceContext.UserService.GetProfileById(42);
// Assert
Assert.IsNull(profile);
}
[Test]
public void GetProfilesById_Must_empty_if_users_not_exists()
{
var profiles = ServiceContext.UserService.GetProfilesById(42);
// Assert
CollectionAssert.IsEmpty(profiles);
}
[Test]
public void Get_User_By_Username()
{

View File

@@ -33,16 +33,23 @@ namespace Umbraco.Tests.UmbracoExamine
/// </summary>
internal static class IndexInitializer
{
public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, bool publishedValuesOnly)
public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, IScopeProvider scopeProvider, bool publishedValuesOnly)
{
var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }), GetMockUserService(), TestHelper.ShortStringHelper, publishedValuesOnly);
var contentValueSetBuilder = new ContentValueSetBuilder(
propertyEditors,
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(TestHelper.ShortStringHelper) }),
GetMockUserService(),
TestHelper.ShortStringHelper,
scopeProvider,
publishedValuesOnly);
return contentValueSetBuilder;
}
public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, ISqlContext sqlContext, bool publishedValuesOnly)
public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, IScopeProvider scopeProvider, bool publishedValuesOnly)
{
var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors, publishedValuesOnly);
var contentIndexDataSource = new ContentIndexPopulator(true, null, contentService, sqlContext, contentValueSetBuilder);
var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors, scopeProvider, publishedValuesOnly);
var contentIndexDataSource = new ContentIndexPopulator(true, null, contentService, scopeProvider.SqlContext, contentValueSetBuilder);
return contentIndexDataSource;
}

View File

@@ -30,7 +30,7 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Index_Property_Data_With_Value_Indexer()
{
var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Factory.GetInstance<PropertyEditorCollection>(), false);
var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Factory.GetInstance<PropertyEditorCollection>(), ScopeProvider, false);
using (var luceneDir = new RandomIdRamDirectory())
using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir,
@@ -122,7 +122,7 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Rebuild_Index()
{
var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false);
var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider, false);
var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockMediaService());
using (var luceneDir = new RandomIdRamDirectory())
@@ -150,7 +150,7 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Index_Protected_Content_Not_Indexed()
{
var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false);
var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider, false);
using (var luceneDir = new RandomIdRamDirectory())
@@ -275,7 +275,7 @@ namespace Umbraco.Tests.UmbracoExamine
[Test]
public void Index_Reindex_Content()
{
var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false);
var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider, false);
using (var luceneDir = new RandomIdRamDirectory())
using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir,
validator: new ContentValueSetValidator(false)))
@@ -316,7 +316,7 @@ namespace Umbraco.Tests.UmbracoExamine
public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
{
var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false);
var rebuilder = IndexInitializer.GetContentIndexRebuilder(Factory.GetInstance<PropertyEditorCollection>(), IndexInitializer.GetMockContentService(), ScopeProvider, false);
using (var luceneDir = new RandomIdRamDirectory())
using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir))

View File

@@ -55,7 +55,7 @@ namespace Umbraco.Tests.UmbracoExamine
allRecs);
var propertyEditors = Factory.GetInstance<PropertyEditorCollection>();
var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider.SqlContext, true);
var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider, true);
using (var luceneDir = new RandomIdRamDirectory())
using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir))

View File

@@ -1,7 +1,7 @@
/** Used to broadcast and listen for global events and allow the ability to add async listeners to the callbacks */
/*
Core app events:
Core app events:
app.ready
app.authenticated
@@ -12,9 +12,9 @@
*/
function eventsService($q, $rootScope) {
return {
/** raise an event with a given name */
emit: function (name, args) {

View File

@@ -185,7 +185,19 @@ angular.module('umbraco.services')
authenticate: function (login, password) {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
.then(function(data) {
// Check if user has a start node set.
if(data.startContentIds.length === 0 && data.startMediaIds.length === 0){
var errorMsg = "User has no start-nodes";
var result = { errorMsg: errorMsg, user: data, authenticated: false, lastUserId: lastUserId, loginType: "credentials" };
eventsService.emit("app.notAuthenticated", result);
throw result;
}
return data;
}).then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {

View File

@@ -72,6 +72,7 @@ function MainController($scope, $location, appState, treeService, notificationsS
$scope.authenticated = null;
$scope.user = null;
const isTimedOut = data && data.isTimedOut ? true : false;
$scope.showLoginScreen(isTimedOut);
// Remove the localstorage items for tours shown

View File

@@ -123,7 +123,9 @@ namespace Umbraco.Web.Security
{
_logger.WriteCore(TraceEventType.Information, 0,
$"Login attempt failed for username {userName} from IP address {_request.RemoteIpAddress}, no content and/or media start nodes could be found for any of the user's groups", null, null);
return SignInStatus.Failure;
// We will say its a sucessful login which it is, but they have no node access
return SignInStatus.Success;
}
}