Merge branch 'refs/heads/dev-v7' into temp-U4-9337
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.70, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.7.0, 5.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.7.1, 5.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 2.0.0)" />
|
||||
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
|
||||
|
||||
@@ -1,15 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.UI.WebControls;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal static class UmbracoEntityExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static void ValidatePathWithException(this NodeDto entity)
|
||||
{
|
||||
//don't validate if it's empty and it has no id
|
||||
if (entity.NodeId == default(int) && entity.Path.IsNullOrWhiteSpace())
|
||||
return;
|
||||
|
||||
if (entity.Path.IsNullOrWhiteSpace())
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an empty path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
|
||||
|
||||
var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (pathParts.Length < 2)
|
||||
{
|
||||
//a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
|
||||
}
|
||||
|
||||
if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString())
|
||||
{
|
||||
//the 2nd last id in the path must be it's parent id
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a quick check on the entity's set path to ensure that it's valid and consistent
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ValidatePath(this IUmbracoEntity entity)
|
||||
{
|
||||
//don't validate if it's empty and it has no id
|
||||
if (entity.HasIdentity == false && entity.Path.IsNullOrWhiteSpace())
|
||||
return true;
|
||||
|
||||
if (entity.Path.IsNullOrWhiteSpace())
|
||||
return false;
|
||||
|
||||
var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (pathParts.Length < 2)
|
||||
{
|
||||
//a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString())
|
||||
{
|
||||
//the 2nd last id in the path must be it's parent id
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will validate the entity's path and if it's invalid it will fix it, if fixing is required it will recursively
|
||||
/// check and fix all ancestors if required.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="getParent">A callback specified to retrieve the parent entity of the entity</param>
|
||||
/// <param name="update">A callback specified to update a fixed entity</param>
|
||||
public static void EnsureValidPath<T>(this T entity,
|
||||
ILogger logger,
|
||||
Func<T, T> getParent,
|
||||
Action<T> update)
|
||||
where T: IUmbracoEntity
|
||||
{
|
||||
if (entity.HasIdentity == false)
|
||||
throw new InvalidOperationException("Could not ensure the entity path, the entity has not been assigned an identity");
|
||||
|
||||
if (entity.ValidatePath() == false)
|
||||
{
|
||||
logger.Warn(typeof(UmbracoEntityExtensions), string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.Id, entity.Path, entity.ParentId));
|
||||
if (entity.ParentId == -1)
|
||||
{
|
||||
entity.Path = string.Concat("-1,", entity.Id);
|
||||
//path changed, update it
|
||||
update(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
var parent = getParent(entity);
|
||||
if (parent == null)
|
||||
throw new NullReferenceException("Could not ensure path for entity " + entity.Id + " could not resolve it's parent " + entity.ParentId);
|
||||
|
||||
//the parent must also be valid!
|
||||
parent.EnsureValidPath(logger, getParent, update);
|
||||
|
||||
entity.Path = string.Concat(parent.Path, ",", entity.Id);
|
||||
//path changed, update it
|
||||
update(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasChildren(this IUmbracoEntity entity)
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Umbraco.Core.Persistence
|
||||
return sql.From(sqlSyntax.GetQuotedTableName(tableName));
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
|
||||
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionVisitor<T>();
|
||||
@@ -34,6 +35,13 @@ namespace Umbraco.Core.Persistence
|
||||
return sql.Where(whereExpression, expresionist.GetSqlParameters());
|
||||
}
|
||||
|
||||
public static Sql Where<T>(this Sql sql, Expression<Func<T, bool>> predicate, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionVisitor<T>(sqlSyntax);
|
||||
var whereExpression = expresionist.Visit(predicate);
|
||||
return sql.Where(whereExpression, expresionist.GetSqlParameters());
|
||||
}
|
||||
|
||||
private static string GetFieldName<T>(Expression<Func<T, object>> fieldSelector, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var field = ExpressionHelper.FindProperty(fieldSelector) as PropertyInfo;
|
||||
|
||||
@@ -14,6 +14,14 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
{
|
||||
private readonly Database.PocoData _pd;
|
||||
|
||||
|
||||
public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider)
|
||||
: base(syntaxProvider)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload the specifies a SqlSyntaxProvider")]
|
||||
public PocoToSqlExpressionVisitor()
|
||||
: base(SqlSyntaxContext.SqlSyntaxProvider)
|
||||
{
|
||||
|
||||
@@ -342,6 +342,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
nodeDto.ValidatePathWithException();
|
||||
Database.Update(nodeDto);
|
||||
|
||||
//Update entity with correct values
|
||||
@@ -479,6 +480,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Updates the (base) node data - umbracoNode
|
||||
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
|
||||
nodeDto.ValidatePathWithException();
|
||||
var o = Database.Update(nodeDto);
|
||||
|
||||
//Only update this DTO if the contentType has actually changed
|
||||
|
||||
@@ -324,6 +324,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Update with new correct path
|
||||
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
|
||||
nodeDto.ValidatePathWithException();
|
||||
Database.Update(nodeDto);
|
||||
|
||||
//Update entity with correct values
|
||||
@@ -397,6 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//Updates the (base) node data - umbracoNode
|
||||
var nodeDto = dto.ContentDto.NodeDto;
|
||||
nodeDto.ValidatePathWithException();
|
||||
var o = Database.Update(nodeDto);
|
||||
|
||||
//Only update this DTO if the contentType has actually changed
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
@@ -11,6 +12,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
@@ -714,7 +716,12 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public IEnumerable<IContent> GetDescendants(IContent content)
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateContentRepository(UowProvider.GetUnitOfWork()))
|
||||
//This is a check to ensure that the path is correct for this entity to avoid problems like: http://issues.umbraco.org/issue/U4-9336 due to data corruption
|
||||
if (content.ValidatePath() == false)
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", content.Id, content.Path, content.ParentId));
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
var pathMatch = content.Path + ",";
|
||||
var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != content.Id);
|
||||
@@ -996,6 +1003,10 @@ namespace Umbraco.Core.Services
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
//Hack: this ensures that the entity's path is valid and if not it fixes/persists it
|
||||
//see: http://issues.umbraco.org/issue/U4-9336
|
||||
content.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate);
|
||||
|
||||
var originalPath = content.Path;
|
||||
|
||||
if (Trashing.IsRaisedEventCancelled(
|
||||
@@ -1683,16 +1694,16 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
public bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var asArray = items.ToArray();
|
||||
if (raiseEvents)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IContent>(items), this))
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IContent>(asArray), this))
|
||||
return false;
|
||||
}
|
||||
|
||||
var shouldBePublished = new List<IContent>();
|
||||
var shouldBeSaved = new List<IContent>();
|
||||
|
||||
var asArray = items.ToArray();
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
@@ -1812,6 +1823,23 @@ namespace Umbraco.Core.Services
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Hack: This is used to fix some data if an entity's properties are invalid/corrupt
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
private void QuickUpdate(IContent content)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
if (content.HasIdentity == false) throw new InvalidOperationException("Cannot update an entity without an Identity");
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateContentRepository(uow))
|
||||
{
|
||||
repository.AddOrUpdate(content);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
private void Audit(AuditType type, string message, int userId, int objectId)
|
||||
{
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
@@ -1919,6 +1947,10 @@ namespace Umbraco.Core.Services
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
//Hack: this ensures that the entity's path is valid and if not it fixes/persists it
|
||||
//see: http://issues.umbraco.org/issue/U4-9336
|
||||
content.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate);
|
||||
|
||||
var result = new List<Attempt<PublishStatus>>();
|
||||
|
||||
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
@@ -527,6 +528,10 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An Enumerable flat list of <see cref="IMedia"/> objects</returns>
|
||||
public IEnumerable<IMedia> GetDescendants(IMedia media)
|
||||
{
|
||||
//This is a check to ensure that the path is correct for this entity to avoid problems like: http://issues.umbraco.org/issue/U4-9336 due to data corruption
|
||||
if (media.ValidatePath() == false)
|
||||
throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", media.Id, media.Path, media.ParentId));
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
@@ -619,7 +624,7 @@ namespace Umbraco.Core.Services
|
||||
public IMedia GetMediaByPath(string mediaPath)
|
||||
{
|
||||
var umbracoFileValue = mediaPath;
|
||||
|
||||
|
||||
const string Pattern = ".*[_][0-9]+[x][0-9]+[.].*";
|
||||
var isResized = Regex.IsMatch(mediaPath, Pattern);
|
||||
|
||||
@@ -998,56 +1003,63 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
if (media == null) throw new ArgumentNullException("media");
|
||||
|
||||
var originalPath = media.Path;
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
if (Trashing.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
return OperationStatus.Cancelled(evtMsgs);
|
||||
}
|
||||
//Hack: this ensures that the entity's path is valid and if not it fixes/persists it
|
||||
//see: http://issues.umbraco.org/issue/U4-9336
|
||||
media.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate);
|
||||
|
||||
var moveInfo = new List<MoveEventInfo<IMedia>>
|
||||
{
|
||||
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)
|
||||
};
|
||||
var originalPath = media.Path;
|
||||
|
||||
//Find Descendants, which will be moved to the recycle bin along with the parent/grandparent.
|
||||
var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList();
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
//TODO: This should be part of the repo!
|
||||
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished media
|
||||
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = media.Id });
|
||||
|
||||
media.ChangeTrashedState(true, Constants.System.RecycleBinMedia);
|
||||
repository.AddOrUpdate(media);
|
||||
|
||||
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
|
||||
foreach (var descendant in descendants)
|
||||
if (Trashing.IsRaisedEventCancelled(
|
||||
new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
|
||||
{
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished media
|
||||
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = descendant.Id });
|
||||
|
||||
descendant.ChangeTrashedState(true, descendant.ParentId);
|
||||
repository.AddOrUpdate(descendant);
|
||||
|
||||
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
|
||||
return OperationStatus.Cancelled(evtMsgs);
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
var moveInfo = new List<MoveEventInfo<IMedia>>
|
||||
{
|
||||
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)
|
||||
};
|
||||
|
||||
//Find Descendants, which will be moved to the recycle bin along with the parent/grandparent.
|
||||
var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList();
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
//TODO: This should be part of the repo!
|
||||
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished media
|
||||
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = media.Id });
|
||||
|
||||
media.ChangeTrashedState(true, Constants.System.RecycleBinMedia);
|
||||
repository.AddOrUpdate(media);
|
||||
|
||||
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
|
||||
foreach (var descendant in descendants)
|
||||
{
|
||||
//Remove 'published' xml from the cmsContentXml table for the unpublished media
|
||||
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = descendant.Id });
|
||||
|
||||
descendant.ChangeTrashedState(true, descendant.ParentId);
|
||||
repository.AddOrUpdate(descendant);
|
||||
|
||||
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
Trashed.RaiseEvent(
|
||||
new MoveEventArgs<IMedia>(false, evtMsgs, moveInfo.ToArray()), this);
|
||||
|
||||
Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
|
||||
|
||||
return OperationStatus.Success(evtMsgs);
|
||||
}
|
||||
|
||||
Trashed.RaiseEvent(
|
||||
new MoveEventArgs<IMedia>(false, evtMsgs, moveInfo.ToArray()), this);
|
||||
|
||||
Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
|
||||
|
||||
return OperationStatus.Success(evtMsgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1064,8 +1076,6 @@ namespace Umbraco.Core.Services
|
||||
((IMediaServiceOperations)this).Delete(media, userId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Permanently deletes versions from an <see cref="IMedia"/> object prior to a specific date.
|
||||
/// This method will never delete the latest version of a content item.
|
||||
@@ -1154,7 +1164,6 @@ namespace Umbraco.Core.Services
|
||||
public bool Sort(IEnumerable<IMedia> items, int userId = 0, bool raiseEvents = true)
|
||||
{
|
||||
var asArray = items.ToArray();
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMedia>(asArray), this))
|
||||
@@ -1316,6 +1325,23 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hack: This is used to fix some data if an entity's properties are invalid/corrupt
|
||||
/// </summary>
|
||||
/// <param name="media"></param>
|
||||
private void QuickUpdate(IMedia media)
|
||||
{
|
||||
if (media == null) throw new ArgumentNullException("media");
|
||||
if (media.HasIdentity == false) throw new InvalidOperationException("Cannot update an entity without an Identity");
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
|
||||
{
|
||||
repository.AddOrUpdate(media);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
@@ -9,6 +12,132 @@ namespace Umbraco.Tests.Models
|
||||
[TestFixture]
|
||||
public class UmbracoEntityTests
|
||||
{
|
||||
[Test]
|
||||
public void Validate_Path()
|
||||
{
|
||||
var entity = new UmbracoEntity();
|
||||
|
||||
//it's empty with no id so we need to allow it
|
||||
Assert.IsTrue(entity.ValidatePath());
|
||||
|
||||
entity.Id = 1234;
|
||||
|
||||
//it has an id but no path, so we can't allow it
|
||||
Assert.IsFalse(entity.ValidatePath());
|
||||
|
||||
entity.Path = "-1";
|
||||
|
||||
//invalid path
|
||||
Assert.IsFalse(entity.ValidatePath());
|
||||
|
||||
entity.Path = string.Concat("-1,", entity.Id);
|
||||
|
||||
//valid path
|
||||
Assert.IsTrue(entity.ValidatePath());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Path_Throws_Without_Id()
|
||||
{
|
||||
var entity = new UmbracoEntity();
|
||||
|
||||
//no id assigned
|
||||
Assert.Throws<InvalidOperationException>(() => entity.EnsureValidPath(Mock.Of<ILogger>(), umbracoEntity => new UmbracoEntity(), umbracoEntity => { }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Path_Throws_Without_Parent()
|
||||
{
|
||||
var entity = new UmbracoEntity {Id = 1234};
|
||||
|
||||
//no parent found
|
||||
Assert.Throws<NullReferenceException>(() => entity.EnsureValidPath(Mock.Of<ILogger>(), umbracoEntity => null, umbracoEntity => { }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Path_Entity_At_Root()
|
||||
{
|
||||
var entity = new UmbracoEntity
|
||||
{
|
||||
Id = 1234,
|
||||
ParentId = -1
|
||||
};
|
||||
|
||||
|
||||
entity.EnsureValidPath(Mock.Of<ILogger>(), umbracoEntity => null, umbracoEntity => { });
|
||||
|
||||
//works because it's under the root
|
||||
Assert.AreEqual("-1,1234", entity.Path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Path_Entity_Valid_Parent()
|
||||
{
|
||||
var entity = new UmbracoEntity
|
||||
{
|
||||
Id = 1234,
|
||||
ParentId = 888
|
||||
};
|
||||
|
||||
entity.EnsureValidPath(Mock.Of<ILogger>(), umbracoEntity => umbracoEntity.ParentId == 888 ? new UmbracoEntity{Id = 888, Path = "-1,888"} : null, umbracoEntity => { });
|
||||
|
||||
//works because the parent was found
|
||||
Assert.AreEqual("-1,888,1234", entity.Path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Path_Entity_Valid_Recursive_Parent()
|
||||
{
|
||||
var parentA = new UmbracoEntity
|
||||
{
|
||||
Id = 999,
|
||||
ParentId = -1
|
||||
};
|
||||
|
||||
var parentB = new UmbracoEntity
|
||||
{
|
||||
Id = 888,
|
||||
ParentId = 999
|
||||
};
|
||||
|
||||
var parentC = new UmbracoEntity
|
||||
{
|
||||
Id = 777,
|
||||
ParentId = 888
|
||||
};
|
||||
|
||||
var entity = new UmbracoEntity
|
||||
{
|
||||
Id = 1234,
|
||||
ParentId = 777
|
||||
};
|
||||
|
||||
Func<IUmbracoEntity, IUmbracoEntity> getParent = umbracoEntity =>
|
||||
{
|
||||
switch (umbracoEntity.ParentId)
|
||||
{
|
||||
case 999:
|
||||
return parentA;
|
||||
case 888:
|
||||
return parentB;
|
||||
case 777:
|
||||
return parentC;
|
||||
case 1234:
|
||||
return entity;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
//this will recursively fix all paths
|
||||
entity.EnsureValidPath(Mock.Of<ILogger>(), getParent, umbracoEntity => { });
|
||||
|
||||
Assert.AreEqual("-1,999", parentA.Path);
|
||||
Assert.AreEqual("-1,999,888", parentB.Path);
|
||||
Assert.AreEqual("-1,999,888,777", parentC.Path);
|
||||
Assert.AreEqual("-1,999,888,777,1234", entity.Path);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UmbracoEntity_Can_Be_Initialized_From_Dynamic()
|
||||
{
|
||||
|
||||
@@ -35,18 +35,64 @@ namespace Umbraco.Tests.Services
|
||||
[SetUp]
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
}
|
||||
|
||||
//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.
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that we don't unpublish all nodes when a node is deleted that has an invalid path of -1
|
||||
/// Note: it is actually the MoveToRecycleBin happening on the initial deletion of a node through the UI
|
||||
/// that causes the issue.
|
||||
/// Regression test: http://issues.umbraco.org/issue/U4-9336
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Moving_Node_To_Recycle_Bin_With_Invalid_Path()
|
||||
{
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
|
||||
Assert.IsTrue(contentService.PublishWithStatus(root).Success);
|
||||
var content = contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
|
||||
Assert.IsTrue(contentService.PublishWithStatus(content).Success);
|
||||
var hierarchy = CreateContentHierarchy().OrderBy(x => x.Level).ToArray();
|
||||
contentService.Save(hierarchy, 0);
|
||||
foreach (var c in hierarchy)
|
||||
{
|
||||
Assert.IsTrue(contentService.PublishWithStatus(c).Success);
|
||||
}
|
||||
|
||||
//now make the data corrupted :/
|
||||
DatabaseContext.Database.Execute("UPDATE umbracoNode SET path = '-1' WHERE id = @id", new {id = content.Id});
|
||||
|
||||
//re-get with the corrupt path
|
||||
content = contentService.GetById(content.Id);
|
||||
|
||||
// here we get all descendants by the path of the node being moved to bin, and unpublish all of them.
|
||||
// since the path is invalid, there's logic in here to fix that if it's possible and re-persist the entity.
|
||||
var moveResult = ServiceContext.ContentService.WithResult().MoveToRecycleBin(content);
|
||||
|
||||
Assert.IsTrue(moveResult.Success);
|
||||
|
||||
//re-get with the fixed/moved path
|
||||
content = contentService.GetById(content.Id);
|
||||
|
||||
Assert.AreEqual("-1,-20," + content.Id, content.Path);
|
||||
|
||||
//re-get
|
||||
hierarchy = contentService.GetByIds(hierarchy.Select(x => x.Id).ToArray()).OrderBy(x => x.Level).ToArray();
|
||||
|
||||
Assert.That(hierarchy.All(c => c.Trashed == false), Is.True);
|
||||
Assert.That(hierarchy.All(c => c.Path.StartsWith("-1,-20") == false), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Remove_Scheduled_Publishing_Date()
|
||||
{
|
||||
@@ -104,7 +150,7 @@ namespace Umbraco.Tests.Services
|
||||
results.Add(contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0));
|
||||
}
|
||||
|
||||
var sortedGet = contentService.GetByIds(new[] {results[10].Id, results[5].Id, results[12].Id}).ToArray();
|
||||
var sortedGet = contentService.GetByIds(new[] { results[10].Id, results[5].Id, results[12].Id }).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(sortedGet[0].Id, results[10].Id);
|
||||
@@ -121,7 +167,7 @@ namespace Umbraco.Tests.Services
|
||||
// Act
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
|
||||
contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
|
||||
}
|
||||
|
||||
// Assert
|
||||
@@ -230,7 +276,7 @@ namespace Umbraco.Tests.Services
|
||||
// Assert
|
||||
|
||||
//there should be no tags for this entity
|
||||
var tags = tagService.GetTagsForEntity(content1.Id);
|
||||
var tags = tagService.GetTagsForEntity(content1.Id);
|
||||
Assert.AreEqual(0, tags.Count());
|
||||
|
||||
//these tags should still be returned since they still have actively published content assigned
|
||||
@@ -265,7 +311,7 @@ namespace Umbraco.Tests.Services
|
||||
contentService.MoveToRecycleBin(content2);
|
||||
|
||||
// Assert
|
||||
|
||||
|
||||
//there should be no exposed content tags now that nothing is published.
|
||||
var allTags = tagService.GetAllContentTags();
|
||||
Assert.AreEqual(0, allTags.Count());
|
||||
@@ -395,7 +441,7 @@ namespace Umbraco.Tests.Services
|
||||
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
|
||||
{
|
||||
DataTypeDefinitionId = 1041
|
||||
});
|
||||
});
|
||||
contentTypeService.Save(contentType);
|
||||
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => contentType.Id), 0, contentType.Alias) };
|
||||
|
||||
@@ -410,7 +456,7 @@ namespace Umbraco.Tests.Services
|
||||
var child2 = MockedContent.CreateSimpleContent(contentType, "child 2 content", content.Id);
|
||||
child2.SetTags("tags", new[] { "hello2", "world2" }, true);
|
||||
contentService.Save(child2);
|
||||
|
||||
|
||||
// Act
|
||||
contentService.PublishWithChildrenWithStatus(content, includeUnpublished: true);
|
||||
|
||||
@@ -461,34 +507,34 @@ namespace Umbraco.Tests.Services
|
||||
new { nodeId = content.Id, propTypeId = propertyTypeId }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Replace_Tag_Data_To_Published_Content()
|
||||
{
|
||||
[Test]
|
||||
public void Can_Replace_Tag_Data_To_Published_Content()
|
||||
{
|
||||
//Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true);
|
||||
contentType.PropertyGroups.First().PropertyTypes.Add(
|
||||
new PropertyType("test", DataTypeDatabaseType.Ntext, "tags")
|
||||
{
|
||||
DataTypeDefinitionId = 1041
|
||||
});
|
||||
{
|
||||
DataTypeDefinitionId = 1041
|
||||
});
|
||||
contentTypeService.Save(contentType);
|
||||
|
||||
var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1);
|
||||
|
||||
|
||||
|
||||
|
||||
// Act
|
||||
content.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
|
||||
contentService.Publish(content);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(4, content.Properties["tags"].Value.ToString().Split(',').Distinct().Count());
|
||||
var propertyTypeId = contentType.PropertyTypes.Single(x => x.Alias == "tags").Id;
|
||||
Assert.AreEqual(4, DatabaseContext.Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId",
|
||||
new {nodeId = content.Id, propTypeId = propertyTypeId}));
|
||||
}
|
||||
var propertyTypeId = contentType.PropertyTypes.Single(x => x.Alias == "tags").Id;
|
||||
Assert.AreEqual(4, DatabaseContext.Database.ExecuteScalar<int>(
|
||||
"SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId",
|
||||
new { nodeId = content.Id, propTypeId = propertyTypeId }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Append_Tag_Data_To_Published_Content()
|
||||
@@ -506,7 +552,7 @@ namespace Umbraco.Tests.Services
|
||||
var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1);
|
||||
content.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true);
|
||||
contentService.PublishWithStatus(content);
|
||||
|
||||
|
||||
// Act
|
||||
content.SetTags("tags", new[] { "another", "world" }, false);
|
||||
contentService.PublishWithStatus(content);
|
||||
@@ -548,9 +594,9 @@ namespace Umbraco.Tests.Services
|
||||
new { nodeId = content.Id, propTypeId = propertyTypeId }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Remove_Property_Type()
|
||||
{
|
||||
[Test]
|
||||
public void Can_Remove_Property_Type()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
|
||||
@@ -560,9 +606,9 @@ namespace Umbraco.Tests.Services
|
||||
// Assert
|
||||
Assert.That(content, Is.Not.Null);
|
||||
Assert.That(content.HasIdentity, Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Test]
|
||||
public void Can_Create_Content()
|
||||
{
|
||||
// Arrange
|
||||
@@ -575,7 +621,7 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(content, Is.Not.Null);
|
||||
Assert.That(content.HasIdentity, Is.False);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Can_Create_Content_Without_Explicitly_Set_User()
|
||||
{
|
||||
@@ -595,12 +641,12 @@ namespace Umbraco.Tests.Services
|
||||
public void Can_Save_New_Content_With_Explicit_User()
|
||||
{
|
||||
var user = new User(ServiceContext.UserService.GetUserTypeByAlias("admin"))
|
||||
{
|
||||
Name = "Test",
|
||||
Email = "test@test.com",
|
||||
Username = "test",
|
||||
{
|
||||
Name = "Test",
|
||||
Email = "test@test.com",
|
||||
Username = "test",
|
||||
RawPasswordValue = "test"
|
||||
};
|
||||
};
|
||||
ServiceContext.UserService.Save(user);
|
||||
var content = new Content("Test", -1, ServiceContext.ContentTypeService.GetContentType("umbTextpage"));
|
||||
|
||||
@@ -857,14 +903,14 @@ namespace Umbraco.Tests.Services
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
using (var uow = provider.GetUnitOfWork())
|
||||
{
|
||||
uow.Database.TruncateTable("cmsContentXml");
|
||||
uow.Database.TruncateTable("cmsContentXml");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//for this test we are also going to save a revision for a content item that is not published, this is to ensure
|
||||
//that it's published version still makes it into the cmsContentXml table!
|
||||
contentService.Save(allContent.Last());
|
||||
|
||||
|
||||
// Act
|
||||
var published = contentService.RePublishAll(0);
|
||||
|
||||
@@ -872,7 +918,7 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsTrue(published);
|
||||
using (var uow = provider.GetUnitOfWork())
|
||||
{
|
||||
Assert.AreEqual(allContent.Count(), uow.Database.ExecuteScalar<int>("select count(*) from cmsContentXml"));
|
||||
Assert.AreEqual(allContent.Count(), uow.Database.ExecuteScalar<int>("select count(*) from cmsContentXml"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,7 +935,7 @@ namespace Umbraco.Tests.Services
|
||||
var allContent = rootContent.Concat(rootContent.SelectMany(x => x.Descendants())).ToList();
|
||||
//for testing we need to clear out the contentXml table so we can see if it worked
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
|
||||
|
||||
using (var uow = provider.GetUnitOfWork())
|
||||
{
|
||||
uow.Database.TruncateTable("cmsContentXml");
|
||||
@@ -899,7 +945,7 @@ namespace Umbraco.Tests.Services
|
||||
contentService.Save(allContent.Last());
|
||||
|
||||
// Act
|
||||
contentService.RePublishAll(new int[]{allContent.Last().ContentTypeId});
|
||||
contentService.RePublishAll(new int[] { allContent.Last().ContentTypeId });
|
||||
|
||||
// Assert
|
||||
using (var uow = provider.GetUnitOfWork())
|
||||
@@ -1085,7 +1131,7 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0);
|
||||
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
|
||||
content.SetValue("author", "Barack Obama");
|
||||
|
||||
// Act
|
||||
@@ -1116,7 +1162,7 @@ namespace Umbraco.Tests.Services
|
||||
var savedVersion = content.Version;
|
||||
|
||||
// Act
|
||||
var publishedDescendants = ((ContentService) contentService).GetPublishedDescendants(root).ToList();
|
||||
var publishedDescendants = ((ContentService)contentService).GetPublishedDescendants(root).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.That(rootPublished, Is.True);
|
||||
@@ -1146,7 +1192,7 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0);
|
||||
var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0);
|
||||
content.SetValue("author", "Barack Obama");
|
||||
|
||||
// Act
|
||||
@@ -1166,7 +1212,7 @@ namespace Umbraco.Tests.Services
|
||||
var contentType = contentTypeService.GetContentType("umbTextpage");
|
||||
Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Subpage 1", NodeDto.NodeIdSeed + 2);
|
||||
Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Subpage 2", NodeDto.NodeIdSeed + 2);
|
||||
var list = new List<IContent> {subpage, subpage2};
|
||||
var list = new List<IContent> { subpage, subpage2 };
|
||||
|
||||
// Act
|
||||
contentService.Save(list, 0);
|
||||
@@ -1186,9 +1232,9 @@ namespace Umbraco.Tests.Services
|
||||
contentService.Save(hierarchy, 0);
|
||||
|
||||
Assert.That(hierarchy.Any(), Is.True);
|
||||
Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False);
|
||||
//all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown
|
||||
Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0));
|
||||
Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False);
|
||||
//all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown
|
||||
Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0));
|
||||
|
||||
}
|
||||
|
||||
@@ -1375,7 +1421,7 @@ namespace Umbraco.Tests.Services
|
||||
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
|
||||
var temp = MockedContent.CreateSimpleContent(contentType, "Simple Text Page", -1);
|
||||
var prop = temp.Properties.First();
|
||||
temp.SetTags(prop.Alias, new[] {"hello", "world"}, true);
|
||||
temp.SetTags(prop.Alias, new[] { "hello", "world" }, true);
|
||||
var status = contentService.PublishWithStatus(temp);
|
||||
|
||||
// Act
|
||||
@@ -1418,14 +1464,14 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
[Test]
|
||||
public void Can_Save_Lazy_Content()
|
||||
{
|
||||
var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(Mock.Of<ILogger>());
|
||||
{
|
||||
var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(Mock.Of<ILogger>());
|
||||
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
|
||||
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
|
||||
|
||||
var c = new Lazy<IContent>(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page", root.Id));
|
||||
var c2 = new Lazy<IContent>(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage", c.Value.Id));
|
||||
var list = new List<Lazy<IContent>> {c, c2};
|
||||
var list = new List<Lazy<IContent>> { c, c2 };
|
||||
|
||||
ContentTypeRepository contentTypeRepository;
|
||||
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
|
||||
@@ -1443,9 +1489,9 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(c2.Value.Id > 0, Is.True);
|
||||
|
||||
Assert.That(c.Value.ParentId > 0, Is.True);
|
||||
Assert.That(c2.Value.ParentId > 0, Is.True);
|
||||
Assert.That(c2.Value.ParentId > 0, Is.True);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -1468,20 +1514,20 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(hasPublishedVersion, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Verify_Property_Types_On_Content()
|
||||
{
|
||||
[Test]
|
||||
public void Can_Verify_Property_Types_On_Content()
|
||||
{
|
||||
// Arrange
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var contentTypeService = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateAllTypesContentType("allDataTypes", "All DataTypes");
|
||||
contentTypeService.Save(contentType);
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1);
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1);
|
||||
contentService.Save(content);
|
||||
var id = content.Id;
|
||||
var id = content.Id;
|
||||
|
||||
// Act
|
||||
var sut = contentService.GetById(id);
|
||||
var sut = contentService.GetById(id);
|
||||
|
||||
// Arrange
|
||||
Assert.That(sut.GetValue<bool>("isTrue"), Is.True);
|
||||
@@ -1496,7 +1542,7 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(sut.GetValue<DateTime>("dateTime").ToString("G"), Is.EqualTo(content.GetValue<DateTime>("dateTime").ToString("G")));
|
||||
Assert.That(sut.GetValue<string>("colorPicker"), Is.EqualTo("black"));
|
||||
//that one is gone in 7.4
|
||||
//Assert.That(sut.GetValue<string>("folderBrowser"), Is.Null);
|
||||
//Assert.That(sut.GetValue<string>("folderBrowser"), Is.Null);
|
||||
Assert.That(sut.GetValue<string>("ddlMultiple"), Is.EqualTo("1234,1235"));
|
||||
Assert.That(sut.GetValue<string>("rbList"), Is.EqualTo("random"));
|
||||
Assert.That(sut.GetValue<DateTime>("date").ToString("G"), Is.EqualTo(content.GetValue<DateTime>("date").ToString("G")));
|
||||
@@ -1507,23 +1553,23 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(sut.GetValue<int>("memberPicker"), Is.EqualTo(1092));
|
||||
Assert.That(sut.GetValue<string>("relatedLinks"), Is.EqualTo("<links><link title=\"google\" link=\"http://google.com\" type=\"external\" newwindow=\"0\" /></links>"));
|
||||
Assert.That(sut.GetValue<string>("tags"), Is.EqualTo("this,is,tags"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Delete_Previous_Versions_Not_Latest()
|
||||
{
|
||||
[Test]
|
||||
public void Can_Delete_Previous_Versions_Not_Latest()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
|
||||
var version = content.Version;
|
||||
var version = content.Version;
|
||||
|
||||
// Act
|
||||
// Act
|
||||
contentService.DeleteVersion(NodeDto.NodeIdSeed + 4, version, true, 0);
|
||||
var sut = contentService.GetById(NodeDto.NodeIdSeed + 4);
|
||||
|
||||
// Assert
|
||||
Assert.That(sut.Version, Is.EqualTo(version));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Content_Xml_Created()
|
||||
@@ -1542,7 +1588,7 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
|
||||
contentService.Publish(content);
|
||||
|
||||
|
||||
using (var uow = provider.GetUnitOfWork())
|
||||
{
|
||||
Assert.IsTrue(uow.Database.Exists<ContentXmlDto>(content.Id));
|
||||
@@ -1559,10 +1605,10 @@ namespace Umbraco.Tests.Services
|
||||
contentService.Save(content);
|
||||
|
||||
var provider = new PetaPocoUnitOfWorkProvider(Logger);
|
||||
|
||||
|
||||
using (var uow = provider.GetUnitOfWork())
|
||||
{
|
||||
Assert.IsTrue(uow.Database.SingleOrDefault<PreviewXmlDto>("WHERE nodeId=@nodeId AND versionId = @versionId", new{nodeId = content.Id, versionId = content.Version}) != null);
|
||||
Assert.IsTrue(uow.Database.SingleOrDefault<PreviewXmlDto>("WHERE nodeId=@nodeId AND versionId = @versionId", new { nodeId = content.Id, versionId = content.Version }) != null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1665,11 +1711,11 @@ namespace Umbraco.Tests.Services
|
||||
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
|
||||
var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1);
|
||||
|
||||
var list = new List<IContent>();
|
||||
var list = new List<IContent>();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var content = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root);
|
||||
var content = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root);
|
||||
|
||||
list.Add(content);
|
||||
list.AddRange(CreateChildrenOf(contentType, content, 4));
|
||||
@@ -1680,12 +1726,12 @@ namespace Umbraco.Tests.Services
|
||||
return list;
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> CreateChildrenOf(IContentType contentType, IContent content, int depth)
|
||||
private IEnumerable<IContent> CreateChildrenOf(IContentType contentType, IContent content, int depth)
|
||||
{
|
||||
var list = new List<IContent>();
|
||||
for (int i = 0; i < depth; i++)
|
||||
{
|
||||
var c = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i, content);
|
||||
var c = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i, content);
|
||||
list.Add(c);
|
||||
|
||||
Debug.Print("Created: 'Hierarchy Simple Text Subpage {0}' - Depth: {1}", i, depth);
|
||||
|
||||
@@ -69,22 +69,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.htmlEncode) {
|
||||
/** htmlEncode extension method for string */
|
||||
String.prototype.htmlEncode = function () {
|
||||
//create a in-memory div, set it's inner text(which jQuery automatically encodes)
|
||||
//then grab the encoded contents back out. The div never exists on the page.
|
||||
return $('<div/>').text(this).html();
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.htmlDecode) {
|
||||
/** htmlDecode extension method for string */
|
||||
String.prototype.htmlDecode = function () {
|
||||
return $('<div/>').html(this).text();
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.startsWith) {
|
||||
/** startsWith extension method for string */
|
||||
String.prototype.startsWith = function (str) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.TagsController",
|
||||
function ($rootScope, $scope, $log, assetsService, umbRequestHelper, angularHelper, $timeout, $element, $sanitize) {
|
||||
function ($rootScope, $scope, $log, assetsService, umbRequestHelper, angularHelper, $timeout, $element) {
|
||||
|
||||
var $typeahead;
|
||||
|
||||
@@ -41,7 +41,6 @@ angular.module("umbraco")
|
||||
|
||||
//Helper method to add a tag on enter or on typeahead select
|
||||
function addTag(tagToAdd) {
|
||||
tagToAdd = String(tagToAdd).htmlEncode();
|
||||
if (tagToAdd != null && tagToAdd.length > 0) {
|
||||
if ($scope.model.value.indexOf(tagToAdd) < 0) {
|
||||
$scope.model.value.push(tagToAdd);
|
||||
|
||||
@@ -138,8 +138,8 @@
|
||||
<Reference Include="ImageProcessor, Version=2.5.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.2.5.0\lib\net45\ImageProcessor.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessor.Web, Version=4.7.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.Web.4.7.0\lib\net45\ImageProcessor.Web.dll</HintPath>
|
||||
<Reference Include="ImageProcessor.Web, Version=4.7.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.Web.4.7.1\lib\net45\ImageProcessor.Web.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor" version="2.5.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web" version="4.7.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web" version="4.7.1" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web.Config" version="2.3.0" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
|
||||
@@ -88,35 +88,37 @@ namespace Umbraco.Web.Editors
|
||||
where TPersisted : IContentBase
|
||||
{
|
||||
//Map the property values
|
||||
foreach (var p in contentItem.ContentDto.Properties)
|
||||
foreach (var property in contentItem.ContentDto.Properties)
|
||||
{
|
||||
//get the dbo property
|
||||
var dboProperty = contentItem.PersistedContent.Properties[p.Alias];
|
||||
var dboProperty = contentItem.PersistedContent.Properties[property.Alias];
|
||||
|
||||
//create the property data to send to the property editor
|
||||
var d = new Dictionary<string, object>();
|
||||
var dictionary = new Dictionary<string, object>();
|
||||
//add the files if any
|
||||
var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == p.Alias).ToArray();
|
||||
if (files.Length > 0)
|
||||
{
|
||||
d.Add("files", files);
|
||||
}
|
||||
var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == property.Alias).ToArray();
|
||||
|
||||
foreach (var file in files)
|
||||
file.FileName = file.FileName.ToSafeFileName();
|
||||
|
||||
if (files.Any())
|
||||
dictionary.Add("files", files);
|
||||
|
||||
var data = new ContentPropertyData(p.Value, p.PreValues, d);
|
||||
var data = new ContentPropertyData(property.Value, property.PreValues, dictionary);
|
||||
|
||||
//get the deserialized value from the property editor
|
||||
if (p.PropertyEditor == null)
|
||||
if (property.PropertyEditor == null)
|
||||
{
|
||||
LogHelper.Warn<ContentController>("No property editor found for property " + p.Alias);
|
||||
LogHelper.Warn<ContentController>("No property editor found for property " + property.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
var valueEditor = p.PropertyEditor.ValueEditor;
|
||||
var valueEditor = property.PropertyEditor.ValueEditor;
|
||||
//don't persist any bound value if the editor is readonly
|
||||
if (valueEditor.IsReadOnly == false)
|
||||
{
|
||||
var propVal = p.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.Value);
|
||||
var supportTagsAttribute = TagExtractor.GetAttribute(p.PropertyEditor);
|
||||
var propVal = property.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.Value);
|
||||
var supportTagsAttribute = TagExtractor.GetAttribute(property.PropertyEditor);
|
||||
if (supportTagsAttribute != null)
|
||||
{
|
||||
TagExtractor.SetPropertyTags(dboProperty, data, propVal, supportTagsAttribute);
|
||||
|
||||
@@ -525,7 +525,8 @@ namespace Umbraco.Web.Editors
|
||||
foreach (var file in result.FileData)
|
||||
{
|
||||
var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }).TrimEnd();
|
||||
var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower();
|
||||
var safeFileName = fileName.ToSafeFileName();
|
||||
var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower();
|
||||
|
||||
if (UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles.Contains(ext) == false)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
@@ -61,14 +60,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
|
||||
{
|
||||
var json = editorValue.Value as JArray;
|
||||
return json == null
|
||||
? null
|
||||
: json.Select(x => x.Value<string>()).Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
//First we will decode it as html because we know that if this is not a malicious post that the value is
|
||||
// already Html encoded by the tags JavaScript controller. Then we'll re-Html Encode it to ensure that in case this
|
||||
// is a malicious post (i.e. someone is submitting data manually by modifying the request).
|
||||
.Select(WebUtility.HtmlDecode)
|
||||
.Select(WebUtility.HtmlEncode);
|
||||
return json == null ? null : json.Select(x => x.Value<string>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -44,7 +44,17 @@ namespace Umbraco.Web.WebServices
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogHelper.Error<ScheduledPublishController>("Error executing scheduled task", ee);
|
||||
var errorMessage = "Error executing scheduled task";
|
||||
if (HttpContext != null && HttpContext.Request != null)
|
||||
{
|
||||
if (HttpContext.Request.Url != null)
|
||||
errorMessage = string.Format("{0} | Request to {1}", errorMessage, HttpContext.Request.Url);
|
||||
if (HttpContext.Request.UserHostAddress != null)
|
||||
errorMessage = string.Format("{0} | Coming from {1}", errorMessage, HttpContext.Request.UserHostAddress);
|
||||
if (HttpContext.Request.UrlReferrer != null)
|
||||
errorMessage = string.Format("{0} | Referrer {1}", errorMessage, HttpContext.Request.UrlReferrer);
|
||||
}
|
||||
LogHelper.Error<ScheduledPublishController>(errorMessage, ee);
|
||||
|
||||
Response.StatusCode = 400;
|
||||
|
||||
|
||||
@@ -162,7 +162,12 @@ namespace UmbracoExamine
|
||||
|
||||
base.Initialize(name, config);
|
||||
|
||||
if (config["useTempStorage"] != null)
|
||||
//NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory
|
||||
// tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher
|
||||
// and not the indexer). So this whole block is just legacy
|
||||
|
||||
//detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated)
|
||||
if (config["directoryFactory"] == null && config["useTempStorage"] != null)
|
||||
{
|
||||
var fsDir = base.GetLuceneDirectory() as FSDirectory;
|
||||
if (fsDir != null)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
@@ -9,6 +10,8 @@ namespace UmbracoExamine.LocalStorage
|
||||
/// <summary>
|
||||
/// When running on Azure websites, we can use the local user's storage space
|
||||
/// </summary>
|
||||
[Obsolete("This has been superceded by IDirectoryFactory in Examine Core and should not be used")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class AzureLocalStorageDirectory : ILocalStorageDirectory
|
||||
{
|
||||
public DirectoryInfo GetLocalStorageDirectory(NameValueCollection config, string configuredPath)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
|
||||
@@ -11,6 +13,8 @@ namespace UmbracoExamine.LocalStorage
|
||||
/// This is the default implementation - but it comes with it's own limitations - the CodeGen folder is cleared whenever new
|
||||
/// DLLs are changed in the /bin folder (among other circumstances) which means the index would be re-synced (or rebuilt) there.
|
||||
/// </remarks>
|
||||
[Obsolete("This has been superceded by IDirectoryFactory in Examine Core and should not be used")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class CodeGenLocalStorageDirectory : ILocalStorageDirectory
|
||||
{
|
||||
public DirectoryInfo GetLocalStorageDirectory(NameValueCollection config, string configuredPath)
|
||||
|
||||
@@ -73,7 +73,12 @@ namespace UmbracoExamine
|
||||
|
||||
base.Initialize(name, config);
|
||||
|
||||
if (config != null && config["useTempStorage"] != null)
|
||||
//NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory
|
||||
// tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher
|
||||
// and not the indexer). So this whole block is just legacy
|
||||
|
||||
//detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated)
|
||||
if (config != null && config["directoryFactory"] == null && config["useTempStorage"] != null)
|
||||
{
|
||||
//Use the temp storage directory which will store the index in the local/codegen folder, this is useful
|
||||
// for websites that are running from a remove file server and file IO latency becomes an issue
|
||||
|
||||
Reference in New Issue
Block a user