Merge remote-tracking branch 'origin/6.2.2' into 7.1.5

Conflicts:
	src/Umbraco.Core/Sync/DefaultServerMessenger.cs
	src/Umbraco.Tests/Services/ContentServiceTests.cs
This commit is contained in:
Shannon
2014-08-05 12:49:51 -06:00
17 changed files with 366 additions and 40 deletions
@@ -12,6 +12,17 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IRepositoryVersionable<TId, TEntity> : IRepositoryQueryable<TId, TEntity>
where TEntity : IAggregateRoot
{
/// <summary>
/// Get the total count of entities
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
int Count(string contentTypeAlias = null);
int CountChildren(int parentId, string contentTypeAlias = null);
int CountDescendants(int parentId, string contentTypeAlias = null);
/// <summary>
/// Gets a list of all versions for an <see cref="TEntity"/>.
/// </summary>
@@ -96,6 +96,90 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
public int CountDescendants(int parentId, string contentTypeAlias = null)
{
var pathMatch = parentId == -1
? "-1,"
: "," + parentId + ",";
var sql = new Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.Path.Contains(pathMatch));
}
else
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.Path.Contains(pathMatch))
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
}
return Database.ExecuteScalar<int>(sql);
}
public int CountChildren(int parentId, string contentTypeAlias = null)
{
var sql = new Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.ParentId == parentId);
}
else
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.ParentId == parentId)
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
}
return Database.ExecuteScalar<int>(sql);
}
/// <summary>
/// Get the total count of entities
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
public int Count(string contentTypeAlias = null)
{
var sql = new Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
}
else
{
sql.Select("COUNT(*)")
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
}
return Database.ExecuteScalar<int>(sql);
}
/// <summary>
/// This removes associated tags from the entity - used generally when an entity is recycled
/// </summary>
@@ -74,6 +74,33 @@ namespace Umbraco.Core.Services
_dataTypeService = dataTypeService;
}
public int Count(string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentRepository(uow))
{
return repository.Count(contentTypeAlias);
}
}
public int CountChildren(int parentId, string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentRepository(uow))
{
return repository.CountChildren(parentId, contentTypeAlias);
}
}
public int CountDescendants(int parentId, string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateContentRepository(uow))
{
return repository.CountDescendants(parentId, contentTypeAlias);
}
}
/// <summary>
/// Used to bulk update the permissions set for a content item. This will replace all permissions
/// assigned to an entity with a list of user id & permission pairs.
@@ -11,6 +11,10 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IContentService : IService
{
int Count(string contentTypeAlias = null);
int CountChildren(int parentId, string contentTypeAlias = null);
int CountDescendants(int parentId, string contentTypeAlias = null);
/// <summary>
/// Used to bulk update the permissions set for a content item. This will replace all permissions
/// assigned to an entity with a list of user id & permission pairs.
@@ -9,6 +9,10 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IMediaService : IService
{
int Count(string contentTypeAlias = null);
int CountChildren(int parentId, string contentTypeAlias = null);
int CountDescendants(int parentId, string contentTypeAlias = null);
IEnumerable<IMedia> GetByIds(IEnumerable<int> ids);
/// <summary>
@@ -11,6 +11,8 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IMemberService : IMembershipMemberService
{
int Count(string contentTypeAlias = null);
IMember CreateMember(string username, string email, string name, string memberTypeAlias);
IMember CreateMember(string username, string email, string name, IMemberType memberType);
IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias);
+27
View File
@@ -251,6 +251,33 @@ namespace Umbraco.Core.Services
}
}
public int Count(string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
return repository.Count(contentTypeAlias);
}
}
public int CountChildren(int parentId, string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
return repository.CountChildren(parentId, contentTypeAlias);
}
}
public int CountDescendants(int parentId, string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMediaRepository(uow))
{
return repository.CountDescendants(parentId, contentTypeAlias);
}
}
/// <summary>
/// Gets an <see cref="IMedia"/> object by Id
/// </summary>
@@ -644,6 +644,15 @@ namespace Umbraco.Core.Services
}
}
public int Count(string contentTypeAlias = null)
{
var uow = _uowProvider.GetUnitOfWork();
using (var repository = _repositoryFactory.CreateMemberRepository(uow))
{
return repository.Count(contentTypeAlias);
}
}
/// <summary>
/// Creates a member object
/// </summary>
+19 -29
View File
@@ -20,18 +20,19 @@ namespace Umbraco.Core.Sync
{
private readonly Func<Tuple<string, string>> _getUserNamePasswordDelegate;
private volatile bool _hasResolvedDelegate = false;
private readonly object _locker = new object();
private bool _useDistributedCalls;
private readonly object _locker = new object();
protected string Login { get; private set; }
protected string Password{ get; private set; }
protected bool UseDistributedCalls { get; private set; }
/// <summary>
/// Without a username/password all distribuion will be disabled
/// </summary>
internal DefaultServerMessenger()
{
_useDistributedCalls = false;
UseDistributedCalls = false;
}
/// <summary>
@@ -55,7 +56,7 @@ namespace Umbraco.Core.Sync
if (login == null) throw new ArgumentNullException("login");
if (password == null) throw new ArgumentNullException("password");
_useDistributedCalls = useDistributedCalls;
UseDistributedCalls = useDistributedCalls;
Login = login;
Password = password;
}
@@ -221,13 +222,13 @@ namespace Umbraco.Core.Sync
{
Login = null;
Password = null;
_useDistributedCalls = false;
UseDistributedCalls = false;
}
else
{
Login = result.Item1;
Password = result.Item2;
_useDistributedCalls = UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled;
UseDistributedCalls = UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled;
}
}
catch (Exception ex)
@@ -235,7 +236,7 @@ namespace Umbraco.Core.Sync
LogHelper.Error<DefaultServerMessenger>("Could not resolve username/password delegate, server distribution will be disabled", ex);
Login = null;
Password = null;
_useDistributedCalls = false;
UseDistributedCalls = false;
}
}
}
@@ -314,7 +315,7 @@ namespace Umbraco.Core.Sync
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
// can definitely not distribute.
if (!_useDistributedCalls || !servers.Any())
if (!UseDistributedCalls || !servers.Any())
{
//if we are not, then just invoke the call on the cache refresher
InvokeMethodOnRefresherInstance(refresher, dispatchType, getId, instances);
@@ -325,7 +326,7 @@ namespace Umbraco.Core.Sync
MessageSeversForIdsOrJson(servers, refresher, dispatchType, instances.Select(getId));
}
private void MessageSeversForIdsOrJson(
protected virtual void MessageSeversForIdsOrJson(
IEnumerable<IServerAddress> servers,
ICacheRefresher refresher,
MessageType dispatchType,
@@ -345,7 +346,7 @@ namespace Umbraco.Core.Sync
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
// can definitely not distribute.
if (!_useDistributedCalls || !servers.Any())
if (!UseDistributedCalls || !servers.Any())
{
//if we are not, then just invoke the call on the cache refresher
InvokeMethodOnRefresherInstance(refresher, dispatchType, ids, jsonPayload);
@@ -456,16 +457,16 @@ namespace Umbraco.Core.Sync
}
}
List<WaitHandle> waitHandlesList;
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
var waitHandlesList = asyncResultsList.Select(x => x.AsyncWaitHandle).ToArray();
var errorCount = 0;
// Once for each WaitHandle that we have, wait for a response and log it
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
foreach (var t in asyncResults)
//Wait for all requests to complete
WaitHandle.WaitAll(waitHandlesList.ToArray());
foreach (var t in asyncResultsList)
{
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
//var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
try
{
@@ -520,18 +521,7 @@ namespace Umbraco.Core.Sync
LogDispatchBatchError(ee);
}
}
internal IEnumerable<IAsyncResult> GetAsyncResults(List<IAsyncResult> asyncResultsList, out List<WaitHandle> waitHandlesList)
{
var asyncResults = asyncResultsList.ToArray();
waitHandlesList = new List<WaitHandle>();
foreach (var asyncResult in asyncResults)
{
waitHandlesList.Add(asyncResult.AsyncWaitHandle);
}
return asyncResults;
}
private void LogDispatchBatchError(Exception ee)
{
LogHelper.Error<DefaultServerMessenger>("Error refreshing distributed list", ee);
+2
View File
@@ -8,5 +8,7 @@ namespace Umbraco.Core.Sync
public interface IServerAddress
{
string ServerAddress { get; }
//TODO : Should probably add things like port, protocol, server name, app id
}
}
@@ -22,19 +22,21 @@ namespace Umbraco.Core.Sync
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/BulkRefresh", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public void BulkRefresh(RefreshInstruction[] instructions, string login, string password)
public void BulkRefresh(RefreshInstruction[] instructions, string appId, string login, string password)
{
this.Invoke("BulkRefresh", new object[] {
instructions,
appId,
login,
password});
}
/// <remarks/>
public System.IAsyncResult BeginBulkRefresh(RefreshInstruction[] instructions, string login, string password, System.AsyncCallback callback, object asyncState)
public System.IAsyncResult BeginBulkRefresh(RefreshInstruction[] instructions, string appId, string login, string password, System.AsyncCallback callback, object asyncState)
{
return this.BeginInvoke("BulkRefresh", new object[] {
instructions,
appId,
login,
password}, callback, asyncState);
}
@@ -39,6 +39,82 @@ namespace Umbraco.Tests.Services
//TODO Add test to verify there is only ONE newest document/content in cmsDocument table after updating.
//TODO Add test to delete specific version (with and without deleting prior versions) and versions by date.
[Test]
public void Count_All()
{
// Arrange
var contentService = ServiceContext.ContentService;
// Act
for (int i = 0; i < 20; i++)
{
contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
}
// Assert
Assert.AreEqual(24, contentService.Count());
}
[Test]
public void Count_By_Content_Type()
{
// Arrange
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
contentTypeService.Save(contentType);
// Act
for (int i = 0; i < 20; i++)
{
contentService.CreateContentWithIdentity("Test", -1, "umbBlah", 0);
}
// Assert
Assert.AreEqual(20, contentService.Count(contentTypeAlias: "umbBlah"));
}
[Test]
public void Count_Children()
{
// Arrange
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
contentTypeService.Save(contentType);
var parent = contentService.CreateContentWithIdentity("Test", -1, "umbBlah", 0);
// Act
for (int i = 0; i < 20; i++)
{
contentService.CreateContentWithIdentity("Test", parent, "umbBlah");
}
// Assert
Assert.AreEqual(20, contentService.CountChildren(parent.Id));
}
[Test]
public void Count_Descendants()
{
// Arrange
var contentService = ServiceContext.ContentService;
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type");
contentTypeService.Save(contentType);
var parent = contentService.CreateContentWithIdentity("Test", -1, "umbBlah", 0);
// Act
IContent current = parent;
for (int i = 0; i < 20; i++)
{
current = contentService.CreateContentWithIdentity("Test", current, "umbBlah");
}
// Assert
Assert.AreEqual(20, contentService.CountDescendants(parent.Id));
}
[Test]
public void Tags_For_Entity_Are_Not_Exposed_Via_Tag_Api_When_Content_Is_Recycled()
{
@@ -2,6 +2,7 @@ using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
@@ -376,6 +377,34 @@ namespace Umbraco.Tests.Services
Assert.IsNull(ServiceContext.MemberService.GetByEmail("do@not.find"));
}
[Test]
public void Get_Member_Name()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
IMember member = MockedMember.CreateSimpleMember(memberType, "Test Real Name", "test@test.com", "pass", "testUsername");
ServiceContext.MemberService.Save(member);
Assert.AreEqual("Test Real Name", member.Name);
}
[Test]
public void Get_Member_Name_In_Created_Event()
{
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
TypedEventHandler<IMemberService, NewEventArgs<IMember>> callback = (sender, args) =>
{
Assert.AreEqual("Test Real Name", args.Entity.Name);
};
MemberService.Created += callback;
var member = ServiceContext.MemberService.CreateMember("testUsername", "test@test.com", "Test Real Name", memberType);
MemberService.Created -= callback;
}
[Test]
public void Get_By_Username()
{
@@ -1,5 +1,8 @@
<%@ Page MasterPageFile="../masterpages/umbracoPage.Master" Language="c#" CodeBehind="EditTemplate.aspx.cs"
ValidateRequest="false" AutoEventWireup="True" Inherits="Umbraco.Web.UI.Umbraco.Settings.EditTemplate" %>
<%@ OutputCache Location="None" %>
<%@ Import Namespace="Umbraco.Core" %>
<%@ Import Namespace="Umbraco.Core.Configuration" %>
<%@ Import Namespace="Umbraco.Core.IO" %>
@@ -2,6 +2,8 @@
CodeBehind="EditView.aspx.cs" Inherits="Umbraco.Web.UI.Umbraco.Settings.Views.EditView"
ValidateRequest="False" %>
<%@ OutputCache Location="None" %>
<%@ Import Namespace="Umbraco.Core" %>
<%@ Import Namespace="Umbraco.Core.IO" %>
<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %>
+49 -8
View File
@@ -67,6 +67,43 @@ namespace Umbraco.Web
public string JsonPayload { get; set; }
}
/// <summary>
/// We need to check if distributed calls are enabled, if they are we also want to make sure
/// that the current server's cache is updated internally in real time instead of at the end of
/// the call. This is because things like the URL cache, etc... might need to be updated during
/// the request that is making these calls.
/// </summary>
/// <param name="servers"></param>
/// <param name="refresher"></param>
/// <param name="dispatchType"></param>
/// <param name="ids"></param>
/// <param name="jsonPayload"></param>
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-2633#comment=67-15604
/// </remarks>
protected override void MessageSeversForIdsOrJson(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, MessageType dispatchType, IEnumerable<object> ids = null, string jsonPayload = null)
{
//do all the normal stuff
base.MessageSeversForIdsOrJson(servers, refresher, dispatchType, ids, jsonPayload);
//Now, check if we are using Distrubuted calls
if (UseDistributedCalls && servers.Any())
{
//invoke on the current server - we will basically be double cache refreshing for the calling
// server but that just needs to be done currently, see the link above for details.
InvokeMethodOnRefresherInstance(refresher, dispatchType, ids, jsonPayload);
}
}
/// <summary>
/// This adds the call to batched list
/// </summary>
/// <param name="servers"></param>
/// <param name="refresher"></param>
/// <param name="dispatchType"></param>
/// <param name="ids"></param>
/// <param name="idArrayType"></param>
/// <param name="jsonPayload"></param>
protected override void PerformDistributedCall(
IEnumerable<IServerAddress> servers,
ICacheRefresher refresher,
@@ -75,6 +112,7 @@ namespace Umbraco.Web
Type idArrayType = null,
string jsonPayload = null)
{
//NOTE: we use UmbracoContext instead of HttpContext.Current because when some web methods run async, the
// HttpContext.Current is null but the UmbracoContext.Current won't be since we manually assign it.
if (UmbracoContext.Current == null || UmbracoContext.Current.HttpContext == null)
@@ -205,19 +243,22 @@ namespace Umbraco.Web
asyncResultsList.Add(
cacheRefresher.BeginBulkRefresh(
instructions, Login, Password, null, null));
instructions,
HttpRuntime.AppDomainAppId,
Login, Password, null, null));
}
List<WaitHandle> waitHandlesList;
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
var waitHandlesList = asyncResultsList.Select(x => x.AsyncWaitHandle).ToArray();
var errorCount = 0;
// Once for each WaitHandle that we have, wait for a response and log it
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
foreach (var t in asyncResults)
//Wait for all requests to complete
WaitHandle.WaitAll(waitHandlesList.ToArray());
foreach (var t in asyncResultsList)
{
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
//var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
try
{
cacheRefresher.EndBulkRefresh(t);
@@ -24,13 +24,26 @@ namespace umbraco.presentation.webservices
{
[WebMethod]
public void BulkRefresh(RefreshInstruction[] instructions, string login, string password)
public void BulkRefresh(RefreshInstruction[] instructions, string appId, string login, string password)
{
if (BusinessLogic.User.validateCredentials(login, password) == false)
{
return;
}
//check if this is the same app id as the one passed in, if it is, then we will ignore
// the request - we will have to assume that the cache refeshing has already been applied to the server
// that executed the request.
if (SystemUtilities.GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
{
//we can only check this in full trust. if it's in medium trust we'll just end up with
// the server refreshing it's cache twice.
if (HttpRuntime.AppDomainAppId == appId)
{
return;
}
}
//only execute distinct instructions - no sense in running the same one.
foreach (var instruction in instructions.Distinct())
{