U4-4847 Refactor ContentService (#1266)

* U4-4748 - refactor Content-, Media- and MemberTypeRepository

* Cleanup Attempt

* Cleanup OperationStatus

* U4-4748 - refactor Content-, Media- and MemberTypeService

* U4-4748 - cleanup locking

* U4-4748 - refactor Content-, Media- and MemberRepository

* U4-4748 - refactor ContentService (in progress)

* U4-4748 - all unit of work must be completed

* U4-4748 - refactor locks, fix tests

* U4-4748 - deal with fixmes

* U4-4748 - lock table migration

* Update UmbracoVersion

* Fix AuthorizeUpgrade

* U4-4748 - cleanup+bugfix lock objects

* U4-4748 - bugfix

* updates a string interpolation
This commit is contained in:
Stephan
2016-05-18 10:55:19 +02:00
committed by Shannon Deminick
parent 12f4873c90
commit ddf38407d8
139 changed files with 7539 additions and 6981 deletions
@@ -1,174 +1,128 @@
using System;
using Umbraco.Core.Dynamics;
namespace Umbraco.Core
{
/// <summary>
/// Represents the result of an operation attempt.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
[Serializable]
public struct Attempt<T>
{
private readonly bool _success;
private readonly T _result;
private readonly Exception _exception;
/// <summary>
/// Gets a value indicating whether this <see cref="Attempt{T}"/> was successful.
/// </summary>
public bool Success
{
get { return _success; }
}
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
public Exception Exception { get { return _exception; } }
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete(".Error is obsolete, you should use .Exception instead.", false)]
public Exception Error { get { return _exception; } }
/// <summary>
/// Gets the attempt result.
/// </summary>
public T Result
{
get { return _result; }
}
// optimize, use a singleton failed attempt
private static readonly Attempt<T> Failed = new Attempt<T>(false, default(T), null);
/// <summary>
/// Represents an unsuccessful attempt.
/// </summary>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete(".Failed is obsolete, you should use Attempt<T>.Fail() instead.", false)]
public static readonly Attempt<T> False = Failed;
// private - use Succeed() or Fail() methods to create attempts
private Attempt(bool success, T result, Exception exception)
{
_success = success;
_result = result;
_exception = exception;
}
/// <summary>
/// Initialize a new instance of the <see cref="Attempt{T}"/> struct with a result.
/// </summary>
/// <param name="success">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete("Attempt ctors are obsolete, you should use Attempt<T>.Succeed(), Attempt<T>.Fail() or Attempt<T>.If() instead.", false)]
public Attempt(bool success, T result)
: this(success, result, null)
{ }
/// <summary>
/// Initialize a new instance of the <see cref="Attempt{T}"/> struct representing a failed attempt, with an exception.
/// </summary>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <remarks>Keep it for backward compatibility sake.</remarks>
[Obsolete("Attempt ctors are obsolete, you should use Attempt<T>.Succeed(), Attempt<T>.Fail() or Attempt<T>.If() instead.", false)]
public Attempt(Exception exception)
: this(false, default(T), exception)
{ }
/// <summary>
/// Creates a successful attempt.
/// </summary>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed()
{
return new Attempt<T>(true, default(T), null);
}
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed(T result)
{
return new Attempt<T>(true, result, null);
}
/// <summary>
/// Creates a failed attempt.
/// </summary>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail()
{
return Failed;
}
/// <summary>
/// Creates a failed attempt with an exception.
/// </summary>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(Exception exception)
{
return new Attempt<T>(false, default(T), exception);
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(T result)
{
return new Attempt<T>(false, result, null);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(T result, Exception exception)
{
return new Attempt<T>(false, result, exception);
}
/// <summary>
/// Creates a successful or a failed attempt.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> SucceedIf(bool condition)
{
return condition ? new Attempt<T>(true, default(T), null) : Failed;
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> SucceedIf(bool condition, T result)
{
return new Attempt<T>(condition, result, null);
}
/// <summary>
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static implicit operator bool(Attempt<T> a)
{
return a.Success;
}
}
using System;
namespace Umbraco.Core
{
/// <summary>
/// Represents the result of an operation attempt.
/// </summary>
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
[Serializable]
public struct Attempt<T>
{
/// <summary>
/// Gets a value indicating whether this <see cref="Attempt{T}"/> was successful.
/// </summary>
public bool Success { get; }
/// <summary>
/// Gets the exception associated with an unsuccessful attempt.
/// </summary>
public Exception Exception { get; }
/// <summary>
/// Gets the attempt result.
/// </summary>
public T Result { get; }
// optimize, use a singleton failed attempt
private static readonly Attempt<T> Failed = new Attempt<T>(false, default(T), null);
// private - use Succeed() or Fail() methods to create attempts
private Attempt(bool success, T result, Exception exception)
{
Success = success;
Result = result;
Exception = exception;
}
/// <summary>
/// Creates a successful attempt.
/// </summary>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed()
{
return new Attempt<T>(true, default(T), null);
}
/// <summary>
/// Creates a successful attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The successful attempt.</returns>
public static Attempt<T> Succeed(T result)
{
return new Attempt<T>(true, result, null);
}
/// <summary>
/// Creates a failed attempt.
/// </summary>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail()
{
return Failed;
}
/// <summary>
/// Creates a failed attempt with an exception.
/// </summary>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(Exception exception)
{
return new Attempt<T>(false, default(T), exception);
}
/// <summary>
/// Creates a failed attempt with a result.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(T result)
{
return new Attempt<T>(false, result, null);
}
/// <summary>
/// Creates a failed attempt with a result and an exception.
/// </summary>
/// <param name="result">The result of the attempt.</param>
/// <param name="exception">The exception causing the failure of the attempt.</param>
/// <returns>The failed attempt.</returns>
public static Attempt<T> Fail(T result, Exception exception)
{
return new Attempt<T>(false, result, exception);
}
/// <summary>
/// Creates a successful or a failed attempt.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> SucceedIf(bool condition)
{
return condition ? new Attempt<T>(true, default(T), null) : Failed;
}
/// <summary>
/// Creates a successful or a failed attempt, with a result.
/// </summary>
/// <param name="condition">A value indicating whether the attempt is successful.</param>
/// <param name="result">The result of the attempt.</param>
/// <returns>The attempt.</returns>
public static Attempt<T> SucceedIf(bool condition, T result)
{
return new Attempt<T>(condition, result, null);
}
/// <summary>
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
public static implicit operator bool(Attempt<T> a)
{
return a.Success;
}
}
}
+14 -1
View File
@@ -69,6 +69,11 @@ namespace Umbraco.Core
/// </summary>
public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
/// <summary>
/// Guid for a Document object.
/// </summary>
public static readonly Guid DocumentGuid = new Guid(Document);
/// <summary>
/// Guid for a Document Type object.
/// </summary>
@@ -84,6 +89,11 @@ namespace Umbraco.Core
/// </summary>
public const string Media = "B796F64C-1F99-4FFB-B886-4BF4BC011A9C";
/// <summary>
/// Guid for a Media object.
/// </summary>
public static readonly Guid MediaGuid = new Guid(Media);
/// <summary>
/// Guid for the Media Recycle Bin.
/// </summary>
@@ -143,7 +153,10 @@ namespace Umbraco.Core
/// </summary>
public const string LockObject = "87A9F1FF-B1E4-4A25-BABB-465A4A47EC41";
/// <summary>
/// Guid for a Lock object.
/// </summary>
public static readonly Guid LockObjectGuid = new Guid(LockObject);
}
}
}
+2 -5
View File
@@ -25,9 +25,6 @@
public const int DefaultContentListViewDataTypeId = -95;
public const int DefaultMediaListViewDataTypeId = -96;
public const int DefaultMembersListViewDataTypeId = -97;
// identifiers for lock objects
public const int ServersLock = -331;
}
}
}
}
}
@@ -19,34 +19,33 @@ namespace Umbraco.Core.DependencyInjection
{
public void Compose(IServiceRegistry container)
{
container.RegisterSingleton<IPublishingStrategy, PublishingStrategy>();
// register a transient messages factory, which will be replaced byt the web
// boot manager when running in a web context
container.Register<IEventMessagesFactory, TransientEventMessagesFactory>();
//the context
container.RegisterSingleton<ServiceContext>();
//now the services...
container.RegisterSingleton<IMigrationEntryService, MigrationEntryService>();
container.RegisterSingleton<IPublicAccessService, PublicAccessService>();
container.RegisterSingleton<ITaskService, TaskService>();
container.RegisterSingleton<IDomainService, DomainService>();
container.RegisterSingleton<IAuditService, AuditService>();
container.RegisterSingleton<IAuditService, AuditService>();
container.RegisterSingleton<ITagService, TagService>();
container.RegisterSingleton<IContentService, ContentService>();
container.RegisterSingleton<IUserService, UserService>();
container.RegisterSingleton<IMemberService, MemberService>();
container.RegisterSingleton<IMediaService, MediaService>();
container.RegisterSingleton<IContentTypeService, ContentTypeService>();
container.RegisterSingleton<IMediaTypeService, MediaTypeService>();
container.RegisterSingleton<IDataTypeService, DataTypeService>();
container.RegisterSingleton<IFileService, FileService>();
container.RegisterSingleton<ILocalizationService, LocalizationService>();
container.RegisterSingleton<IPackagingService, PackagingService>();
container.RegisterSingleton<IServerRegistrationService, ServerRegistrationService>();
container.RegisterSingleton<IEntityService, EntityService>();
container.RegisterSingleton<IRelationService, RelationService>();
container.RegisterSingleton<IRelationService, RelationService>();
container.RegisterSingleton<IMacroService, MacroService>();
container.RegisterSingleton<IMemberTypeService, MemberTypeService>();
container.RegisterSingleton<IMemberGroupService, MemberGroupService>();
@@ -84,7 +83,7 @@ namespace Umbraco.Core.DependencyInjection
factory.GetInstance<Lazy<LocalizedTextServiceFileSources>>(),
factory.GetInstance<ILogger>()));
//TODO: These are replaced in the web project - we need to declare them so that
//TODO: These are replaced in the web project - we need to declare them so that
// something is wired up, just not sure this is very nice but will work for now.
container.RegisterSingleton<IApplicationTreeService, EmptyApplicationTreeService>();
container.RegisterSingleton<ISectionService, EmptySectionService>();
@@ -17,6 +17,16 @@ namespace Umbraco.Core.Events
Files = new List<string>();
}
public RecycleBinEventArgs(Guid nodeObjectType, bool emptiedSuccessfully)
: base(false)
{
AllPropertyData = new Dictionary<int, IEnumerable<Property>>();
NodeObjectType = nodeObjectType;
Ids = new int[0];
RecycleBinEmptiedSuccessfully = emptiedSuccessfully;
Files = new List<string>();
}
public RecycleBinEventArgs(Guid nodeObjectType, Dictionary<int, IEnumerable<Property>> allPropertyData)
: base(true)
{
@@ -26,6 +36,15 @@ namespace Umbraco.Core.Events
Files = new List<string>();
}
public RecycleBinEventArgs(Guid nodeObjectType)
: base(true)
{
AllPropertyData = new Dictionary<int, IEnumerable<Property>>();
NodeObjectType = nodeObjectType;
Ids = new int[0];
Files = new List<string>();
}
/// <summary>
/// Backwards compatibility constructor
/// </summary>
+54 -8
View File
@@ -4,8 +4,10 @@ using System.Globalization;
using System.Reflection;
using System.IO;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web.Hosting;
using ICSharpCode.SharpZipLib.Zip;
using Umbraco.Core.Configuration;
@@ -306,7 +308,7 @@ namespace Umbraco.Core.IO
var debugFolder = Path.Combine(binFolder, "debug");
if (Directory.Exists(debugFolder))
return debugFolder;
#endif
#endif
var releaseFolder = Path.Combine(binFolder, "release");
if (Directory.Exists(releaseFolder))
return releaseFolder;
@@ -341,7 +343,7 @@ namespace Umbraco.Core.IO
public static void EnsurePathExists(string path)
{
var absolutePath = IOHelper.MapPath(path);
var absolutePath = MapPath(path);
if (Directory.Exists(absolutePath) == false)
Directory.CreateDirectory(absolutePath);
}
@@ -349,14 +351,58 @@ namespace Umbraco.Core.IO
public static void EnsureFileExists(string path, string contents)
{
var absolutePath = IOHelper.MapPath(path);
if (File.Exists(absolutePath) == false)
if (File.Exists(absolutePath)) return;
using (var writer = File.CreateText(absolutePath))
{
using (var writer = File.CreateText(absolutePath))
{
writer.Write(contents);
}
writer.Write(contents);
}
}
/// <summary>
/// Deletes all files passed in.
/// </summary>
/// <param name="files"></param>
/// <param name="onError"></param>
/// <returns></returns>
internal static bool DeleteFiles(IEnumerable<string> files, Action<string, Exception> onError = null)
{
//ensure duplicates are removed
files = files.Distinct();
var allsuccess = true;
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
Parallel.ForEach(files, file =>
{
try
{
if (file.IsNullOrWhiteSpace()) return;
var relativeFilePath = fs.GetRelativePath(file);
if (fs.FileExists(relativeFilePath) == false) return;
var parentDirectory = Path.GetDirectoryName(relativeFilePath);
// don't want to delete the media folder if not using directories.
if (UmbracoConfig.For.UmbracoSettings().Content.UploadAllowDirectories && parentDirectory != fs.GetRelativePath("/"))
{
//issue U4-771: if there is a parent directory the recursive parameter should be true
fs.DeleteDirectory(parentDirectory, String.IsNullOrEmpty(parentDirectory) == false);
}
else
{
fs.DeleteFile(file, true);
}
}
catch (Exception e)
{
onError?.Invoke(file, e);
allsuccess = false;
}
});
return allsuccess;
}
}
}
+46 -24
View File
@@ -17,12 +17,14 @@ namespace Umbraco.Core.Models
private IContentType _contentType;
private ITemplate _template;
private bool _published;
private bool? _publishedOriginal;
private string _language;
private DateTime? _releaseDate;
private DateTime? _expireDate;
private int _writer;
private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name.
private bool _permissionsChanged;
/// <summary>
/// Constructor for creating a Content object
/// </summary>
@@ -31,8 +33,7 @@ namespace Umbraco.Core.Models
/// <param name="contentType">ContentType for the current Content object</param>
public Content(string name, IContent parent, IContentType contentType)
: this(name, parent, contentType, new PropertyCollection())
{
}
{ }
/// <summary>
/// Constructor for creating a Content object
@@ -47,6 +48,7 @@ namespace Umbraco.Core.Models
Mandate.ParameterNotNull(contentType, "contentType");
_contentType = contentType;
PublishedState = PublishedState.Unpublished;
}
/// <summary>
@@ -57,8 +59,7 @@ namespace Umbraco.Core.Models
/// <param name="contentType">ContentType for the current Content object</param>
public Content(string name, int parentId, IContentType contentType)
: this(name, parentId, contentType, new PropertyCollection())
{
}
{ }
/// <summary>
/// Constructor for creating a Content object
@@ -73,6 +74,7 @@ namespace Umbraco.Core.Models
Mandate.ParameterNotNull(contentType, "contentType");
_contentType = contentType;
PublishedState = PublishedState.Unpublished;
}
private static readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo<Content, ITemplate>(x => x.Template);
@@ -95,7 +97,13 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual ITemplate Template
{
get { return _template; }
get
{
if (_template == null)
return _contentType.DefaultTemplate;
return _template;
}
set
{
SetPropertyValueAndDetectChanges(o =>
@@ -146,11 +154,19 @@ namespace Umbraco.Core.Models
SetPropertyValueAndDetectChanges(o =>
{
_published = value;
_publishedOriginal = _publishedOriginal ?? _published;
PublishedState = _published ? PublishedState.Published : PublishedState.Unpublished;
return _published;
}, _published, PublishedSelector);
}
}
[IgnoreDataMember]
public bool PublishedOriginal
{
get { return _publishedOriginal ?? false; }
}
/// <summary>
/// Language of the data contained within this Content object.
/// </summary>
@@ -305,12 +321,14 @@ namespace Umbraco.Core.Models
/// </summary>
public void ChangePublishedState(PublishedState state)
{
Published = state == PublishedState.Published;
if (state == PublishedState.Published || state == PublishedState.Unpublished)
throw new ArgumentException("Invalid state.");
Published = state == PublishedState.Publishing;
PublishedState = state;
}
[DataMember]
internal PublishedState PublishedState { get; set; }
internal PublishedState PublishedState { get; private set; }
/// <summary>
/// Gets or sets the unique identifier of the published version, if any.
@@ -322,24 +340,26 @@ namespace Umbraco.Core.Models
/// Gets a value indicating whether the content has a published version.
/// </summary>
public bool HasPublishedVersion { get { return PublishedVersionGuid != default(Guid); } }
/// <summary>
/// Changes the Trashed state of the content object
/// </summary>
/// <param name="isTrashed">Boolean indicating whether content is trashed (true) or not trashed (false)</param>
/// <param name="parentId"> </param>
public override void ChangeTrashedState(bool isTrashed, int parentId = -20)
{
Trashed = isTrashed;
ParentId = parentId;
//If the content is trashed and is published it should be marked as unpublished
if (isTrashed && Published)
{
ChangePublishedState(PublishedState.Unpublished);
}
}
public override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
{
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
// take care of the published state
switch (PublishedState)
{
case PublishedState.Saving:
case PublishedState.Unpublishing:
PublishedState = PublishedState.Unpublished;
break;
case PublishedState.Publishing:
PublishedState = PublishedState.Published;
break;
}
_publishedOriginal = _published;
}
/// <summary>
/// Method to call when Entity is being updated
/// </summary>
@@ -377,6 +397,8 @@ namespace Umbraco.Core.Models
property.Version = clone.Version;
}
clone.PublishedVersionGuid = Guid.Empty;
return clone;
}
+10 -2
View File
@@ -118,6 +118,16 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Sets the ParentId from the lazy integer id
/// </summary>
/// <param name="id">Id of the Parent</param>
internal protected void SetLazyParentId(Lazy<int> parentId)
{
_parentId = parentId;
OnPropertyChanged(ParentIdSelector);
}
/// <summary>
/// Gets or sets the name of the entity
/// </summary>
@@ -483,8 +493,6 @@ namespace Umbraco.Core.Models
get { return _lastInvalidProperties; }
}
public abstract void ChangeTrashedState(bool isTrashed, int parentId = -20);
#region Dirty property handling
/// <summary>
+177 -155
View File
@@ -1,13 +1,10 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -17,9 +14,6 @@ using Umbraco.Core.IO;
using Umbraco.Core.Media;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Strings;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core.Models
@@ -43,175 +37,184 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Determines if the item should be persisted at all
/// Determines whether the content should be persisted.
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
/// <remarks>
/// In one particular case, a content item shouldn't be persisted:
/// * The item exists and is published
/// * A call to ContentService.Save is made
/// * The item has not been modified whatsoever apart from changing it's published status from published to saved
///
/// In this case, there is no reason to make any database changes at all
/// </remarks>
/// <param name="entity">The content.</param>
/// <returns>True is the content should be persisted, otherwise false.</returns>
/// <remarks>See remarks in overload.</remarks>
internal static bool RequiresSaving(this IContent entity)
{
var publishedState = ((Content)entity).PublishedState;
return RequiresSaving(entity, publishedState);
return RequiresSaving(entity, ((Content) entity).PublishedState);
}
/// <summary>
/// Determines if the item should be persisted at all
/// Determines whether the content should be persisted.
/// </summary>
/// <param name="entity"></param>
/// <param name="publishedState"></param>
/// <returns></returns>
/// <param name="entity">The content.</param>
/// <param name="publishedState">The published state of the content.</param>
/// <returns>True is the content should be persisted, otherwise false.</returns>
/// <remarks>
/// In one particular case, a content item shouldn't be persisted:
/// * The item exists and is published
/// * A call to ContentService.Save is made
/// * The item has not been modified whatsoever apart from changing it's published status from published to saved
///
/// In this case, there is no reason to make any database changes at all
/// This is called by the repository when persisting an existing content, to
/// figure out whether it needs to persist the content at all.
/// </remarks>
internal static bool RequiresSaving(this IContent entity, PublishedState publishedState)
{
var publishedChanged = entity.IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished;
//check if any user prop has changed
var propertyValueChanged = entity.IsAnyUserPropertyDirty();
//We need to know if any other property apart from Published was changed here
//don't create a new version if the published state has changed to 'Save' but no data has actually been changed
if (publishedChanged && entity.Published == false && propertyValueChanged == false)
// note: publishedState is always the entity's PublishedState except for tests
var content = (Content) entity;
var userPropertyChanged = content.IsAnyUserPropertyDirty();
var dirtyProps = content.GetDirtyProperties();
//var contentPropertyChanged = content.IsEntityDirty();
var contentPropertyChangedExceptPublished = dirtyProps.Any(x => x != "Published");
// we don't want to save (write to DB) if we are "saving" either a published content
// (.Saving) or an unpublished content (.Unpublished) and strictly nothing has changed
var noSave = (publishedState == PublishedState.Saving || publishedState == PublishedState.Unpublished)
&& userPropertyChanged == false
&& contentPropertyChangedExceptPublished == false;
return noSave == false;
}
/// <summary>
/// Determines whether a new version of the content should be created.
/// </summary>
/// <param name="entity">The content.</param>
/// <returns>True if a new version should be created, otherwise false.</returns>
/// <remarks>See remarks in overload.</remarks>
internal static bool RequiresNewVersion(this IContent entity)
{
return RequiresNewVersion(entity, ((Content) entity).PublishedState);
}
/// <summary>
/// Determines whether a new version of the content should be created.
/// </summary>
/// <param name="entity">The content.</param>
/// <param name="publishedState">The published state of the content.</param>
/// <returns>True if a new version should be created, otherwise false.</returns>
/// <remarks>
/// This is called by the repository when persisting an existing content, to
/// figure out whether it needs to create a new version for that content.
/// A new version needs to be created when:
/// * The publish status is changed
/// * The language is changed
/// * A content property is changed (? why ?)
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
/// </remarks>
internal static bool RequiresNewVersion(this IContent entity, PublishedState publishedState)
{
// note: publishedState is always the entity's PublishedState except for tests
// read
// http://issues.umbraco.org/issue/U4-2589 (save & publish & creating new versions)
// http://issues.umbraco.org/issue/U4-3404 (pressing preview does save then preview)
// http://issues.umbraco.org/issue/U4-5510 (previewing & creating new versions)
//
// slightly modifying the rules to make more sense (marked with CHANGE)
// but should respect the result of the discussions in those issues
// figure out whether .Language has changed
// this language stuff was an old POC and should be removed
var hasLanguageChanged = entity.IsPropertyDirty("Language");
if (hasLanguageChanged)
return true; // language change => new version
var content = (Content) entity;
//var contentPropertyChanged = content2.IsEntityDirty();
var userPropertyChanged = content.IsAnyUserPropertyDirty();
var dirtyProps = content.GetDirtyProperties();
var contentPropertyChangedExceptPublished = dirtyProps.Any(x => x != "Published");
var wasPublished = content.PublishedOriginal;
switch (publishedState)
{
//at this point we need to check if any non property value has changed that wasn't the published state
var changedProps = ((TracksChangesEntityBase)entity).GetDirtyProperties();
if (changedProps.Any(x => x != "Published") == false)
{
case PublishedState.Publishing:
// changed state, publishing either a published or an unpublished version:
// DO create a new (published) version IF it was published already AND
// anything has changed, else can reuse the current version
return (contentPropertyChangedExceptPublished || userPropertyChanged) && wasPublished;
case PublishedState.Unpublishing:
// changed state, unpublishing a published version:
// DO create a new (draft) version and preserve the (formerly) published
// version for rollback purposes IF the version that's being saved is the
// published version, else it's a draft that we can reuse
return wasPublished;
case PublishedState.Saving:
// changed state, saving a published version:
// DO create a new (draft) version and preserve the published version IF
// anything has changed, else do NOT create a new version (pointless)
return contentPropertyChangedExceptPublished || userPropertyChanged;
case PublishedState.Published:
// unchanged state, saving a published version:
// (can happen eg when moving content, never otherwise)
// do NOT create a new version as we're just saving after operations (eg
// move) that cannot be rolled back anyway - ensure that's really it
if (userPropertyChanged)
throw new InvalidOperationException("Invalid PublishedState \"Published\" with user property changes.");
return false;
}
case PublishedState.Unpublished:
// unchanged state, saving an unpublished version:
// do NOT create a new version for user property changes,
// BUT create a new version in case of content property changes, for
// rollback purposes
return contentPropertyChangedExceptPublished;
default:
throw new NotSupportedException();
}
return true;
}
/// <summary>
/// Determines if a new version should be created
/// Determines whether the database published flag should be cleared for versions
/// other than this content version.
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
/// <param name="entity">The content.</param>
/// <returns>True if the published flag should be cleared, otherwise false.</returns>
/// <returns>See remarks in overload.</returns>
internal static bool RequiresClearPublishedFlag(this IContent entity)
{
var publishedState = ((Content) entity).PublishedState;
var requiresNewVersion = entity.RequiresNewVersion(publishedState);
return entity.RequiresClearPublishedFlag(publishedState, requiresNewVersion);
}
/// <summary>
/// Determines whether the database published flag should be cleared for versions
/// other than this content version.
/// </summary>
/// <param name="entity">The content.</param>
/// <param name="publishedState">The published state of the content.</param>
/// <param name="isNewVersion">Indicates whether the content is a new version.</param>
/// <returns>True if the published flag should be cleared, otherwise false.</returns>
/// <remarks>
/// A new version needs to be created when:
/// * The publish status is changed
/// * The language is changed
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
/// This is called by the repository when persisting an existing content, to
/// figure out whether it needs to clear the published flag for other versions.
/// </remarks>
internal static bool ShouldCreateNewVersion(this IContent entity)
internal static bool RequiresClearPublishedFlag(this IContent entity, PublishedState publishedState, bool isNewVersion)
{
var publishedState = ((Content)entity).PublishedState;
return ShouldCreateNewVersion(entity, publishedState);
}
// note: publishedState is always the entity's PublishedState except for tests
/// <summary>
/// Returns a list of all dirty user defined properties
/// </summary>
/// <returns></returns>
public static IEnumerable<string> GetDirtyUserProperties(this IContentBase entity)
{
return entity.Properties.Where(x => x.IsDirty()).Select(x => x.Alias);
}
public static bool IsAnyUserPropertyDirty(this IContentBase entity)
{
return entity.Properties.Any(x => x.IsDirty());
}
public static bool WasAnyUserPropertyDirty(this IContentBase entity)
{
return entity.Properties.Any(x => x.WasDirty());
}
/// <summary>
/// Determines if a new version should be created
/// </summary>
/// <param name="entity"></param>
/// <param name="publishedState"></param>
/// <returns></returns>
/// <remarks>
/// A new version needs to be created when:
/// * The publish status is changed
/// * The language is changed
/// * The item is already published and is being published again and any property value is changed (to enable a rollback)
/// </remarks>
internal static bool ShouldCreateNewVersion(this IContent entity, PublishedState publishedState)
{
//check if the published state has changed or the language
var publishedChanged = entity.IsPropertyDirty("Published") && publishedState != PublishedState.Unpublished;
var langChanged = entity.IsPropertyDirty("Language");
var contentChanged = publishedChanged || langChanged;
//check if any user prop has changed
var propertyValueChanged = entity.IsAnyUserPropertyDirty();
//return true if published or language has changed
if (contentChanged)
{
// new, published version => everything else must be cleared
if (isNewVersion && entity.Published)
return true;
}
//check if any content prop has changed
var contentDataChanged = ((Content)entity).IsEntityDirty();
// if that entity was published then that entity has the flag and
// it does not need to be cleared for other versions
// NOT TRUE when unpublishing we create a NEW version
//var wasPublished = ((Content)entity).PublishedOriginal;
//if (wasPublished)
// return false;
//return true if the item is published and a property has changed or if any content property has changed
return (propertyValueChanged && publishedState == PublishedState.Published) || contentDataChanged;
}
/// <summary>
/// Determines if the published db flag should be set to true for the current entity version and all other db
/// versions should have their flag set to false.
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
/// <remarks>
/// This is determined by:
/// * If a new version is being created and the entity is published
/// * If the published state has changed and the entity is published OR the entity has been un-published.
/// </remarks>
internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity)
{
var publishedState = ((Content)entity).PublishedState;
return entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, entity.ShouldCreateNewVersion(publishedState));
}
/// <summary>
/// Determines if the published db flag should be set to true for the current entity version and all other db
/// versions should have their flag set to false.
/// </summary>
/// <param name="entity"></param>
/// <param name="publishedState"></param>
/// <param name="isCreatingNewVersion"></param>
/// <returns></returns>
/// <remarks>
/// This is determined by:
/// * If a new version is being created and the entity is published
/// * If the published state has changed and the entity is published OR the entity has been un-published.
/// </remarks>
internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity, PublishedState publishedState, bool isCreatingNewVersion)
{
if (isCreatingNewVersion && entity.Published)
{
return true;
}
//If Published state has changed then previous versions should have their publish state reset.
//If state has been changed to unpublished the previous versions publish state should also be reset.
if (entity.IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
{
return true;
}
return false;
// clear whenever we are publishing or unpublishing
// publishing: because there might be a previously published version, which needs to be cleared
// unpublishing: same - we might be a saved version, not the published one, which needs to be cleared
return publishedState == PublishedState.Publishing || publishedState == PublishedState.Unpublishing;
}
/// <summary>
@@ -312,9 +315,9 @@ namespace Umbraco.Core.Models
return content.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Contains(recycleBinId.ToInvariantString());
}
/// <summary>
/// Removes characters that are not valide XML characters from all entity properties
/// Removes characters that are not valide XML characters from all entity properties
/// of type string. See: http://stackoverflow.com/a/961504/5018
/// </summary>
/// <returns></returns>
@@ -454,7 +457,7 @@ namespace Umbraco.Core.Models
/// <param name="value">The <see cref="HttpPostedFileBase"/> containing the file that will be uploaded</param>
public static void SetValue(this IContentBase content, string propertyTypeAlias, HttpPostedFileBase value)
{
// Ensure we get the filename without the path in IE in intranet mode
// Ensure we get the filename without the path in IE in intranet mode
// http://stackoverflow.com/questions/382464/httppostedfile-filename-different-from-ie
var fileName = value.FileName;
if (fileName.LastIndexOf(@"\") > 0)
@@ -597,7 +600,7 @@ namespace Umbraco.Core.Models
#endregion
#region User/Profile methods
/// <summary>
/// Gets the <see cref="IProfile"/> for the Creator of this media item.
/// </summary>
@@ -672,7 +675,7 @@ namespace Umbraco.Core.Models
///// <param name="tagGroup"></param>
///// <returns></returns>
///// <remarks>
///// The tags returned are only relavent for published content & saved media or members
///// The tags returned are only relavent for published content & saved media or members
///// </remarks>
//public static IEnumerable<ITag> GetTags(this IContentBase content, string propertyTypeAlias, string tagGroup = "default")
//{
@@ -909,5 +912,24 @@ namespace Umbraco.Core.Models
return ((PackagingService)(packagingService)).Export(member);
}
#endregion
#region Dirty
public static IEnumerable<string> GetDirtyUserProperties(this IContentBase entity)
{
return entity.Properties.Where(x => x.IsDirty()).Select(x => x.Alias);
}
public static bool IsAnyUserPropertyDirty(this IContentBase entity)
{
return entity.Properties.Any(x => x.IsDirty());
}
public static bool WasAnyUserPropertyDirty(this IContentBase entity)
{
return entity.Properties.Any(x => x.WasDirty());
}
#endregion
}
}
-26
View File
@@ -152,31 +152,5 @@ namespace Umbraco.Core.Models
{
return DeepCloneWithResetIdentities(alias);
}
/// <summary>
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
/// </summary>
/// <returns></returns>
public IContentType DeepCloneWithResetIdentities(string alias)
{
var clone = (ContentType)DeepClone();
clone.Alias = alias;
clone.Key = Guid.Empty;
foreach (var propertyGroup in clone.PropertyGroups)
{
propertyGroup.ResetIdentity();
propertyGroup.ResetDirtyProperties(false);
}
foreach (var propertyType in clone.PropertyTypes)
{
propertyType.ResetIdentity();
propertyType.ResetDirtyProperties(false);
}
clone.ResetIdentity();
clone.ResetDirtyProperties(false);
return clone;
}
}
}
@@ -642,5 +642,26 @@ namespace Umbraco.Core.Models
return clone;
}
public IContentType DeepCloneWithResetIdentities(string alias)
{
var clone = (ContentType)DeepClone();
clone.Alias = alias;
clone.Key = Guid.Empty;
foreach (var propertyGroup in clone.PropertyGroups)
{
propertyGroup.ResetIdentity();
propertyGroup.ResetDirtyProperties(false);
}
foreach (var propertyType in clone.PropertyTypes)
{
propertyType.ResetIdentity();
propertyType.ResetDirtyProperties(false);
}
clone.ResetIdentity();
clone.ResetDirtyProperties(false);
return clone;
}
}
}
@@ -14,8 +14,8 @@ namespace Umbraco.Core.Models
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType)
{
var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
.SelectRecursive(type => contentTypeService.GetContentTypeChildren(type.Id));
var descendants = contentTypeService.GetChildren(contentType.Id)
.SelectRecursive(type => contentTypeService.GetChildren(type.Id));
return descendants;
}
-7
View File
@@ -72,12 +72,5 @@ namespace Umbraco.Core.Models
/// </summary>
/// <returns>True if content is valid otherwise false</returns>
bool IsValid();
/// <summary>
/// Changes the Trashed state of the content object
/// </summary>
/// <param name="isTrashed">Boolean indicating whether content is trashed (true) or not trashed (false)</param>
/// <param name="parentId"> </param>
void ChangeTrashedState(bool isTrashed, int parentId = -20);
}
}
+1 -1
View File
@@ -112,7 +112,7 @@ namespace Umbraco.Core.Models
/// </summary>
/// <param name="isTrashed">Boolean indicating whether content is trashed (true) or not trashed (false)</param>
/// <param name="parentId"> </param>
public override void ChangeTrashedState(bool isTrashed, int parentId = -20)
public void ChangeTrashedState(bool isTrashed, int parentId = -20)
{
Trashed = isTrashed;
//The Media Recycle Bin Id is -21 so we correct that here
-5
View File
@@ -522,11 +522,6 @@ namespace Umbraco.Core.Models
get { return _contentType; }
}
public override void ChangeTrashedState(bool isTrashed, int parentId = -20)
{
throw new NotSupportedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid");
}
/* Internal experiment - only used for mapping queries.
* Adding these to have first level properties instead of the Properties collection.
*/
@@ -166,10 +166,10 @@ namespace Umbraco.Core.Models.PublishedContent
switch (itemType)
{
case PublishedItemType.Content:
contentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(alias);
contentType = ApplicationContext.Current.Services.ContentTypeService.Get(alias);
break;
case PublishedItemType.Media:
contentType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(alias);
contentType = ApplicationContext.Current.Services.MediaTypeService.Get(alias);
break;
case PublishedItemType.Member:
contentType = ApplicationContext.Current.Services.MemberTypeService.Get(alias);
+47 -2
View File
@@ -1,9 +1,54 @@
namespace Umbraco.Core.Models
using System;
namespace Umbraco.Core.Models
{
/// <summary>
/// The <c>IContent</c> states of a content version.
/// </summary>
public enum PublishedState
{
// when a content version is loaded, its state is one of those two:
/// <summary>
/// The version is published.
/// </summary>
Published,
/// <summary>
/// The version is not published.
/// </summary>
/// <remarks>Also: the version is being saved, in order to register changes
/// made to an unpublished version of the content.</remarks>
Unpublished,
Saved
// legacy - remove
[Obsolete("kill!", true)]
Saved,
// when it is saved, its state can also be one of those:
/// <summary>
/// The version is being saved, in order to register changes made to a published content.
/// </summary>
/// <remarks>The <value>Saving</value> state is transitional. Once the version
/// is saved, its state changes to <value>Unpublished</value>.</remarks>
Saving,
/// <summary>
/// The version is being saved, in order to publish the content.
/// </summary>
/// <remarks>The <value>Publishing</value> state is transitional. Once the version
/// is saved, its state changes to <value>Published</value>. The content is published,
/// and all other versions are unpublished.</remarks>
Publishing,
/// <summary>
/// The version is being saved, in order to unpublish the content.
/// </summary>
/// <remarks>The <value>Unpublishing</value> state is transitional. Once the version
/// is saved, its state changes to <value>Unpublished</value>. The content and all
/// other versions are unpublished.</remarks>
Unpublishing
}
}
+24
View File
@@ -0,0 +1,24 @@
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoLock")]
[PrimaryKey("id")]
[ExplicitColumns]
internal class LockDto
{
[Column("id")]
[PrimaryKeyColumn(Name = "PK_umbracoLock")]
public int Id { get; set; }
[Column("value")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public int Value { get; set; } = 1;
[Column("name")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Length(64)]
public string Name { get; set; }
}
}
@@ -0,0 +1,18 @@
// ReSharper disable once CheckNamespace
namespace Umbraco.Core
{
static partial class Constants
{
public static class Locks
{
public const int Servers = -331;
public const int ContentTypes = -332;
public const int ContentTree = -333;
public const int MediaTree = -334;
public const int MemberTree = -335;
public const int MediaTypes = -336;
public const int MemberTypes = -337;
public const int Domains = -338;
}
}
}
@@ -48,7 +48,6 @@ namespace Umbraco.Core.Persistence.Factories
ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null,
ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null,
Version = dto.ContentVersionDto.VersionId,
PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished,
PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId
};
//on initial construction we don't want to have dirty properties tracked
@@ -27,14 +27,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
/// <param name="tableName">Name of the table to create base data for</param>
public void InitializeBaseData(string tableName)
{
_logger.Info<BaseDataCreation>(string.Format("Creating data in table {0}", tableName));
_logger.Info<BaseDataCreation>($"Creating data in table {tableName}");
if(tableName.Equals("umbracoNode"))
{
CreateUmbracNodeData();
CreateUmbracoNodeData();
}
if(tableName.Equals("cmsContentType"))
if (tableName.Equals("umbracoLock"))
{
CreateUmbracoLockData();
}
if (tableName.Equals("cmsContentType"))
{
CreateCmsContentTypeData();
}
@@ -102,7 +107,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_logger.Info<BaseDataCreation>(string.Format("Done creating data in table {0}", tableName));
}
private void CreateUmbracNodeData()
private void CreateUmbracoNodeData()
{
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -1, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1", SortOrder = 0, UniqueId = new Guid("916724a5-173d-4619-b97e-b9de133dd6f5"), Text = "SYSTEM DATA: umbraco master root", NodeObjectType = new Guid(Constants.ObjectTypes.SystemRoot), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -20, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,-20", SortOrder = 0, UniqueId = new Guid("0F582A79-1E41-4CF0-BFA0-76340651891A"), Text = "Recycle Bin", NodeObjectType = new Guid(Constants.ObjectTypes.ContentRecycleBin), CreateDate = DateTime.Now });
@@ -135,15 +140,28 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1043, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1043", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1045, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1045", SortOrder = 2, UniqueId = new Guid("7E3962CC-CE20-4FFC-B661-5897A894BA7E"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//TODO: We're not creating these for 7.0
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1039, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1039", SortOrder = 2, UniqueId = new Guid("06f349a9-c949-4b6a-8660-59c10451af42"), Text = "Ultimate Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1038, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1038", SortOrder = 2, UniqueId = new Guid("1251c96c-185c-4e9b-93f4-b48205573cbd"), Text = "Simple Editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1042, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1042", SortOrder = 2, UniqueId = new Guid("0a452bd5-83f9-4bc3-8403-1286e13fb77e"), Text = "Macro Container", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
}
private void CreateUmbracoLockData()
{
// all lock objects
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.Servers, Name = "Servers" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.ContentTypes, Name = "ContentTypes" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.ContentTree, Name = "ContentTree" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.MediaTypes, Name = "MediaTypes" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.MediaTree, Name = "MediaTree" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.MemberTypes, Name = "MemberTypes" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.MemberTree, Name = "MemberTree" });
_database.Insert("umbracoLock", "id", false, new LockDto { Id = Constants.Locks.Domains, Name = "Domains" });
}
private void CreateCmsContentTypeData()
{
_database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 532, NodeId = 1031, Alias = Constants.Conventions.MediaTypes.Folder, Icon = "icon-folder", Thumbnail = "icon-folder", IsContainer = false, AllowAtRoot = true });
@@ -84,7 +84,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{44, typeof (ExternalLoginDto)},
{45, typeof (MigrationDto)},
{46, typeof (UmbracoDeployChecksumDto)},
{47, typeof (UmbracoDeployDependencyDto)}
{47, typeof (UmbracoDeployDependencyDto)},
{48, typeof (LockDto) }
};
#endregion
@@ -0,0 +1,48 @@
using System;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionEight
{
[Migration("8.0.0", 101, GlobalSettings.UmbracoMigrationName)]
public class AddLockObjects : MigrationBase
{
public AddLockObjects(ILogger logger)
: base(logger)
{ }
public override void Up()
{
// some may already exist, just ensure everything we need is here
EnsureLockObject(Constants.Locks.Servers, "Servers");
EnsureLockObject(Constants.Locks.ContentTypes, "ContentTypes");
EnsureLockObject(Constants.Locks.ContentTree, "ContentTree");
EnsureLockObject(Constants.Locks.MediaTree, "MediaTree");
EnsureLockObject(Constants.Locks.MemberTree, "MemberTree");
EnsureLockObject(Constants.Locks.MediaTypes, "MediaTypes");
EnsureLockObject(Constants.Locks.MemberTypes, "MemberTypes");
EnsureLockObject(Constants.Locks.Domains, "Domains");
}
public override void Down()
{
// not implemented
}
private void EnsureLockObject(int id, string name)
{
Execute.Code(db =>
{
var exists = db.Exists<LockDto>(id);
if (exists) return string.Empty;
// be safe: delete old umbracoNode lock objects if any
db.Execute($"DELETE FROM umbracoNode WHERE id={id};");
// then create umbracoLock object
db.Execute($"INSERT umbracoLock (id, name, value) VALUES ({id}, '{name}', 1);");
return string.Empty;
});
}
}
}
@@ -0,0 +1,31 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionEight
{
[Migration("8.0.0", 100, GlobalSettings.UmbracoMigrationName)]
public class AddLockTable : MigrationBase
{
public AddLockTable(ILogger logger)
: base(logger)
{ }
public override void Up()
{
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains("umbracoLock") == false)
{
Create.Table("umbracoLock")
.WithColumn("id").AsInt32().PrimaryKey("PK_umbracoLock")
.WithColumn("value").AsInt32().NotNullable()
.WithColumn("name").AsString(64).NotNullable();
}
}
public override void Down()
{
// not implemented
}
}
}
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
Create.Column("isMaster").OnTable("umbracoServer").AsBoolean().NotNullable().WithDefaultValue(0);
}
EnsureLockObject(Constants.System.ServersLock, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
EnsureLockObject(Constants.Locks.Servers, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
}
public override void Down()
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
sortOrder = 0,
uniqueId = new Guid(uniqueId),
text = text,
nodeObjectType = new Guid(Constants.ObjectTypes.LockObject),
nodeObjectType = Constants.ObjectTypes.LockObjectGuid,
createDate = DateTime.Now
});
}
@@ -1,17 +1,9 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http.Headers;
using System.Text;
using System.Xml.Linq;
using NPoco;
using StackExchange.Profiling.Helpers.Dapper;
using Umbraco.Core.Configuration;
using Umbraco.Core.Dynamics;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -30,7 +22,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents a repository for doing CRUD operations for <see cref="IContent"/>
/// Represents a repository for doing CRUD operations for <see cref="IContent"/>.
/// </summary>
internal class ContentRepository : RecycleBinRepository<int, IContent>, IContentRepository
{
@@ -44,9 +36,9 @@ namespace Umbraco.Core.Persistence.Repositories
public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection, IMappingResolver mappingResolver)
: base(work, cacheHelper, logger, contentSection, mappingResolver)
{
if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository");
if (templateRepository == null) throw new ArgumentNullException("templateRepository");
if (tagRepository == null) throw new ArgumentNullException("tagRepository");
if (contentTypeRepository == null) throw new ArgumentNullException(nameof(contentTypeRepository));
if (templateRepository == null) throw new ArgumentNullException(nameof(templateRepository));
if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository));
_contentTypeRepository = contentTypeRepository;
_templateRepository = templateRepository;
_tagRepository = tagRepository;
@@ -73,7 +65,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateContentFromDto(dto, dto.ContentVersionDto.VersionId, sql);
var content = CreateContentFromDto(dto, dto.ContentVersionDto.VersionId);
return content;
}
@@ -83,7 +75,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
if (ids.Any())
{
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
sql.Where("umbracoNode.id in (@ids)", new { /*ids =*/ ids });
}
//we only want the newest ones with this method
@@ -98,7 +90,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Newest)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
//.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
.OrderBy<NodeDto>(x => x.Level)
.OrderBy<NodeDto>(x => x.SortOrder);
return MapQueryDtos(Database.Fetch<DocumentDto>(sql));
@@ -173,100 +166,12 @@ namespace Umbraco.Core.Persistence.Repositories
return list;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid(Constants.ObjectTypes.Document); }
}
protected override Guid NodeObjectTypeId => new Guid(Constants.ObjectTypes.Document);
#endregion
#region Overrides of VersionableRepositoryBase<IContent>
public void RebuildXmlStructures(Func<IContent, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
{
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = Database.GetTransaction())
{
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<DocumentDto>()
.On<ContentXmlDto, DocumentDto>(left => left.NodeId, right => right.NodeId);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
foreach (var id in contentTypeIds)
{
var id1 = id;
var subQuery = Sql()
.Select("cmsDocument.nodeId")
.From<DocumentDto>()
.InnerJoin<ContentDto>()
.On<DocumentDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.Where<DocumentDto>(dto => dto.Published)
.Where<ContentDto>( dto => dto.ContentTypeId == id1);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
}
//now insert the data, again if something fails here, the whole transaction is reversed
if (contentTypeIds == null)
{
var query = Query.Where(x => x.Published == true);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
else
{
foreach (var contentTypeId in contentTypeIds)
{
//copy local
var id = contentTypeId;
var query = Query.Where(x => x.Published == true && x.ContentTypeId == id && x.Trashed == false);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
}
tr.Complete();
}
}
private void RebuildXmlStructuresProcessQuery(Func<IContent, XElement> serializer, IQuery<IContent> query, ITransaction tr, int pageSize)
{
var pageIndex = 0;
var total = long.MinValue;
var processed = 0;
do
{
//NOTE: This is an important call, we cannot simply make a call to:
// GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
// because that method is used to query 'latest' content items where in this case we don't necessarily
// want latest content items because a pulished content item might not actually be the latest.
// see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
var descendants = GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out total,
MapQueryDtos, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
processed += xmlItems.Length;
pageIndex++;
} while (processed < total);
}
public override IContent GetByVersion(Guid versionId)
{
var sql = GetBaseQuery(false);
@@ -278,7 +183,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateContentFromDto(dto, versionId, sql);
var content = CreateContentFromDto(dto, versionId);
return content;
}
@@ -360,9 +265,7 @@ namespace Umbraco.Core.Persistence.Repositories
//ensure the default template is assigned
if (entity.Template == null)
{
entity.Template = entity.ContentType.DefaultTemplate;
}
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
@@ -375,10 +278,10 @@ namespace Umbraco.Core.Persistence.Repositories
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
//Logic for setting Path, Level and SortOrder
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = entity.ParentId });
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { /*ParentId =*/ entity.ParentId });
var level = parent.Level + 1;
var maxSortOrder = Database.ExecuteScalar<int>(
"SELECT coalesce(max(sortOrder),-1) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
"SELECT coalesce(max(sortOrder),-1) FROM umbracoNode WHERE parentId = @ParentId AND nodeObjectType = @NodeObjectType",
new { /*ParentId =*/ entity.ParentId, NodeObjectType = NodeObjectTypeId });
var sortOrder = maxSortOrder + 1;
@@ -387,7 +290,7 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew<NodeDto>(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -401,6 +304,8 @@ namespace Umbraco.Core.Persistence.Repositories
//Assign the same permissions to it as the parent node
// http://issues.umbraco.org/issue/U4-2161
// fixme STOP new-ing repos everywhere!
// var prepo = UnitOfWork.CreateRepository<IPermissionRepository<IContent>>();
var permissionsRepo = new PermissionRepository<IContent>(UnitOfWork, _cacheHelper);
var parentPermissions = permissionsRepo.GetPermissionsForEntity(entity.ParentId).ToArray();
//if there are parent permissions then assign them, otherwise leave null and permissions will become the
@@ -448,15 +353,11 @@ namespace Umbraco.Core.Persistence.Repositories
//Update Properties with its newly set Id
foreach (var property in entity.Properties)
{
property.Id = keyDictionary[property.PropertyTypeId];
}
//lastly, check if we are a creating a published version , then update the tags table
if (entity.Published)
{
UpdatePropertyTags(entity, _tagRepository);
}
UpdateEntityTags(entity, _tagRepository);
// published => update published version infos, else leave it blank
if (entity.Published)
@@ -476,7 +377,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override void PersistUpdatedItem(IContent entity)
{
var publishedState = ((Content)entity).PublishedState;
var content = (Content) entity;
var publishedState = content.PublishedState;
var publishedStateChanged = publishedState == PublishedState.Publishing || publishedState == PublishedState.Unpublishing;
//check if we need to make any database changes at all
if (entity.RequiresSaving(publishedState) == false)
@@ -486,11 +389,11 @@ namespace Umbraco.Core.Persistence.Repositories
}
//check if we need to create a new version
bool shouldCreateNewVersion = entity.ShouldCreateNewVersion(publishedState);
if (shouldCreateNewVersion)
var requiresNewVersion = entity.RequiresNewVersion(publishedState);
if (requiresNewVersion)
{
//Updates Modified date and Version Guid
((Content)entity).UpdatingEntity();
content.UpdatingEntity();
}
else
{
@@ -506,14 +409,10 @@ namespace Umbraco.Core.Persistence.Repositories
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
if (entity.IsPropertyDirty("ParentId"))
{
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = entity.ParentId });
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { /*ParentId =*/ entity.ParentId });
entity.Path = string.Concat(parent.Path, ",", entity.Id);
entity.Level = parent.Level + 1;
var maxSortOrder =
Database.ExecuteScalar<int>(
"SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId });
entity.SortOrder = maxSortOrder + 1;
entity.SortOrder = NextChildSortOrder(entity.ParentId);
//Question: If we move a node, should we update permissions to inherit from the new parent if the parent has permissions assigned?
// if we do that, then we'd need to propogate permissions all the way downward which might not be ideal for many people.
@@ -522,7 +421,7 @@ namespace Umbraco.Core.Persistence.Repositories
var factory = new ContentFactory(NodeObjectTypeId, entity.Id);
//Look up Content entry to get Primary for updating the DTO
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { /*Id =*/ entity.Id });
factory.SetPrimaryKey(contentDto.PrimaryKey);
var dto = factory.BuildDto(entity);
@@ -538,37 +437,17 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(newContentDto);
}
//a flag that we'll use later to create the tags in the tag db table
var publishedStateChanged = false;
//If Published state has changed then previous versions should have their publish state reset.
//If state has been changed to unpublished the previous versions publish state should also be reset.
//if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished))
if (entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, shouldCreateNewVersion))
{
var publishedDocs = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND published = @IsPublished", new { Id = entity.Id, IsPublished = true });
foreach (var doc in publishedDocs)
{
var docDto = doc;
docDto.Published = false;
Database.Update(docDto);
}
//this is a newly published version so we'll update the tags table too (end of this method)
publishedStateChanged = true;
}
if (entity.RequiresClearPublishedFlag(publishedState, requiresNewVersion))
ClearPublishedFlag(entity);
//Look up (newest) entries by id in cmsDocument table to set newest = false
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId = @Id AND newest = @IsNewest", new { Id = entity.Id, IsNewest = true });
foreach (var documentDto in documentDtos)
{
var docDto = documentDto;
docDto.Newest = false;
Database.Update(docDto);
}
ClearNewestFlag(entity);
var contentVersionDto = dto.ContentVersionDto;
if (shouldCreateNewVersion)
if (requiresNewVersion)
{
//Create a new version - cmsContentVersion
//Assumes a new Version guid and Version date (modified date) has been set
@@ -580,7 +459,7 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
//In order to update the ContentVersion we need to retrieve its primary key id
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { /*Version =*/ entity.Version });
contentVersionDto.Id = contentVerDto.Id;
Database.Update(contentVersionDto);
@@ -595,7 +474,7 @@ namespace Umbraco.Core.Persistence.Repositories
//Add Properties
foreach (var propertyDataDto in propertyDataDtos)
{
if (shouldCreateNewVersion == false && propertyDataDto.Id > 0)
if (requiresNewVersion == false && propertyDataDto.Id > 0)
{
Database.Update(propertyDataDto);
}
@@ -617,19 +496,38 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
//lastly, check if we are a newly published version and then update the tags table
if (publishedStateChanged && entity.Published)
// tags:
if (HasTagProperty(entity))
{
UpdatePropertyTags(entity, _tagRepository);
}
else if (publishedStateChanged && (entity.Trashed || entity.Published == false))
{
//it's in the trash or not published remove all entity tags
ClearEntityTags(entity, _tagRepository);
// if path-published, update tags, else clear tags
switch (content.PublishedState)
{
case PublishedState.Publishing:
// explicitely publishing, must update tags
UpdateEntityTags(entity, _tagRepository);
break;
case PublishedState.Unpublishing:
// explicitely unpublishing, must clear tags
ClearEntityTags(entity, _tagRepository);
break;
case PublishedState.Saving:
// saving, nothing to do
break;
case PublishedState.Published:
case PublishedState.Unpublished:
// no change, depends on path-published
// that should take care of trashing and un-trashing
if (IsPathPublished(entity)) // slightly expensive ;-(
UpdateEntityTags(entity, _tagRepository);
else
ClearEntityTags(entity, _tagRepository);
break;
}
}
// published => update published version infos,
// else if unpublished then clear published version infos
// else leave unchanged
if (entity.Published)
{
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
@@ -639,7 +537,7 @@ namespace Umbraco.Core.Persistence.Repositories
NodeId = dto.NodeId,
Published = true
};
((Content)entity).PublishedVersionGuid = dto.VersionId;
content.PublishedVersionGuid = dto.VersionId;
}
else if (publishedStateChanged)
{
@@ -650,12 +548,20 @@ namespace Umbraco.Core.Persistence.Repositories
NodeId = dto.NodeId,
Published = false
};
((Content)entity).PublishedVersionGuid = default(Guid);
content.PublishedVersionGuid = default(Guid);
}
entity.ResetDirtyProperties();
}
private int NextChildSortOrder(int parentId)
{
var maxSortOrder =
Database.ExecuteScalar<int>(
"SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = parentId, NodeObjectType = NodeObjectTypeId });
return maxSortOrder + 1;
}
#endregion
@@ -664,9 +570,7 @@ namespace Umbraco.Core.Persistence.Repositories
public IEnumerable<IContent> GetByPublishedVersion(IQuery<IContent> query)
{
// we WANT to return contents in top-down order, ie parents should come before children
// ideal would be pure xml "document order" which can be achieved with:
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
// but that's probably an overkill - sorting by level,sortOrder should be enough
// ideal would be pure xml "document order" - which we cannot achieve at database level
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IContent>(sqlClause, query);
@@ -680,27 +584,42 @@ namespace Umbraco.Core.Persistence.Repositories
foreach (var dto in dtos)
{
//Check in the cache first. If it exists there AND it is published
// then we can use that entity. Otherwise if it is not published (which can be the case
// because we only store the 'latest' entries in the cache which might not be the published
// version)
// check cache first, if it exists and is published, use it
// it may exist and not be published as the cache has 'latest version used'
var fromCache = RuntimeCache.GetCacheItem<IContent>(GetCacheIdKey<IContent>(dto.NodeId));
//var fromCache = TryGetFromCache(dto.NodeId);
if (fromCache != null && fromCache.Published)
{
yield return fromCache;
}
else
{
yield return CreateContentFromDto(dto, dto.VersionId, sql);
}
yield return fromCache != null && fromCache.Published
? fromCache
: CreateContentFromDto(dto, dto.VersionId);
}
}
public int CountPublished()
public int CountPublished(string contentTypeAlias = null)
{
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
.Where<DocumentDto>(x => x.Published == true);
var sql = Sql();
if (contentTypeAlias.IsNullOrWhiteSpace())
{
sql.SelectCount()
.From<NodeDto>()
.InnerJoin<DocumentDto>()
.On<NodeDto, DocumentDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.Trashed == false)
.Where<DocumentDto>(x => x.Published);
}
else
{
sql.SelectCount()
.From<NodeDto>()
.InnerJoin<ContentDto>()
.On<NodeDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<DocumentDto>()
.On<NodeDto, DocumentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId && x.Trashed == false)
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias)
.Where<DocumentDto>(x => x.Published);
}
return Database.ExecuteScalar<int>(sql);
}
@@ -710,10 +629,10 @@ namespace Umbraco.Core.Persistence.Repositories
repo.ReplaceEntityPermissions(permissionSet);
}
public void ClearPublished(IContent content)
public void ClearPublishedFlag(IContent content)
{
// race cond!
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@id AND published=@published", new { id = content.Id, published = true });
// no race cond if locked
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@Id AND published=@IsPublished", new { /*Id =*/ content.Id, IsPublished = true });
foreach (var documentDto in documentDtos)
{
documentDto.Published = false;
@@ -721,6 +640,17 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
public void ClearNewestFlag(IContent content)
{
// no race cond if locked
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@Id AND newest=@IsNewest", new { /*Id =*/ content.Id, IsNewest = true });
foreach (var documentDto in documentDtos)
{
documentDto.Newest = false;
Database.Update(documentDto);
}
}
/// <summary>
/// Assigns a single permission to the current content item for the specified user ids
/// </summary>
@@ -739,35 +669,6 @@ namespace Umbraco.Core.Persistence.Repositories
return repo.GetPermissionsForEntity(entityId);
}
/// <summary>
/// Adds/updates content/published xml
/// </summary>
/// <param name="content"></param>
/// <param name="xml"></param>
public void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml)
{
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(content, xml));
}
/// <summary>
/// Used to remove the content xml for a content item
/// </summary>
/// <param name="content"></param>
public void DeleteContentXml(IContent content)
{
_contentXmlRepository.Delete(new ContentXmlEntity<IContent>(content));
}
/// <summary>
/// Adds/updates preview xml
/// </summary>
/// <param name="content"></param>
/// <param name="xml"></param>
public void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml)
{
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(content, xml));
}
/// <summary>
/// Gets paged content results
/// </summary>
@@ -793,42 +694,40 @@ namespace Umbraco.Core.Persistence.Repositories
filterSql);
}
/// <summary>
/// Returns the persisted content's preview XML structure
/// </summary>
/// <param name="contentId"></param>
/// <returns></returns>
public XElement GetContentXml(int contentId)
public bool IsPathPublished(IContent content)
{
var sql = Sql().SelectAll().From<ContentXmlDto>().Where<ContentXmlDto>(d => d.NodeId == contentId);
var dto = Database.SingleOrDefault<ContentXmlDto>(sql);
if (dto == null) return null;
return XElement.Parse(dto.Xml);
}
// fail fast
if (content.Path.StartsWith("-1,-20,"))
return false;
// succeed fast
if (content.ParentId == -1)
return content.HasPublishedVersion;
/// <summary>
/// Returns the persisted content's preview XML structure
/// </summary>
/// <param name="contentId"></param>
/// <param name="version"></param>
/// <returns></returns>
public XElement GetContentPreviewXml(int contentId, Guid version)
{
var sql = Sql().SelectAll().From<PreviewXmlDto>()
.Where<PreviewXmlDto>(d => d.NodeId == contentId && d.VersionId == version);
var dto = Database.SingleOrDefault<PreviewXmlDto>(sql);
if (dto == null) return null;
return XElement.Parse(dto.Xml);
var syntaxUmbracoNode = SqlSyntax.GetQuotedTableName("umbracoNode");
var syntaxPath = SqlSyntax.GetQuotedColumnName("path");
var syntaxConcat = SqlSyntax.GetConcat(syntaxUmbracoNode + "." + syntaxPath, "',%'");
var sql = string.Format(@"SELECT COUNT({0}.{1})
FROM {0}
JOIN {2} ON ({0}.{1}={2}.{3} AND {2}.{4}=@published)
WHERE (@path LIKE {5})",
syntaxUmbracoNode,
SqlSyntax.GetQuotedColumnName("id"),
SqlSyntax.GetQuotedTableName("cmsDocument"),
SqlSyntax.GetQuotedColumnName("nodeId"),
SqlSyntax.GetQuotedColumnName("published"),
syntaxConcat);
var count = Database.ExecuteScalar<int>(sql, new { @published=true, @path=content.Path });
count += 1; // because content does not count
return count == content.Level;
}
#endregion
#region IRecycleBinRepository members
protected override int RecycleBinId
{
get { return Constants.System.RecycleBinContent; }
}
protected override int RecycleBinId => Constants.System.RecycleBinContent;
#endregion
@@ -885,7 +784,7 @@ namespace Umbraco.Core.Persistence.Repositories
return dtosWithContentTypes.Select(d => CreateContentFromDto(
d.dto,
contentTypes.First(ct => ct.Id == d.dto.ContentVersionDto.ContentDto.ContentTypeId),
templates.FirstOrDefault(tem => tem.Id == (d.dto.TemplateId.HasValue ? d.dto.TemplateId.Value : -1)),
templates.FirstOrDefault(tem => tem.Id == (d.dto.TemplateId ?? -1)),
propertyData[d.dto.NodeId]));
}
@@ -900,7 +799,7 @@ namespace Umbraco.Core.Persistence.Repositories
private IContent CreateContentFromDto(DocumentDto dto,
IContentType contentType,
ITemplate template,
Models.PropertyCollection propCollection)
PropertyCollection propCollection)
{
var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId);
var content = factory.BuildEntity(dto);
@@ -929,9 +828,8 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IContent CreateContentFromDto(DocumentDto dto, Guid versionId, Sql docSql)
private IContent CreateContentFromDto(DocumentDto dto, Guid versionId)
{
var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
@@ -979,7 +877,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
{
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
currentName = nodeName + $" ({uniqueNumber})";
uniqueNumber++;
}
}
@@ -987,5 +885,151 @@ namespace Umbraco.Core.Persistence.Repositories
return currentName;
}
#region Xml - Should Move!
public void RebuildXmlStructures(Func<IContent, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
{
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = Database.GetTransaction())
{
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<DocumentDto>()
.On<ContentXmlDto, DocumentDto>(left => left.NodeId, right => right.NodeId);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
foreach (var id in contentTypeIds)
{
var id1 = id;
var subQuery = Sql()
.Select("cmsDocument.nodeId")
.From<DocumentDto>()
.InnerJoin<ContentDto>()
.On<DocumentDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.Where<DocumentDto>(dto => dto.Published)
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
}
//now insert the data, again if something fails here, the whole transaction is reversed
if (contentTypeIds == null)
{
var query = Query.Where(x => x.Published);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
else
{
foreach (var contentTypeId in contentTypeIds)
{
//copy local
var id = contentTypeId;
var query = Query.Where(x => x.Published && x.ContentTypeId == id && x.Trashed == false);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
}
tr.Complete();
}
}
private void RebuildXmlStructuresProcessQuery(Func<IContent, XElement> serializer, IQuery<IContent> query, ITransaction tr, int pageSize)
{
var pageIndex = 0;
long total;
var processed = 0;
do
{
//NOTE: This is an important call, we cannot simply make a call to:
// GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
// because that method is used to query 'latest' content items where in this case we don't necessarily
// want latest content items because a pulished content item might not actually be the latest.
// see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
var descendants = GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out total,
MapQueryDtos, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
processed += xmlItems.Length;
pageIndex++;
} while (processed < total);
}
/// <summary>
/// Adds/updates content/published xml
/// </summary>
/// <param name="content"></param>
/// <param name="xml"></param>
public void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml)
{
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(content, xml));
}
/// <summary>
/// Used to remove the content xml for a content item
/// </summary>
/// <param name="content"></param>
public void DeleteContentXml(IContent content)
{
_contentXmlRepository.Delete(new ContentXmlEntity<IContent>(content));
}
/// <summary>
/// Adds/updates preview xml
/// </summary>
/// <param name="content"></param>
/// <param name="xml"></param>
public void AddOrUpdatePreviewXml(IContent content, Func<IContent, XElement> xml)
{
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IContent>(content, xml));
}
/// <summary>
/// Returns the persisted content's preview XML structure
/// </summary>
/// <param name="contentId"></param>
/// <returns></returns>
public XElement GetContentXml(int contentId)
{
var sql = Sql().SelectAll().From<ContentXmlDto>().Where<ContentXmlDto>(d => d.NodeId == contentId);
var dto = Database.SingleOrDefault<ContentXmlDto>(sql);
if (dto == null) return null;
return XElement.Parse(dto.Xml);
}
/// <summary>
/// Returns the persisted content's preview XML structure
/// </summary>
/// <param name="contentId"></param>
/// <param name="version"></param>
/// <returns></returns>
public XElement GetContentPreviewXml(int contentId, Guid version)
{
var sql = Sql().SelectAll().From<PreviewXmlDto>()
.Where<PreviewXmlDto>(d => d.NodeId == contentId && d.VersionId == version);
var dto = Database.SingleOrDefault<PreviewXmlDto>(sql);
if (dto == null) return null;
return XElement.Parse(dto.Xml);
}
#endregion
}
}
@@ -3,26 +3,21 @@ using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents a repository for doing CRUD operations for <see cref="IContentType"/>
/// </summary>
internal class ContentTypeRepository : ContentTypeBaseRepository<IContentType>, IContentTypeRepository
internal class ContentTypeRepository : ContentTypeRepositoryBase<IContentType>, IContentTypeRepository
{
private readonly ITemplateRepository _templateRepository;
@@ -51,6 +46,23 @@ namespace Umbraco.Core.Persistence.Repositories
return GetAll().FirstOrDefault(x => x.Id == id);
}
protected override IContentType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Key == id);
}
protected override IContentType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
protected override bool PerformExists(Guid id)
{
return GetAll().FirstOrDefault(x => x.Key == id) != null;
}
protected override IEnumerable<IContentType> PerformGetAll(params int[] ids)
{
if (ids.Any())
@@ -63,6 +75,12 @@ namespace Umbraco.Core.Persistence.Repositories
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository);
}
protected override IEnumerable<IContentType> PerformGetAll(params Guid[] ids)
{
// use the underlying GetAll which will force cache all content types
return ids.Any() ? GetAll().Where(x => ids.Contains(x.Key)) : GetAll();
}
protected override IEnumerable<IContentType> PerformGetByQuery(IQuery<IContentType> query)
{
var sqlClause = GetBaseQuery(false);
@@ -154,28 +172,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<string> GetDeleteClauses()
{
var list = new List<string>
{
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
"DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @Id",
"DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @Id",
"DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @Id",
"DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id",
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
"DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @Id",
"DELETE FROM cmsContentType WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;
var l = (List<string>) base.GetDeleteClauses(); // we know it's a list
l.Add("DELETE FROM cmsDocumentType WHERE contentTypeNodeId = @Id");
l.Add("DELETE FROM cmsContentType WHERE nodeId = @Id");
l.Add("DELETE FROM umbracoNode WHERE id = @Id");
return l;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid(Constants.ObjectTypes.DocumentType); }
}
protected override Guid NodeObjectTypeId => Constants.ObjectTypes.DocumentTypeGuid;
/// <summary>
/// Deletes a content type
@@ -290,36 +294,5 @@ namespace Umbraco.Core.Persistence.Repositories
entity.ResetDirtyProperties();
}
protected override IContentType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Key == id);
}
protected override IContentType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
protected override IEnumerable<IContentType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetAll().Where(x => ids.Contains(x.Key));
}
else
{
return GetAll();
}
}
protected override bool PerformExists(Guid id)
{
return GetAll().FirstOrDefault(x => x.Key == id) != null;
}
}
}
@@ -103,6 +103,14 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
// so... we are modifying content types here. the service will trigger Deleted event,
// which will propagate to DataTypeCacheRefresher which will clear almost every cache
// there is to clear... and in addition facade caches will clear themselves too, so
// this is probably safe alghough it looks... weird.
//
// what IS weird is that a content type is losing a property and we do NOT raise any
// content type event... so ppl better listen on the data type events too.
_contentTypeRepository.AddOrUpdate(contentType);
}
@@ -282,8 +290,6 @@ AND umbracoNode.id <> @id",
#endregion
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
{
var cached = RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
@@ -485,8 +491,6 @@ AND umbracoNode.id <> @id",
private string EnsureUniqueNodeName(string nodeName, int id = 0)
{
var sql = Sql()
.SelectAll()
.From<NodeDto>()
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -9,7 +8,7 @@ using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentRepository : IRepositoryVersionable<int, IContent>, IRecycleBinRepository<IContent>, IDeleteMediaFilesRepository
public interface IContentRepository : IRepositoryVersionable<int, IContent>, IRecycleBinRepository<IContent>
{
/// <summary>
/// Get the count of published items
@@ -18,7 +17,9 @@ namespace Umbraco.Core.Persistence.Repositories
/// <remarks>
/// We require this on the repo because the IQuery{IContent} cannot supply the 'newest' parameter
/// </remarks>
int CountPublished();
int CountPublished(string contentTypeAlias = null);
bool IsPathPublished(IContent content);
/// <summary>
/// Used to bulk update the permissions set for a content item. This will replace all permissions
@@ -31,7 +32,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// Clears the published flag for a content.
/// </summary>
/// <param name="content"></param>
void ClearPublished(IContent content);
void ClearPublishedFlag(IContent content);
/// <summary>
/// Gets all published Content by the specified query
@@ -1,11 +0,0 @@
using System;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeCompositionRepository<TEntity> : IRepositoryQueryable<int, TEntity>, IReadRepository<Guid, TEntity>
where TEntity : IContentTypeComposition
{
TEntity Get(string alias);
}
}
@@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeRepository : IContentTypeCompositionRepository<IContentType>
public interface IContentTypeRepository : IContentTypeRepositoryBase<IContentType>
{
/// <summary>
/// Gets all entities of the specified <see cref="PropertyType"/> query
@@ -21,8 +21,6 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns></returns>
IEnumerable<string> GetAllPropertyTypeAliases();
IEnumerable<MoveEventInfo<IContentType>> Move(IContentType toMove, EntityContainer container);
/// <summary>
/// Gets all content type aliases
/// </summary>
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeRepositoryBase<TItem> : IRepositoryQueryable<int, TItem>, IReadRepository<Guid, TItem>
where TItem : IContentTypeComposition
{
TItem Get(string alias);
IEnumerable<MoveEventInfo<TItem>> Move(TItem moving, EntityContainer container);
IEnumerable<TItem> GetTypesDirectlyComposedOf(int id);
/// <summary>
/// Derives a unique alias from an existing alias.
/// </summary>
/// <param name="alias">The original alias.</param>
/// <returns>The original alias with a number appended to it, so that it is unique.</returns>
/// <remarks>Unique accross all content, media and member types.</remarks>
string GetUniqueAlias(string alias);
}
}
@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IMediaTypeRepository : IContentTypeCompositionRepository<IMediaType>
public interface IMediaTypeRepository : IContentTypeRepositoryBase<IMediaType>
{
/// <summary>
/// Gets all entities of the specified <see cref="PropertyType"/> query
@@ -13,15 +13,5 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="query"></param>
/// <returns>An enumerable list of <see cref="IContentType"/> objects</returns>
IEnumerable<IMediaType> GetByQuery(IQuery<PropertyType> query);
IEnumerable<MoveEventInfo<IMediaType>> Move(IMediaType toMove, EntityContainer container);
/// <summary>
/// Derives a unique alias from an existing alias.
/// </summary>
/// <param name="alias">The original alias.</param>
/// <returns>The original alias with a number appended to it, so that it is unique.</returns>
/// <remarks>Unique accross all content, media and member types.</remarks>
string GetUniqueAlias(string alias);
}
}
@@ -1,10 +1,7 @@
using System;
using Umbraco.Core.Models;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IMemberTypeRepository : IContentTypeCompositionRepository<IMemberType>
{
}
public interface IMemberTypeRepository : IContentTypeRepositoryBase<IMemberType>
{ }
}
@@ -6,8 +6,5 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IServerRegistrationRepository : IRepositoryQueryable<int, IServerRegistration>
{
void DeactiveStaleServers(TimeSpan staleTimeout);
void ReadLockServers();
void WriteLockServers();
}
}
@@ -2,14 +2,10 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using NPoco;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dynamics;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -21,7 +17,6 @@ using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -38,8 +33,8 @@ namespace Umbraco.Core.Persistence.Repositories
public MediaRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, IMappingResolver mappingResolver)
: base(work, cache, logger, contentSection, mappingResolver)
{
if (mediaTypeRepository == null) throw new ArgumentNullException("mediaTypeRepository");
if (tagRepository == null) throw new ArgumentNullException("tagRepository");
if (mediaTypeRepository == null) throw new ArgumentNullException(nameof(mediaTypeRepository));
if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository));
_mediaTypeRepository = mediaTypeRepository;
_tagRepository = tagRepository;
_contentXmlRepository = new ContentXmlRepository<IMedia>(work, CacheHelper.CreateDisabledCacheHelper(), logger, mappingResolver);
@@ -47,7 +42,7 @@ namespace Umbraco.Core.Persistence.Repositories
EnsureUniqueNaming = contentSection.EnsureUniqueNaming;
}
public bool EnsureUniqueNaming { get; private set; }
public bool EnsureUniqueNaming { get; }
#region Overrides of RepositoryBase<int,IMedia>
@@ -62,7 +57,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateMediaFromDto(dto, dto.VersionId, sql);
var content = CreateMediaFromDto(dto, dto.VersionId);
return content;
}
@@ -72,7 +67,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
if (ids.Any())
{
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
sql.Where("umbracoNode.id in (@ids)", new { /*ids =*/ ids });
}
return ProcessQuery(sql);
@@ -139,10 +134,7 @@ namespace Umbraco.Core.Persistence.Repositories
return list;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid(Constants.ObjectTypes.Media); }
}
protected override Guid NodeObjectTypeId => new Guid(Constants.ObjectTypes.Media);
#endregion
@@ -174,94 +166,11 @@ namespace Umbraco.Core.Persistence.Repositories
return media;
}
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
{
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = Database.GetTransaction())
{
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
foreach (var id in contentTypeIds)
{
var id1 = id;
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType)
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
}
//now insert the data, again if something fails here, the whole transaction is reversed
if (contentTypeIds == null)
{
RebuildXmlStructuresProcessQuery(serializer, Query, tr, groupSize);
}
else
{
foreach (var contentTypeId in contentTypeIds)
{
//copy local
var id = contentTypeId;
var query = Query.Where(x => x.ContentTypeId == id && x.Trashed == false);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
}
tr.Complete();
}
}
private void RebuildXmlStructuresProcessQuery(Func<IMedia, XElement> serializer, IQuery<IMedia> query, ITransaction tr, int pageSize)
{
var pageIndex = 0;
var total = long.MinValue;
var processed = 0;
do
{
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
processed += xmlItems.Length;
pageIndex++;
} while (processed < total);
}
public IMedia GetMediaByPath(string mediaPath)
{
var umbracoFileValue = mediaPath;
const string Pattern = ".*[_][0-9]+[x][0-9]+[.].*";
var isResized = Regex.IsMatch(mediaPath, Pattern);
const string pattern = ".*[_][0-9]+[x][0-9]+[.].*";
var isResized = Regex.IsMatch(mediaPath, pattern);
// If the image has been resized we strip the "_403x328" of the original "/media/1024/koala_403x328.jpg" url.
if (isResized)
@@ -292,17 +201,6 @@ namespace Umbraco.Core.Persistence.Repositories
return propertyDataDto == null ? null : Get(propertyDataDto.NodeId);
}
public void AddOrUpdateContentXml(IMedia content, Func<IMedia, XElement> xml)
{
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMedia>(content, xml));
}
public void AddOrUpdatePreviewXml(IMedia content, Func<IMedia, XElement> xml)
{
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IMedia>(content, xml));
}
protected override void PerformDeleteVersion(int id, Guid versionId)
{
Database.Delete<PreviewXmlDto>("WHERE nodeId = @Id AND versionId = @VersionId", new { Id = id, VersionId = versionId });
@@ -329,10 +227,10 @@ namespace Umbraco.Core.Persistence.Repositories
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
//Logic for setting Path, Level and SortOrder
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = entity.ParentId });
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { /*ParentId =*/ entity.ParentId });
var level = parent.Level + 1;
var maxSortOrder = Database.ExecuteScalar<int>(
"SELECT coalesce(max(sortOrder),-1) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
"SELECT coalesce(max(sortOrder),-1) FROM umbracoNode WHERE parentId = @ParentId AND nodeObjectType = @NodeObjectType",
new { /*ParentId =*/ entity.ParentId, NodeObjectType = NodeObjectTypeId });
var sortOrder = maxSortOrder + 1;
@@ -341,7 +239,7 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew<NodeDto>(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -381,7 +279,7 @@ namespace Umbraco.Core.Persistence.Repositories
property.Id = keyDictionary[property.PropertyTypeId];
}
UpdatePropertyTags(entity, _tagRepository);
UpdateEntityTags(entity, _tagRepository);
entity.ResetDirtyProperties();
}
@@ -400,19 +298,19 @@ namespace Umbraco.Core.Persistence.Repositories
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
if (entity.IsPropertyDirty("ParentId"))
{
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = entity.ParentId });
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { /*ParentId =*/ entity.ParentId });
entity.Path = string.Concat(parent.Path, ",", entity.Id);
entity.Level = parent.Level + 1;
var maxSortOrder =
Database.ExecuteScalar<int>(
"SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId });
new { /*ParentId =*/ entity.ParentId, NodeObjectType = NodeObjectTypeId });
entity.SortOrder = maxSortOrder + 1;
}
var factory = new MediaFactory(NodeObjectTypeId, entity.Id);
//Look up Content entry to get Primary for updating the DTO
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { /*Id =*/ entity.Id });
factory.SetPrimaryKey(contentDto.PrimaryKey);
var dto = factory.BuildDto(entity);
@@ -429,7 +327,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
//In order to update the ContentVersion we need to retrieve its primary key id
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { /*Version =*/ entity.Version });
dto.Id = contentVerDto.Id;
//Updates the current version - cmsContentVersion
//Assumes a Version guid exists and Version date (modified date) has been set/updated
@@ -463,7 +361,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
UpdatePropertyTags(entity, _tagRepository);
UpdateEntityTags(entity, _tagRepository);
entity.ResetDirtyProperties();
}
@@ -472,10 +370,7 @@ namespace Umbraco.Core.Persistence.Repositories
#region IRecycleBinRepository members
protected override int RecycleBinId
{
get { return Constants.System.RecycleBinMedia; }
}
protected override int RecycleBinId => Constants.System.RecycleBinMedia;
#endregion
@@ -544,7 +439,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Private method to create a media object from a ContentDto
/// </summary>
/// <param name="d"></param>
/// <param name="dto"></param>
/// <param name="contentType"></param>
/// <param name="propCollection"></param>
/// <returns></returns>
@@ -566,11 +461,10 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Private method to create a media object from a ContentDto
/// </summary>
/// <param name="d"></param>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IMedia CreateMediaFromDto(ContentVersionDto dto, Guid versionId, Sql docSql)
private IMedia CreateMediaFromDto(ContentVersionDto dto, Guid versionId)
{
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
@@ -612,7 +506,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto.Text.ToLowerInvariant().Equals(currentName.ToLowerInvariant()))
{
currentName = nodeName + string.Format(" ({0})", uniqueNumber);
currentName = $"{nodeName} ({uniqueNumber})";
uniqueNumber++;
}
}
@@ -620,5 +514,103 @@ namespace Umbraco.Core.Persistence.Repositories
return currentName;
}
#region Xml - Should Move!
public void RebuildXmlStructures(Func<IMedia, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
{
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = Database.GetTransaction())
{
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
foreach (var id in contentTypeIds)
{
var id1 = id;
var mediaObjectType = Guid.Parse(Constants.ObjectTypes.Media);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == mediaObjectType)
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
}
//now insert the data, again if something fails here, the whole transaction is reversed
if (contentTypeIds == null)
{
RebuildXmlStructuresProcessQuery(serializer, Query, tr, groupSize);
}
else
{
foreach (var contentTypeId in contentTypeIds)
{
//copy local
var id = contentTypeId;
var query = Query.Where(x => x.ContentTypeId == id && x.Trashed == false);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
}
tr.Complete();
}
}
private void RebuildXmlStructuresProcessQuery(Func<IMedia, XElement> serializer, IQuery<IMedia> query, ITransaction tr, int pageSize)
{
var pageIndex = 0;
long total;
var processed = 0;
do
{
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
processed += xmlItems.Length;
pageIndex++;
} while (processed < total);
}
public void AddOrUpdateContentXml(IMedia content, Func<IMedia, XElement> xml)
{
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMedia>(content, xml));
}
public void AddOrUpdatePreviewXml(IMedia content, Func<IMedia, XElement> xml)
{
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IMedia>(content, xml));
}
#endregion
}
}
@@ -3,32 +3,23 @@ using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents a repository for doing CRUD operations for <see cref="IMediaType"/>
/// </summary>
internal class MediaTypeRepository : ContentTypeBaseRepository<IMediaType>, IMediaTypeRepository
internal class MediaTypeRepository : ContentTypeRepositoryBase<IMediaType>, IMediaTypeRepository
{
public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMappingResolver mappingResolver)
: base(work, cache, logger, mappingResolver)
{
}
{ }
private FullDataSetRepositoryCachePolicyFactory<IMediaType, int> _cachePolicyFactory;
protected override IRepositoryCachePolicyFactory<IMediaType, int> CachePolicyFactory
@@ -49,6 +40,23 @@ namespace Umbraco.Core.Persistence.Repositories
return GetAll().FirstOrDefault(x => x.Id == id);
}
protected override IMediaType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Key == id);
}
protected override bool PerformExists(Guid id)
{
return GetAll().FirstOrDefault(x => x.Key == id) != null;
}
protected override IMediaType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
protected override IEnumerable<IMediaType> PerformGetAll(params int[] ids)
{
if (ids.Any())
@@ -61,6 +69,20 @@ namespace Umbraco.Core.Persistence.Repositories
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this);
}
protected override IEnumerable<IMediaType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetAll().Where(x => ids.Contains(x.Key));
}
else
{
return GetAll();
}
}
protected override IEnumerable<IMediaType> PerformGetByQuery(IQuery<IMediaType> query)
{
var sqlClause = GetBaseQuery(false);
@@ -82,7 +104,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// Gets all entities of the specified <see cref="PropertyType"/> query
/// </summary>
/// <param name="query"></param>
/// <returns>An enumerable list of <see cref="IContentType"/> objects</returns>
/// <returns>An enumerable list of <see cref="IMediaType"/> objects</returns>
public IEnumerable<IMediaType> GetByQuery(IQuery<PropertyType> query)
{
var ints = PerformGetByQuery(query).ToArray();
@@ -116,28 +138,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<string> GetDeleteClauses()
{
var list = new List<string>
{
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
"DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @Id",
"DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @Id",
"DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @Id",
"DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id",
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
"DELETE FROM cmsContentType WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;
var l = (List<string>) base.GetDeleteClauses(); // we know it's a list
l.Add("DELETE FROM cmsContentType WHERE nodeId = @Id");
l.Add("DELETE FROM umbracoNode WHERE id = @Id");
return l;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid(Constants.ObjectTypes.MediaType); }
}
protected override Guid NodeObjectTypeId => Constants.ObjectTypes.MediaTypeGuid;
protected override void PersistNewItem(IMediaType entity)
{
((MediaType)entity).AddingEntity();
@@ -171,36 +179,5 @@ namespace Umbraco.Core.Persistence.Repositories
entity.ResetDirtyProperties();
}
protected override IMediaType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Key == id);
}
protected override IEnumerable<IMediaType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetAll().Where(x => ids.Contains(x.Key));
}
else
{
return GetAll();
}
}
protected override bool PerformExists(Guid id)
{
return GetAll().FirstOrDefault(x => x.Key == id) != null;
}
protected override IMediaType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
}
}
@@ -2,13 +2,9 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Xml.Linq;
using NPoco;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models;
@@ -19,7 +15,6 @@ using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Persistence.Repositories
@@ -38,8 +33,8 @@ namespace Umbraco.Core.Persistence.Repositories
public MemberRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection, IMappingResolver mappingResolver)
: base(work, cache, logger, contentSection, mappingResolver)
{
if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository");
if (tagRepository == null) throw new ArgumentNullException("tagRepository");
if (memberTypeRepository == null) throw new ArgumentNullException(nameof(memberTypeRepository));
if (tagRepository == null) throw new ArgumentNullException(nameof(tagRepository));
_memberTypeRepository = memberTypeRepository;
_tagRepository = tagRepository;
_memberGroupRepository = memberGroupRepository;
@@ -60,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
var content = CreateMemberFromDto(dto, dto.ContentVersionDto.VersionId, sql);
var content = CreateMemberFromDto(dto, dto.ContentVersionDto.VersionId);
return content;
@@ -71,7 +66,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
if (ids.Any())
{
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
sql.Where("umbracoNode.id in (@ids)", new { /*ids =*/ ids });
}
return MapQueryDtos(Database.Fetch<MemberDto>(sql));
@@ -117,8 +112,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = Sql();
sql = isCount
? sql.SelectCount()
sql = isCount
? sql.SelectCount()
: sql.Select<MemberDto>(r =>
r.Select<ContentVersionDto>(rr =>
rr.Select<ContentDto>(rrr =>
@@ -184,10 +179,7 @@ namespace Umbraco.Core.Persistence.Repositories
return list;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid(Constants.ObjectTypes.Member); }
}
protected override Guid NodeObjectTypeId => new Guid(Constants.ObjectTypes.Member);
#endregion
@@ -205,18 +197,18 @@ namespace Umbraco.Core.Persistence.Repositories
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
//Logic for setting Path, Level and SortOrder
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = ((IUmbracoEntity)entity).ParentId });
int level = parent.Level + 1;
int sortOrder =
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { /*ParentId =*/ entity.ParentId });
var level = parent.Level + 1;
var sortOrder =
Database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoNode WHERE parentID = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = ((IUmbracoEntity)entity).ParentId, NodeObjectType = NodeObjectTypeId });
new { /*ParentId =*/ entity.ParentId, NodeObjectType = NodeObjectTypeId });
//Create the (base) node data - umbracoNode
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew<NodeDto>(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -265,7 +257,7 @@ namespace Umbraco.Core.Persistence.Repositories
property.Id = keyDictionary[property.PropertyTypeId];
}
UpdatePropertyTags(entity, _tagRepository);
UpdateEntityTags(entity, _tagRepository);
((Member)entity).ResetDirtyProperties();
}
@@ -283,19 +275,19 @@ namespace Umbraco.Core.Persistence.Repositories
//Look up parent to get and set the correct Path and update SortOrder if ParentId has changed
if (dirtyEntity.IsPropertyDirty("ParentId"))
{
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { ParentId = ((IUmbracoEntity)entity).ParentId });
((IUmbracoEntity)entity).Path = string.Concat(parent.Path, ",", entity.Id);
((IUmbracoEntity)entity).Level = parent.Level + 1;
var parent = Database.First<NodeDto>("WHERE id = @ParentId", new { /*ParentId =*/ entity.ParentId });
entity.Path = string.Concat(parent.Path, ",", entity.Id);
entity.Level = parent.Level + 1;
var maxSortOrder =
Database.ExecuteScalar<int>(
"SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
new { ParentId = ((IUmbracoEntity)entity).ParentId, NodeObjectType = NodeObjectTypeId });
((IUmbracoEntity)entity).SortOrder = maxSortOrder + 1;
new { /*ParentId =*/ entity.ParentId, NodeObjectType = NodeObjectTypeId });
entity.SortOrder = maxSortOrder + 1;
}
var factory = new MemberFactory(NodeObjectTypeId, entity.Id);
//Look up Content entry to get Primary for updating the DTO
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { Id = entity.Id });
var contentDto = Database.SingleOrDefault<ContentDto>("WHERE nodeId = @Id", new { /*Id =*/ entity.Id });
factory.SetPrimaryKey(contentDto.PrimaryKey);
var dto = factory.BuildDto(entity);
@@ -312,7 +304,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
//In order to update the ContentVersion we need to retrieve its primary key id
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { Version = entity.Version });
var contentVerDto = Database.SingleOrDefault<ContentVersionDto>("WHERE VersionId = @Version", new { /*Version =*/ entity.Version });
dto.ContentVersionDto.Id = contentVerDto.Id;
//Updates the current version - cmsContentVersion
//Assumes a Version guid exists and Version date (modified date) has been set/updated
@@ -378,7 +370,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
UpdatePropertyTags(entity, _tagRepository);
UpdateEntityTags(entity, _tagRepository);
dirtyEntity.ResetDirtyProperties();
}
@@ -387,90 +379,6 @@ namespace Umbraco.Core.Persistence.Repositories
#region Overrides of VersionableRepositoryBase<IMembershipUser>
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
{
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = Database.GetTransaction())
{
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == memberObjectType);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
foreach (var id in contentTypeIds)
{
var id1 = id;
var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == memberObjectType)
.Where<ContentDto>( dto => dto.ContentTypeId == id1);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
}
//now insert the data, again if something fails here, the whole transaction is reversed
if (contentTypeIds == null)
{
var query = Query;
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
else
{
foreach (var contentTypeId in contentTypeIds)
{
//copy local
var id = contentTypeId;
var query = Query.Where(x => x.ContentTypeId == id && x.Trashed == false);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
}
tr.Complete();
}
}
private void RebuildXmlStructuresProcessQuery(Func<IMember, XElement> serializer, IQuery<IMember> query, ITransaction tr, int pageSize)
{
var pageIndex = 0;
var total = long.MinValue;
var processed = 0;
do
{
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
processed += xmlItems.Length;
pageIndex++;
} while (processed < total);
}
public override IMember GetByVersion(Guid versionId)
{
var sql = GetBaseQuery(false);
@@ -534,7 +442,7 @@ namespace Umbraco.Core.Persistence.Repositories
query.Where(member => member.Username.SqlWildcard(usernameToMatch, TextColumnType.NVarchar));
break;
default:
throw new ArgumentOutOfRangeException("matchType");
throw new ArgumentOutOfRangeException(nameof(matchType));
}
var matchedMembers = GetByQuery(query).ToArray();
@@ -635,16 +543,6 @@ namespace Umbraco.Core.Persistence.Repositories
filterSql);
}
public void AddOrUpdateContentXml(IMember content, Func<IMember, XElement> xml)
{
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMember>(content, xml));
}
public void AddOrUpdatePreviewXml(IMember content, Func<IMember, XElement> xml)
{
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IMember>(content, xml));
}
protected override string GetDatabaseFieldNameForOrderBy(string orderBy)
{
//Some custom ones
@@ -716,9 +614,8 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
private IMember CreateMemberFromDto(MemberDto dto, Guid versionId, Sql docSql)
private IMember CreateMemberFromDto(MemberDto dto, Guid versionId)
{
var memberType = _memberTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId);
@@ -736,5 +633,103 @@ namespace Umbraco.Core.Persistence.Repositories
((Entity)member).ResetDirtyProperties(false);
return member;
}
#region Xml - Should Move!
public void AddOrUpdateContentXml(IMember content, Func<IMember, XElement> xml)
{
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IMember>(content, xml));
}
public void AddOrUpdatePreviewXml(IMember content, Func<IMember, XElement> xml)
{
_contentPreviewRepository.AddOrUpdate(new ContentPreviewEntity<IMember>(content, xml));
}
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 5000, IEnumerable<int> contentTypeIds = null)
{
//Ok, now we need to remove the data and re-insert it, we'll do this all in one transaction too.
using (var tr = Database.GetTransaction())
{
//Remove all the data first, if anything fails after this it's no problem the transaction will be reverted
if (contentTypeIds == null)
{
var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == memberObjectType);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
else
{
foreach (var id in contentTypeIds)
{
var id1 = id;
var memberObjectType = Guid.Parse(Constants.ObjectTypes.Member);
var subQuery = Sql()
.Select("DISTINCT cmsContentXml.nodeId")
.From<ContentXmlDto>()
.InnerJoin<NodeDto>()
.On<ContentXmlDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(dto => dto.NodeObjectType == memberObjectType)
.Where<ContentDto>(dto => dto.ContentTypeId == id1);
var deleteSql = SqlSyntax.GetDeleteSubquery("cmsContentXml", "nodeId", subQuery);
Database.Execute(deleteSql);
}
}
//now insert the data, again if something fails here, the whole transaction is reversed
if (contentTypeIds == null)
{
var query = Query;
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
else
{
foreach (var contentTypeId in contentTypeIds)
{
//copy local
var id = contentTypeId;
var query = Query.Where(x => x.ContentTypeId == id && x.Trashed == false);
RebuildXmlStructuresProcessQuery(serializer, query, tr, groupSize);
}
}
tr.Complete();
}
}
private void RebuildXmlStructuresProcessQuery(Func<IMember, XElement> serializer, IQuery<IMember> query, ITransaction tr, int pageSize)
{
var pageIndex = 0;
long total;
var processed = 0;
do
{
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending, true);
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
processed += xmlItems.Length;
pageIndex++;
} while (processed < total);
}
#endregion
}
}
@@ -1,18 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using log4net;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
@@ -20,13 +17,11 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Represents a repository for doing CRUD operations for <see cref="IMemberType"/>
/// </summary>
internal class MemberTypeRepository : ContentTypeBaseRepository<IMemberType>, IMemberTypeRepository
internal class MemberTypeRepository : ContentTypeRepositoryBase<IMemberType>, IMemberTypeRepository
{
public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, IMappingResolver mappingResolver)
: base(work, cache, logger, mappingResolver)
{
}
{ }
private FullDataSetRepositoryCachePolicyFactory<IMemberType, int> _cachePolicyFactory;
protected override IRepositoryCachePolicyFactory<IMemberType, int> CachePolicyFactory
@@ -47,6 +42,37 @@ namespace Umbraco.Core.Persistence.Repositories
return GetAll().FirstOrDefault(x => x.Id == id);
}
protected override IMemberType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Key == id);
}
protected override IEnumerable<IMemberType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetAll().Where(x => ids.Contains(x.Key));
}
else
{
return GetAll();
}
}
protected override bool PerformExists(Guid id)
{
return GetAll().FirstOrDefault(x => x.Key == id) != null;
}
protected override IMemberType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
protected override IEnumerable<IMemberType> PerformGetAll(params int[] ids)
{
var sql = GetBaseQuery(false);
@@ -165,28 +191,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<string> GetDeleteClauses()
{
var list = new List<string>
{
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
"DELETE FROM cmsTagRelationship WHERE nodeId = @Id",
"DELETE FROM cmsContentTypeAllowedContentType WHERE Id = @Id",
"DELETE FROM cmsContentTypeAllowedContentType WHERE AllowedId = @Id",
"DELETE FROM cmsContentType2ContentType WHERE parentContentTypeId = @Id",
"DELETE FROM cmsContentType2ContentType WHERE childContentTypeId = @Id",
"DELETE FROM cmsPropertyType WHERE contentTypeId = @Id",
"DELETE FROM cmsPropertyTypeGroup WHERE contenttypeNodeId = @Id",
"DELETE FROM cmsMemberType WHERE NodeId = @Id",
"DELETE FROM cmsContentType WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;
var l = (List<string>)base.GetDeleteClauses(); // we know it's a list
l.Add("DELETE FROM cmsMemberType WHERE NodeId = @Id");
l.Add("DELETE FROM cmsContentType WHERE nodeId = @Id");
l.Add("DELETE FROM umbracoNode WHERE id = @Id");
return l;
}
protected override Guid NodeObjectTypeId
{
get { return new Guid(Constants.ObjectTypes.MemberType); }
}
protected override Guid NodeObjectTypeId => Constants.ObjectTypes.MemberTypeGuid;
protected override void PersistNewItem(IMemberType entity)
{
@@ -278,37 +290,6 @@ namespace Umbraco.Core.Persistence.Repositories
propertyTypeAlias);
}
protected override IMemberType PerformGet(Guid id)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Key == id);
}
protected override IEnumerable<IMemberType> PerformGetAll(params Guid[] ids)
{
//use the underlying GetAll which will force cache all content types
if (ids.Any())
{
return GetAll().Where(x => ids.Contains(x.Key));
}
else
{
return GetAll();
}
}
protected override bool PerformExists(Guid id)
{
return GetAll().FirstOrDefault(x => x.Key == id) != null;
}
protected override IMemberType PerformGet(string alias)
{
//use the underlying GetAll which will force cache all content types
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
/// <summary>
/// Ensure that all the built-in membership provider properties have their correct data type
/// and property editors assigned. This occurs prior to saving so that the correct values are persisted.
@@ -18,7 +18,6 @@ namespace Umbraco.Core.Persistence.Repositories
internal class ServerRegistrationRepository : NPocoRepositoryBase<int, IServerRegistration>, IServerRegistrationRepository
{
private readonly ICacheProvider _staticCache;
private readonly int[] _lockIds = { Constants.System.ServersLock };
public ServerRegistrationRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IMappingResolver mappingResolver)
: base(work, CacheHelper.CreateDisabledCacheHelper(), logger, mappingResolver)
@@ -147,15 +146,5 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update<ServerRegistrationDto>("SET isActive=0, isMaster=0 WHERE lastNotifiedDate < @timeoutDate", new { /*timeoutDate =*/ timeoutDate });
ReloadCache();
}
public void ReadLockServers()
{
UnitOfWork.ReadLockNodes(_lockIds);
}
public void WriteLockServers()
{
UnitOfWork.WriteLockNodes(_lockIds);
}
}
}
@@ -230,7 +230,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="entity"></param>
/// <param name="tagRepo"></param>
protected void UpdatePropertyTags(IContentBase entity, ITagRepository tagRepo)
protected void UpdateEntityTags(IContentBase entity, ITagRepository tagRepo)
{
foreach (var tagProp in entity.Properties.Where(x => x.TagSupport.Enable))
{
@@ -254,6 +254,10 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
protected bool HasTagProperty(IContentBase entity)
{
return entity.Properties.Any(x => x.TagSupport.Enable);
}
private Sql<SqlContext> PrepareSqlForPagedResults(Sql<SqlContext> sql, Sql<SqlContext> filterSql, string orderBy, Direction orderDirection, bool orderBySystemField)
{
@@ -315,14 +319,14 @@ namespace Umbraco.Core.Persistence.Repositories
END AS CustomPropVal,
cd.nodeId AS CustomPropValContentId
FROM cmsDocument cd
INNER JOIN cmsPropertyData cpd ON cpd.contentNodeId = cd.nodeId AND cpd.versionId = cd.versionId
INNER JOIN cmsPropertyData cpd ON cpd.contentNodeId = cd.nodeId AND cpd.versionId = cd.versionId
INNER JOIN cmsPropertyType cpt ON cpt.Id = cpd.propertytypeId
WHERE cpt.Alias = @2 AND cd.newest = 1) AS CustomPropData
ON CustomPropData.CustomPropValContentId = umbracoNode.id
", sortedInt, sortedDecimal, sortedDate, sortedString);
//insert this just above the LEFT OUTER JOIN
var newSql = psql.SQL.Insert(psql.SQL.IndexOf("LEFT OUTER JOIN"), innerJoinTempTable);
var newSql = psql.SQL.Insert(psql.SQL.IndexOf("LEFT OUTER JOIN"), innerJoinTempTable);
var newArgs = psql.Arguments.ToList();
newArgs.Add(orderBy);
@@ -522,7 +526,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="files"></param>
/// <returns></returns>
public virtual bool DeleteMediaFiles(IEnumerable<string> files)
public virtual bool DeleteMediaFiles(IEnumerable<string> files) // fixme kill eventually
{
//ensure duplicates are removed
files = files.Distinct();
@@ -17,6 +17,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
string GetWildcardPlaceholder();
string GetStringColumnEqualComparison(string column, int paramIndex, TextColumnType columnType);
string GetStringColumnWildcardComparison(string column, int paramIndex, TextColumnType columnType);
string GetConcat(params string[] args);
[Obsolete("Use the overload with the parameter index instead")]
string GetStringColumnEqualComparison(string column, string value, TextColumnType columnType);
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// <summary>
/// Represents an SqlSyntaxProvider for MySql
/// </summary>
[SqlSyntaxProviderAttribute(Constants.DbProviderNames.MySql)]
[SqlSyntaxProvider(Constants.DbProviderNames.MySql)]
public class MySqlSyntaxProvider : SqlSyntaxProviderBase<MySqlSyntaxProvider>
{
private readonly ILogger _logger;
@@ -11,14 +11,9 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Ce
/// </summary>
[SqlSyntaxProviderAttribute(Constants.DbProviderNames.SqlCe)]
[SqlSyntaxProvider(Constants.DbProviderNames.SqlCe)]
public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlCeSyntaxProvider>
{
public SqlCeSyntaxProvider()
{
}
public override bool SupportsClustered()
{
return false;
@@ -64,7 +59,10 @@ namespace Umbraco.Core.Persistence.SqlSyntax
}
}
public override string GetConcat(params string[] args)
{
return "(" + string.Join("+", args) + ")";
}
public override string FormatColumnRename(string tableName, string oldName, string newName)
{
@@ -71,7 +71,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
public string DateTimeColumnDefinition = "DATETIME";
public string TimeColumnDefinition = "DATETIME";
protected IList<Func<ColumnDefinition, string>> ClauseOrder { get; set; }
protected IList<Func<ColumnDefinition, string>> ClauseOrder { get; }
protected DbTypes DbTypeMap = new DbTypes();
protected void InitColumnTypeMap()
@@ -172,6 +172,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
return string.Format("upper({0}) LIKE '{1}'", column, value.ToUpper());
}
public virtual string GetConcat(params string[] args)
{
return "concat(" + string.Join(",", args) + ")";
}
public virtual string GetQuotedTableName(string tableName)
{
return string.Format("\"{0}\"", tableName);
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
{
UmbracoDatabase Database { get; }
void ReadLockNodes(params int[] lockIds);
void WriteLockNodes(params int[] lockIds);
void ReadLock(params int[] lockIds);
void WriteLock(params int[] lockIds);
}
}
@@ -63,6 +63,9 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// If any operation is added to the unit of work after it has been completed, then its completion
/// status is resetted. So in a way it could be possible to always complete and never flush, but flush
/// is preferred when appropriate to indicate that you understand what you are doing.
/// Every units of work should be completed, unless a rollback is required. That is, even if the unit of
/// work contains only read operations, that do not need to be "commited", the unit of work should be
/// properly completed, else it may force an unexpected rollback of a higher-level transaction.
/// </remarks>
void Complete();
@@ -70,7 +70,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
_transaction = null;
}
public void ReadLockNodes(params int[] lockIds)
public void ReadLock(params int[] lockIds)
{
Begin(); // we need a transaction
@@ -78,11 +78,15 @@ namespace Umbraco.Core.Persistence.UnitOfWork
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
Database.ExecuteScalar<int>("SELECT sortOrder FROM umbracoNode WHERE id=@id",
{
var i = Database.ExecuteScalar<int?>("SELECT value FROM umbracoLock WHERE id=@id",
new { @id = lockId });
if (i == null) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
public void WriteLockNodes(params int[] lockIds)
public void WriteLock(params int[] lockIds)
{
Begin(); // we need a transaction
@@ -90,8 +94,12 @@ namespace Umbraco.Core.Persistence.UnitOfWork
throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
foreach (var lockId in lockIds)
Database.Execute("UPDATE umbracoNode SET sortOrder = (CASE WHEN (sortOrder=1) THEN -1 ELSE 1 END) WHERE id=@id",
{
var i = Database.Execute("UPDATE umbracoLock SET value = (CASE WHEN (value=1) THEN -1 ELSE 1 END) WHERE id=@id",
new { @id = lockId });
if (i == 0) // ensure we are actually locking!
throw new Exception($"LockObject with id={lockId} does not exist.");
}
}
}
}
+24 -18
View File
@@ -6,30 +6,36 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.Publishing
{
/// <summary>
/// The result of publishing a content item
/// Represents the result of publishing a content item.
/// </summary>
public class PublishStatus : OperationStatus<IContent, PublishStatusType>
public class PublishStatus : OperationStatus<PublishStatusType, IContent>
{
public PublishStatus(IContent content, PublishStatusType statusType, EventMessages eventMessages)
: base(content, statusType, eventMessages)
{
}
/// <summary>
/// Creates a successful publish status
/// Creates a new instance of the <see cref="PublishStatus"/> class with a status type, event messages, and a content item.
/// </summary>
public PublishStatus(IContent content, EventMessages eventMessages)
: this(content, PublishStatusType.Success, eventMessages)
{
}
public IContent ContentItem
{
get { return Entity; }
}
/// <param name="statusType">The status of the operation.</param>
/// <param name="eventMessages">Event messages produced by the operation.</param>
/// <param name="content">The content item.</param>
public PublishStatus(PublishStatusType statusType, EventMessages eventMessages, IContent content)
: base(statusType, eventMessages, content)
{ }
/// <summary>
/// Gets sets the invalid properties if the status failed due to validation.
/// Creates a new successful instance of the <see cref="PublishStatus"/> class with a event messages, and a content item.
/// </summary>
/// <param name="eventMessages">Event messages produced by the operation.</param>
/// <param name="content">The content item.</param>
public PublishStatus(IContent content, EventMessages eventMessages)
: base(PublishStatusType.Success, eventMessages, content)
{ }
/// <summary>
/// Gets the content item.
/// </summary>
public IContent ContentItem => Value;
/// <summary>
/// Gets or sets the invalid properties, if the status failed due to validation.
/// </summary>
public IEnumerable<Property> InvalidProperties { get; set; }
}
@@ -1,10 +1,11 @@
namespace Umbraco.Core.Publishing
{
/// <summary>
/// A status type of the result of publishing a content item
/// A value indicating the result of publishing a content item.
/// </summary>
/// <remarks>
/// Anything less than 10 = Success!
/// <remarks>Do NOT compare against a hard-coded numeric value to check for success or failure,
/// but instead use the IsSuccess() extension method defined below - which should be the unique
/// place where the numeric test should take place.
/// </remarks>
public enum PublishStatusType
{
@@ -14,40 +15,70 @@ namespace Umbraco.Core.Publishing
Success = 0,
/// <summary>
/// The item was already published
/// The item was already published.
/// </summary>
SuccessAlreadyPublished = 1,
// Values below this value indicate a success, values above it indicate a failure.
// This value is considered a failure.
//Reserved = 10,
/// <summary>
/// The content could not be published because it's ancestor path isn't published
/// The content could not be published because it's ancestor path isn't published.
/// </summary>
FailedPathNotPublished = 10,
FailedPathNotPublished = 11,
/// <summary>
/// The content item was scheduled to be un-published and it has expired so we cannot force it to be
/// published again as part of a bulk publish operation.
/// </summary>
FailedHasExpired = 11,
FailedHasExpired = 12,
/// <summary>
/// The content item is scheduled to be released in the future and therefore we cannot force it to
/// be published during a bulk publish operation.
/// </summary>
FailedAwaitingRelease = 12,
FailedAwaitingRelease = 13,
/// <summary>
/// The content item is in the trash, it cannot be published
/// The content item could not be published because it is in the trash.
/// </summary>
FailedIsTrashed = 13,
FailedIsTrashed = 14,
/// <summary>
/// The publish action has been cancelled by an event handler
/// The publish action has been cancelled by an event handler.
/// </summary>
FailedCancelledByEvent = 14,
FailedCancelledByEvent = 15,
/// <summary>
/// The content item contains invalid data (has not passed validation requirements)
/// The content item could not be published because it contains invalid data (has not passed validation requirements).
/// </summary>
FailedContentInvalid = 15
FailedContentInvalid = 16
}
/// <summary>
/// Provides extension methods for the <see cref="PublishStatusType"/> enum.
/// </summary>
public static class PublicStatusTypeExtensions
{
/// <summary>
/// Gets a value indicating whether the status indicates a success.
/// </summary>
/// <param name="status">The status.</param>
/// <returns>A value indicating whether the status indicates a success.</returns>
public static bool IsSuccess(this PublishStatusType status)
{
return (int) status < 10;
}
/// <summary>
/// Gets a value indicating whether the status indicates a failure.
/// </summary>
/// <param name="status">The status.</param>
/// <returns>A value indicating whether the status indicates a failure.</returns>
public static bool IsFailure(this PublishStatusType status)
{
return (int) status >= 10;
}
}
}
@@ -1,469 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Services;
namespace Umbraco.Core.Publishing
{
//TODO: Do we need this anymore??
/// <summary>
/// Currently acts as an interconnection between the new public api and the legacy api for publishing
/// </summary>
public class PublishingStrategy : BasePublishingStrategy
{
private readonly IEventMessagesFactory _eventMessagesFactory;
private readonly ILogger _logger;
public PublishingStrategy(IEventMessagesFactory eventMessagesFactory, ILogger logger)
{
if (eventMessagesFactory == null) throw new ArgumentNullException("eventMessagesFactory");
if (logger == null) throw new ArgumentNullException("logger");
_eventMessagesFactory = eventMessagesFactory;
_logger = logger;
}
/// <summary>
/// Publishes a single piece of Content
/// </summary>
/// <param name="content"><see cref="IContent"/> to publish</param>
/// <param name="userId">Id of the User issueing the publish operation</param>
internal Attempt<PublishStatus> PublishInternal(IContent content, int userId)
{
var evtMsgs = _eventMessagesFactory.Get();
if (Publishing.IsRaisedEventCancelled(
new PublishEventArgs<IContent>(content, evtMsgs), this))
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published, the event was cancelled.", content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs));
}
//Check if the Content is Expired to verify that it can in fact be published
if (content.Status == ContentStatus.Expired)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has expired and could not be published.",
content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedHasExpired, evtMsgs));
}
//Check if the Content is Awaiting Release to verify that it can in fact be published
if (content.Status == ContentStatus.AwaitingRelease)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is awaiting release and could not be published.",
content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedAwaitingRelease, evtMsgs));
}
//Check if the Content is Trashed to verify that it can in fact be published
if (content.Status == ContentStatus.Trashed)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is trashed and could not be published.",
content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedIsTrashed, evtMsgs));
}
content.ChangePublishedState(PublishedState.Published);
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has been published.",
content.Name, content.Id));
return Attempt.Succeed(new PublishStatus(content, evtMsgs));
}
/// <summary>
/// Publishes a single piece of Content
/// </summary>
/// <param name="content"><see cref="IContent"/> to publish</param>
/// <param name="userId">Id of the User issueing the publish operation</param>
/// <returns>True if the publish operation was successfull and not cancelled, otherwise false</returns>
public override bool Publish(IContent content, int userId)
{
return PublishInternal(content, userId).Success;
}
/// <summary>
/// Publishes a list of content items
/// </summary>
/// <param name="content"></param>
/// <param name="userId"></param>
/// <param name="includeUnpublishedDocuments">
/// By default this is set to true which means that it will publish any content item in the list that is completely unpublished and
/// not visible on the front-end. If set to false, this will only publish content that is live on the front-end but has new versions
/// that have yet to be published.
/// </param>
/// <returns></returns>
/// <remarks>
///
/// This method becomes complex once we start to be able to cancel events or stop publishing a content item in any way because if a
/// content item is not published then it's children shouldn't be published either. This rule will apply for the following conditions:
/// * If a document fails to be published, do not proceed to publish it's children if:
/// ** The document does not have a publish version
/// ** The document does have a published version but the includeUnpublishedDocuments = false
///
/// In order to do this, we will order the content by level and begin by publishing each item at that level, then proceed to the next
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
/// parentsIdsCancelled so that it's children don't get published.
///
/// Its important to note that all 'root' documents included in the list *will* be published regardless of the rules mentioned
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
/// its children. The reason we have to do this is because if a user is publishing a document and it's children, it is implied that
/// the user definitely wants to publish it even if it has never been published before.
///
/// </remarks>
internal IEnumerable<Attempt<PublishStatus>> PublishWithChildrenInternal(
IEnumerable<IContent> content, int userId, bool includeUnpublishedDocuments = true)
{
var statuses = new List<Attempt<PublishStatus>>();
//a list of all document ids that had their publishing cancelled during these iterations.
//this helps us apply the rule listed in the notes above by checking if a document's parent id
//matches one in this list.
var parentsIdsCancelled = new List<int>();
//group by levels and iterate over the sorted ascending level.
//TODO: This will cause all queries to execute, they will not be lazy but I'm not really sure being lazy actually made
// much difference because we iterate over them all anyways?? Morten?
// Because we're grouping I think this will execute all the queries anyways so need to fetch it all first.
var fetchedContent = content.ToArray();
var evtMsgs = _eventMessagesFactory.Get();
//We're going to populate the statuses with all content that is already published because below we are only going to iterate over
// content that is not published. We'll set the status to "AlreadyPublished"
statuses.AddRange(fetchedContent.Where(x => x.Published)
.Select(x => Attempt.Succeed(new PublishStatus(x, PublishStatusType.SuccessAlreadyPublished, evtMsgs))));
int? firstLevel = null;
//group by level and iterate over each level (sorted ascending)
var levelGroups = fetchedContent.GroupBy(x => x.Level);
foreach (var level in levelGroups.OrderBy(x => x.Key))
{
//set the first level flag, used to ensure that all documents at the first level will
//be published regardless of the rules mentioned in the remarks.
if (!firstLevel.HasValue)
{
firstLevel = level.Key;
}
/* Only update content thats not already been published - we want to loop through
* all unpublished content to write skipped content (expired and awaiting release) to log.
*/
foreach (var item in level.Where(x => x.Published == false))
{
//Check if this item should be excluded because it's parent's publishing has failed/cancelled
if (parentsIdsCancelled.Contains(item.ParentId))
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published because it's parent's publishing action failed or was cancelled.", item.Name, item.Id));
//if this cannot be published, ensure that it's children can definitely not either!
parentsIdsCancelled.Add(item.Id);
continue;
}
//Check if this item has never been published (and that it is not at the root level)
if (item.Level != firstLevel && !includeUnpublishedDocuments && !item.HasPublishedVersion())
{
//this item does not have a published version and the flag is set to not include them
parentsIdsCancelled.Add(item.Id);
continue;
}
//Fire Publishing event
if (Publishing.IsRaisedEventCancelled(
new PublishEventArgs<IContent>(item, evtMsgs), this))
{
//the publishing has been cancelled.
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published, the event was cancelled.", item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedCancelledByEvent, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
//Check if the content is valid if the flag is set to check
if (item.IsValid() == false)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published because some of it's content is not passing validation rules.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
//Check if the Content is Expired to verify that it can in fact be published
if (item.Status == ContentStatus.Expired)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has expired and could not be published.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedHasExpired, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
//Check if the Content is Awaiting Release to verify that it can in fact be published
if (item.Status == ContentStatus.AwaitingRelease)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is awaiting release and could not be published.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedAwaitingRelease, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
//Check if the Content is Trashed to verify that it can in fact be published
if (item.Status == ContentStatus.Trashed)
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is trashed and could not be published.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedIsTrashed, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
item.ChangePublishedState(PublishedState.Published);
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has been published.",
item.Name, item.Id));
statuses.Add(Attempt.Succeed(new PublishStatus(item, evtMsgs)));
}
}
return statuses;
}
/// <summary>
/// Based on the information provider we'll check if we should cancel the publishing of this document's children
/// </summary>
/// <param name="content"></param>
/// <param name="parentsIdsCancelled"></param>
/// <param name="includeUnpublishedDocuments"></param>
/// <remarks>
/// See remarks on method: PublishWithChildrenInternal
/// </remarks>
private void CheckCancellingOfChildPublishing(IContent content, List<int> parentsIdsCancelled, bool includeUnpublishedDocuments)
{
//Does this document apply to our rule to cancel it's children being published?
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
// any document that fails to publish...
var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
if (hasPublishedVersion && !includeUnpublishedDocuments)
{
//it has a published version but our flag tells us to not include un-published documents and therefore we should
// not be forcing decendant/child documents to be published if their parent fails.
parentsIdsCancelled.Add(content.Id);
}
else if (!hasPublishedVersion)
{
//it doesn't have a published version so we certainly cannot publish it's children.
parentsIdsCancelled.Add(content.Id);
}
}
/// <summary>
/// Publishes a list of Content
/// </summary>
/// <param name="content">An enumerable list of <see cref="IContent"/></param>
/// <param name="userId">Id of the User issueing the publish operation</param>
/// <returns>True if the publish operation was successfull and not cancelled, otherwise false</returns>
public override bool PublishWithChildren(IEnumerable<IContent> content, int userId)
{
var result = PublishWithChildrenInternal(content, userId);
//NOTE: This previously always returned true so I've left it that way. It returned true because (from Morten)...
// ... if one item couldn't be published it wouldn't be correct to return false.
// in retrospect it should have returned a list of with Ids and Publish Status
// come to think of it ... the cache would still be updated for a failed item or at least tried updated.
// It would call the Published event for the entire list, but if the Published property isn't set to True it
// wouldn't actually update the cache for that item. But not really ideal nevertheless...
return true;
}
/// <summary>
/// Unpublishes a single piece of Content
/// </summary>
/// <param name="content"><see cref="IContent"/> to unpublish</param>
/// <param name="userId">Id of the User issueing the unpublish operation</param>
/// <returns>True if the unpublish operation was successfull and not cancelled, otherwise false</returns>
public override bool UnPublish(IContent content, int userId)
{
return UnPublishInternal(content, userId).Success;
}
/// <summary>
/// Unpublishes a list of Content
/// </summary>
/// <param name="content">An enumerable list of <see cref="IContent"/></param>
/// <param name="userId">Id of the User issueing the unpublish operation</param>
/// <returns>A list of publish statuses</returns>
private IEnumerable<Attempt<PublishStatus>> UnPublishInternal(IEnumerable<IContent> content, int userId)
{
return content.Select(x => UnPublishInternal(x, userId));
}
private Attempt<PublishStatus> UnPublishInternal(IContent content, int userId)
{
// content should (is assumed to ) be the newest version, which may not be published
// don't know how to test this, so it's not verified
// NOTE
// if published != newest, then the published flags need to be reseted by whoever is calling that method
// at the moment it's done by the content service
var evtMsgs = _eventMessagesFactory.Get();
//Fire UnPublishing event
if (UnPublishing.IsRaisedEventCancelled(
new PublishEventArgs<IContent>(content, evtMsgs), this))
{
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be unpublished, the event was cancelled.", content.Name, content.Id));
return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs));
}
//If Content has a release date set to before now, it should be removed so it doesn't interrupt an unpublish
//Otherwise it would remain released == published
if (content.ReleaseDate.HasValue && content.ReleaseDate.Value <= DateTime.Now)
{
content.ReleaseDate = null;
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' had its release date removed, because it was unpublished.",
content.Name, content.Id));
}
// if newest is published, unpublish
if (content.Published)
content.ChangePublishedState(PublishedState.Unpublished);
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has been unpublished.",
content.Name, content.Id));
return Attempt.Succeed(new PublishStatus(content, evtMsgs));
}
/// <summary>
/// Unpublishes a list of Content
/// </summary>
/// <param name="content">An enumerable list of <see cref="IContent"/></param>
/// <param name="userId">Id of the User issueing the unpublish operation</param>
/// <returns>True if the unpublish operation was successfull and not cancelled, otherwise false</returns>
public override bool UnPublish(IEnumerable<IContent> content, int userId)
{
var result = UnPublishInternal(content, userId);
//NOTE: This previously always returned true so I've left it that way. It returned true because (from Morten)...
// ... if one item couldn't be published it wouldn't be correct to return false.
// in retrospect it should have returned a list of with Ids and Publish Status
// come to think of it ... the cache would still be updated for a failed item or at least tried updated.
// It would call the Published event for the entire list, but if the Published property isn't set to True it
// wouldn't actually update the cache for that item. But not really ideal nevertheless...
return true;
}
/// <summary>
/// Call to fire event that updating the published content has finalized.
/// </summary>
/// <remarks>
/// This seperation of the OnPublished event is done to ensure that the Content
/// has been properly updated (committed unit of work) and xml saved in the db.
/// </remarks>
/// <param name="content"><see cref="IContent"/> thats being published</param>
public override void PublishingFinalized(IContent content)
{
var evtMsgs = _eventMessagesFactory.Get();
Published.RaiseEvent(
new PublishEventArgs<IContent>(content, false, false, evtMsgs), this);
}
/// <summary>
/// Call to fire event that updating the published content has finalized.
/// </summary>
/// <param name="content">An enumerable list of <see cref="IContent"/> thats being published</param>
/// <param name="isAllRepublished">Boolean indicating whether its all content that is republished</param>
public override void PublishingFinalized(IEnumerable<IContent> content, bool isAllRepublished)
{
var evtMsgs = _eventMessagesFactory.Get();
Published.RaiseEvent(
new PublishEventArgs<IContent>(content, false, isAllRepublished, evtMsgs), this);
}
/// <summary>
/// Call to fire event that updating the unpublished content has finalized.
/// </summary>
/// <param name="content"><see cref="IContent"/> thats being unpublished</param>
public override void UnPublishingFinalized(IContent content)
{
var evtMsgs = _eventMessagesFactory.Get();
UnPublished.RaiseEvent(
new PublishEventArgs<IContent>(content, false, false, evtMsgs), this);
}
/// <summary>
/// Call to fire event that updating the unpublished content has finalized.
/// </summary>
/// <param name="content">An enumerable list of <see cref="IContent"/> thats being unpublished</param>
public override void UnPublishingFinalized(IEnumerable<IContent> content)
{
var evtMsgs = _eventMessagesFactory.Get();
UnPublished.RaiseEvent(
new PublishEventArgs<IContent>(content, false, false, evtMsgs), this);
}
/// <summary>
/// Occurs before publish
/// </summary>
public static event TypedEventHandler<IPublishingStrategy, PublishEventArgs<IContent>> Publishing;
/// <summary>
/// Occurs after publish
/// </summary>
public static event TypedEventHandler<IPublishingStrategy, PublishEventArgs<IContent>> Published;
/// <summary>
/// Occurs before unpublish
/// </summary>
public static event TypedEventHandler<IPublishingStrategy, PublishEventArgs<IContent>> UnPublishing;
/// <summary>
/// Occurs after unpublish
/// </summary>
public static event TypedEventHandler<IPublishingStrategy, PublishEventArgs<IContent>> UnPublished;
}
}
+20 -14
View File
@@ -5,26 +5,32 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.Publishing
{
/// <summary>
/// The result of unpublishing a content item
/// Represents the result of unpublishing a content item.
/// </summary>
public class UnPublishStatus : OperationStatus<IContent, UnPublishedStatusType>
public class UnPublishStatus : OperationStatus<UnPublishedStatusType, IContent>
{
public UnPublishStatus(IContent content, UnPublishedStatusType statusType, EventMessages eventMessages)
: base(content, statusType, eventMessages)
{
}
/// <summary>
/// Creates a new instance of the <see cref="UnPublishStatus"/> class with a status type, event messages, and a content item.
/// </summary>
/// <param name="statusType">The status of the operation.</param>
/// <param name="eventMessages">Event messages produced by the operation.</param>
/// <param name="content">The content item.</param>
public UnPublishStatus(UnPublishedStatusType statusType, EventMessages eventMessages, IContent content)
: base(statusType, eventMessages, content)
{ }
/// <summary>
/// Creates a successful unpublish status
/// Creates a new successful instance of the <see cref="UnPublishStatus"/> class with a event messages, and a content item.
/// </summary>
/// <param name="eventMessages">Event messages produced by the operation.</param>
/// <param name="content">The content item.</param>
public UnPublishStatus(IContent content, EventMessages eventMessages)
: this(content, UnPublishedStatusType.Success, eventMessages)
{
}
: base(UnPublishedStatusType.Success, eventMessages, content)
{ }
public IContent ContentItem
{
get { return Entity; }
}
/// <summary>
/// Gets the content item.
/// </summary>
public IContent ContentItem => Value;
}
}
@@ -0,0 +1,21 @@
using System;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Umbraco.Core
{
public static class ServiceContextExtensions
{
public static IContentTypeServiceBase<T> GetContentTypeService<T>(this ServiceContext services)
where T : IContentTypeComposition
{
if (typeof(T).Implements<IContentType>())
return services.ContentTypeService as IContentTypeServiceBase<T>;
if (typeof(T).Implements<IMediaType>())
return services.MediaTypeService as IContentTypeServiceBase<T>;
if (typeof(T).Implements<IMemberType>())
return services.MemberTypeService as IContentTypeServiceBase<T>;
throw new ArgumentException("Type " + typeof(T).FullName + " does not have a service.");
}
}
}
@@ -33,6 +33,7 @@ namespace Umbraco.Core.Services
{
var repo = uow.CreateRepository<IAuditRepository>();
var result = repo.GetByQuery(repo.Query.Where(x => x.Id == objectId));
uow.Complete();
return result;
}
}
@@ -45,6 +46,7 @@ namespace Umbraco.Core.Services
var result = sinceDate.HasValue == false
? repo.GetByQuery(repo.Query.Where(x => x.UserId == userId && x.AuditType == type))
: repo.GetByQuery(repo.Query.Where(x => x.UserId == userId && x.AuditType == type && x.CreateDate >= sinceDate.Value));
uow.Complete();
return result;
}
}
@@ -57,6 +59,7 @@ namespace Umbraco.Core.Services
var result = sinceDate.HasValue == false
? repo.GetByQuery(repo.Query.Where(x => x.AuditType == type))
: repo.GetByQuery(repo.Query.Where(x => x.AuditType == type && x.CreateDate >= sinceDate.Value));
uow.Complete();
return result;
}
}
@@ -67,6 +70,7 @@ namespace Umbraco.Core.Services
{
var repo = uow.CreateRepository<IAuditRepository>();
repo.CleanLogs(maximumAgeOfLogsInMinutes);
uow.Complete();
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,21 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
public class ContentTypeServiceBase : RepositoryService
internal abstract class ContentTypeServiceBase : RepositoryService
{
public ContentTypeServiceBase(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory)
protected ContentTypeServiceBase(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory)
: base(provider, logger, eventMessagesFactory)
{
}
{ }
/// <summary>
/// This is called after an content type is saved and is used to update the content xml structures in the database
/// if they are required to be updated.
@@ -33,7 +35,7 @@ namespace Umbraco.Core.Services
// - a content type changes it's alias OR
// - if a content type has it's property removed OR
// - if a content type has a property whose alias has changed
//here we need to check if the alias of the content type changed or if one of the properties was removed.
//here we need to check if the alias of the content type changed or if one of the properties was removed.
var dirty = contentType as IRememberBeingDirty;
if (dirty == null) continue;
@@ -50,18 +52,18 @@ namespace Umbraco.Core.Services
&& (dirty.WasPropertyDirty("Alias") || dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") || hasAnyPropertiesChangedAlias))
{
//If the alias was changed then we only need to update the xml structures for content of the current content type.
//If a property was deleted or a property alias was changed then we need to update the xml structures for any
//If a property was deleted or a property alias was changed then we need to update the xml structures for any
// content of the current content type and any of the content type's child content types.
if (dirty.WasPropertyDirty("Alias")
&& dirty.WasPropertyDirty("HasPropertyTypeBeenRemoved") == false && hasAnyPropertiesChangedAlias == false)
{
//if only the alias changed then only update the current content type
//if only the alias changed then only update the current content type
toUpdate.Add(contentType);
}
else
{
//if a property was deleted or alias changed, then update all content of the current content type
// and all of it's desscendant doc types.
// and all of it's desscendant doc types.
toUpdate.AddRange(contentType.DescendantsAndSelf());
}
}
@@ -71,4 +73,887 @@ namespace Umbraco.Core.Services
}
}
internal abstract class ContentTypeServiceBase<TItem, TService> : ContentTypeServiceBase
where TItem : class, IContentTypeComposition
where TService : class, IContentTypeServiceBase<TItem>
{
protected ContentTypeServiceBase(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory)
: base(provider, logger, eventMessagesFactory)
{
_this = this as TService;
if (_this == null) throw new Exception("Oops.");
}
private readonly TService _this;
public static event TypedEventHandler<TService, SaveEventArgs<TItem>> Saving;
public static event TypedEventHandler<TService, SaveEventArgs<TItem>> Saved;
protected void OnSaving(SaveEventArgs<TItem> args)
{
Saving.RaiseEvent(args, _this);
}
protected bool OnSavingCancelled(SaveEventArgs<TItem> args)
{
return Saving.IsRaisedEventCancelled(args, _this);
}
protected void OnSaved(SaveEventArgs<TItem> args)
{
Saved.RaiseEvent(args, _this);
}
public static event TypedEventHandler<TService, DeleteEventArgs<TItem>> Deleting;
public static event TypedEventHandler<TService, DeleteEventArgs<TItem>> Deleted;
protected void OnDeleting(DeleteEventArgs<TItem> args)
{
Deleting.RaiseEvent(args, _this);
}
protected bool OnDeletingCancelled(DeleteEventArgs<TItem> args)
{
return Deleting.IsRaisedEventCancelled(args, _this);
}
protected void OnDeleted(DeleteEventArgs<TItem> args)
{
Deleted.RaiseEvent(args, (TService)(object)this);
}
public static event TypedEventHandler<TService, MoveEventArgs<TItem>> Moving;
public static event TypedEventHandler<TService, MoveEventArgs<TItem>> Moved;
protected void OnMoving(MoveEventArgs<TItem> args)
{
Moving.RaiseEvent(args, _this);
}
protected bool OnMovingCancelled(MoveEventArgs<TItem> args)
{
return Moving.IsRaisedEventCancelled(args, _this);
}
protected void OnMoved(MoveEventArgs<TItem> args)
{
Moved.RaiseEvent(args, _this);
}
public static event TypedEventHandler<TService, SaveEventArgs<EntityContainer>> SavingContainer;
public static event TypedEventHandler<TService, SaveEventArgs<EntityContainer>> SavedContainer;
protected void OnSavingContainer(SaveEventArgs<EntityContainer> args)
{
SavingContainer.RaiseEvent(args, _this);
}
protected bool OnSavingContainerCancelled(SaveEventArgs<EntityContainer> args)
{
return SavingContainer.IsRaisedEventCancelled(args, _this);
}
protected void OnSavedContainer(SaveEventArgs<EntityContainer> args)
{
SavedContainer.RaiseEvent(args, _this);
}
public static event TypedEventHandler<TService, DeleteEventArgs<EntityContainer>> DeletingContainer;
public static event TypedEventHandler<TService, DeleteEventArgs<EntityContainer>> DeletedContainer;
protected void OnDeletingContainer(DeleteEventArgs<EntityContainer> args)
{
DeletingContainer.RaiseEvent(args, _this);
}
protected bool OnDeletingContainerCancelled(DeleteEventArgs<EntityContainer> args)
{
return DeletingContainer.IsRaisedEventCancelled(args, _this);
}
protected void OnDeletedContainer(DeleteEventArgs<EntityContainer> args)
{
DeletedContainer.RaiseEvent(args, _this);
}
// for later usage
//public static event TypedEventHandler<TService, Change.EventArgs> TxRefreshed;
//protected void OnTxRefreshed(Change.EventArgs args)
//{
// TxRefreshed.RaiseEvent(args, this);
//}
}
internal abstract class ContentTypeServiceBase<TRepository, TItem, TService> : ContentTypeServiceBase<TItem, TService>, IContentTypeServiceBase<TItem>
where TRepository : IContentTypeRepositoryBase<TItem>
where TItem : class, IContentTypeComposition
where TService : class, IContentTypeServiceBase<TItem>
{
protected ContentTypeServiceBase(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory)
: base(provider, logger, eventMessagesFactory)
{ }
protected abstract int[] WriteLockIds { get; }
protected abstract int[] ReadLockIds { get; }
#region Validation
public Attempt<string[]> ValidateComposition(TItem compo)
{
try
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
ValidateLocked(repo, compo);
uow.Complete();
}
return Attempt<string[]>.Succeed();
}
catch (InvalidCompositionException ex)
{
return Attempt.Fail(ex.PropertyTypeAliases, ex);
}
}
protected void ValidateLocked(TRepository repository, TItem compositionContentType)
{
// performs business-level validation of the composition
// should ensure that it is absolutely safe to save the composition
// eg maybe a property has been added, with an alias that's OK (no conflict with ancestors)
// but that cannot be used (conflict with descendants)
var allContentTypes = repository.GetAll(new int[0]).Cast<IContentTypeComposition>().ToArray();
var compositionAliases = compositionContentType.CompositionAliases();
var compositions = allContentTypes.Where(x => compositionAliases.Any(y => x.Alias.Equals(y)));
var propertyTypeAliases = compositionContentType.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()).ToArray();
var indirectReferences = allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == compositionContentType.Id));
var comparer = new DelegateEqualityComparer<IContentTypeComposition>((x, y) => x.Id == y.Id, x => x.Id);
var dependencies = new HashSet<IContentTypeComposition>(compositions, comparer);
var stack = new Stack<IContentTypeComposition>();
indirectReferences.ForEach(stack.Push); // push indirect references to a stack, so we can add recursively
while (stack.Count > 0)
{
var indirectReference = stack.Pop();
dependencies.Add(indirectReference);
// get all compositions for the current indirect reference
var directReferences = indirectReference.ContentTypeComposition;
foreach (var directReference in directReferences)
{
if (directReference.Id == compositionContentType.Id || directReference.Alias.Equals(compositionContentType.Alias)) continue;
dependencies.Add(directReference);
// a direct reference has compositions of its own - these also need to be taken into account
var directReferenceGraph = directReference.CompositionAliases();
allContentTypes.Where(x => directReferenceGraph.Any(y => x.Alias.Equals(y, StringComparison.InvariantCultureIgnoreCase))).ForEach(c => dependencies.Add(c));
}
// recursive lookup of indirect references
allContentTypes.Where(x => x.ContentTypeComposition.Any(y => y.Id == indirectReference.Id)).ForEach(stack.Push);
}
foreach (var dependency in dependencies)
{
if (dependency.Id == compositionContentType.Id) continue;
var contentTypeDependency = allContentTypes.FirstOrDefault(x => x.Alias.Equals(dependency.Alias, StringComparison.InvariantCultureIgnoreCase));
if (contentTypeDependency == null) continue;
var intersect = contentTypeDependency.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()).Intersect(propertyTypeAliases).ToArray();
if (intersect.Length == 0) continue;
throw new InvalidCompositionException(compositionContentType.Alias, intersect.ToArray());
}
}
#endregion
#region Composition
#endregion
#region Get, Has, Is, Count
public TItem Get(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var item = repo.Get(id);
uow.Complete();
return item;
}
}
public TItem Get(string alias)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var item = repo.Get(alias);
uow.Complete();
return item;
}
}
public TItem Get(Guid id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var item = repo.Get(id);
uow.Complete();
return item;
}
}
public IEnumerable<TItem> GetAll(params int[] ids)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var items = repo.GetAll(ids);
uow.Complete();
return items;
}
}
public IEnumerable<TItem> GetAll(params Guid[] ids)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
// IReadRepository<Guid, TEntity> is explicitely implemented, need to cast the repo
var items = ((IReadRepository<Guid, TItem>) repo).GetAll(ids);
uow.Complete();
return items;
}
}
public IEnumerable<TItem> GetChildren(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var query = repo.Query.Where(x => x.ParentId == id);
var items = repo.GetByQuery(query);
uow.Complete();
return items;
}
}
public IEnumerable<TItem> GetChildren(Guid id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var found = Get(id);
if (found == null) return Enumerable.Empty<TItem>();
var query = repo.Query.Where(x => x.ParentId == found.Id);
var items = repo.GetByQuery(query);
uow.Complete();
return items;
}
}
public bool HasChildren(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var query = repo.Query.Where(x => x.ParentId == id);
var count = repo.Count(query);
uow.Complete();
return count > 0;
}
}
public bool HasChildren(Guid id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var found = Get(id);
if (found == null) return false;
var query = repo.Query.Where(x => x.ParentId == found.Id);
var count = repo.Count(query);
uow.Complete();
return count > 0;
}
}
public IEnumerable<TItem> GetDescendants(int id, bool andSelf)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var descendants = new List<TItem>();
if (andSelf) descendants.Add(repo.Get(id));
var ids = new Stack<int>();
ids.Push(id);
while (ids.Count > 0)
{
var i = ids.Pop();
var query = repo.Query.Where(x => x.ParentId == i);
var result = repo.GetByQuery(query).ToArray();
foreach (var c in result)
{
descendants.Add(c);
ids.Push(c.Id);
}
}
var descendantsA = descendants.ToArray();
uow.Complete();
return descendantsA;
}
}
public IEnumerable<TItem> GetComposedOf(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
// hash set handles duplicates
var composed = new HashSet<TItem>(new DelegateEqualityComparer<TItem>(
(x, y) => x.Id == y.Id,
x => x.Id.GetHashCode()));
var ids = new Stack<int>();
ids.Push(id);
while (ids.Count > 0)
{
var i = ids.Pop();
var result = repo.GetTypesDirectlyComposedOf(i).ToArray();
foreach (var c in result)
{
composed.Add(c);
ids.Push(c.Id);
}
}
var composedA = composed.ToArray();
uow.Complete();
return composedA;
}
}
public int Count()
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.ReadLock(ReadLockIds);
var count = repo.Count(repo.Query);
uow.Complete();
return count;
}
}
#endregion
#region Save
public void Save(TItem item, int userId = 0)
{
if (OnSavingCancelled(new SaveEventArgs<TItem>(item)))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.WriteLock(WriteLockIds);
// validate the DAG transform, within the lock
ValidateLocked(repo, item); // throws if invalid
item.CreatorId = userId;
repo.AddOrUpdate(item); // also updates content/media/member items
uow.Flush(); // to db but no commit yet
// ...
uow.Complete();
}
// todo: should use TxRefreshed event within the transaction instead, see CC branch
UpdateContentXmlStructure(item);
OnSaved(new SaveEventArgs<TItem>(item, false));
Audit(AuditType.Save, $"Save {typeof(TItem).Name} performed by user", userId, item.Id);
}
public void Save(IEnumerable<TItem> items, int userId = 0)
{
var itemsA = items.ToArray();
if (OnSavingCancelled(new SaveEventArgs<TItem>(itemsA)))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.WriteLock(WriteLockIds);
// all-or-nothing, validate them all first
foreach (var contentType in itemsA)
{
ValidateLocked(repo, contentType); // throws if invalid
}
foreach (var contentType in itemsA)
{
contentType.CreatorId = userId;
repo.AddOrUpdate(contentType);
}
//save it all in one go
uow.Complete();
}
// todo: should use TxRefreshed event within the transaction instead, see CC branch
UpdateContentXmlStructure(itemsA.Cast<IContentTypeBase>().ToArray());
OnSaved(new SaveEventArgs<TItem>(itemsA, false));
Audit(AuditType.Save, $"Save {typeof(TItem).Name} performed by user", userId, -1);
}
#endregion
#region Delete
public void Delete(TItem item, int userId = 0)
{
if (OnDeletingCancelled(new DeleteEventArgs<TItem>(item)))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.WriteLock(WriteLockIds);
// all descendants are going to be deleted
var descendantsAndSelf = item.DescendantsAndSelf()
.ToArray();
// delete content
DeleteItemsOfTypes(descendantsAndSelf.Select(x => x.Id));
// finally delete the content type
// - recursively deletes all descendants
// - deletes all associated property data
// (contents of any descendant type have been deleted but
// contents of any composed (impacted) type remain but
// need to have their property data cleared)
repo.Delete(item);
uow.Flush(); // to db but no commit yet
//...
uow.Complete();
}
OnDeleted(new DeleteEventArgs<TItem>(item, false));
Audit(AuditType.Delete, $"Delete {typeof(TItem).Name} performed by user", userId, item.Id);
}
public void Delete(IEnumerable<TItem> items, int userId = 0)
{
var itemsA = items.ToArray();
if (OnDeletingCancelled(new DeleteEventArgs<TItem>(itemsA)))
return;
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.WriteLock(WriteLockIds);
// all descendants are going to be deleted
var allDescendantsAndSelf = itemsA.SelectMany(xx => xx.DescendantsAndSelf())
.Distinct()
.ToArray();
// delete content
DeleteItemsOfTypes(allDescendantsAndSelf.Select(x => x.Id));
// finally delete the content types
// (see notes in overload)
foreach (var item in itemsA)
repo.Delete(item);
uow.Flush(); // to db but no commit yet
// ...
uow.Complete();
}
OnDeleted(new DeleteEventArgs<TItem>(itemsA, false));
Audit(AuditType.Delete, $"Delete {typeof(TItem).Name} performed by user", userId, -1);
}
protected abstract void DeleteItemsOfTypes(IEnumerable<int> typeIds);
#endregion
#region Copy
public TItem Copy(TItem original, string alias, string name, int parentId = -1)
{
TItem parent = null;
if (parentId > 0)
{
parent = Get(parentId);
if (parent == null)
{
throw new InvalidOperationException("Could not find parent with id " + parentId);
}
}
return Copy(original, alias, name, parent);
}
public TItem Copy(TItem original, string alias, string name, TItem parent)
{
Mandate.ParameterNotNull(original, "original");
Mandate.ParameterNotNullOrEmpty(alias, "alias");
if (parent != null)
Mandate.That(parent.HasIdentity, () => new InvalidOperationException("The parent must have an identity"));
// this is illegal
//var originalb = (ContentTypeCompositionBase)original;
// but we *know* it has to be a ContentTypeCompositionBase anyways
var originalb = (ContentTypeCompositionBase) (object) original;
var clone = (TItem) originalb.DeepCloneWithResetIdentities(alias);
clone.Name = name;
//remove all composition that is not it's current alias
var compositionAliases = clone.CompositionAliases().Except(new[] { alias }).ToList();
foreach (var a in compositionAliases)
{
clone.RemoveContentType(a);
}
//if a parent is specified set it's composition and parent
if (parent != null)
{
//add a new parent composition
clone.AddContentType(parent);
clone.ParentId = parent.Id;
}
else
{
//set to root
clone.ParentId = -1;
}
Save(clone);
return clone;
}
public Attempt<OperationStatus<MoveOperationStatusType, TItem>> Copy(TItem copying, int containerId)
{
var evtMsgs = EventMessagesFactory.Get();
TItem copy;
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<TRepository>();
uow.WriteLock(WriteLockIds);
var containerRepository = uow.CreateContainerRepository(ContainerObjectType);
try
{
if (containerId > 0)
{
var container = containerRepository.Get(containerId);
if (container == null)
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound); // causes rollback
}
var alias = repo.GetUniqueAlias(copying.Alias);
// this is illegal
//var copyingb = (ContentTypeCompositionBase) copying;
// but we *know* it has to be a ContentTypeCompositionBase anyways
var copyingb = (ContentTypeCompositionBase) (object)copying;
copy = (TItem) copyingb.DeepCloneWithResetIdentities(alias);
copy.Name = copy.Name + " (copy)"; // might not be unique
// if it has a parent, and the parent is a content type, unplug composition
// all other compositions remain in place in the copied content type
if (copy.ParentId > 0)
{
var parent = repo.Get(copy.ParentId);
if (parent != null)
copy.RemoveContentType(parent.Alias);
}
copy.ParentId = containerId;
repo.AddOrUpdate(copy);
uow.Complete();
}
catch (DataOperationException<MoveOperationStatusType> ex)
{
return OperationStatus.Attempt.Fail<MoveOperationStatusType, TItem>(ex.Operation, evtMsgs); // causes rollback
}
}
return OperationStatus.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs, copy);
}
#endregion
#region Move
public Attempt<OperationStatus<MoveOperationStatusType>> Move(TItem moving, int containerId)
{
var evtMsgs = EventMessagesFactory.Get();
if (OnMovingCancelled(new MoveEventArgs<TItem>(evtMsgs, new MoveEventInfo<TItem>(moving, moving.Path, containerId))))
return OperationStatus.Attempt.Fail(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs);
var moveInfo = new List<MoveEventInfo<TItem>>();
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(WriteLockIds); // also for containers
var repo = uow.CreateRepository<TRepository>();
var containerRepo = uow.CreateRepository<IDocumentTypeContainerRepository>();
try
{
EntityContainer container = null;
if (containerId > 0)
{
container = containerRepo.Get(containerId);
if (container == null)
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound); // causes rollback
}
moveInfo.AddRange(repo.Move(moving, container));
uow.Complete();
}
catch (DataOperationException<MoveOperationStatusType> ex)
{
return OperationStatus.Attempt.Fail(ex.Operation, evtMsgs); // causes rollback
}
}
OnMoved(new MoveEventArgs<TItem>(false, evtMsgs, moveInfo.ToArray()));
return OperationStatus.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs);
}
#endregion
#region Containers
protected abstract Guid ContainedObjectType { get; }
protected Guid ContainerObjectType => EntityContainer.GetContainerObjectType(ContainedObjectType);
public Attempt<OperationStatus<OperationStatusType, EntityContainer>> CreateContainer(int parentId, string name, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(WriteLockIds); // also for containers
var repo = uow.CreateContainerRepository(ContainerObjectType);
try
{
var container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid)
{
Name = name,
ParentId = parentId,
CreatorId = userId
};
if (OnSavingContainerCancelled(new SaveEventArgs<EntityContainer>(container, evtMsgs)))
return OperationStatus.Attempt.Cancel(evtMsgs, container); // causes rollback
repo.AddOrUpdate(container);
uow.Complete();
OnSavedContainer(new SaveEventArgs<EntityContainer>(container, evtMsgs));
//TODO: Audit trail ?
return OperationStatus.Attempt.Succeed(evtMsgs, container);
}
catch (Exception ex)
{
return OperationStatus.Attempt.Fail<OperationStatusType, EntityContainer>(OperationStatusType.FailedCancelledByEvent, evtMsgs, ex);
}
}
}
public Attempt<OperationStatus> SaveContainer(EntityContainer container, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var containerObjectType = ContainerObjectType;
if (container.ContainedObjectType != containerObjectType)
{
var ex = new InvalidOperationException("Not a container of the proper type.");
return OperationStatus.Attempt.Fail(evtMsgs, ex);
}
if (container.HasIdentity && container.IsPropertyDirty("ParentId"))
{
var ex = new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
return OperationStatus.Attempt.Fail(evtMsgs, ex);
}
if (OnSavingContainerCancelled(new SaveEventArgs<EntityContainer>(container, evtMsgs)))
return OperationStatus.Attempt.Cancel(evtMsgs);
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(WriteLockIds); // also for containers
var repo = uow.CreateContainerRepository(containerObjectType);
repo.AddOrUpdate(container);
uow.Complete();
}
OnSavedContainer(new SaveEventArgs<EntityContainer>(container, evtMsgs));
//TODO: Audit trail ?
return OperationStatus.Attempt.Succeed(evtMsgs);
}
public EntityContainer GetContainer(int containerId)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(ReadLockIds); // also for containers
var repo = uow.CreateContainerRepository(ContainerObjectType);
var container = repo.Get(containerId);
uow.Complete();
return container;
}
}
public EntityContainer GetContainer(Guid containerId)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(ReadLockIds); // also for containers
var repo = uow.CreateContainerRepository(ContainerObjectType);
var container = ((EntityContainerRepository) repo).Get(containerId);
uow.Complete();
return container;
}
}
public IEnumerable<EntityContainer> GetContainers(int[] containerIds)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(ReadLockIds); // also for containers
var repo = uow.CreateContainerRepository(ContainerObjectType);
var containers = repo.GetAll(containerIds);
uow.Complete();
return containers;
}
}
public IEnumerable<EntityContainer> GetContainers(TItem item)
{
var ancestorIds = item.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x =>
{
var asInt = x.TryConvertTo<int>();
return asInt ? asInt.Result : int.MinValue;
})
.Where(x => x != int.MinValue && x != item.Id)
.ToArray();
return GetContainers(ancestorIds);
}
public IEnumerable<EntityContainer> GetContainers(string name, int level)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(ReadLockIds); // also for containers
var repo = uow.CreateContainerRepository(ContainerObjectType);
var containers = ((EntityContainerRepository) repo).Get(name, level);
uow.Complete();
return containers;
}
}
// fixme - what happens if deleting a non-empty container?
public Attempt<OperationStatus> DeleteContainer(int containerId, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(WriteLockIds); // also for containers
var repo = uow.CreateContainerRepository(ContainerObjectType);
var container = repo.Get(containerId);
if (container == null) return OperationStatus.Attempt.NoOperation(evtMsgs);
if (OnDeletingContainerCancelled(new DeleteEventArgs<EntityContainer>(container, evtMsgs)))
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs)); // causes rollback
repo.Delete(container);
uow.Complete();
OnDeletedContainer(new DeleteEventArgs<EntityContainer>(container, evtMsgs));
return OperationStatus.Attempt.Succeed(evtMsgs);
//TODO: Audit trail ?
}
}
#endregion
#region Audit
private void Audit(AuditType type, string message, int userId, int objectId)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IAuditRepository>();
repo.AddOrUpdate(new AuditItem(objectId, message, type, userId));
uow.Complete();
}
}
#endregion
#region Xml - Should Move!
protected abstract void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes);
#endregion
}
}
+59 -57
View File
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Services
#region Containers
public Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContainer(int parentId, string name, int userId = 0)
public Attempt<OperationStatus<OperationStatusType, EntityContainer>> CreateContainer(int parentId, string name, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
using (var uow = UowProvider.CreateUnitOfWork())
@@ -43,12 +43,8 @@ namespace Umbraco.Core.Services
CreatorId = userId
};
if (SavingContainer.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
if (SavingContainer.IsRaisedEventCancelled(new SaveEventArgs<EntityContainer>(container, evtMsgs), this))
return OperationStatus.Attempt.Cancel(evtMsgs, container); // causes rollback
repo.AddOrUpdate(container);
uow.Complete();
@@ -56,11 +52,11 @@ namespace Umbraco.Core.Services
SavedContainer.RaiseEvent(new SaveEventArgs<EntityContainer>(container, evtMsgs), this);
//TODO: Audit trail ?
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
return OperationStatus.Attempt.Succeed(evtMsgs, container);
}
catch (Exception ex)
{
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(null, OperationStatusType.FailedExceptionThrown, evtMsgs), ex);
return OperationStatus.Attempt.Fail<EntityContainer>(evtMsgs, ex);
}
}
}
@@ -71,6 +67,7 @@ namespace Umbraco.Core.Services
{
var repo = uow.CreateRepository<IDataTypeContainerRepository>();
var container = repo.Get(containerId);
uow.Complete();
return container;
}
}
@@ -81,6 +78,7 @@ namespace Umbraco.Core.Services
{
var repo = uow.CreateRepository<IDataTypeContainerRepository>();
var container = ((EntityContainerRepository)repo).Get(containerId);
uow.Complete();
return container;
}
}
@@ -90,7 +88,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IDataTypeContainerRepository>();
return ((EntityContainerRepository)repo).Get(name, level);
var containers = ((EntityContainerRepository)repo).Get(name, level);
uow.Complete();
return containers;
}
}
@@ -114,7 +114,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IDataTypeContainerRepository>();
return repo.GetAll(containerIds);
var containers = repo.GetAll(containerIds);
uow.Complete();
return containers;
}
}
@@ -125,20 +127,20 @@ namespace Umbraco.Core.Services
if (container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid)
{
var ex = new InvalidOperationException("Not a " + Constants.ObjectTypes.DataTypeGuid + " container.");
return OperationStatus.Exception(evtMsgs, ex);
return OperationStatus.Attempt.Fail(evtMsgs, ex);
}
if (container.HasIdentity && container.IsPropertyDirty("ParentId"))
{
var ex = new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
return OperationStatus.Exception(evtMsgs, ex);
return OperationStatus.Attempt.Fail(evtMsgs, ex);
}
if (SavingContainer.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
using (var uow = UowProvider.CreateUnitOfWork())
@@ -152,7 +154,7 @@ namespace Umbraco.Core.Services
//TODO: Audit trail ?
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
public Attempt<OperationStatus> DeleteContainer(int containerId, int userId = 0)
@@ -162,21 +164,17 @@ namespace Umbraco.Core.Services
{
var repo = uow.CreateRepository<IDataTypeContainerRepository>();
var container = repo.Get(containerId);
if (container == null) return OperationStatus.NoOperation(evtMsgs);
if (container == null) return OperationStatus.Attempt.NoOperation(evtMsgs);
if (DeletingContainer.IsRaisedEventCancelled(
new DeleteEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
if (DeletingContainer.IsRaisedEventCancelled(new DeleteEventArgs<EntityContainer>(container, evtMsgs), this))
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs)); // causes rollback
repo.Delete(container);
uow.Complete();
DeletedContainer.RaiseEvent(new DeleteEventArgs<EntityContainer>(container, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
//TODO: Audit trail ?
}
}
@@ -193,7 +191,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
return repository.GetByQuery(repository.Query.Where(x => x.Name == name)).FirstOrDefault();
var def = repository.GetByQuery(repository.Query.Where(x => x.Name == name)).FirstOrDefault();
uow.Complete();
return def;
}
}
@@ -207,7 +207,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
return repository.Get(id);
var def = repository.Get(id);
uow.Complete();
return def;
}
}
@@ -222,8 +224,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
var query = repository.Query.Where(x => x.Key == id);
var definitions = repository.GetByQuery(query);
return definitions.FirstOrDefault();
var definition = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return definition;
}
}
@@ -239,6 +242,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
var query = repository.Query.Where(x => x.PropertyEditorAlias == propertyEditorAlias);
var definitions = repository.GetByQuery(query);
uow.Complete();
return definitions;
}
}
@@ -253,7 +257,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
return repository.GetAll(ids);
var defs = repository.GetAll(ids);
uow.Complete();
return defs;
}
}
@@ -272,6 +278,7 @@ namespace Umbraco.Core.Services
var list = collection.FormatAsDictionary()
.Select(x => x.Value.Value)
.ToList();
uow.Complete();
return list;
}
}
@@ -286,7 +293,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
return repository.GetPreValuesCollectionByDataTypeId(id);
var vals = repository.GetPreValuesCollectionByDataTypeId(id);
uow.Complete();
return vals;
}
}
@@ -300,7 +309,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDataTypeDefinitionRepository>();
return repository.GetPreValueAsString(id);
var val = repository.GetPreValueAsString(id);
uow.Complete();
return val;
}
}
@@ -312,9 +323,7 @@ namespace Umbraco.Core.Services
new MoveEventArgs<IDataTypeDefinition>(evtMsgs, new MoveEventInfo<IDataTypeDefinition>(toMove, toMove.Path, parentId)),
this))
{
return Attempt.Fail(
new OperationStatus<MoveOperationStatusType>(
MoveOperationStatusType.FailedCancelledByEvent, evtMsgs));
return OperationStatus.Attempt.Fail(MoveOperationStatusType.FailedCancelledByEvent, evtMsgs);
}
var moveInfo = new List<MoveEventInfo<IDataTypeDefinition>>();
@@ -330,22 +339,20 @@ namespace Umbraco.Core.Services
{
container = containerRepository.Get(parentId);
if (container == null)
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound);
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound); // causes rollback
}
moveInfo.AddRange(repository.Move(toMove, container));
uow.Complete();
}
catch (DataOperationException<MoveOperationStatusType> ex)
{
return Attempt.Fail(
new OperationStatus<MoveOperationStatusType>(ex.Operation, evtMsgs));
return OperationStatus.Attempt.Fail(ex.Operation, evtMsgs);
}
uow.Complete();
}
Moved.RaiseEvent(new MoveEventArgs<IDataTypeDefinition>(false, evtMsgs, moveInfo.ToArray()), this);
return Attempt.Succeed(
new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.Success, evtMsgs));
return OperationStatus.Attempt.Succeed(MoveOperationStatusType.Success, evtMsgs);
}
/// <summary>
@@ -424,26 +431,21 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
using (var transaction = uow.Database.GetTransaction())
var sortOrderObj = uow.Database.ExecuteScalar<object>(
"SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = dataTypeId });
int sortOrder;
if (sortOrderObj == null || int.TryParse(sortOrderObj.ToString(), out sortOrder) == false)
sortOrder = 1;
foreach (var value in values)
{
var sortOrderObj =
uow.Database.ExecuteScalar<object>(
"SELECT max(sortorder) FROM cmsDataTypePreValues WHERE datatypeNodeId = @DataTypeId", new { DataTypeId = dataTypeId });
int sortOrder;
if (sortOrderObj == null || int.TryParse(sortOrderObj.ToString(), out sortOrder) == false)
{
sortOrder = 1;
}
foreach (var value in values)
{
var dto = new DataTypePreValueDto { DataTypeNodeId = dataTypeId, Value = value, SortOrder = sortOrder };
uow.Database.Insert(dto);
sortOrder++;
}
transaction.Complete();
var dto = new DataTypePreValueDto { DataTypeNodeId = dataTypeId, Value = value, SortOrder = sortOrder };
uow.Database.Insert(dto);
sortOrder++;
}
uow.Complete();
}
}
+19 -9
View File
@@ -23,7 +23,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IDomainRepository>();
return repo.Exists(domainName);
var exists = repo.Exists(domainName);
uow.Complete();
return exists;
}
}
@@ -34,7 +36,7 @@ namespace Umbraco.Core.Services
new DeleteEventArgs<IDomain>(domain, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
using (var uow = UowProvider.CreateUnitOfWork())
@@ -46,7 +48,7 @@ namespace Umbraco.Core.Services
var args = new DeleteEventArgs<IDomain>(domain, false, evtMsgs);
Deleted.RaiseEvent(args, this);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
public IDomain GetByName(string name)
@@ -54,7 +56,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDomainRepository>();
return repository.GetByName(name);
var domain = repository.GetByName(name);
uow.Complete();
return domain;
}
}
@@ -63,7 +67,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IDomainRepository>();
return repo.Get(id);
var domain = repo.Get(id);
uow.Complete();
return domain;
}
}
@@ -72,7 +78,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IDomainRepository>();
return repo.GetAll(includeWildcards);
var domains = repo.GetAll(includeWildcards);
uow.Complete();
return domains;
}
}
@@ -81,7 +89,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IDomainRepository>();
return repo.GetAssignedDomains(contentId, includeWildcards);
var domains = repo.GetAssignedDomains(contentId, includeWildcards);
uow.Complete();
return domains;
}
}
@@ -92,7 +102,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IDomain>(domainEntity, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
using (var uow = UowProvider.CreateUnitOfWork())
@@ -103,7 +113,7 @@ namespace Umbraco.Core.Services
}
Saved.RaiseEvent(new SaveEventArgs<IDomain>(domainEntity, false, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
#region Event Handlers
+58 -24
View File
@@ -23,20 +23,22 @@ namespace Umbraco.Core.Services
public EntityService(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory,
IContentService contentService, IContentTypeService contentTypeService, IMediaService mediaService, IDataTypeService dataTypeService,
IMemberService memberService, IMemberTypeService memberTypeService, IRuntimeCacheProvider runtimeCache)
IContentService contentService, IContentTypeService contentTypeService,
IMediaService mediaService, IMediaTypeService mediaTypeService,
IDataTypeService dataTypeService,
IMemberService memberService, IMemberTypeService memberTypeService,
IRuntimeCacheProvider runtimeCache)
: base(provider, logger, eventMessagesFactory)
{
_runtimeCache = runtimeCache;
IContentTypeService contentTypeService1 = contentTypeService;
_supportedObjectTypes = new Dictionary<string, Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>>
{
{typeof (IDataTypeDefinition).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.DataType, dataTypeService.GetDataTypeDefinitionById)},
{typeof (IContent).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.Document, contentService.GetById)},
{typeof (IContentType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.DocumentType, contentTypeService1.GetContentType)},
{typeof (IContentType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.DocumentType, contentTypeService.Get)},
{typeof (IMedia).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.Media, mediaService.GetById)},
{typeof (IMediaType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.MediaType, contentTypeService1.GetMediaType)},
{typeof (IMediaType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.MediaType, mediaTypeService.Get)},
{typeof (IMember).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.Member, memberService.GetById)},
{typeof (IMemberType).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.MemberType, memberTypeService.Get)},
//{typeof (IUmbracoEntity).FullName, new Tuple<UmbracoObjectTypes, Func<int, IUmbracoEntity>>(UmbracoObjectTypes.EntityContainer, id =>
@@ -74,6 +76,7 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<int?>(CacheKeys.IdToKeyCacheKey + key, () =>
{
int? id;
using (var uow = UowProvider.CreateUnitOfWork())
{
switch (umbracoObjectType)
@@ -87,11 +90,12 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
case UmbracoObjectTypes.DocumentTypeContainer:
return uow.Database.ExecuteScalar<int?>(
id = uow.Database.ExecuteScalar<int?>(
uow.Database.Sql()
.Select("id")
.From<NodeDto>()
.Where<NodeDto>(dto => dto.UniqueId == key));
break;
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
case UmbracoObjectTypes.MemberGroup:
@@ -102,6 +106,8 @@ namespace Umbraco.Core.Services
default:
throw new NotSupportedException();
}
uow.Complete();
return id;
}
});
return result.HasValue ? Attempt.Succeed(result.Value) : Attempt<int>.Fail();
@@ -119,6 +125,7 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.CreateUnitOfWork())
{
Guid? guid;
switch (umbracoObjectType)
{
case UmbracoObjectTypes.Document:
@@ -129,11 +136,12 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.DocumentType:
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
return uow.Database.ExecuteScalar<Guid?>(
guid = uow.Database.ExecuteScalar<Guid?>(
uow.Database.Sql()
.Select("uniqueID")
.From<NodeDto>()
.Where<NodeDto>(dto => dto.NodeId == id));
break;
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
case UmbracoObjectTypes.MemberGroup:
@@ -144,6 +152,8 @@ namespace Umbraco.Core.Services
default:
throw new NotSupportedException();
}
uow.Complete();
return guid;
}
});
return result.HasValue ? Attempt.Succeed(result.Value) : Attempt<Guid>.Fail();
@@ -156,7 +166,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.GetByKey(key);
var entity = repository.GetByKey(key);
uow.Complete();
return entity;
}
}
@@ -187,7 +199,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.Get(id);
var e = repository.Get(id);
uow.Complete();
return e;
}
}
@@ -207,7 +221,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.GetByKey(key, objectTypeId);
var entity = repository.GetByKey(key, objectTypeId);
uow.Complete();
return entity;
}
}
@@ -239,7 +255,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.Get(id, objectTypeId);
var e = repository.Get(id, objectTypeId);
uow.Complete();
return e;
}
}
@@ -272,7 +290,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.Get(id);
var e = repository.Get(id);
uow.Complete();
return e;
}
}
@@ -301,7 +321,9 @@ namespace Umbraco.Core.Services
if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21)
return null;
return repository.Get(entity.ParentId);
var e = repository.Get(entity.ParentId);
uow.Complete();
return e;
}
}
@@ -321,7 +343,9 @@ namespace Umbraco.Core.Services
return null;
var objectTypeId = umbracoObjectType.GetGuid();
return repository.Get(entity.ParentId, objectTypeId);
var e = repository.Get(entity.ParentId, objectTypeId);
uow.Complete();
return e;
}
}
@@ -337,7 +361,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IEntityRepository>();
var query = repository.Query.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query);
uow.Complete();
return contents;
}
}
@@ -356,7 +380,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IEntityRepository>();
var query = repository.Query.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query, objectTypeId).ToList(); // run within using!
uow.Complete();
return contents;
}
}
@@ -375,7 +399,7 @@ namespace Umbraco.Core.Services
var pathMatch = entity.Path + ",";
var query = repository.Query.Where(x => x.Path.StartsWith(pathMatch) && x.Id != id);
var entities = repository.GetByQuery(query);
uow.Complete();
return entities;
}
}
@@ -395,7 +419,7 @@ namespace Umbraco.Core.Services
var entity = repository.Get(id);
var query = repository.Query.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id);
var entities = repository.GetByQuery(query, objectTypeId);
uow.Complete();
return entities;
}
}
@@ -413,7 +437,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IEntityRepository>();
var query = repository.Query.Where(x => x.ParentId == -1);
var entities = repository.GetByQuery(query, objectTypeId);
uow.Complete();
return entities;
}
}
@@ -456,7 +480,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.GetAll(objectTypeId, ids);
var entities = repository.GetAll(objectTypeId, ids);
uow.Complete();
return entities;
}
}
@@ -474,7 +500,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.GetAll(objectTypeId, keys);
var entities = repository.GetAll(objectTypeId, keys);
uow.Complete();
return entities;
}
}
@@ -498,7 +526,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IEntityRepository>();
return repository.GetAll(objectTypeId, ids);
var entities = repository.GetAll(objectTypeId, ids);
uow.Complete();
return entities;
}
}
@@ -517,7 +547,9 @@ namespace Umbraco.Core.Services
.Where<NodeDto>(x => x.NodeId == id);
var nodeObjectTypeId = uow.Database.ExecuteScalar<Guid>(sql);
var objectTypeId = nodeObjectTypeId;
return UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId);
var t = UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId);
uow.Complete();
return t;
}
}
@@ -536,7 +568,9 @@ namespace Umbraco.Core.Services
.Where<NodeDto>(x => x.UniqueId == key);
var nodeObjectTypeId = uow.Database.ExecuteScalar<Guid>(sql);
var objectTypeId = nodeObjectTypeId;
return UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId);
var t = UmbracoObjectTypesExtensions.GetUmbracoObjectType(objectTypeId);
uow.Complete();
return t;
}
}
@@ -446,7 +446,7 @@ namespace Umbraco.Core.Services
if (contentType.Level != 1 && masterContentType == null)
{
//get url encoded folder names
var folders = contentTypeService.GetContentTypeContainers(contentType)
var folders = contentTypeService.GetContainers(contentType)
.OrderBy(x => x.Level)
.Select(x => HttpUtility.UrlEncode(x.Name));
@@ -25,7 +25,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IExternalLoginRepository>();
return repo.GetByQuery(repo.Query.Where(x => x.UserId == userId));
var ident = repo.GetByQuery(repo.Query.Where(x => x.UserId == userId));
uow.Complete();
return ident;
}
}
@@ -40,8 +42,10 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IExternalLoginRepository>();
return repo.GetByQuery(repo.Query
var idents = repo.GetByQuery(repo.Query
.Where(x => x.ProviderKey == login.ProviderKey && x.LoginProvider == login.LoginProvider));
uow.Complete();
return idents;
}
}
+94 -33
View File
@@ -51,7 +51,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IStylesheetRepository>();
return repository.GetAll(names);
var stylesheets = repository.GetAll(names);
uow.Complete();
return stylesheets;
}
}
@@ -65,7 +67,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IStylesheetRepository>();
return repository.Get(name);
var stylesheet = repository.Get(name);
uow.Complete();
return stylesheet;
}
}
@@ -102,10 +106,14 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IStylesheetRepository>();
stylesheet = repository.Get(path);
if (stylesheet == null) return;
if (stylesheet == null)
{
uow.Complete();
return;
}
if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs<Stylesheet>(stylesheet), this))
return;
return; // causes rollback
repository.Delete(stylesheet);
uow.Complete();
@@ -125,7 +133,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IStylesheetRepository>();
return repository.ValidateStylesheet(stylesheet);
var valid = repository.ValidateStylesheet(stylesheet);
uow.Complete();
return valid;
}
}
@@ -141,7 +151,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IScriptRepository>();
return repository.GetAll(names);
var scripts = repository.GetAll(names);
uow.Complete();
return scripts;
}
}
@@ -155,7 +167,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IScriptRepository>();
return repository.Get(name);
var script = repository.Get(name);
uow.Complete();
return script;
}
}
@@ -193,10 +207,14 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IScriptRepository>();
script = repository.Get(path);
if (script == null) return;
if (script == null)
{
uow.Complete();
return;
}
if (DeletingScript.IsRaisedEventCancelled(new DeleteEventArgs<Script>(script), this))
return;
return; // causes rollback
repository.Delete(script);
uow.Complete();
@@ -216,7 +234,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IScriptRepository>();
return repository.ValidateScript(script);
var valid = repository.ValidateScript(script);
uow.Complete();
return valid;
}
}
@@ -253,7 +273,7 @@ namespace Umbraco.Core.Services
/// <returns>
/// The template created
/// </returns>
public Attempt<OperationStatus<ITemplate, OperationStatusType>> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = 0)
public Attempt<OperationStatus<OperationStatusType, ITemplate>> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = 0)
{
var template = new Template(contentTypeName,
//NOTE: We are NOT passing in the content type alias here, we want to use it's name since we don't
@@ -277,7 +297,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<ITemplate>(template, true, evtMsgs, additionalData),
this))
{
return Attempt.Fail(new OperationStatus<ITemplate, OperationStatusType>(template, OperationStatusType.FailedCancelledByEvent, evtMsgs));
return OperationStatus.Attempt.Fail<OperationStatusType, ITemplate>(OperationStatusType.FailedCancelledByEvent, evtMsgs, template);
}
using (var uow = UowProvider.CreateUnitOfWork())
@@ -290,7 +310,7 @@ namespace Umbraco.Core.Services
SavedTemplate.RaiseEvent(new SaveEventArgs<ITemplate>(template, false, evtMsgs), this);
Audit(AuditType.Save, "Save Template performed by user", userId, template.Id);
return Attempt.Succeed(new OperationStatus<ITemplate, OperationStatusType>(template, OperationStatusType.Success, evtMsgs));
return OperationStatus.Attempt.Succeed<OperationStatusType, ITemplate>(OperationStatusType.Success, evtMsgs, template);
}
public ITemplate CreateTemplateWithIdentity(string name, string content, ITemplate masterTemplate = null, int userId = 0)
@@ -316,7 +336,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetAll(aliases).OrderBy(x => x.Name);
var templates = repository.GetAll(aliases).OrderBy(x => x.Name);
uow.Complete();
return templates;
}
}
@@ -329,7 +351,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetChildren(masterTemplateId).OrderBy(x => x.Name);
var templates = repository.GetChildren(masterTemplateId).OrderBy(x => x.Name);
uow.Complete();
return templates;
}
}
@@ -343,7 +367,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.Get(alias);
var template = repository.Get(alias);
uow.Complete();
return template;
}
}
@@ -357,7 +383,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.Get(id);
var template = repository.Get(id);
uow.Complete();
return template;
}
}
@@ -372,7 +400,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<ITemplateRepository>();
var query = repository.Query.Where(x => x.Key == id);
return repository.GetByQuery(query).SingleOrDefault();
var template = repository.GetByQuery(query).SingleOrDefault();
uow.Complete();
return template;
}
}
@@ -381,7 +411,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetDescendants(alias);
var templates = repository.GetDescendants(alias);
uow.Complete();
return templates;
}
}
@@ -395,7 +427,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetDescendants(masterTemplateId);
var templates = repository.GetDescendants(masterTemplateId);
uow.Complete();
return templates;
}
}
@@ -409,7 +443,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetChildren(alias);
var templates = repository.GetChildren(alias);
uow.Complete();
return templates;
}
}
@@ -423,7 +459,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetChildren(masterTemplateId);
var templates = repository.GetChildren(masterTemplateId);
uow.Complete();
return templates;
}
}
@@ -439,7 +477,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.GetTemplateNode(alias);
var template = repository.GetTemplateNode(alias);
uow.Complete();
return template;
}
}
@@ -456,7 +496,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.FindTemplateInTree(anyNode, alias);
var template = repository.FindTemplateInTree(anyNode, alias);
uow.Complete();
return template;
}
}
@@ -521,7 +563,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.DetermineTemplateRenderingEngine(template);
var engine = repository.DetermineTemplateRenderingEngine(template);
uow.Complete();
return engine;
}
}
@@ -537,10 +581,14 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<ITemplateRepository>();
template = repository.Get(alias);
if (template == null) return;
if (template == null)
{
uow.Complete();
return;
}
if (DeletingTemplate.IsRaisedEventCancelled(new DeleteEventArgs<ITemplate>(template), this))
return;
return; // causes rollback
repository.Delete(template);
uow.Complete();
@@ -560,7 +608,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITemplateRepository>();
return repository.ValidateTemplate(template);
var valid = repository.ValidateTemplate(template);
uow.Complete();
return valid;
}
}
@@ -609,7 +659,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IPartialViewRepository>();
return repository.Get(path);
var view = repository.Get(path);
uow.Complete();
return view;
}
}
@@ -618,7 +670,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IPartialViewMacroRepository>();
return repository.Get(path);
var view = repository.Get(path);
uow.Complete();
return view;
}
}
@@ -703,10 +757,13 @@ namespace Umbraco.Core.Services
var repository = uow.CreatePartialViewRepository(partialViewType);
partialView = repository.Get(path);
if (partialView == null)
{
uow.Complete();
return true;
}
if (DeletingPartialView.IsRaisedEventCancelled(new DeleteEventArgs<IPartialView>(partialView), this))
return false;
return false; // causes rollback
repository.Delete(partialView);
uow.Complete();
@@ -749,7 +806,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IPartialViewRepository>();
return repository.ValidatePartialView(partialView);
var valid = repository.ValidatePartialView(partialView);
uow.Complete();
return valid;
}
}
@@ -758,7 +817,9 @@ namespace Umbraco.Core.Services
using (var uow = _fileUowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IPartialViewMacroRepository>();
return repository.ValidatePartialView(partialView);
var valid = repository.ValidatePartialView(partialView);
uow.Complete();
return valid;
}
}
@@ -1,44 +1,14 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Services
{
/// <summary>
/// Defines the ContentTypeService, which is an easy access to operations involving <see cref="IContentType"/>
/// Manages <see cref="IContentType"/> objects.
/// </summary>
public interface IContentTypeService : IService
public interface IContentTypeService : IContentTypeServiceBase<IContentType>
{
int CountContentTypes();
int CountMediaTypes();
/// <summary>
/// Validates the composition, if its invalid a list of property type aliases that were duplicated is returned
/// </summary>
/// <param name="compo"></param>
/// <returns></returns>
Attempt<string[]> ValidateComposition(IContentTypeComposition compo);
Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContentTypeContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateMediaTypeContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus> SaveContentTypeContainer(EntityContainer container, int userId = 0);
Attempt<OperationStatus> SaveMediaTypeContainer(EntityContainer container, int userId = 0);
EntityContainer GetContentTypeContainer(int containerId);
EntityContainer GetContentTypeContainer(Guid containerId);
IEnumerable<EntityContainer> GetContentTypeContainers(int[] containerIds);
IEnumerable<EntityContainer> GetContentTypeContainers(IContentType contentType);
IEnumerable<EntityContainer> GetContentTypeContainers(string folderName, int level);
EntityContainer GetMediaTypeContainer(int containerId);
EntityContainer GetMediaTypeContainer(Guid containerId);
IEnumerable<EntityContainer> GetMediaTypeContainers(int[] containerIds);
IEnumerable<EntityContainer> GetMediaTypeContainers(string folderName, int level);
IEnumerable<EntityContainer> GetMediaTypeContainers(IMediaType mediaType);
Attempt<OperationStatus> DeleteMediaTypeContainer(int folderId, int userId = 0);
Attempt<OperationStatus> DeleteContentTypeContainer(int containerId, int userId = 0);
/// <summary>
/// Gets all property type aliases.
/// </summary>
@@ -55,200 +25,6 @@ namespace Umbraco.Core.Services
/// <returns></returns>
IEnumerable<string> GetAllContentTypeAliases(params Guid[] objectTypes);
/// <summary>
/// Copies a content type as a child under the specified parent if specified (otherwise to the root)
/// </summary>
/// <param name="original">
/// The content type to copy
/// </param>
/// <param name="alias">
/// The new alias of the content type
/// </param>
/// <param name="name">
/// The new name of the content type
/// </param>
/// <param name="parentId">
/// The parent to copy the content type to, default is -1 (root)
/// </param>
/// <returns></returns>
IContentType Copy(IContentType original, string alias, string name, int parentId = -1);
/// <summary>
/// Copies a content type as a child under the specified parent if specified (otherwise to the root)
/// </summary>
/// <param name="original">
/// The content type to copy
/// </param>
/// <param name="alias">
/// The new alias of the content type
/// </param>
/// <param name="name">
/// The new name of the content type
/// </param>
/// <param name="parent">
/// The parent to copy the content type to
/// </param>
/// <returns></returns>
IContentType Copy(IContentType original, string alias, string name, IContentType parent);
/// <summary>
/// Gets an <see cref="IContentType"/> object by its Id
/// </summary>
/// <param name="id">Id of the <see cref="IContentType"/> to retrieve</param>
/// <returns><see cref="IContentType"/></returns>
IContentType GetContentType(int id);
/// <summary>
/// Gets an <see cref="IContentType"/> object by its Alias
/// </summary>
/// <param name="alias">Alias of the <see cref="IContentType"/> to retrieve</param>
/// <returns><see cref="IContentType"/></returns>
IContentType GetContentType(string alias);
/// <summary>
/// Gets an <see cref="IContentType"/> object by its Key
/// </summary>
/// <param name="id">Alias of the <see cref="IContentType"/> to retrieve</param>
/// <returns><see cref="IContentType"/></returns>
IContentType GetContentType(Guid id);
/// <summary>
/// Gets a list of all available <see cref="IContentType"/> objects
/// </summary>
/// <param name="ids">Optional list of ids</param>
/// <returns>An Enumerable list of <see cref="IContentType"/> objects</returns>
IEnumerable<IContentType> GetAllContentTypes(params int[] ids);
/// <summary>
/// Gets a list of all available <see cref="IContentType"/> objects
/// </summary>
/// <param name="ids">Optional list of ids</param>
/// <returns>An Enumerable list of <see cref="IContentType"/> objects</returns>
IEnumerable<IContentType> GetAllContentTypes(IEnumerable<Guid> ids);
/// <summary>
/// Gets a list of children for a <see cref="IContentType"/> object
/// </summary>
/// <param name="id">Id of the Parent</param>
/// <returns>An Enumerable list of <see cref="IContentType"/> objects</returns>
IEnumerable<IContentType> GetContentTypeChildren(int id);
/// <summary>
/// Gets a list of children for a <see cref="IContentType"/> object
/// </summary>
/// <param name="id">Id of the Parent</param>
/// <returns>An Enumerable list of <see cref="IContentType"/> objects</returns>
IEnumerable<IContentType> GetContentTypeChildren(Guid id);
/// <summary>
/// Saves a single <see cref="IContentType"/> object
/// </summary>
/// <param name="contentType"><see cref="IContentType"/> to save</param>
/// <param name="userId">Optional Id of the User saving the ContentType</param>
void Save(IContentType contentType, int userId = 0);
/// <summary>
/// Saves a collection of <see cref="IContentType"/> objects
/// </summary>
/// <param name="contentTypes">Collection of <see cref="IContentType"/> to save</param>
/// <param name="userId">Optional Id of the User saving the ContentTypes</param>
void Save(IEnumerable<IContentType> contentTypes, int userId = 0);
/// <summary>
/// Deletes a single <see cref="IContentType"/> object
/// </summary>
/// <param name="contentType"><see cref="IContentType"/> to delete</param>
/// <remarks>Deleting a <see cref="IContentType"/> will delete all the <see cref="IContent"/> objects based on this <see cref="IContentType"/></remarks>
/// <param name="userId">Optional Id of the User deleting the ContentType</param>
void Delete(IContentType contentType, int userId = 0);
/// <summary>
/// Deletes a collection of <see cref="IContentType"/> objects
/// </summary>
/// <param name="contentTypes">Collection of <see cref="IContentType"/> to delete</param>
/// <remarks>Deleting a <see cref="IContentType"/> will delete all the <see cref="IContent"/> objects based on this <see cref="IContentType"/></remarks>
/// <param name="userId">Optional Id of the User deleting the ContentTypes</param>
void Delete(IEnumerable<IContentType> contentTypes, int userId = 0);
/// <summary>
/// Gets an <see cref="IMediaType"/> object by its Id
/// </summary>
/// <param name="id">Id of the <see cref="IMediaType"/> to retrieve</param>
/// <returns><see cref="IMediaType"/></returns>
IMediaType GetMediaType(int id);
/// <summary>
/// Gets an <see cref="IMediaType"/> object by its Alias
/// </summary>
/// <param name="alias">Alias of the <see cref="IMediaType"/> to retrieve</param>
/// <returns><see cref="IMediaType"/></returns>
IMediaType GetMediaType(string alias);
/// <summary>
/// Gets an <see cref="IMediaType"/> object by its Id
/// </summary>
/// <param name="id">Id of the <see cref="IMediaType"/> to retrieve</param>
/// <returns><see cref="IMediaType"/></returns>
IMediaType GetMediaType(Guid id);
/// <summary>
/// Gets a list of all available <see cref="IMediaType"/> objects
/// </summary>
/// <param name="ids">Optional list of ids</param>
/// <returns>An Enumerable list of <see cref="IMediaType"/> objects</returns>
IEnumerable<IMediaType> GetAllMediaTypes(params int[] ids);
/// <summary>
/// Gets a list of all available <see cref="IMediaType"/> objects
/// </summary>
/// <param name="ids">Optional list of ids</param>
/// <returns>An Enumerable list of <see cref="IMediaType"/> objects</returns>
IEnumerable<IMediaType> GetAllMediaTypes(IEnumerable<Guid> ids);
/// <summary>
/// Gets a list of children for a <see cref="IMediaType"/> object
/// </summary>
/// <param name="id">Id of the Parent</param>
/// <returns>An Enumerable list of <see cref="IMediaType"/> objects</returns>
IEnumerable<IMediaType> GetMediaTypeChildren(int id);
/// <summary>
/// Gets a list of children for a <see cref="IMediaType"/> object
/// </summary>
/// <param name="id">Id of the Parent</param>
/// <returns>An Enumerable list of <see cref="IMediaType"/> objects</returns>
IEnumerable<IMediaType> GetMediaTypeChildren(Guid id);
/// <summary>
/// Saves a single <see cref="IMediaType"/> object
/// </summary>
/// <param name="mediaType"><see cref="IMediaType"/> to save</param>
/// <param name="userId">Optional Id of the User saving the MediaType</param>
void Save(IMediaType mediaType, int userId = 0);
/// <summary>
/// Saves a collection of <see cref="IMediaType"/> objects
/// </summary>
/// <param name="mediaTypes">Collection of <see cref="IMediaType"/> to save</param>
/// <param name="userId">Optional Id of the User saving the MediaTypes</param>
void Save(IEnumerable<IMediaType> mediaTypes, int userId = 0);
/// <summary>
/// Deletes a single <see cref="IMediaType"/> object
/// </summary>
/// <param name="mediaType"><see cref="IMediaType"/> to delete</param>
/// <remarks>Deleting a <see cref="IMediaType"/> will delete all the <see cref="IMedia"/> objects based on this <see cref="IMediaType"/></remarks>
/// <param name="userId">Optional Id of the User deleting the MediaType</param>
void Delete(IMediaType mediaType, int userId = 0);
/// <summary>
/// Deletes a collection of <see cref="IMediaType"/> objects
/// </summary>
/// <param name="mediaTypes">Collection of <see cref="IMediaType"/> to delete</param>
/// <remarks>Deleting a <see cref="IMediaType"/> will delete all the <see cref="IMedia"/> objects based on this <see cref="IMediaType"/></remarks>
/// <param name="userId">Optional Id of the User deleting the MediaTypes</param>
void Delete(IEnumerable<IMediaType> mediaTypes, int userId = 0);
/// <summary>
/// Generates the complete (simplified) XML DTD.
/// </summary>
@@ -260,38 +36,5 @@ namespace Umbraco.Core.Services
/// </summary>
/// <returns>The DTD as a string</returns>
string GetContentTypesDtd();
/// <summary>
/// Checks whether an <see cref="IContentType"/> item has any children
/// </summary>
/// <param name="id">Id of the <see cref="IContentType"/></param>
/// <returns>True if the content type has any children otherwise False</returns>
bool HasChildren(int id);
/// <summary>
/// Checks whether an <see cref="IContentType"/> item has any children
/// </summary>
/// <param name="id">Id of the <see cref="IContentType"/></param>
/// <returns>True if the content type has any children otherwise False</returns>
bool HasChildren(Guid id);
/// <summary>
/// Checks whether an <see cref="IMediaType"/> item has any children
/// </summary>
/// <param name="id">Id of the <see cref="IMediaType"/></param>
/// <returns>True if the media type has any children otherwise False</returns>
bool MediaTypeHasChildren(int id);
/// <summary>
/// Checks whether an <see cref="IMediaType"/> item has any children
/// </summary>
/// <param name="id">Id of the <see cref="IMediaType"/></param>
/// <returns>True if the media type has any children otherwise False</returns>
bool MediaTypeHasChildren(Guid id);
Attempt<OperationStatus<MoveOperationStatusType>> MoveMediaType(IMediaType toMove, int containerId);
Attempt<OperationStatus<MoveOperationStatusType>> MoveContentType(IContentType toMove, int containerId);
Attempt<OperationStatus<IMediaType, MoveOperationStatusType>> CopyMediaType(IMediaType toCopy, int containerId);
Attempt<OperationStatus<IContentType, MoveOperationStatusType>> CopyContentType(IContentType toCopy, int containerId);
}
}
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
/// <summary>
/// Provides a common base interface for <see cref="IContentTypeService"/>, <see cref="IMediaTypeService"/> and <see cref="IMemberTypeService"/>.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
public interface IContentTypeServiceBase<TItem> : IService
where TItem : IContentTypeComposition
{
TItem Get(int id);
TItem Get(Guid key);
TItem Get(string alias);
int Count();
IEnumerable<TItem> GetAll(params int[] ids);
IEnumerable<TItem> GetDescendants(int id, bool andSelf); // parent-child axis
IEnumerable<TItem> GetComposedOf(int id); // composition axis
IEnumerable<TItem> GetChildren(int id);
bool HasChildren(int id);
void Save(TItem item, int userId = 0);
void Save(IEnumerable<TItem> items, int userId = 0);
void Delete(TItem item, int userId = 0);
void Delete(IEnumerable<TItem> item, int userId = 0);
Attempt<string[]> ValidateComposition(TItem compo);
Attempt<OperationStatus<OperationStatusType, EntityContainer>> CreateContainer(int parentContainerId, string name, int userId = 0);
Attempt<OperationStatus> SaveContainer(EntityContainer container, int userId = 0);
EntityContainer GetContainer(int containerId);
EntityContainer GetContainer(Guid containerId);
IEnumerable<EntityContainer> GetContainers(int[] containerIds);
IEnumerable<EntityContainer> GetContainers(TItem contentType);
IEnumerable<EntityContainer> GetContainers(string folderName, int level);
Attempt<OperationStatus> DeleteContainer(int containerId, int userId = 0);
Attempt<OperationStatus<MoveOperationStatusType>> Move(TItem moving, int containerId);
Attempt<OperationStatus<MoveOperationStatusType, TItem>> Copy(TItem copying, int containerId);
TItem Copy(TItem original, string alias, string name, int parentId = -1);
TItem Copy(TItem original, string alias, string name, TItem parent);
}
}
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IDataTypeService : IService
{
Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus<OperationStatusType, EntityContainer>> CreateContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus> SaveContainer(EntityContainer container, int userId = 0);
EntityContainer GetContainer(int containerId);
EntityContainer GetContainer(Guid containerId);
+1 -1
View File
@@ -196,7 +196,7 @@ namespace Umbraco.Core.Services
/// <returns>
/// The template created
/// </returns>
Attempt<OperationStatus<ITemplate, OperationStatusType>> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = 0);
Attempt<OperationStatus<OperationStatusType, ITemplate>> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = 0);
ITemplate CreateTemplateWithIdentity(string name, string content, ITemplate masterTemplate = null, int userId = 0);
@@ -0,0 +1,10 @@
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
/// <summary>
/// Manages <see cref="IMediaType"/> objects.
/// </summary>
public interface IMediaTypeService : IContentTypeServiceBase<IMediaType>
{ }
}
@@ -4,7 +4,10 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
public interface IMemberTypeService : IService
/// <summary>
/// Manages <see cref="IMemberType"/> objects.
/// </summary>
public interface IMemberTypeService : IContentTypeServiceBase<IMemberType>
{
/// <summary>
/// Gets a list of all available <see cref="IContentType"/> objects
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Services
/// <param name="ruleType"></param>
/// <param name="ruleValue"></param>
/// <returns></returns>
Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>> AddRule(IContent content, string ruleType, string ruleValue);
Attempt<OperationStatus<OperationStatusType, PublicAccessEntry>> AddRule(IContent content, string ruleType, string ruleValue);
/// <summary>
/// Removes a rule
@@ -72,9 +72,7 @@ namespace Umbraco.Core.Services
{
var parent = GetDictionaryItemById(parentId.Value);
if (parent == null)
{
throw new ArgumentException("No parent dictionary item was found with id " + parentId.Value);
}
throw new ArgumentException("No parent dictionary item was found with id " + parentId.Value); // causes rollback
}
item = new DictionaryItem(parentId, key);
@@ -90,7 +88,7 @@ namespace Umbraco.Core.Services
}
if (SavingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs<IDictionaryItem>(item), this))
return item;
return item; // causes rollback
repository.AddOrUpdate(item);
uow.Complete();
@@ -116,6 +114,7 @@ namespace Umbraco.Core.Services
var item = repository.Get(id);
//ensure the lazy Language callback is assigned
EnsureDictionaryItemLanguageCallback(item);
uow.Complete();
return item;
}
}
@@ -133,6 +132,7 @@ namespace Umbraco.Core.Services
var item = repository.Get(id);
//ensure the lazy Language callback is assigned
EnsureDictionaryItemLanguageCallback(item);
uow.Complete();
return item;
}
}
@@ -150,6 +150,7 @@ namespace Umbraco.Core.Services
var item = repository.Get(key);
//ensure the lazy Language callback is assigned
EnsureDictionaryItemLanguageCallback(item);
uow.Complete();
return item;
}
}
@@ -168,6 +169,7 @@ namespace Umbraco.Core.Services
var items = repository.GetByQuery(query).ToArray();
//ensure the lazy Language callback is assigned
items.ForEach(EnsureDictionaryItemLanguageCallback);
uow.Complete();
return items;
}
}
@@ -185,6 +187,7 @@ namespace Umbraco.Core.Services
var items = repository.GetDictionaryItemDescendants(parentId).ToArray();
//ensure the lazy Language callback is assigned
items.ForEach(EnsureDictionaryItemLanguageCallback);
uow.Complete();
return items;
}
}
@@ -202,6 +205,7 @@ namespace Umbraco.Core.Services
var items = repository.GetByQuery(query).ToArray();
//ensure the lazy Language callback is assigned
items.ForEach(EnsureDictionaryItemLanguageCallback);
uow.Complete();
return items;
}
}
@@ -216,7 +220,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IDictionaryRepository>();
return repository.Get(key) != null;
var item = repository.Get(key);
uow.Complete();
return item != null;
}
}
@@ -276,7 +282,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ILanguageRepository>();
return repository.Get(id);
var lang = repository.Get(id);
uow.Complete();
return lang;
}
}
@@ -290,7 +298,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ILanguageRepository>();
return repository.GetByCultureName(cultureName);
var lang = repository.GetByCultureName(cultureName);
uow.Complete();
return lang;
}
}
@@ -304,7 +314,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ILanguageRepository>();
return repository.GetByIsoCode(isoCode);
var lang = repository.GetByIsoCode(isoCode);
uow.Complete();
return lang;
}
}
@@ -317,8 +329,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ILanguageRepository>();
var languages = repository.GetAll();
return languages;
var langs = repository.GetAll();
uow.Complete();
return langs;
}
}
+10 -4
View File
@@ -54,8 +54,10 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMacroRepository>();
var q = repository.Query.Where(macro => macro.Alias == alias);
return repository.GetByQuery(q).FirstOrDefault();
var q = repository.Query.Where(x => x.Alias == alias);
var macro = repository.GetByQuery(q).FirstOrDefault();
uow.Complete();
return macro;
}
}
@@ -82,7 +84,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMacroRepository>();
return repository.GetAll(ids);
var macros = repository.GetAll(ids);
uow.Complete();
return macros;
}
}
@@ -91,7 +95,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMacroRepository>();
return repository.Get(id);
var macro = repository.Get(id);
uow.Complete();
return macro;
}
}
+61 -36
View File
@@ -240,7 +240,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.Get(id);
var media = repository.Get(id);
uow.Complete();
return media;
}
}
@@ -249,7 +251,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.Count(contentTypeAlias);
var count = repository.Count(contentTypeAlias);
uow.Complete();
return count;
}
}
@@ -258,7 +262,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.CountChildren(parentId, contentTypeAlias);
var count = repository.CountChildren(parentId, contentTypeAlias);
uow.Complete();
return count;
}
}
@@ -267,7 +273,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.CountDescendants(parentId, contentTypeAlias);
var count = repository.CountDescendants(parentId, contentTypeAlias);
uow.Complete();
return count;
}
}
@@ -283,7 +291,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.GetAll(ids.ToArray());
var items = repository.GetAll(ids.ToArray());
uow.Complete();
return items;
}
}
@@ -298,8 +308,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.Key == key);
var contents = repository.GetByQuery(query);
return contents.SingleOrDefault();
var item = repository.GetByQuery(query).SingleOrDefault();
uow.Complete();
return item;
}
}
@@ -313,9 +324,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.Level == level && !x.Path.StartsWith("-21"));
var query = repository.Query.Where(x => x.Level == level && x.Path.StartsWith("-21") == false);
var contents = repository.GetByQuery(query);
uow.Complete();
return contents;
}
}
@@ -330,7 +341,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.GetByVersion(versionId);
var item = repository.GetByVersion(versionId);
uow.Complete();
return item;
}
}
@@ -345,6 +358,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMediaRepository>();
var versions = repository.GetAllVersions(id);
uow.Complete();
return versions;
}
}
@@ -374,7 +388,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMediaRepository>();
return repository.GetAll(ids);
var items = repository.GetAll(ids);
uow.Complete();
return items;
}
}
@@ -390,7 +406,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.ParentId == id);
var medias = repository.GetByQuery(query);
uow.Complete();
return medias;
}
}
@@ -436,7 +452,7 @@ namespace Umbraco.Core.Services
query.Where(x => x.ParentId == id);
var medias = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
uow.Complete();
return medias;
}
}
@@ -484,7 +500,7 @@ namespace Umbraco.Core.Services
query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar));
}
var contents = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
uow.Complete();
return contents;
}
}
@@ -517,7 +533,7 @@ namespace Umbraco.Core.Services
var pathMatch = media.Path + ",";
var query = repository.Query.Where(x => x.Path.StartsWith(pathMatch) && x.Id != media.Id);
var medias = repository.GetByQuery(query);
uow.Complete();
return medias;
}
}
@@ -558,7 +574,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.ContentTypeId == id);
var medias = repository.GetByQuery(query);
uow.Complete();
return medias;
}
}
@@ -574,7 +590,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.ParentId == -1);
var medias = repository.GetByQuery(query);
uow.Complete();
return medias;
}
}
@@ -590,7 +606,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.Path.Contains("-21"));
var medias = repository.GetByQuery(query);
uow.Complete();
return medias;
}
}
@@ -605,7 +621,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IMediaRepository>();
return repo.GetMediaByPath(mediaPath);
var item = repo.GetMediaByPath(mediaPath);
uow.Complete();
return item;
}
}
@@ -620,7 +638,8 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMediaRepository>();
var query = repository.Query.Where(x => x.ParentId == id);
int count = repository.Count(query);
var count = repository.Count(query);
uow.Complete();
return count > 0;
}
}
@@ -658,7 +677,7 @@ namespace Umbraco.Core.Services
media.ParentId = parentId;
if (media.Trashed)
{
media.ChangeTrashedState(false, parentId);
((Models.Media)media).ChangeTrashedState(false, parentId);
}
Save(media, userId,
//no events!
@@ -716,7 +735,7 @@ namespace Umbraco.Core.Services
if (Deleting.IsRaisedEventCancelled(
new DeleteEventArgs<IMedia>(media, evtMsgs), this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
//Delete children before deleting the 'possible parent'
@@ -741,7 +760,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Delete, "Delete Media performed by user", userId, media.Id);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
@@ -760,7 +779,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IMedia>(media, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
}
@@ -784,7 +803,7 @@ namespace Umbraco.Core.Services
Saved.RaiseEvent(new SaveEventArgs<IMedia>(media, false, evtMsgs), this);
Audit(AuditType.Save, "Save Media performed by user", userId, media.Id);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
@@ -804,7 +823,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IMedia>(asArray, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
}
@@ -831,7 +850,7 @@ namespace Umbraco.Core.Services
Saved.RaiseEvent(new SaveEventArgs<IMedia>(asArray, false, evtMsgs), this);
Audit(AuditType.Save, "Save Media items performed by user", userId, -1);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
@@ -858,7 +877,7 @@ namespace Umbraco.Core.Services
files = ((MediaRepository)repository).GetFilesInRecycleBinForUploadField();
if (EmptyingRecycleBin.IsRaisedEventCancelled(new RecycleBinEventArgs(nodeObjectType, entities, files), this))
return;
return; // causes rollback
success = repository.EmptyRecycleBin();
@@ -866,6 +885,8 @@ namespace Umbraco.Core.Services
if (success)
repository.DeleteMediaFiles(files);
uow.Complete();
}
}
Audit(AuditType.Delete, "Empty Media Recycle Bin performed by user", 0, -21);
@@ -893,7 +914,7 @@ namespace Umbraco.Core.Services
var contents = repository.GetByQuery(query).ToArray();
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMedia>(contents), this))
return;
return; // causes rollback
foreach (var content in contents.OrderByDescending(x => x.ParentId))
{
@@ -911,6 +932,8 @@ namespace Umbraco.Core.Services
//Permanently delete the content
Delete(content, userId);
}
uow.Complete();
}
Audit(AuditType.Delete, "Delete Media items by Type performed by user", userId, -1);
@@ -933,7 +956,7 @@ namespace Umbraco.Core.Services
if (Trashing.IsRaisedEventCancelled(
new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
var moveInfo = new List<MoveEventInfo<IMedia>>
@@ -952,7 +975,7 @@ namespace Umbraco.Core.Services
//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);
((Models.Media)media).ChangeTrashedState(true, Constants.System.RecycleBinMedia);
repository.AddOrUpdate(media);
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
@@ -961,7 +984,7 @@ namespace Umbraco.Core.Services
//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);
((Models.Media)descendant).ChangeTrashedState(true, descendant.ParentId);
repository.AddOrUpdate(descendant);
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
@@ -974,7 +997,7 @@ namespace Umbraco.Core.Services
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);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
@@ -1138,6 +1161,7 @@ namespace Umbraco.Core.Services
repository.RebuildXmlStructures(
media => _entitySerializer.Serialize(this, _dataTypeService, _userService, _urlSegmentProviders, media),
contentTypeIds: contentTypeIds.Length == 0 ? null : contentTypeIds);
uow.Complete();
}
Audit(AuditType.Publish, "MediaService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
@@ -1163,7 +1187,7 @@ namespace Umbraco.Core.Services
child.Level = parentLevel + 1;
if (parentTrashed != child.Trashed)
{
child.ChangeTrashedState(parentTrashed, child.ParentId);
((Models.Media)child).ChangeTrashedState(parentTrashed, child.ParentId);
}
eventInfo.Add(new MoveEventInfo<IMedia>(child, originalPath, child.ParentId));
@@ -1197,14 +1221,15 @@ namespace Umbraco.Core.Services
if (mediaTypes.Any() == false)
throw new Exception(string.Format("No MediaType matching the passed in Alias: '{0}' was found",
mediaTypeAlias));
mediaTypeAlias)); // causes rollback
var mediaType = mediaTypes.First();
if (mediaType == null)
throw new Exception(string.Format("MediaType matching the passed in Alias: '{0}' was null",
mediaTypeAlias));
mediaTypeAlias)); // causes rollback
uow.Complete();
return mediaType;
}
}
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
internal class MediaTypeService : ContentTypeServiceBase<IMediaTypeRepository, IMediaType, IMediaTypeService>, IMediaTypeService
{
private IMediaService _mediaService;
public MediaTypeService(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMediaService mediaService)
: base(provider, logger, eventMessagesFactory)
{
_mediaService = mediaService;
}
// beware! order is important to avoid deadlocks
protected override int[] ReadLockIds { get; } = { Constants.Locks.MediaTypes };
protected override int[] WriteLockIds { get; } = { Constants.Locks.MediaTree, Constants.Locks.MediaTypes };
// don't remove, will need it later
private IMediaService MediaService => _mediaService;
//// handle circular dependencies
//internal IMediaService MediaService
//{
// get
// {
// if (_mediaService == null)
// throw new InvalidOperationException("MediaTypeService.MediaService has not been initialized.");
// return _mediaService;
// }
// set { _mediaService = value; }
//}
protected override Guid ContainedObjectType => Constants.ObjectTypes.MediaTypeGuid;
protected override void DeleteItemsOfTypes(IEnumerable<int> typeIds)
{
foreach (var typeId in typeIds)
MediaService.DeleteMediaOfType(typeId);
}
protected override void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes)
{
var toUpdate = GetContentTypesForXmlUpdates(contentTypes).ToArray();
if (toUpdate.Any() == false) return;
var mediaService = _mediaService as MediaService;
mediaService?.RebuildXmlStructures(toUpdate.Select(x => x.Id).ToArray());
}
}
}
@@ -42,7 +42,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
return repository.GetAll();
var groups = repository.GetAll();
uow.Complete();
return groups;
}
}
@@ -51,7 +53,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
return repository.Get(id);
var group = repository.Get(id);
uow.Complete();
return group;
}
}
@@ -60,7 +64,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
return repository.GetByName(name);
var group = repository.GetByName(name);
uow.Complete();
return group;
}
}
+95 -38
View File
@@ -57,9 +57,9 @@ namespace Umbraco.Core.Services
if (types.Any() == false)
{
throw new InvalidOperationException("No member types could be resolved");
throw new InvalidOperationException("No member types could be resolved"); // causes rollback
}
uow.Complete();
if (types.InvariantContains("Member"))
{
return types.First(x => x.InvariantEquals("Member"));
@@ -79,7 +79,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.Exists(username);
var exists = repository.Exists(username);
uow.Complete();
return exists;
}
}
@@ -126,7 +128,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.Exists(id);
var exists = repository.Exists(id);
uow.Complete();
return exists;
}
}
@@ -140,7 +144,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.Get(id);
var member = repository.Get(id);
uow.Complete();
return member;
}
}
@@ -158,6 +164,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMemberRepository>();
var query = repository.Query.Where(x => x.Key == id);
var member = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return member;
}
}
@@ -174,6 +181,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMemberRepository>();
var query = repository.Query.Where(x => x.ContentTypeAlias == memberTypeAlias);
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
@@ -191,6 +199,7 @@ namespace Umbraco.Core.Services
repository.Get(memberTypeId);
var query = repository.Query.Where(x => x.ContentTypeId == memberTypeId);
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
@@ -205,7 +214,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.GetByMemberGroup(memberGroupName);
var members = repository.GetByMemberGroup(memberGroupName);
uow.Complete();
return members;
}
}
@@ -220,7 +231,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.GetAll(ids);
var members = repository.GetAll(ids);
uow.Complete();
return members;
}
}
@@ -240,13 +253,15 @@ namespace Umbraco.Core.Services
var members = repository.GetByQuery(query).ToArray();
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMember>(members), this))
return;
return; // causes rollback
foreach (var member in members)
{
//Permantly delete the member
Delete(member);
}
uow.Complete();
}
}
}
@@ -295,10 +310,12 @@ namespace Umbraco.Core.Services
query.Where(member => member.Name.SqlWildcard(displayNameToMatch, TextColumnType.NVarchar));
break;
default:
throw new ArgumentOutOfRangeException("matchType");
throw new ArgumentOutOfRangeException("matchType"); // causes rollback
}
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "Name", Direction.Ascending, true);
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "Name", Direction.Ascending, true);
uow.Complete();
return members;
}
}
@@ -349,7 +366,9 @@ namespace Umbraco.Core.Services
throw new ArgumentOutOfRangeException("matchType");
}
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "Email", Direction.Ascending, true);
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "Email", Direction.Ascending, true);
uow.Complete();
return members;
}
}
@@ -400,7 +419,9 @@ namespace Umbraco.Core.Services
throw new ArgumentOutOfRangeException("matchType");
}
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true);
var members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true);
uow.Complete();
return members;
}
}
@@ -453,10 +474,11 @@ namespace Umbraco.Core.Services
((Member)x).ShortStringPropertyValue.SqlEndsWith(value, TextColumnType.NVarchar)));
break;
default:
throw new ArgumentOutOfRangeException("matchType");
throw new ArgumentOutOfRangeException("matchType"); // causes rollback
}
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
@@ -513,10 +535,11 @@ namespace Umbraco.Core.Services
((Member)x).IntegerPropertyValue <= value);
break;
default:
throw new ArgumentOutOfRangeException("matchType");
throw new ArgumentOutOfRangeException("matchType"); // causes rollback
}
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
@@ -539,6 +562,7 @@ namespace Umbraco.Core.Services
((Member)x).BoolPropertyValue == value);
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
@@ -595,11 +619,12 @@ namespace Umbraco.Core.Services
((Member)x).DateTimePropertyValue <= value);
break;
default:
throw new ArgumentOutOfRangeException("matchType");
throw new ArgumentOutOfRangeException("matchType"); // causes rollback
}
//TODO: Since this is by property value, we need a GetByPropertyQuery on the repo!
var members = repository.GetByQuery(query);
uow.Complete();
return members;
}
}
@@ -620,6 +645,7 @@ namespace Umbraco.Core.Services
repository.RebuildXmlStructures(
member => _entitySerializer.Serialize(_dataTypeService, member),
contentTypeIds: memberTypeIds.Length == 0 ? null : memberTypeIds);
uow.Complete();
}
Audit(AuditType.Publish, "MemberService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1);
@@ -650,7 +676,7 @@ namespace Umbraco.Core.Services
{
case MemberCountType.All:
query = repository.Query;
return repository.Count(query);
break;
case MemberCountType.Online:
var fromDate = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);
query =
@@ -658,24 +684,28 @@ namespace Umbraco.Core.Services
x =>
((Member)x).PropertyTypeAlias == Constants.Conventions.Member.LastLoginDate &&
((Member)x).DateTimePropertyValue > fromDate);
return repository.GetCountByQuery(query);
break;
case MemberCountType.LockedOut:
query =
repository.Query.Where(
x =>
((Member)x).PropertyTypeAlias == Constants.Conventions.Member.IsLockedOut &&
((Member)x).BoolPropertyValue == true);
return repository.GetCountByQuery(query);
((Member)x).BoolPropertyValue);
break;
case MemberCountType.Approved:
query =
repository.Query.Where(
x =>
((Member)x).PropertyTypeAlias == Constants.Conventions.Member.IsApproved &&
((Member)x).BoolPropertyValue == true);
return repository.GetCountByQuery(query);
((Member)x).BoolPropertyValue);
break;
default:
throw new ArgumentOutOfRangeException("countType");
throw new ArgumentOutOfRangeException("countType"); // causes rollback;
}
var count = repository.GetCountByQuery(query);
uow.Complete();
return count;
}
}
@@ -702,7 +732,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true);
var members = repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, "LoginName", Direction.Ascending, true);
uow.Complete();
return members;
}
}
@@ -729,12 +761,18 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
IEnumerable<IMember> members;
if (memberTypeAlias == null)
{
return repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, filter);
members = repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, filter);
}
var query = repository.Query.Where(x => x.ContentTypeAlias == memberTypeAlias);
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, filter);
else
{
var query = repository.Query.Where(x => x.ContentTypeAlias == memberTypeAlias);
members = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, orderBySystemField, filter);
}
uow.Complete();
return members;
}
}
@@ -749,7 +787,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.Count(memberTypeAlias);
var count = repository.Count(memberTypeAlias);
uow.Complete();
return count;
}
}
@@ -927,7 +967,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMemberRepository>();
var query = repository.Query.Where(x => x.Email.Equals(email));
var member = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return member;
}
}
@@ -948,7 +988,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IMemberRepository>();
var query = repository.Query.Where(x => x.Username.Equals(username));
var member = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return member;
}
}
@@ -1060,6 +1100,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
repository.CreateIfNotExists(roleName);
uow.Complete();
}
}
@@ -1068,8 +1109,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
var result = repository.GetAll();
return result.Select(x => x.Name).Distinct();
var result = repository.GetAll().Select(x => x.Name).Distinct();
uow.Complete();
return result;
}
}
@@ -1079,7 +1121,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
var result = repository.GetMemberGroupsForMember(memberId);
return result.Select(x => x.Name).Distinct();
var roles = result.Select(x => x.Name).Distinct();
uow.Complete();
return roles;
}
}
@@ -1089,7 +1133,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
var result = repository.GetMemberGroupsForMember(username);
return result.Select(x => x.Name).Distinct();
var roles = result.Select(x => x.Name).Distinct();
uow.Complete();
return roles;
}
}
@@ -1098,7 +1144,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.GetByMemberGroup(roleName);
var members = repository.GetByMemberGroup(roleName);
uow.Complete();
return members;
}
}
@@ -1107,7 +1155,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberRepository>();
return repository.FindMembersInRole(roleName, usernameToMatch, matchType);
var members = repository.FindMembersInRole(roleName, usernameToMatch, matchType);
uow.Complete();
return members;
}
}
@@ -1134,7 +1184,9 @@ namespace Umbraco.Core.Services
{
_memberGroupService.Delete(memberGroup);
}
return found.Any();
var deleted = found.Any();
uow.Complete();
return deleted;
}
}
}
@@ -1149,6 +1201,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
repository.AssignRoles(usernames, roleNames);
uow.Complete();
}
}
@@ -1163,6 +1216,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
repository.DissociateRoles(usernames, roleNames);
uow.Complete();
}
}
@@ -1177,6 +1231,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
repository.AssignRoles(memberIds, roleNames);
uow.Complete();
}
}
@@ -1191,6 +1246,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IMemberGroupRepository>();
repository.DissociateRoles(memberIds, roleNames);
uow.Complete();
}
}
@@ -1209,14 +1265,15 @@ namespace Umbraco.Core.Services
if (types.Any() == false)
throw new Exception(
string.Format("No MemberType matching the passed in Alias: '{0}' was found",
memberTypeAlias));
memberTypeAlias)); // causes rollback
var contentType = types.First();
if (contentType == null)
throw new Exception(string.Format("MemberType matching the passed in Alias: '{0}' was null",
memberTypeAlias));
memberTypeAlias)); // causes rollback
uow.Complete();
return contentType;
}
}
+29 -189
View File
@@ -1,218 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
public class MemberTypeService : ContentTypeServiceBase, IMemberTypeService
internal class MemberTypeService : ContentTypeServiceBase<IMemberTypeRepository, IMemberType, IMemberTypeService>, IMemberTypeService
{
private readonly IMemberService _memberService;
private IMemberService _memberService;
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
public MemberTypeService(IDatabaseUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberService memberService)
: base(provider, logger, eventMessagesFactory)
{
if (memberService == null) throw new ArgumentNullException("memberService");
_memberService = memberService;
}
public IEnumerable<IMemberType> GetAll(params int[] ids)
// beware! order is important to avoid deadlocks
protected override int[] ReadLockIds { get; } = { Constants.Locks.MemberTypes };
protected override int[] WriteLockIds { get; } = { Constants.Locks.MemberTree, Constants.Locks.MemberTypes };
// don't remove, will need it later
private IMemberService MemberService => _memberService;
//// handle circular dependencies
//internal IMemberService MemberService
//{
// get
// {
// if (_memberService == null)
// throw new InvalidOperationException("MemberTypeService.MemberService has not been initialized.");
// return _memberService;
// }
// set { _memberService = value; }
//}
protected override Guid ContainedObjectType => Constants.ObjectTypes.MemberTypeGuid;
protected override void DeleteItemsOfTypes(IEnumerable<int> typeIds)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
return repository.GetAll(ids);
}
foreach (var typeId in typeIds)
MemberService.DeleteMembersOfType(typeId);
}
/// <summary>
/// Gets an <see cref="IMemberType"/> object by its Id
/// </summary>
/// <param name="id">Id of the <see cref="IMemberType"/> to retrieve</param>
/// <returns><see cref="IMemberType"/></returns>
public IMemberType Get(int id)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
return repository.Get(id);
}
}
/// <summary>
/// Gets an <see cref="IMemberType"/> object by its Key
/// </summary>
/// <param name="key">Key of the <see cref="IMemberType"/> to retrieve</param>
/// <returns><see cref="IMemberType"/></returns>
public IMemberType Get(Guid key)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
return repository.Get(key);
}
}
/// <summary>
/// Gets an <see cref="IMemberType"/> object by its Alias
/// </summary>
/// <param name="alias">Alias of the <see cref="IMemberType"/> to retrieve</param>
/// <returns><see cref="IMemberType"/></returns>
public IMemberType Get(string alias)
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
return repository.Get(alias);
}
}
public void Save(IMemberType memberType, int userId = 0)
{
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMemberType>(memberType), this))
return;
using (new WriteLock(Locker))
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
memberType.CreatorId = userId;
repository.AddOrUpdate(memberType);
uow.Complete();
}
UpdateContentXmlStructure(memberType);
}
Saved.RaiseEvent(new SaveEventArgs<IMemberType>(memberType, false), this);
}
public void Save(IEnumerable<IMemberType> memberTypes, int userId = 0)
{
var asArray = memberTypes.ToArray();
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<IMemberType>(asArray), this))
return;
using (new WriteLock(Locker))
{
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
foreach (var memberType in asArray)
{
memberType.CreatorId = userId;
repository.AddOrUpdate(memberType);
}
//save it all in one go
uow.Complete();
}
UpdateContentXmlStructure(asArray.Cast<IContentTypeBase>().ToArray());
}
Saved.RaiseEvent(new SaveEventArgs<IMemberType>(asArray, false), this);
}
public void Delete(IMemberType memberType, int userId = 0)
{
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMemberType>(memberType), this))
return;
using (new WriteLock(Locker))
{
_memberService.DeleteMembersOfType(memberType.Id);
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
repository.Delete(memberType);
uow.Complete();
Deleted.RaiseEvent(new DeleteEventArgs<IMemberType>(memberType, false), this);
}
}
}
public void Delete(IEnumerable<IMemberType> memberTypes, int userId = 0)
{
var asArray = memberTypes.ToArray();
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMemberType>(asArray), this))
return;
using (new WriteLock(Locker))
{
foreach (var contentType in asArray)
{
_memberService.DeleteMembersOfType(contentType.Id);
}
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IMemberTypeRepository>();
foreach (var memberType in asArray)
{
repository.Delete(memberType);
}
uow.Complete();
}
Deleted.RaiseEvent(new DeleteEventArgs<IMemberType>(asArray, false), this);
}
}
/// <summary>
/// This is called after an IContentType is saved and is used to update the content xml structures in the database
/// if they are required to be updated.
/// </summary>
/// <param name="contentTypes">A tuple of a content type and a boolean indicating if it is new (HasIdentity was false before committing)</param>
private void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes)
protected override void UpdateContentXmlStructure(params IContentTypeBase[] contentTypes)
{
var toUpdate = GetContentTypesForXmlUpdates(contentTypes).ToArray();
if (toUpdate.Any() == false) return;
if (toUpdate.Any())
{
//if it is a media type then call the rebuilding methods for media
var typedMemberService = _memberService as MemberService;
if (typedMemberService != null)
{
typedMemberService.RebuildXmlStructures(toUpdate.Select(x => x.Id).ToArray());
}
}
var memberService = _memberService as MemberService;
memberService?.RebuildXmlStructures(toUpdate.Select(x => x.Id).ToArray());
}
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler<IMemberTypeService, SaveEventArgs<IMemberType>> Saving;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler<IMemberTypeService, SaveEventArgs<IMemberType>> Saved;
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IMemberTypeService, DeleteEventArgs<IMemberType>> Deleting;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IMemberTypeService, DeleteEventArgs<IMemberType>> Deleted;
}
}
@@ -57,7 +57,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IMigrationEntryRepository>();
return repo.FindEntry(migrationName, version);
var entry = repo.FindEntry(migrationName, version);
uow.Complete();
return entry;
}
}
@@ -73,7 +75,9 @@ namespace Umbraco.Core.Services
var repo = uow.CreateRepository<IMigrationEntryRepository>();
var query = repo.Query
.Where(x => x.MigrationName.ToUpper() == migrationName.ToUpper());
return repo.GetByQuery(query);
var entries = repo.GetByQuery(query);
uow.Complete();
return entries;
}
}
@@ -110,7 +110,9 @@ namespace Umbraco.Core.Services
using (var uow = _uowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<INotificationsRepository>();
return repository.GetUserNotifications(user);
var notifications = repository.GetUserNotifications(user);
uow.Complete();
return notifications;
}
}
@@ -140,7 +142,9 @@ namespace Umbraco.Core.Services
using (var uow = _uowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<INotificationsRepository>();
return repository.GetEntityNotifications(entity);
var notifications = repository.GetEntityNotifications(entity);
uow.Complete();
return notifications;
}
}
@@ -154,6 +158,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<INotificationsRepository>();
repository.DeleteNotifications(entity);
uow.Complete();
}
}
@@ -167,6 +172,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<INotificationsRepository>();
repository.DeleteNotifications(user);
uow.Complete();
}
}
@@ -181,6 +187,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<INotificationsRepository>();
repository.DeleteNotifications(user, entity);
uow.Complete();
}
}
@@ -198,7 +205,9 @@ namespace Umbraco.Core.Services
using (var uow = _uowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<INotificationsRepository>();
return repository.SetNotifications(user, entity, actions);
var notifications = repository.SetNotifications(user, entity, actions);
uow.Complete();
return notifications;
}
}
@@ -214,7 +223,9 @@ namespace Umbraco.Core.Services
using (var uow = _uowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<INotificationsRepository>();
return repository.CreateNotification(user, entity, action);
var notification = repository.CreateNotification(user, entity, action);
uow.Complete();
return notification;
}
}
+186 -48
View File
@@ -1,72 +1,210 @@
using System;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
/// <summary>
/// The status returned by many of the service methods
/// Represents the status of a service operation.
/// </summary>
public class OperationStatus<TEntity, TStatus> : OperationStatus<TStatus>
where TStatus : struct
/// <typeparam name="TStatusType">The type of the status type.</typeparam>
public class OperationStatus<TStatusType>
where TStatusType : struct
{
public OperationStatus(TEntity entity, TStatus statusType, EventMessages eventMessages) : base(statusType, eventMessages)
/// <summary>
/// Initializes a new instance of the <see cref="OperationStatus{TStatusType}"/> class with a status and event messages.
/// </summary>
/// <param name="statusType">The status of the operation.</param>
/// <param name="eventMessages">Event messages produced by the operation.</param>
public OperationStatus(TStatusType statusType, EventMessages eventMessages)
{
Entity = entity;
}
public TEntity Entity { get; private set; }
}
public class OperationStatus<TStatus>
where TStatus : struct
{
public OperationStatus(TStatus statusType, EventMessages eventMessages)
{
if (eventMessages == null) throw new ArgumentNullException("eventMessages");
if (eventMessages == null) throw new ArgumentNullException(nameof(eventMessages));
StatusType = statusType;
EventMessages = eventMessages;
}
public TStatus StatusType { get; internal set; }
public EventMessages EventMessages { get; private set; }
/// <summary>
/// Gets or sets the status of the operation.
/// </summary>
/// <remarks>May be internally updated during the operation, but should NOT be updated once the operation has completed.</remarks>
public TStatusType StatusType { get; internal set; }
/// <summary>
/// Gets the event messages produced by the operation.
/// </summary>
public EventMessages EventMessages { get; }
}
/// <summary>
/// The default operation status
/// Represents the status of a service operation that manages (processes, produces...) a value.
/// </summary>
public class OperationStatus : OperationStatus<OperationStatusType>
/// <typeparam name="TStatusType">The type of the status type.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
public class OperationStatus<TStatusType, TValue> : OperationStatus<TStatusType>
where TStatusType : struct
{
public OperationStatus(OperationStatusType statusType, EventMessages eventMessages) : base(statusType, eventMessages)
/// <summary>
/// Initializes a new instance of the <see cref="OperationStatus{TStatusType, TValue}"/> class with a status type and event messages.
/// </summary>
/// <param name="statusType">The status of the operation.</param>
/// <param name="eventMessages">Event messages produced by the operation.</param>
public OperationStatus(TStatusType statusType, EventMessages eventMessages)
: base(statusType, eventMessages)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="OperationStatus{TStatusType, TValue}"/> class with a status type, event messages and a value.
/// </summary>
/// <param name="statusType">The status of the operation.</param>
/// <param name="eventMessages">Event messages produced by the operation.</param>
/// <param name="value">The value managed by the operation.</param>
public OperationStatus(TStatusType statusType, EventMessages eventMessages, TValue value)
: base(statusType, eventMessages)
{
Value = value;
}
#region Static Helper methods
internal static Attempt<OperationStatus> Exception(EventMessages eventMessages, Exception ex)
{
eventMessages.Add(new EventMessage("", ex.Message, EventMessageType.Error));
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedExceptionThrown, eventMessages), ex);
}
internal static Attempt<OperationStatus> Cancelled(EventMessages eventMessages)
{
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, eventMessages));
}
internal static Attempt<OperationStatus> Success(EventMessages eventMessages)
{
return Attempt.Succeed(new OperationStatus(OperationStatusType.Success, eventMessages));
}
internal static Attempt<OperationStatus> NoOperation(EventMessages eventMessages)
{
return Attempt.Succeed(new OperationStatus(OperationStatusType.NoOperation, eventMessages));
}
#endregion
/// <summary>
/// Gets the value managed by the operation.
/// </summary>
public TValue Value { get; }
}
/// <summary>
/// Represents the default operation status.
/// </summary>
/// <remarks>Also provides static helper methods to create operation statuses.</remarks>
public class OperationStatus : OperationStatus<OperationStatusType>
{
/// <summary>
/// Initializes a new instance of the <see cref="OperationStatus"/> class with a status and event messages.
/// </summary>
/// <param name="statusType">The status of the operation.</param>
/// <param name="eventMessages">Event messages produced by the operation.</param>
public OperationStatus(OperationStatusType statusType, EventMessages eventMessages)
: base(statusType, eventMessages)
{ }
internal static class Attempt
{
/// <summary>
/// Creates a successful operation attempt.
/// </summary>
/// <param name="eventMessages">The event messages produced by the operation.</param>
/// <returns>A new attempt instance.</returns>
public static Attempt<OperationStatus> Succeed(EventMessages eventMessages)
{
return Core.Attempt.Succeed(new OperationStatus(OperationStatusType.Success, eventMessages));
}
public static Attempt<OperationStatus<OperationStatusType, TValue>> Succeed<TValue>(EventMessages eventMessages)
{
return Core.Attempt.Succeed(new OperationStatus<OperationStatusType, TValue>(OperationStatusType.Success, eventMessages));
}
public static Attempt<OperationStatus<OperationStatusType, TValue>> Succeed<TValue>(EventMessages eventMessages, TValue value)
{
return Core.Attempt.Succeed(new OperationStatus<OperationStatusType, TValue>(OperationStatusType.Success, eventMessages, value));
}
public static Attempt<OperationStatus<TStatusType>> Succeed<TStatusType>(TStatusType statusType, EventMessages eventMessages)
where TStatusType : struct
{
return Core.Attempt.Succeed(new OperationStatus<TStatusType>(statusType, eventMessages));
}
public static Attempt<OperationStatus<TStatusType, TValue>> Succeed<TStatusType, TValue>(TStatusType statusType, EventMessages eventMessages, TValue value)
where TStatusType : struct
{
return Core.Attempt.Succeed(new OperationStatus<TStatusType, TValue>(statusType, eventMessages, value));
}
/// <summary>
/// Creates a successful operation attempt indicating that nothing was done.
/// </summary>
/// <param name="eventMessages">The event messages produced by the operation.</param>
/// <returns>A new attempt instance.</returns>
public static Attempt<OperationStatus> NoOperation(EventMessages eventMessages)
{
return Core.Attempt.Succeed(new OperationStatus(OperationStatusType.NoOperation, eventMessages));
}
/// <summary>
/// Creates a failed operation attempt indicating that the operation has been cancelled.
/// </summary>
/// <param name="eventMessages">The event messages produced by the operation.</param>
/// <returns>A new attempt instance.</returns>
public static Attempt<OperationStatus> Cancel(EventMessages eventMessages)
{
return Core.Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, eventMessages));
}
public static Attempt<OperationStatus<OperationStatusType, TValue>> Cancel<TValue>(EventMessages eventMessages)
{
return Core.Attempt.Fail(new OperationStatus<OperationStatusType, TValue>(OperationStatusType.FailedCancelledByEvent, eventMessages));
}
public static Attempt<OperationStatus<OperationStatusType, TValue>> Cancel<TValue>(EventMessages eventMessages, TValue value)
{
return Core.Attempt.Fail(new OperationStatus<OperationStatusType, TValue>(OperationStatusType.FailedCancelledByEvent, eventMessages, value));
}
/// <summary>
/// Creates a failed operation attempt indicating that an exception was thrown during the operation.
/// </summary>
/// <param name="eventMessages">The event messages produced by the operation.</param>
/// <param name="exception">The exception that caused the operation to fail.</param>
/// <returns>A new attempt instance.</returns>
public static Attempt<OperationStatus> Fail(EventMessages eventMessages, Exception exception)
{
eventMessages.Add(new EventMessage("", exception.Message, EventMessageType.Error));
return Core.Attempt.Fail(new OperationStatus(OperationStatusType.FailedExceptionThrown, eventMessages), exception);
}
public static Attempt<OperationStatus<OperationStatusType, TValue>> Fail<TValue>(EventMessages eventMessages, Exception exception)
{
return Core.Attempt.Fail(new OperationStatus<OperationStatusType, TValue>(OperationStatusType.FailedExceptionThrown, eventMessages), exception);
}
public static Attempt<OperationStatus<TStatusType>> Fail<TStatusType>(TStatusType statusType, EventMessages eventMessages)
where TStatusType : struct
{
return Core.Attempt.Fail(new OperationStatus<TStatusType>(statusType, eventMessages));
}
public static Attempt<OperationStatus<TStatusType>> Fail<TStatusType>(TStatusType statusType, EventMessages eventMessages, Exception exception)
where TStatusType : struct
{
return Core.Attempt.Fail(new OperationStatus<TStatusType>(statusType, eventMessages), exception);
}
public static Attempt<OperationStatus<TStatusType, TValue>> Fail<TStatusType, TValue>(TStatusType statusType, EventMessages eventMessages)
where TStatusType : struct
{
return Core.Attempt.Fail(new OperationStatus<TStatusType, TValue>(statusType, eventMessages));
}
public static Attempt<OperationStatus<TStatusType, TValue>> Fail<TStatusType, TValue>(TStatusType statusType, EventMessages eventMessages, TValue value)
where TStatusType : struct
{
return Core.Attempt.Fail(new OperationStatus<TStatusType, TValue>(statusType, eventMessages, value));
}
public static Attempt<OperationStatus<TStatusType, TValue>> Fail<TStatusType, TValue>(TStatusType statusType, EventMessages eventMessages, Exception exception)
where TStatusType : struct
{
return Core.Attempt.Fail(new OperationStatus<TStatusType, TValue>(statusType, eventMessages), exception);
}
public static Attempt<OperationStatus<TStatusType, TValue>> Fail<TStatusType, TValue>(TStatusType statusType, EventMessages eventMessages, TValue value, Exception exception)
where TStatusType : struct
{
return Core.Attempt.Fail(new OperationStatus<TStatusType, TValue>(statusType, eventMessages, value), exception);
}
public static Attempt<OperationStatus<OperationStatusType, TValue>> Cannot<TValue>(EventMessages eventMessages)
{
return Core.Attempt.Fail(new OperationStatus<OperationStatusType, TValue>(OperationStatusType.FailedCannot, eventMessages));
}
}
}
}
@@ -1,33 +1,70 @@
namespace Umbraco.Core.Services
{
/// <summary>
/// A status type of the result of saving an item
/// A value indicating the result of an operation.
/// </summary>
/// <remarks>
/// Anything less than 10 = Success!
/// <remarks>Do NOT compare against a hard-coded numeric value to check for success or failure,
/// but instead use the IsSuccess() extension method defined below - which should be the unique
/// place where the numeric test should take place.
/// </remarks>
public enum OperationStatusType
{
/// <summary>
/// The saving was successful.
/// The operation was successful.
/// </summary>
Success = 0,
// Values below this value indicate a success, values above it indicate a failure.
// This value is considered a failure.
//Reserved = 10,
/// <summary>
/// The saving has been cancelled by a 3rd party add-in
/// The operation could not complete because of invalid preconditions (eg creating a reference
/// to an item that does not exist).
/// </summary>
FailedCannot = 12,
/// <summary>
/// The operation has been cancelled by an event handler.
/// </summary>
FailedCancelledByEvent = 14,
/// <summary>
/// Failed, an exception was thrown/handled
/// The operation could not complete due to an exception.
/// </summary>
FailedExceptionThrown = 15,
/// <summary>
/// When no operation is executed because it was not needed (i.e. deleting an item that doesn't exist)
/// No operation has been executed because it was not needed (eg deleting an item that doesn't exist).
/// </summary>
NoOperation = 100,
//TODO: In the future, we might need to add more operations statuses, potentially like 'FailedByPermissions', etc...
}
/// <summary>
/// Provides extension methods for the <see cref="OperationStatusType"/> enum.
/// </summary>
public static class OperationStatusTypeExtensions
{
/// <summary>
/// Gets a value indicating whether the status indicates a success.
/// </summary>
/// <param name="status">The status.</param>
/// <returns>A value indicating whether the status indicates a success.</returns>
public static bool IsSuccess(this OperationStatusType status)
{
return (int) status < 10;
}
/// <summary>
/// Gets a value indicating whether the status indicates a failure.
/// </summary>
/// <param name="status">The status.</param>
/// <returns>A value indicating whether the status indicates a failure.</returns>
public static bool IsFailure(this OperationStatusType status)
{
return (int) status >= 10;
}
}
}
+16 -15
View File
@@ -407,7 +407,7 @@ namespace Umbraco.Core.Services
var alias = documentType.Element("Info").Element("Alias").Value;
if (_importedContentTypes.ContainsKey(alias) == false)
{
var contentType = _contentTypeService.GetContentType(alias);
var contentType = _contentTypeService.Get(alias);
_importedContentTypes.Add(alias, contentType == null
? CreateContentTypeFromXml(documentType)
: UpdateContentTypeFromXml(documentType, contentType));
@@ -470,18 +470,18 @@ namespace Umbraco.Core.Services
var folders = foldersAttribute.Value.Split('/');
var rootFolder = HttpUtility.UrlDecode(folders[0]);
//level 1 = root level folders, there can only be one with the same name
var current = _contentTypeService.GetContentTypeContainers(rootFolder, 1).FirstOrDefault();
var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault();
if (current == null)
{
var tryCreateFolder = _contentTypeService.CreateContentTypeContainer(-1, rootFolder);
var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder);
if (tryCreateFolder == false)
{
_logger.Error<PackagingService>("Could not create folder: " + rootFolder, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
var rootFolderId = tryCreateFolder.Result.Entity.Id;
current = _contentTypeService.GetContentTypeContainer(rootFolderId);
var rootFolderId = tryCreateFolder.Result.Value.Id;
current = _contentTypeService.GetContainer(rootFolderId);
}
importedFolders.Add(alias, current.Id);
@@ -505,16 +505,16 @@ namespace Umbraco.Core.Services
if (found)
{
var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id;
return _contentTypeService.GetContentTypeContainer(containerId);
return _contentTypeService.GetContainer(containerId);
}
var tryCreateFolder = _contentTypeService.CreateContentTypeContainer(current.Id, folderName);
var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName);
if (tryCreateFolder == false)
{
_logger.Error<PackagingService>("Could not create folder: " + folderName, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
return _contentTypeService.GetContentTypeContainer(tryCreateFolder.Result.Entity.Id);
return _contentTypeService.GetContainer(tryCreateFolder.Result.Value.Id);
}
private IContentType CreateContentTypeFromXml(XElement documentType)
@@ -529,7 +529,7 @@ namespace Umbraco.Core.Services
var masterAlias = masterElement.Value;
parent = _importedContentTypes.ContainsKey(masterAlias)
? _importedContentTypes[masterAlias]
: _contentTypeService.GetContentType(masterAlias);
: _contentTypeService.Get(masterAlias);
}
var alias = infoElement.Element("Alias").Value;
@@ -570,7 +570,7 @@ namespace Umbraco.Core.Services
var masterAlias = masterElement.Value;
IContentType parent = _importedContentTypes.ContainsKey(masterAlias)
? _importedContentTypes[masterAlias]
: _contentTypeService.GetContentType(masterAlias);
: _contentTypeService.Get(masterAlias);
contentType.SetLazyParentId(new Lazy<int>(() => parent.Id));
}
@@ -587,7 +587,7 @@ namespace Umbraco.Core.Services
var compositionAlias = composition.Value;
var compositionContentType = _importedContentTypes.ContainsKey(compositionAlias)
? _importedContentTypes[compositionAlias]
: _contentTypeService.GetContentType(compositionAlias);
: _contentTypeService.Get(compositionAlias);
var added = contentType.AddContentType(compositionContentType);
}
}
@@ -791,14 +791,15 @@ namespace Umbraco.Core.Services
if (types.Any() == false)
throw new Exception(
string.Format("No ContentType matching the passed in Alias: '{0}' was found",
contentTypeAlias));
contentTypeAlias)); // causes rollback
var contentType = types.FirstOrDefault();
if (contentType == null)
throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null",
contentTypeAlias));
contentTypeAlias)); // causes rollback
uow.Complete();
return contentType;
}
}
@@ -953,7 +954,7 @@ namespace Umbraco.Core.Services
_logger.Error<PackagingService>("Could not create folder: " + rootFolder, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id);
current = _dataTypeService.GetContainer(tryCreateFolder.Result.Value.Id);
}
importedFolders.Add(name, current.Id);
@@ -986,7 +987,7 @@ namespace Umbraco.Core.Services
_logger.Error<PackagingService>("Could not create folder: " + folderName, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id);
return _dataTypeService.GetContainer(tryCreateFolder.Result.Value.Id);
}
private void SavePrevaluesFromXml(List<IDataTypeDefinition> dataTypes, IEnumerable<XElement> dataTypeElements)
@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -28,7 +25,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<IPublicAccessRepository>();
return repo.GetAll();
var entries = repo.GetAll();
uow.Complete();
return entries;
}
}
@@ -76,6 +75,7 @@ namespace Umbraco.Core.Services
//This will retrieve from cache!
var entries = repo.GetAll().ToArray();
uow.Complete();
foreach (var id in ids)
{
@@ -116,7 +116,7 @@ namespace Umbraco.Core.Services
/// <param name="ruleType"></param>
/// <param name="ruleValue"></param>
/// <returns></returns>
public Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>> AddRule(IContent content, string ruleType, string ruleValue)
public Attempt<OperationStatus<OperationStatusType, PublicAccessEntry>> AddRule(IContent content, string ruleType, string ruleValue)
{
var evtMsgs = EventMessagesFactory.Get();
PublicAccessEntry entry;
@@ -126,7 +126,7 @@ namespace Umbraco.Core.Services
entry = repo.GetAll().FirstOrDefault(x => x.ProtectedNodeId == content.Id);
if (entry == null)
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Fail();
return OperationStatus.Attempt.Cannot<PublicAccessEntry>(evtMsgs); // causes rollback
var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue);
if (existingRule == null)
@@ -136,17 +136,11 @@ namespace Umbraco.Core.Services
else
{
//If they are both the same already then there's nothing to update, exit
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Succeed(
new OperationStatus<PublicAccessEntry, OperationStatusType>(entry, OperationStatusType.Success, evtMsgs));
return OperationStatus.Attempt.Succeed(evtMsgs, entry);
}
if (Saving.IsRaisedEventCancelled(
new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs),
this))
{
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Fail(
new OperationStatus<PublicAccessEntry, OperationStatusType>(entry, OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs), this))
return OperationStatus.Attempt.Cancel(evtMsgs, entry); // causes rollback
repo.AddOrUpdate(entry);
@@ -154,8 +148,7 @@ namespace Umbraco.Core.Services
}
Saved.RaiseEvent(new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return Attempt<OperationStatus<PublicAccessEntry, OperationStatusType>>.Succeed(
new OperationStatus<PublicAccessEntry, OperationStatusType>(entry, OperationStatusType.Success, evtMsgs));
return OperationStatus.Attempt.Succeed(evtMsgs, entry);
}
/// <summary>
@@ -173,22 +166,22 @@ namespace Umbraco.Core.Services
var repo = uow.CreateRepository<IPublicAccessRepository>();
entry = repo.GetAll().FirstOrDefault(x => x.ProtectedNodeId == content.Id);
if (entry == null) return Attempt<OperationStatus>.Fail();
if (entry == null) return Attempt<OperationStatus>.Fail(); // causes rollback
var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue);
if (existingRule == null) return Attempt<OperationStatus>.Fail();
if (existingRule == null) return Attempt<OperationStatus>.Fail(); // causes rollback
entry.RemoveRule(existingRule);
if (Saving.IsRaisedEventCancelled(new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs), this))
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs); // causes rollback
repo.AddOrUpdate(entry);
uow.Complete();
}
Saved.RaiseEvent(new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
@@ -202,7 +195,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
using (var uow = UowProvider.CreateUnitOfWork())
@@ -213,7 +206,7 @@ namespace Umbraco.Core.Services
}
Saved.RaiseEvent(new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
@@ -227,7 +220,7 @@ namespace Umbraco.Core.Services
new DeleteEventArgs<PublicAccessEntry>(entry, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
return OperationStatus.Attempt.Cancel(evtMsgs);
}
using (var uow = UowProvider.CreateUnitOfWork())
@@ -238,7 +231,7 @@ namespace Umbraco.Core.Services
}
Deleted.RaiseEvent(new DeleteEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
return OperationStatus.Attempt.Succeed(evtMsgs);
}
/// <summary>
+53 -16
View File
@@ -33,7 +33,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IRelationRepository>();
return repository.Get(id);
var relation = repository.Get(id);
uow.Complete();
return relation;
}
}
@@ -47,7 +49,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IRelationTypeRepository>();
return repository.Get(id);
var type = repository.Get(id);
uow.Complete();
return type;
}
}
@@ -62,7 +66,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationTypeRepository>();
var query = repository.Query.Where(x => x.Alias == alias);
return repository.GetByQuery(query).FirstOrDefault();
var type = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return type;
}
}
@@ -76,7 +82,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IRelationRepository>();
return repository.GetAll(ids);
var relations = repository.GetAll(ids);
uow.Complete();
return relations;
}
}
@@ -101,7 +109,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.RelationTypeId == relationTypeId);
return repository.GetByQuery(query);
var relations = repository.GetByQuery(query);
uow.Complete();
return relations;
}
}
@@ -115,7 +125,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IRelationTypeRepository>();
return repository.GetAll(ids);
var types = repository.GetAll(ids);
uow.Complete();
return types;
}
}
@@ -130,7 +142,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.ParentId == id);
return repository.GetByQuery(query);
var relations = repository.GetByQuery(query);
uow.Complete();
return relations;
}
}
@@ -166,7 +180,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.ChildId == id);
return repository.GetByQuery(query);
var relations = repository.GetByQuery(query);
uow.Complete();
return relations;
}
}
@@ -203,7 +219,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.ChildId == id || x.ParentId == id);
return repository.GetByQuery(query);
var relations = repository.GetByQuery(query);
uow.Complete();
return relations;
}
}
@@ -215,11 +233,17 @@ namespace Umbraco.Core.Services
var rtQuery = repository.Query.Where(x => x.Alias == relationTypeAlias);
var relationType = repository.GetByQuery(rtQuery).FirstOrDefault();
if (relationType == null) return Enumerable.Empty<IRelation>();
if (relationType == null)
{
uow.Complete();
return Enumerable.Empty<IRelation>();
}
var relationRepo = uow.CreateRepository<IRelationRepository>();
var query = relationRepo.Query.Where(x => (x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id);
return relationRepo.GetByQuery(query);
var relations = relationRepo.GetByQuery(query);
uow.Complete();
return relations;
}
}
@@ -240,6 +264,7 @@ namespace Umbraco.Core.Services
{
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
}
uow.Complete();
}
if (relationTypeIds == null)
@@ -265,6 +290,7 @@ namespace Umbraco.Core.Services
{
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
}
uow.Complete();
}
if (relationTypeIds == null)
@@ -284,7 +310,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.RelationTypeId == relationTypeId);
return repository.GetByQuery(query);
var relations = repository.GetByQuery(query);
uow.Complete();
return relations;
}
}
@@ -449,7 +477,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.RelationTypeId == relationType.Id);
return repository.GetByQuery(query).Any();
var has = repository.GetByQuery(query).Any();
uow.Complete();
return has;
}
}
@@ -464,7 +494,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.ParentId == id || x.ChildId == id);
return repository.GetByQuery(query).Any();
var isRelated = repository.GetByQuery(query).Any();
uow.Complete();
return isRelated;
}
}
@@ -480,7 +512,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.ParentId == parentId && x.ChildId == childId);
return repository.GetByQuery(query).Any();
var areRelated = repository.GetByQuery(query).Any();
uow.Complete();
return areRelated;
}
}
@@ -514,7 +548,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IRelationRepository>();
var query = repository.Query.Where(x => x.ParentId == parentId && x.ChildId == childId && x.RelationTypeId == relationType.Id);
return repository.GetByQuery(query).Any();
var areRelated = repository.GetByQuery(query).Any();
uow.Complete();
return areRelated;
}
}
@@ -653,6 +689,7 @@ namespace Umbraco.Core.Services
var query = repository.Query.Where(x => x.RelationTypeId == id);
relations.AddRange(repository.GetByQuery(query).ToList());
}
uow.Complete();
}
return relations;
}
@@ -42,8 +42,8 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.Servers);
var repo = uow.CreateRepository<IServerRegistrationRepository>();
repo.WriteLockServers();
((ServerRegistrationRepository) repo).ReloadCache(); // ensure we have up-to-date cache
@@ -103,8 +103,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.Servers);
var repo = uow.CreateRepository<IServerRegistrationRepository>();
repo.WriteLockServers();
((ServerRegistrationRepository) repo).ReloadCache(); // ensure we have up-to-date cache
@@ -125,10 +125,10 @@ namespace Umbraco.Core.Services
{
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.Servers);
var repo = uow.CreateRepository<IServerRegistrationRepository>();
repo.WriteLockServers();
repo.DeactiveStaleServers(staleTimeout);
uow.Complete();
}
}
@@ -165,10 +165,11 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.Servers);
var repo = uow.CreateRepository<IServerRegistrationRepository>();
repo.ReadLockServers();
return repo.GetAll().Where(x => x.IsActive).ToArray(); // fast, cached
var servers = repo.GetAll().Where(x => x.IsActive).ToArray(); // fast, cached
uow.Complete();
return servers;
}
}
+10 -1
View File
@@ -19,6 +19,7 @@ namespace Umbraco.Core.Services
private readonly Lazy<IMemberService> _memberService;
private readonly Lazy<IMediaService> _mediaService;
private readonly Lazy<IContentTypeService> _contentTypeService;
private readonly Lazy<IMediaTypeService> _mediaTypeService;
private readonly Lazy<IDataTypeService> _dataTypeService;
private readonly Lazy<IFileService> _fileService;
private readonly Lazy<ILocalizationService> _localizationService;
@@ -38,7 +39,7 @@ namespace Umbraco.Core.Services
/// Initializes a new instance of the <see cref="ServiceContext"/> class with lazy services.
/// </summary>
/// <remarks>Used by IoC. Note that LightInject will favor lazy args when picking a constructor.</remarks>
public ServiceContext(Lazy<IMigrationEntryService> migrationEntryService, Lazy<IPublicAccessService> publicAccessService, Lazy<ITaskService> taskService, Lazy<IDomainService> domainService, Lazy<IAuditService> auditService, Lazy<ILocalizedTextService> localizedTextService, Lazy<ITagService> tagService, Lazy<IContentService> contentService, Lazy<IUserService> userService, Lazy<IMemberService> memberService, Lazy<IMediaService> mediaService, Lazy<IContentTypeService> contentTypeService, Lazy<IDataTypeService> dataTypeService, Lazy<IFileService> fileService, Lazy<ILocalizationService> localizationService, Lazy<IPackagingService> packagingService, Lazy<IServerRegistrationService> serverRegistrationService, Lazy<IEntityService> entityService, Lazy<IRelationService> relationService, Lazy<IApplicationTreeService> treeService, Lazy<ISectionService> sectionService, Lazy<IMacroService> macroService, Lazy<IMemberTypeService> memberTypeService, Lazy<IMemberGroupService> memberGroupService, Lazy<INotificationService> notificationService, Lazy<IExternalLoginService> externalLoginService)
public ServiceContext(Lazy<IMigrationEntryService> migrationEntryService, Lazy<IPublicAccessService> publicAccessService, Lazy<ITaskService> taskService, Lazy<IDomainService> domainService, Lazy<IAuditService> auditService, Lazy<ILocalizedTextService> localizedTextService, Lazy<ITagService> tagService, Lazy<IContentService> contentService, Lazy<IUserService> userService, Lazy<IMemberService> memberService, Lazy<IMediaService> mediaService, Lazy<IContentTypeService> contentTypeService, Lazy<IMediaTypeService> mediaTypeService, Lazy<IDataTypeService> dataTypeService, Lazy<IFileService> fileService, Lazy<ILocalizationService> localizationService, Lazy<IPackagingService> packagingService, Lazy<IServerRegistrationService> serverRegistrationService, Lazy<IEntityService> entityService, Lazy<IRelationService> relationService, Lazy<IApplicationTreeService> treeService, Lazy<ISectionService> sectionService, Lazy<IMacroService> macroService, Lazy<IMemberTypeService> memberTypeService, Lazy<IMemberGroupService> memberGroupService, Lazy<INotificationService> notificationService, Lazy<IExternalLoginService> externalLoginService)
{
_migrationEntryService = migrationEntryService;
_publicAccessService = publicAccessService;
@@ -52,6 +53,7 @@ namespace Umbraco.Core.Services
_memberService = memberService;
_mediaService = mediaService;
_contentTypeService = contentTypeService;
_mediaTypeService = mediaTypeService;
_dataTypeService = dataTypeService;
_fileService = fileService;
_localizationService = localizationService;
@@ -76,6 +78,7 @@ namespace Umbraco.Core.Services
IContentService contentService = null,
IMediaService mediaService = null,
IContentTypeService contentTypeService = null,
IMediaTypeService mediaTypeService = null,
IDataTypeService dataTypeService = null,
IFileService fileService = null,
ILocalizationService localizationService = null,
@@ -109,6 +112,7 @@ namespace Umbraco.Core.Services
if (contentService != null) _contentService = new Lazy<IContentService>(() => contentService);
if (mediaService != null) _mediaService = new Lazy<IMediaService>(() => mediaService);
if (contentTypeService != null) _contentTypeService = new Lazy<IContentTypeService>(() => contentTypeService);
if (mediaTypeService != null) _mediaTypeService = new Lazy<IMediaTypeService>(() => mediaTypeService);
if (dataTypeService != null) _dataTypeService = new Lazy<IDataTypeService>(() => dataTypeService);
if (fileService != null) _fileService = new Lazy<IFileService>(() => fileService);
if (localizationService != null) _localizationService = new Lazy<ILocalizationService>(() => localizationService);
@@ -198,6 +202,11 @@ namespace Umbraco.Core.Services
/// </summary>
public IContentTypeService ContentTypeService => _contentTypeService.Value;
/// <summary>
/// Gets the <see cref="IMediaTypeService"/>
/// </summary>
public IMediaTypeService MediaTypeService => _mediaTypeService.Value;
/// <summary>
/// Gets the <see cref="IDataTypeService"/>
/// </summary>
+48 -16
View File
@@ -29,7 +29,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntityById(id);
var entity = repository.GetTaggedEntityById(id);
uow.Complete();
return entity;
}
}
@@ -38,7 +40,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntityByKey(key);
var entity = repository.GetTaggedEntityByKey(key);
uow.Complete();
return entity;
}
}
@@ -53,7 +57,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntitiesByTagGroup(TaggableObjectTypes.Content, tagGroup);
var entities = repository.GetTaggedEntitiesByTagGroup(TaggableObjectTypes.Content, tagGroup);
uow.Complete();
return entities;
}
}
@@ -69,7 +75,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Content, tag, tagGroup);
var entities = repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Content, tag, tagGroup);
uow.Complete();
return entities;
}
}
@@ -84,7 +92,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntitiesByTagGroup(TaggableObjectTypes.Media, tagGroup);
var entities = repository.GetTaggedEntitiesByTagGroup(TaggableObjectTypes.Media, tagGroup);
uow.Complete();
return entities;
}
}
@@ -100,7 +110,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Media, tag, tagGroup);
var entities = repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Media, tag, tagGroup);
uow.Complete();
return entities;
}
}
@@ -115,7 +127,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntitiesByTagGroup(TaggableObjectTypes.Member, tagGroup);
var entities = repository.GetTaggedEntitiesByTagGroup(TaggableObjectTypes.Member, tagGroup);
uow.Complete();
return entities;
}
}
@@ -131,7 +145,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Member, tag, tagGroup);
var entities = repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Member, tag, tagGroup);
uow.Complete();
return entities;
}
}
@@ -145,7 +161,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForEntityType(TaggableObjectTypes.All, tagGroup);
var tags = repository.GetTagsForEntityType(TaggableObjectTypes.All, tagGroup);
uow.Complete();
return tags;
}
}
@@ -161,7 +179,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForEntityType(TaggableObjectTypes.Content, tagGroup);
var tags = repository.GetTagsForEntityType(TaggableObjectTypes.Content, tagGroup);
uow.Complete();
return tags;
}
}
@@ -177,7 +197,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForEntityType(TaggableObjectTypes.Media, tagGroup);
var tags = repository.GetTagsForEntityType(TaggableObjectTypes.Media, tagGroup);
uow.Complete();
return tags;
}
}
@@ -193,7 +215,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForEntityType(TaggableObjectTypes.Member, tagGroup);
var tags = repository.GetTagsForEntityType(TaggableObjectTypes.Member, tagGroup);
uow.Complete();
return tags;
}
}
@@ -211,7 +235,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForProperty(contentId, propertyTypeAlias, tagGroup);
var tags = repository.GetTagsForProperty(contentId, propertyTypeAlias, tagGroup);
uow.Complete();
return tags;
}
}
@@ -228,7 +254,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForEntity(contentId, tagGroup);
var tags = repository.GetTagsForEntity(contentId, tagGroup);
uow.Complete();
return tags;
}
}
@@ -246,7 +274,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForProperty(contentId, propertyTypeAlias, tagGroup);
var tags = repository.GetTagsForProperty(contentId, propertyTypeAlias, tagGroup);
uow.Complete();
return tags;
}
}
@@ -263,7 +293,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<ITagRepository>();
return repository.GetTagsForEntity(contentId, tagGroup);
var tags = repository.GetTagsForEntity(contentId, tagGroup);
uow.Complete();
return tags;
}
}
}
+15 -5
View File
@@ -23,7 +23,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<ITaskTypeRepository>();
return repo.GetByQuery(repo.Query.Where(type => type.Alias == taskTypeAlias)).FirstOrDefault();
var type = repo.GetByQuery(repo.Query.Where(x => x.Alias == taskTypeAlias)).FirstOrDefault();
uow.Complete();
return type;
}
}
@@ -32,7 +34,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<ITaskTypeRepository>();
return repo.Get(id);
var type = repo.Get(id);
uow.Complete();
return type;
}
}
@@ -61,7 +65,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<ITaskTypeRepository>();
return repo.GetAll();
var types = repo.GetAll();
uow.Complete();
return types;
}
}
@@ -71,7 +77,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<ITaskRepository>();
return repo.GetTasks(itemId, assignedUser, ownerUser, taskTypeAlias);
var tasks = repo.GetTasks(itemId, assignedUser, ownerUser, taskTypeAlias);
uow.Complete();
return tasks;
}
}
@@ -104,7 +112,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repo = uow.CreateRepository<ITaskRepository>();
return repo.Get(id);
var task = repo.Get(id);
uow.Complete();
return task;
}
}
}
+50 -36
View File
@@ -50,9 +50,11 @@ namespace Umbraco.Core.Services
if (types.Any() == false)
{
throw new EntityNotFoundException("No member types could be resolved");
throw new EntityNotFoundException("No member types could be resolved"); // causes rollback
}
uow.Complete();
if (types.InvariantContains("writer"))
{
return types.First(x => x.InvariantEquals("writer"));
@@ -78,7 +80,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserRepository>();
return repository.Exists(username);
var exists = repository.Exists(username);
uow.Complete();
return exists;
}
}
@@ -136,7 +140,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IUserRepository>();
var loginExists = uow.Database.ExecuteScalar<int>("SELECT COUNT(id) FROM umbracoUser WHERE userLogin = @Login", new { Login = username }) != 0;
if (loginExists)
throw new ArgumentException("Login already exists");
throw new ArgumentException("Login already exists"); // causes rollback
user = new User(userType)
{
@@ -177,8 +181,8 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserRepository>();
var user = repository.Get((int)id);
var user = repository.Get(id);
uow.Complete();
return user;
}
}
@@ -211,7 +215,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IUserRepository>();
var query = repository.Query.Where(x => x.Email.Equals(email));
var user = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return user;
}
}
@@ -228,6 +232,7 @@ namespace Umbraco.Core.Services
var repository = uow.CreateRepository<IUserRepository>();
var query = repository.Query.Where(x => x.Username.Equals(username));
var user = repository.GetByQuery(query).FirstOrDefault();
uow.Complete();
return user;
}
}
@@ -322,9 +327,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserRepository>();
repository.AddOrUpdate(entity);
try
{
repository.AddOrUpdate(entity);
uow.Complete();
}
catch (DbException ex)
@@ -407,7 +412,9 @@ namespace Umbraco.Core.Services
throw new ArgumentOutOfRangeException("matchType");
}
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, dto => dto.Email);
var users = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, dto => dto.Email);
uow.Complete();
return users;
}
}
@@ -448,7 +455,9 @@ namespace Umbraco.Core.Services
throw new ArgumentOutOfRangeException("matchType");
}
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, dto => dto.Username);
var users = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, dto => dto.Username);
uow.Complete();
return users;
}
}
@@ -473,7 +482,7 @@ namespace Umbraco.Core.Services
{
case MemberCountType.All:
query = repository.Query;
return repository.Count(query);
break;
case MemberCountType.Online:
throw new NotImplementedException();
//var fromDate = DateTime.Now.AddMinutes(-Membership.UserIsOnlineTimeWindow);
@@ -484,18 +493,18 @@ namespace Umbraco.Core.Services
// ((Member)x).DateTimePropertyValue > fromDate);
//return repository.GetCountByQuery(query);
case MemberCountType.LockedOut:
query =
repository.Query.Where(
x => x.IsLockedOut);
return repository.GetCountByQuery(query);
query = repository.Query.Where(x => x.IsLockedOut);
break;
case MemberCountType.Approved:
query =
repository.Query.Where(
x => x.IsApproved);
return repository.GetCountByQuery(query);
query = repository.Query.Where(x => x.IsApproved);
break;
default:
throw new ArgumentOutOfRangeException("countType");
}
var count = repository.GetCountByQuery(query);
uow.Complete();
return count;
}
}
@@ -511,7 +520,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserRepository>();
return repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, member => member.Username);
var users = repository.GetPagedResultsByQuery(null, pageIndex, pageSize, out totalRecords, member => member.Username);
uow.Complete();
return users;
}
}
@@ -551,7 +562,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserRepository>();
return repository.Get(id);
var user = repository.Get(id);
uow.Complete();
return user;
}
}
@@ -568,6 +581,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IUserRepository>();
repository.ReplaceUserPermissions(userId, permissions, entityIds);
uow.Complete();
}
}
@@ -583,6 +597,7 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IUserRepository>();
repository.AssignUserPermission(userId, permission, entityIds);
uow.Complete();
}
}
@@ -596,7 +611,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserTypeRepository>();
return repository.GetAll(ids);
var types = repository.GetAll(ids);
uow.Complete();
return types;
}
}
@@ -611,8 +628,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IUserTypeRepository>();
var query = repository.QueryFactory.Create<IUserType>().Where(x => x.Alias == alias);
var contents = repository.GetByQuery(query);
return contents.SingleOrDefault();
var type = repository.GetByQuery(query).SingleOrDefault();
uow.Complete();
return type;
}
}
@@ -626,7 +644,9 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.CreateUnitOfWork())
{
var repository = uow.CreateRepository<IUserTypeRepository>();
return repository.Get(id);
var type = repository.Get(id);
uow.Complete();
return type;
}
}
@@ -641,8 +661,9 @@ namespace Umbraco.Core.Services
{
var repository = uow.CreateRepository<IUserTypeRepository>();
var query = repository.QueryFactory.Create<IUserType>().Where(x => x.Name == name);
var contents = repository.GetByQuery(query);
return contents.SingleOrDefault();
var type = repository.GetByQuery(query).SingleOrDefault();
uow.Complete();
return type;
}
}
@@ -763,23 +784,16 @@ namespace Umbraco.Core.Services
if (id == -1 && user.DefaultPermissions.Any() == false)
{
// exception to everything. If default cruds is empty and we're on root node; allow browse of root node
result.Add(
new EntityPermission(
user.Id,
id,
user.DefaultPermissions.ToArray()));
result.Add(new EntityPermission(user.Id, id, user.DefaultPermissions.ToArray()));
}
else
{
//use the user's user type permissions
result.Add(
new EntityPermission(
user.Id,
id,
user.DefaultPermissions.ToArray()));
result.Add(new EntityPermission(user.Id, id, user.DefaultPermissions.ToArray()));
}
}
uow.Complete();
return result;
}
}
+11 -4
View File
@@ -147,7 +147,7 @@
<Compile Include="ApplicationEventHandler.cs" />
<Compile Include="AssemblyExtensions.cs" />
<Compile Include="AsyncLock.cs" />
<Compile Include="Attempt{T}.cs" />
<Compile Include="AttemptOfT.cs" />
<Compile Include="CacheHelper.cs" />
<Compile Include="Cache\CacheKeys.cs" />
<Compile Include="Cache\CacheProviderExtensions.cs" />
@@ -356,6 +356,7 @@
<Compile Include="Models\ContentTypeAvailableCompositionsResult.cs" />
<Compile Include="Models\ContentTypeAvailableCompositionsResults.cs" />
<Compile Include="Models\EntityContainer.cs" />
<Compile Include="Models\Rdbms\LockDto.cs" />
<Compile Include="ObjectResolution\ContainerLazyManyObjectsResolver.cs" />
<Compile Include="ObjectResolution\ContainerManyObjectsResolver.cs" />
<Compile Include="Models\Identity\IdentityModelMappings.cs" />
@@ -378,10 +379,13 @@
<Compile Include="Models\Rdbms\AccessDto.cs" />
<Compile Include="ObjectResolution\ContainerSingleObjectResolver.cs" />
<Compile Include="Persistence\Constants-DbProviderNames.cs" />
<Compile Include="Persistence\Constants-Locks.cs" />
<Compile Include="Persistence\FaultHandling\RetryDbConnection.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Persistence\IUmbracoDatabaseConfig.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionEight\AddLockObjects.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionEight\AddLockTable.cs" />
<Compile Include="Persistence\UnitOfWorkExtensions.cs" />
<Compile Include="Persistence\Mappers\AuditItemMapper.cs" />
<Compile Include="Persistence\Mappers\IMappingResolver.cs" />
@@ -471,7 +475,7 @@
<Compile Include="Persistence\Repositories\Interfaces\IMediaTypeContainerRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\INotificationsRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IAuditRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IContentTypeCompositionRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IContentTypeRepositoryBase.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IDomainRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IExternalLoginRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IMigrationEntryRepository.cs" />
@@ -508,6 +512,7 @@
<Compile Include="Security\EmailService.cs" />
<Compile Include="SemVersionExtensions.cs" />
<Compile Include="Serialization\NoTypeConverterJsonConverter.cs" />
<Compile Include="ServiceContextExtensions.cs" />
<Compile Include="ServiceProviderExtensions.cs" />
<Compile Include="IO\ResizedImage.cs" />
<Compile Include="IO\UmbracoMediaFile.cs" />
@@ -524,14 +529,17 @@
<Compile Include="Services\DomainService.cs" />
<Compile Include="Services\ExternalLoginService.cs" />
<Compile Include="Services\IAuditService.cs" />
<Compile Include="Services\IContentTypeServiceBase.cs" />
<Compile Include="Services\IDomainService.cs" />
<Compile Include="Services\IExternalLoginService.cs" />
<Compile Include="Services\IMediaTypeService.cs" />
<Compile Include="Services\IMigrationEntryService.cs" />
<Compile Include="Services\IPublicAccessService.cs" />
<Compile Include="Services\IServerRegistrationService.cs" />
<Compile Include="Services\ITaskService.cs" />
<Compile Include="Services\LocalizedTextServiceSupplementaryFileSource.cs" />
<Compile Include="Models\MediaExtensions.cs" />
<Compile Include="Services\MediaTypeService.cs" />
<Compile Include="Services\MigrationEntryService.cs" />
<Compile Include="Services\MoveOperationStatusType.cs" />
<Compile Include="Services\PublicAccessService.cs" />
@@ -996,7 +1004,7 @@
<Compile Include="Persistence\Querying\Query.cs" />
<Compile Include="Persistence\Querying\SqlTranslator.cs" />
<Compile Include="Persistence\Repositories\ContentRepository.cs" />
<Compile Include="Persistence\Repositories\ContentTypeBaseRepository.cs" />
<Compile Include="Persistence\Repositories\ContentTypeRepositoryBase.cs" />
<Compile Include="Persistence\Repositories\ContentTypeRepository.cs" />
<Compile Include="Persistence\Repositories\DataTypeDefinitionRepository.cs" />
<Compile Include="Persistence\Repositories\DictionaryRepository.cs" />
@@ -1196,7 +1204,6 @@
<Compile Include="PropertyEditors\ValueConverters\YesNoValueConverter.cs" />
<Compile Include="Publishing\BasePublishingStrategy.cs" />
<Compile Include="Publishing\IPublishingStrategy.cs" />
<Compile Include="Publishing\PublishingStrategy.cs" />
<Compile Include="Publishing\PublishStatus.cs" />
<Compile Include="Publishing\PublishStatusType.cs" />
<Compile Include="Dynamics\QueryableExtensions.cs" />
+574 -109
View File
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
@@ -9,69 +10,167 @@ namespace Umbraco.Tests.Models
[TestFixture]
public class ContentExtensionsTests : BaseUmbracoConfigurationTest
{
#region RequiresSaving
// when Published...
[Test]
public void Should_Persist_Values_When_Saving_After_Publishing_But_No_Data_Changed()
public void RequireSaving_PublishedAndThatsAll_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
//no version will be created if no data is changed
content.Properties.First().Value = "hello world";
content.ChangePublishedState(PublishedState.Saved);
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void Should_Not_Persist_Values_When_Saving_After_Publishing_But_No_Data_Changed()
public void RequireSaving_PublishedAndSavingAndNothingChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Saving); // saving
content.ChangePublishedState(PublishedState.Saved);
Assert.IsFalse(content.RequiresSaving());
}
[Test]
public void Should_Create_New_Version_When_Publishing()
public void RequireSaving_PublishedAndSavingAndUserPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.Properties.First().Value = "hello world"; // change data
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void RequireSaving_PublishedAndSavingAndContentPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ReleaseDate = DateTime.Now; // change data
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void RequireSaving_PublishedAndUnpublishing_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void RequireSaving_PublishedAndPublishingAndNothingChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Publishing); // publishing
Assert.IsTrue(content.RequiresSaving());
}
// when Unpublished...
[Test]
public void RequireSaving_UnpublishedAndSavingAndNothingChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
Assert.IsTrue(content.ShouldCreateNewVersion());
Assert.IsFalse(content.RequiresSaving());
}
[Test]
public void Should_Create_New_Version_When_Saving_After_Publishing()
public void RequireSaving_UnpublishedAndSavingAndUserPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
content.ResetDirtyProperties(false);
//no version will be created if no data is changed
content.Properties.First().Value = "hello world";
content.Properties.First().Value = "hello world"; // change data
content.ChangePublishedState(PublishedState.Saved);
Assert.IsTrue(content.ShouldCreateNewVersion());
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void Should_Create_New_Version_When_Language_Changed()
public void RequireSaving_UnpublishedAndSavingAndContentPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ReleaseDate = DateTime.Now; // change data
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void RequireSaving_UnpublishedAndPublishing_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Publishing); // publishing
Assert.IsTrue(content.RequiresSaving());
}
[Test]
public void RequireSaving_When_UnpublishedAndUnpublishingAndNothingChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing
Assert.IsTrue(content.RequiresSaving());
}
#endregion
#region RequiresNewVersion
// when language...
[Test]
public void RequireNewVersion_LanguageChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
@@ -79,99 +178,465 @@ namespace Umbraco.Tests.Models
content.ResetDirtyProperties(false);
content.Language = "en-AU";
Assert.IsTrue(content.ShouldCreateNewVersion(PublishedState.Unpublished));
Assert.IsTrue(content.RequiresNewVersion());
}
// when Published...
[Test]
public void RequireNewVersion_PublishedAndThatsAll_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void Should_Create_New_Version_When_Any_Property_Value_Changed_And_Its_Already_Published()
public void RequireNewVersion_PublishedAndPublishingAndNothingChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Publishing);
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_PublishedAndPublishdingAndUserPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.Properties.First().Value = "hello world"; // change data
content.ChangePublishedState(PublishedState.Publishing);
Assert.IsTrue(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_PublishedAndPublishdingAndContentPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ReleaseDate = DateTime.Now; // change content property
content.ChangePublishedState(PublishedState.Publishing);
Assert.IsTrue(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_PublishedAndSavingAndNothingChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_PublishedAndSavingAndUserPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.Properties.First().Value = "hello world"; // change data
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsTrue(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_PublishedAndSavingAndContentPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ReleaseDate = DateTime.Now; // change content property
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsTrue(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_PublishedAndUnpublishing_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing
Assert.IsTrue(content.RequiresNewVersion());
}
// when Unpublished...
[Test]
public void RequireNewVersion_UnpublishedAndSavingAndNothingChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.Properties.First().Value = "hello world";
Assert.IsTrue(content.ShouldCreateNewVersion(PublishedState.Published));
Assert.IsFalse(content.RequiresNewVersion());
}
[Test] // debatable
public void RequireNewVersion_UnpublishedAndSavingAndUserPropertyChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.Properties.First().Value = "hello world"; // change user property
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_UnpublishedAndSavingAndContentPropertyChanged_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ReleaseDate = DateTime.Now; // change content property
Assert.IsTrue(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_UnpublishedAndPublishingAndNothingChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Publishing);
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_UnpublishedAndPublishingAndUserPropertyChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.Properties.First().Value = "hello world"; // change user property
content.ChangePublishedState(PublishedState.Publishing); // publishing
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_UnpublishedAndPublishingAndContentPropertyChanged_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ReleaseDate = DateTime.Now; // change content property
content.ChangePublishedState(PublishedState.Publishing); // publishing
Assert.IsFalse(content.RequiresNewVersion());
}
[Test]
public void RequireNewVersion_UnpublishedAndUnpublishing_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing
Assert.IsFalse(content.RequiresNewVersion());
}
#endregion
#region ClearPublishedFlag
[Test]
public void ClearPublishedFlag_UnpublishedAndPublishing_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Publishing);
Assert.IsTrue(content.RequiresClearPublishedFlag());
}
[Test]
public void ClearPublishedFlag_UnpublishedAndUnpublishing_Should()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing - does not "change it"
Assert.IsTrue(content.RequiresClearPublishedFlag());
}
[Test]
public void ClearPublishedFlag_UnpublishedAndSaving_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsFalse(content.RequiresClearPublishedFlag());
}
[Test]
public void ClearPublishedFlag_PublishedAndSaving_ShouldNot()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Saving); // saving
Assert.IsFalse(content.RequiresClearPublishedFlag());
}
[Test]
public void ClearPublishedFlag_PublishedAndUnpublishing_Not()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Unpublishing); // unpublishing
Assert.IsTrue(content.RequiresClearPublishedFlag());
}
[Test]
public void ClearPublishedFlag_When_PublishedAndPublishing_Not()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
content.ChangePublishedState(PublishedState.Publishing); // publishing - does not "change it"
Assert.IsTrue(content.RequiresClearPublishedFlag());
}
#endregion
#region Others
[Test]
public void DirtyProperty_Reset_Clears_SavedPublishedState()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ChangePublishedState(PublishedState.Saving); // saved
content.ResetDirtyProperties(false); // reset to .Unpublished
Assert.AreEqual(PublishedState.Unpublished, content.PublishedState);
}
[Test]
public void DirtyProperty_OnlyIfActuallyChanged_Content()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// if you assign a content property with its value it is not dirty
// if you assign it with another value then back, it is dirty
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsPropertyDirty("Published"));
content.Published = true;
Assert.IsTrue(content.IsPropertyDirty("Published"));
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsPropertyDirty("Published"));
content.Published = true;
Assert.IsFalse(content.IsPropertyDirty("Published"));
content.Published = false;
content.Published = true;
Assert.IsTrue(content.IsPropertyDirty("Published"));
}
[Test]
public void DirtyProperty_OnlyIfActuallyChanged_User()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
var prop = content.Properties.First();
// if you assign a user property with its value it is not dirty
// if you assign it with another value then back, it is dirty
prop.Value = "A";
content.ResetDirtyProperties(false);
Assert.IsFalse(prop.IsDirty());
prop.Value = "B";
Assert.IsTrue(prop.IsDirty());
content.ResetDirtyProperties(false);
Assert.IsFalse(prop.IsDirty());
prop.Value = "B";
Assert.IsFalse(prop.IsDirty());
prop.Value = "A";
prop.Value = "B";
Assert.IsTrue(prop.IsDirty());
}
[Test]
public void DirtyProperty_UpdateDate()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
var prop = content.Properties.First();
content.ResetDirtyProperties(false);
var d = content.UpdateDate;
prop.Value = "A";
Assert.IsTrue(content.IsAnyUserPropertyDirty());
Assert.IsFalse(content.IsEntityDirty());
Assert.AreEqual(d, content.UpdateDate);
content.UpdateDate = DateTime.Now;
Assert.IsTrue(content.IsEntityDirty());
Assert.AreNotEqual(d, content.UpdateDate);
// so... changing UpdateDate would count as a content property being changed
// however in ContentRepository.PersistUpdatedItem, we change UpdateDate AFTER
// we've tested for RequiresSaving & RequiresNewVersion so it's OK
}
[Test]
public void DirtyProperty_WasDirty_ContentProperty()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.Published = false;
content.Published = true;
Assert.IsTrue(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.Published = false;
content.Published = true;
content.ResetDirtyProperties(true); // what PersistUpdatedItem does
Assert.IsFalse(content.IsDirty());
Assert.IsTrue(content.WasDirty());
content.Published = false;
content.Published = true;
content.ResetDirtyProperties(); // what PersistUpdatedItem does
Assert.IsFalse(content.IsDirty());
Assert.IsTrue(content.WasDirty());
}
[Test]
public void DirtyProperty_WasDirty_ContentSortOrder()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.SortOrder = 0;
content.SortOrder = 1;
Assert.IsTrue(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.SortOrder = 0;
content.SortOrder = 1;
content.ResetDirtyProperties(true); // what PersistUpdatedItem does
Assert.IsFalse(content.IsDirty());
Assert.IsTrue(content.WasDirty());
content.SortOrder = 0;
content.SortOrder = 1;
content.ResetDirtyProperties(); // what PersistUpdatedItem does
Assert.IsFalse(content.IsDirty());
Assert.IsTrue(content.WasDirty());
}
[Test]
public void DirtyProperty_WasDirty_UserProperty()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
var prop = content.Properties.First();
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsDirty());
Assert.IsFalse(content.WasDirty());
prop.Value = "a";
prop.Value = "b";
Assert.IsTrue(content.IsDirty());
Assert.IsFalse(content.WasDirty());
content.ResetDirtyProperties(false);
Assert.IsFalse(content.IsDirty());
Assert.IsFalse(content.WasDirty());
prop.Value = "a";
prop.Value = "b";
content.ResetDirtyProperties(true); // what PersistUpdatedItem does
Assert.IsFalse(content.IsDirty());
//Assert.IsFalse(content.WasDirty()); // not impacted by user properties
Assert.IsTrue(content.WasDirty()); // now it is!
prop.Value = "a";
prop.Value = "b";
content.ResetDirtyProperties(); // what PersistUpdatedItem does
Assert.IsFalse(content.IsDirty());
//Assert.IsFalse(content.WasDirty()); // not impacted by user properties
Assert.IsTrue(content.WasDirty()); // now it is!
}
[Test]
public void Should_Not_Create_New_Version_When_Published_Status_Not_Changed()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
Assert.IsFalse(content.ShouldCreateNewVersion(PublishedState.Unpublished));
}
[Test]
public void Should_Not_Create_New_Version_When_Not_Published_And_Property_Changed()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.Properties.First().Value = "hello world";
Assert.IsFalse(content.ShouldCreateNewVersion(PublishedState.Unpublished));
}
[Test]
public void Should_Clear_Published_Flag_When_Newly_Published_Version()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
Assert.IsTrue(content.ShouldClearPublishedFlagForPreviousVersions());
}
[Test]
public void Should_Not_Clear_Published_Flag_When_Saving_Version()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Saved);
Assert.IsFalse(content.ShouldClearPublishedFlagForPreviousVersions());
}
[Test]
public void Should_Clear_Published_Flag_When_Unpublishing_From_Published()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Published);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Unpublished);
Assert.IsTrue(content.ShouldClearPublishedFlagForPreviousVersions());
}
[Test]
public void Should_Not_Clear_Published_Flag_When_Unpublishing_From_Saved()
{
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Saved);
content.ResetDirtyProperties(false);
content.ChangePublishedState(PublishedState.Unpublished);
Assert.IsFalse(content.ShouldClearPublishedFlagForPreviousVersions());
}
#endregion
}
}
+32 -25
View File
@@ -31,7 +31,7 @@ namespace Umbraco.Tests.Models
[TearDown]
public void Dispose()
{
}
[TestCase("-1,-20,12,34,56", false)]
@@ -41,10 +41,19 @@ namespace Umbraco.Tests.Models
{
var mediaType = MockedContentTypes.CreateImageMediaType();
var media = MockedMedia.CreateMediaFile(mediaType, -1);
// this is NOT how we should do it, we should "move to recycle bin"
// as it causes various things to change... IsInRecycleBin looks for
// the path but Trashed looks for the flag!
// fixme .Trashed is weird anyways, should be refactored
media.Path = path;
((Media)media).Trashed = isInBin;
media.Id = 34;
Assert.AreEqual(isInBin, media.IsInRecycleBin());
if (isInBin)
Assert.IsTrue(media.Trashed);
}
[TestCase("-1,-20,12,34,56", true)]
@@ -54,10 +63,19 @@ namespace Umbraco.Tests.Models
{
var contentType = MockedContentTypes.CreateSimpleContentType();
var content = MockedContent.CreateSimpleContent(contentType);
// this is NOT how we should do it, we should "move to recycle bin"
// as it causes various things to change... IsInRecycleBin looks for
// the path but Trashed looks for the flag!
// fixme .Trashed is weird anyways, should be refactored
content.Path = path;
content.Trashed = isInBin;
content.Id = 34;
Assert.AreEqual(isInBin, content.IsInRecycleBin());
if (isInBin)
Assert.IsTrue(content.Trashed);
}
[Test]
@@ -67,7 +85,7 @@ namespace Umbraco.Tests.Models
//add non-grouped properties
contentType.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Ntext, "nonGrouped1") { Name = "Non Grouped 1", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
contentType.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Ntext, "nonGrouped2") { Name = "Non Grouped 2", Description = "", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -88 });
//ensure that nothing is marked as dirty
contentType.ResetDirtyProperties(false);
@@ -249,6 +267,13 @@ namespace Umbraco.Tests.Models
var contentType = MockedContentTypes.CreateTextpageContentType();
contentType.Id = 99;
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// should not try to clone something that's not Published or Unpublished
// (and in fact it will not work)
// but we cannot directly set the state to Published - hence this trick
content.ChangePublishedState(PublishedState.Publishing);
content.ResetDirtyProperties(false); // => .Published
var i = 200;
foreach (var property in content.Properties)
{
@@ -263,7 +288,6 @@ namespace Umbraco.Tests.Models
content.Level = 3;
content.Path = "-1,4,10";
content.ReleaseDate = DateTime.Now;
content.ChangePublishedState(PublishedState.Published);
content.SortOrder = 5;
content.Template = new Template((string) "Test Template", (string) "testTemplate")
{
@@ -366,7 +390,7 @@ namespace Umbraco.Tests.Models
content.Level = 3;
content.Path = "-1,4,10";
content.ReleaseDate = DateTime.Now;
content.ChangePublishedState(PublishedState.Published);
content.ChangePublishedState(PublishedState.Publishing);
content.SortOrder = 5;
content.Template = new Template((string) "Test Template", (string) "testTemplate")
{
@@ -642,7 +666,7 @@ namespace Umbraco.Tests.Models
// Act
content.ResetDirtyProperties();
content.ChangePublishedState(PublishedState.Published);
content.ChangePublishedState(PublishedState.Publishing);
// Assert
Assert.That(content.IsPropertyDirty("Published"), Is.True);
@@ -650,23 +674,6 @@ namespace Umbraco.Tests.Models
Assert.That(content.IsPropertyDirty("Name"), Is.False);
}
[Test]
public void Can_Verify_Content_Is_Trashed()
{
// Arrange
var contentType = MockedContentTypes.CreateTextpageContentType();
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act
content.ResetDirtyProperties();
content.ChangeTrashedState(true);
// Assert
Assert.That(content.IsPropertyDirty("Trashed"), Is.True);
Assert.That(content.Trashed, Is.True);
Assert.That(content.IsPropertyDirty("Name"), Is.False);
}
[Test]
public void Adding_PropertyGroup_To_ContentType_Results_In_Dirty_Entity()
{
@@ -689,7 +696,7 @@ namespace Umbraco.Tests.Models
{
// Arrange
var contentType = MockedContentTypes.CreateTextpageContentType();
contentType.ResetDirtyProperties(); //reset
contentType.ResetDirtyProperties(); //reset
// Act
contentType.Alias = "newAlias";
@@ -719,7 +726,7 @@ namespace Umbraco.Tests.Models
Assert.That(content.GetDirtyUserProperties().Count(), Is.EqualTo(1));
Assert.That(content.Properties[0].IsDirty(), Is.True);
Assert.That(content.Properties["title"].IsDirty(), Is.True);
content.ResetDirtyProperties(); //this would be like committing the entity
// Assert
@@ -739,7 +746,7 @@ namespace Umbraco.Tests.Models
var contentType = MockedContentTypes.CreateTextpageContentType();
// Act
contentType.Alias = "newAlias";
contentType.Alias = "newAlias";
// Assert
Assert.That(contentType.IsDirty(), Is.True);
+1 -1
View File
@@ -35,7 +35,7 @@ namespace Umbraco.Tests.Models
{
// Arrange
var mediaType = MockedContentTypes.CreateImageMediaType("image2");
ServiceContext.ContentTypeService.Save(mediaType);
ServiceContext.MediaTypeService.Save(mediaType);
var media = MockedMedia.CreateMediaImage(mediaType, -1);
ServiceContext.MediaService.Save(media, 0);
@@ -82,20 +82,20 @@ namespace Umbraco.Tests.Persistence.Repositories
for (var i = 0; i < 100; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType1);
c1.ChangePublishedState(PublishedState.Published);
c1.ChangePublishedState(PublishedState.Publishing);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
unitOfWork.Flush();
//now create some versions of this content - this shouldn't affect the xml structures saved
for (int i = 0; i < allCreated.Count; i++)
for (var i = 0; i < allCreated.Count; i++)
{
allCreated[i].Name = "blah" + i;
//IMPORTANT testing note here: We need to changed the published state here so that
// it doesn't automatically think this is simply publishing again - this forces the latest
// version to be Saved and not published
allCreated[i].ChangePublishedState(PublishedState.Saved);
allCreated[i].ChangePublishedState(PublishedState.Saving);
repository.AddOrUpdate(allCreated[i]);
}
unitOfWork.Flush();
@@ -104,7 +104,7 @@ namespace Umbraco.Tests.Persistence.Repositories
unitOfWork.Database.Execute("DELETE FROM cmsContentXml");
Assert.AreEqual(0, unitOfWork.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM cmsContentXml"));
repository.RebuildXmlStructures(media => new XElement("test"), 10);
repository.RebuildXmlStructures(content => new XElement("test"), 10);
Assert.AreEqual(100, unitOfWork.Database.ExecuteScalar<int>("SELECT COUNT(*) FROM cmsContentXml"));
@@ -135,7 +135,7 @@ namespace Umbraco.Tests.Persistence.Repositories
for (var i = 0; i < 100; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType1);
c1.ChangePublishedState(PublishedState.Published);
c1.ChangePublishedState(PublishedState.Publishing);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
@@ -188,21 +188,21 @@ namespace Umbraco.Tests.Persistence.Repositories
for (var i = 0; i < 30; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType1);
c1.ChangePublishedState(PublishedState.Published);
c1.ChangePublishedState(PublishedState.Publishing);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
for (var i = 0; i < 30; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType2);
c1.ChangePublishedState(PublishedState.Published);
c1.ChangePublishedState(PublishedState.Publishing);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
for (var i = 0; i < 30; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType3);
c1.ChangePublishedState(PublishedState.Published);
c1.ChangePublishedState(PublishedState.Publishing);
repository.AddOrUpdate(c1);
allCreated.Add(c1);
}
@@ -536,13 +536,13 @@ namespace Umbraco.Tests.Persistence.Repositories
var result = repository.GetAll().ToArray();
foreach (var content in result)
{
content.ChangePublishedState(PublishedState.Saved);
content.ChangePublishedState(PublishedState.Saving);
repository.AddOrUpdate(content);
}
unitOfWork.Flush();
foreach (var content in result)
{
content.ChangePublishedState(PublishedState.Published);
content.ChangePublishedState(PublishedState.Publishing);
repository.AddOrUpdate(content);
}
unitOfWork.Flush();
@@ -553,17 +553,17 @@ namespace Umbraco.Tests.Persistence.Repositories
public void CreateTestData()
{
//Create and Save folder-Media -> (NodeDto.NodeIdSeed)
var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031);
var folderMediaType = ServiceContext.MediaTypeService.Get(1031);
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
ServiceContext.MediaService.Save(folder, 0);
//Create and Save image-Media -> (NodeDto.NodeIdSeed + 1)
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
var imageMediaType = ServiceContext.MediaTypeService.Get(1032);
var image = MockedMedia.CreateMediaImage(imageMediaType, folder.Id);
ServiceContext.MediaService.Save(image, 0);
//Create and Save file-Media -> (NodeDto.NodeIdSeed + 2)
var fileMediaType = ServiceContext.ContentTypeService.GetMediaType(1033);
var fileMediaType = ServiceContext.MediaTypeService.Get(1033);
var file = MockedMedia.CreateMediaFile(fileMediaType, folder.Id);
ServiceContext.MediaService.Save(file, 0);
}
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Persistence
{
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture]
public class UnitOfWorkTests : BaseDatabaseFactoryTest
{
[Test]
public void ReadLockNonExisting()
{
var provider = new NPocoUnitOfWorkProvider(Logger);
Assert.Throws<Exception>(() =>
{
using (var uow = provider.CreateUnitOfWork())
{
uow.ReadLock(-666);
uow.Complete();
}
});
}
[Test]
public void ReadLockExisting()
{
var provider = new NPocoUnitOfWorkProvider(Logger);
using (var uow = provider.CreateUnitOfWork())
{
uow.ReadLock(Constants.Locks.Servers);
uow.Complete();
}
}
[Test]
public void WriteLockNonExisting()
{
var provider = new NPocoUnitOfWorkProvider(Logger);
Assert.Throws<Exception>(() =>
{
using (var uow = provider.CreateUnitOfWork())
{
uow.WriteLock(-666);
uow.Complete();
}
});
}
[Test]
public void WriteLockExisting()
{
var provider = new NPocoUnitOfWorkProvider(Logger);
using (var uow = provider.CreateUnitOfWork())
{
uow.WriteLock(Constants.Locks.Servers);
uow.Complete();
}
}
}
}
@@ -99,7 +99,7 @@ namespace Umbraco.Tests.PublishedContent
Name = "Rich Text",
DataTypeDefinitionId = -87 //tiny mce
});
ServiceContext.ContentTypeService.Save(mType);
ServiceContext.MediaTypeService.Save(mType);
var media = MockedMedia.CreateMediaImage(mType, -1);
media.Properties["content"].Value = "<div>This is some content</div>";
ServiceContext.MediaService.Save(media);
@@ -7,6 +7,7 @@ using Umbraco.Core.Publishing;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
using System.Linq;
using Umbraco.Core.Services;
namespace Umbraco.Tests.Publishing
{
@@ -28,7 +29,7 @@ namespace Umbraco.Tests.Publishing
base.TearDown();
//ensure event handler is gone
PublishingStrategy.Publishing -= PublishingStrategyPublishing;
ContentService.Publishing -= StrategyPublishing;
}
private IContent _homePage;
@@ -49,18 +50,17 @@ namespace Umbraco.Tests.Publishing
var testData = CreateTestData();
//Create some other data which are descendants of Text Page 2
var mandatorContent = MockedContent.CreateSimpleContent(
ServiceContext.ContentTypeService.GetContentType("umbMandatory"), "Invalid Content", testData.Single(x => x.Name == "Text Page 2").Id);
ServiceContext.ContentTypeService.Get("umbMandatory"), "Invalid Content", testData.Single(x => x.Name == "Text Page 2").Id);
mandatorContent.SetValue("author", string.Empty);
ServiceContext.ContentService.Save(mandatorContent, 0);
var subContent = MockedContent.CreateSimpleContent(
ServiceContext.ContentTypeService.GetContentType("umbTextpage"), "Sub Sub Sub", mandatorContent.Id);
ServiceContext.ContentTypeService.Get("umbTextpage"), "Sub Sub Sub", mandatorContent.Id);
ServiceContext.ContentService.Save(subContent, 0);
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
//publish root and nodes at it's children level
var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] { _homePage });
var result = strategy.PublishWithChildrenInternal(listToPublish, 0, true);
var evtMsgs = new EventMessages();
var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(listToPublish, null, 0, evtMsgs, true);
Assert.AreEqual(listToPublish.Count() - 2, result.Count(x => x.Success));
Assert.IsTrue(result.Where(x => x.Success).Select(x => x.Result.ContentItem.Id)
@@ -83,26 +83,22 @@ namespace Umbraco.Tests.Publishing
{
CreateTestData();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
PublishingStrategy.Publishing +=PublishingStrategyPublishing;
ContentService.Publishing += StrategyPublishing;
//publish root and nodes at it's children level
var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] {_homePage});
var result = strategy.PublishWithChildrenInternal(listToPublish, 0);
var evtMsgs = new EventMessages();
var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(listToPublish, null, 0, evtMsgs);
Assert.AreEqual(listToPublish.Count() - 2, result.Count(x => x.Success));
Assert.IsTrue(result.Where(x => x.Success).Select(x => x.Result.ContentItem.Id)
.ContainsAll(listToPublish.Where(x => x.Name != "Text Page 2" && x.Name != "Text Page 3").Select(x => x.Id)));
}
static void PublishingStrategyPublishing(IPublishingStrategy sender, PublishEventArgs<IContent> e)
static void StrategyPublishing(IContentService sender, PublishEventArgs<IContent> e)
{
foreach (var i in e.PublishedEntities.Where(i => i.Name == "Text Page 2"))
{
if (e.PublishedEntities.Any(x => x.Name == "Text Page 2"))
e.Cancel = true;
}
}
[Test]
@@ -110,26 +106,27 @@ namespace Umbraco.Tests.Publishing
{
CreateTestData();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
var evtMsgs = new EventMessages();
//publish root and nodes at it's children level
var result1 = strategy.Publish(_homePage, 0);
var result1 = ((ContentService)ServiceContext.ContentService).StrategyPublish(_homePage, false, 0, evtMsgs);
Assert.IsTrue(result1);
Assert.IsTrue(_homePage.Published);
foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id))
{
var r = strategy.Publish(c, 0);
var r = ((ContentService)ServiceContext.ContentService).StrategyPublish(c, false, 0, evtMsgs);
Assert.IsTrue(r);
Assert.IsTrue(c.Published);
}
//ok, all are published except the deepest descendant, we will pass in a flag to not include it to
//be published
var result = strategy.PublishWithChildrenInternal(
ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] {_homePage}), 0, false);
//be published - beware that StrategyPublishWithChildren expects content to be ordered by level
var content = new[] {_homePage}.Union(ServiceContext.ContentService.GetDescendants(_homePage));
var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(content, null, 0, evtMsgs, false);
//all of them will be SuccessAlreadyPublished unless the unpublished one gets included, in that case
//we'll have a 'Success' result which we don't want.
Assert.AreEqual(0, result.Count(x => x.Result.StatusType == PublishStatusType.Success));
// NOTE - not true, we'll have Success for _homePage because we DO want to publish it
Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.Success));
}
[Test]
@@ -137,17 +134,17 @@ namespace Umbraco.Tests.Publishing
{
CreateTestData();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
var evtMsgs = new EventMessages();
//publish root and nodes at it's children level
var result1 = strategy.Publish(_homePage, 0);
var result1 = ((ContentService)ServiceContext.ContentService).StrategyPublish(_homePage, false, 0, evtMsgs);
Assert.IsTrue(result1);
Assert.IsTrue(_homePage.Published);
//NOTE (MCH) This isn't persisted, so not really a good test as it will look like the result should be something else.
foreach (var c in ServiceContext.ContentService.GetChildren(_homePage.Id))
{
var r = strategy.Publish(c, 0);
var r = ((ContentService)ServiceContext.ContentService).StrategyPublish(c, false, 0, evtMsgs);
Assert.IsTrue(r);
Assert.IsTrue(c.Published);
}
@@ -158,12 +155,16 @@ namespace Umbraco.Tests.Publishing
//means the result will be 1 'SuccessAlreadyPublished' and 3 'Success' because the Homepage is
//inserted in the list and since that item has the status of already being Published it will be the one item
//with 'SuccessAlreadyPublished'
//
// NOTE - this is WRONG, the first one (_homepage) we really want to publish!
// not sure this test makes sense at all...
var descendants = ServiceContext.ContentService.GetDescendants(_homePage).Concat(new[] {_homePage});
var result = strategy.PublishWithChildrenInternal(descendants, 0, true);
var contents = new[] {_homePage} // top-level MUST be the first one here
.Concat(ServiceContext.ContentService.GetDescendants(_homePage));
var result = ((ContentService)ServiceContext.ContentService).StrategyPublishWithChildren(contents, null, 0, evtMsgs, true);
Assert.AreEqual(3, result.Count(x => x.Result.StatusType == PublishStatusType.Success));
Assert.AreEqual(1, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished));
Assert.AreEqual(4, result.Count(x => x.Result.StatusType == PublishStatusType.Success));
Assert.AreEqual(0, result.Count(x => x.Result.StatusType == PublishStatusType.SuccessAlreadyPublished));
Assert.IsTrue(result.First(x => x.Result.StatusType == PublishStatusType.Success).Success);
Assert.IsTrue(result.First(x => x.Result.StatusType == PublishStatusType.Success).Result.ContentItem.Published);
}
@@ -40,7 +40,7 @@ namespace Umbraco.Tests.Services
//Create and Save Content "Text Page 1" based on "umbTextpage" -> 1047
Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id);
subpage.ReleaseDate = DateTime.Now.AddMinutes(-5);
subpage.ChangePublishedState(PublishedState.Saved);
subpage.ChangePublishedState(PublishedState.Saving);
ServiceContext.ContentService.Save(subpage, 0);
//Create and Save Content "Text Page 1" based on "umbTextpage" -> 1048

Some files were not shown because too many files have changed in this diff Show More