Merge remote-tracking branch 'origin/v8/dev' into v8/feature/AB4550-segments-ui
This commit is contained in:
@@ -26,10 +26,11 @@ namespace Umbraco.Core.Compose
|
||||
|
||||
if (relationType == null)
|
||||
{
|
||||
relationType = new RelationType(Constants.ObjectTypes.Document,
|
||||
relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyName,
|
||||
true,
|
||||
Constants.ObjectTypes.Document,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyName) { IsBidirectional = true };
|
||||
Constants.ObjectTypes.Document);
|
||||
|
||||
relationService.Save(relationType);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Umbraco.Core.Compose
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
|
||||
|
||||
relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName);
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
|
||||
relationService.Save(relationType);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Umbraco.Core.Compose
|
||||
{
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
|
||||
relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName);
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
|
||||
relationService.Save(relationType);
|
||||
}
|
||||
foreach (var item in e.MoveInfoCollection)
|
||||
|
||||
@@ -154,6 +154,9 @@ namespace Umbraco.Core.Composing
|
||||
public static DataEditorCollection DataEditors
|
||||
=> Factory.GetInstance<DataEditorCollection>();
|
||||
|
||||
public static DataValueReferenceFactoryCollection DataValueReferenceFactories
|
||||
=> Factory.GetInstance<DataValueReferenceFactoryCollection>();
|
||||
|
||||
public static PropertyEditorCollection PropertyEditors
|
||||
=> Factory.GetInstance<PropertyEditorCollection>();
|
||||
|
||||
|
||||
@@ -49,6 +49,13 @@ namespace Umbraco.Core
|
||||
public static DataEditorCollectionBuilder DataEditors(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<DataEditorCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data value reference factory collection builder.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
public static DataValueReferenceFactoryCollectionBuilder DataValueReferenceFactories(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<DataValueReferenceFactoryCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value converters collection builder.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class AppSettings
|
||||
{
|
||||
public const string MainDomLock = "Umbraco.Core.MainDom.Lock";
|
||||
|
||||
// TODO: Kill me - still used in Umbraco.Core.IO.SystemFiles:27
|
||||
[Obsolete("We need to kill this appsetting as we do not use XML content cache umbraco.config anymore due to NuCache")]
|
||||
public const string ContentXML = "Umbraco.Core.ContentXML"; //umbracoContentXML
|
||||
|
||||
@@ -315,34 +315,65 @@ namespace Umbraco.Core
|
||||
public static class RelationTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Document On Copy".
|
||||
/// Name for default relation type "Related Media".
|
||||
/// </summary>
|
||||
public const string RelatedMediaName = "Related Media";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Media"
|
||||
/// </summary>
|
||||
public const string RelatedMediaAlias = "umbMedia";
|
||||
|
||||
/// <summary>
|
||||
/// Name for default relation type "Related Document".
|
||||
/// </summary>
|
||||
public const string RelatedDocumentName = "Related Document";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Document"
|
||||
/// </summary>
|
||||
public const string RelatedDocumentAlias = "umbDocument";
|
||||
|
||||
/// <summary>
|
||||
/// Name for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyName = "Relate Document On Copy";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Document On Copy".
|
||||
/// Alias for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Parent Document On Delete".
|
||||
/// Name for default relation type "Relate Parent Document On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentDocumentOnDeleteName = "Relate Parent Document On Delete";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Parent Document On Delete".
|
||||
/// Alias for default relation type "Relate Parent Document On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentDocumentOnDeleteAlias = "relateParentDocumentOnDelete";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// Name for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentMediaFolderOnDeleteName = "Relate Parent Media Folder On Delete";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// Alias for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentMediaFolderOnDeleteAlias = "relateParentMediaFolderOnDelete";
|
||||
|
||||
/// <summary>
|
||||
/// Returns the types of relations that are automatically tracked
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Developers should not manually use these relation types since they will all be cleared whenever an entity
|
||||
/// (content, media or member) is saved since they are auto-populated based on property values.
|
||||
/// </remarks>
|
||||
public static string[] AutomaticRelationTypes = new[] { RelatedMediaAlias, RelatedDocumentAlias };
|
||||
|
||||
//TODO: return a list of built in types so we can use that to prevent deletion in the uI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,126 +12,260 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this ISimpleContentType contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
public static bool VariesBySegment(this ISimpleContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this IContentTypeBase contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
public static bool VariesByCulture(this IContentTypeBase contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
public static bool VariesBySegment(this IContentTypeBase contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this IContentTypeBase contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type is invariant.
|
||||
/// </summary>
|
||||
public static bool VariesByNothing(this PropertyType propertyType) => propertyType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
public static bool VariesByCulture(this PropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
public static bool VariesBySegment(this PropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this PropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this IPublishedContentType contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// Determines whether the property type is invariant.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
public static bool VariesByCulture(this IPublishedContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
public static bool VariesBySegment(this IPublishedContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this IPublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this PropertyType propertyType) => propertyType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
public static bool VariesByCulture(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
public static bool VariesBySegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation is invariant.
|
||||
/// </summary>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this ContentVariation variation) => variation == ContentVariation.Nothing;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this IContentTypeBase contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this IPublishedContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this PropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation varies by culture.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this ContentVariation variation) => (variation & ContentVariation.Culture) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this ISimpleContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this IContentTypeBase contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this IPublishedContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this PropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this ContentVariation variation) => (variation & ContentVariation.Segment) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this ISimpleContentType contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this IContentTypeBase contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this IPublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this PropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this ContentVariation variation) => (variation & ContentVariation.CultureAndSegment) == ContentVariation.CultureAndSegment;
|
||||
|
||||
/// <summary>
|
||||
/// Sets or removes the content type variation depending on the specified value.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <param name="variation">The variation to set or remove.</param>
|
||||
/// <param name="value">If set to <c>true</c> sets the variation; otherwise, removes the variation.</param>
|
||||
/// <remarks>
|
||||
/// This method does not support setting the variation to nothing.
|
||||
/// </remarks>
|
||||
public static void SetVariesBy(this IContentTypeBase contentType, ContentVariation variation, bool value = true) => contentType.Variations = contentType.Variations.SetFlag(variation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets or removes the property type variation depending on the specified value.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <param name="variation">The variation to set or remove.</param>
|
||||
/// <param name="value">If set to <c>true</c> sets the variation; otherwise, removes the variation.</param>
|
||||
/// <remarks>
|
||||
/// This method does not support setting the variation to nothing.
|
||||
/// </remarks>
|
||||
public static void SetVariesBy(this PropertyType propertyType, ContentVariation variation, bool value = true) => propertyType.Variations = propertyType.Variations.SetFlag(variation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the variations with the variation set or removed depending on the specified value.
|
||||
/// </summary>
|
||||
/// <param name="variations">The existing variations.</param>
|
||||
/// <param name="variation">The variation to set or remove.</param>
|
||||
/// <param name="value">If set to <c>true</c> sets the variation; otherwise, removes the variation.</param>
|
||||
/// <returns>
|
||||
/// The variations with the variation set or removed.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This method does not support setting the variation to nothing.
|
||||
/// </remarks>
|
||||
public static ContentVariation SetFlag(this ContentVariation variations, ContentVariation variation, bool value = true)
|
||||
{
|
||||
return value
|
||||
? variations | variation // Set flag using bitwise logical OR
|
||||
: variations & ~variation; // Remove flag using bitwise logical AND with bitwise complement (reversing the bit)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a combination of culture and segment is valid for the variation.
|
||||
/// </summary>
|
||||
@@ -140,16 +274,18 @@ namespace Umbraco.Core
|
||||
/// <param name="segment">The segment.</param>
|
||||
/// <param name="exact">A value indicating whether to perform exact validation.</param>
|
||||
/// <param name="wildcards">A value indicating whether to support wildcards.</param>
|
||||
/// <param name="throwIfInvalid">A value indicating whether to throw a <see cref="NotSupportedException"/> when the combination is invalid.</param>
|
||||
/// <returns>True if the combination is valid; otherwise false.</returns>
|
||||
/// <param name="throwIfInvalid">A value indicating whether to throw a <see cref="NotSupportedException" /> when the combination is invalid.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the combination is valid; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="NotSupportedException">Occurs when the combination is invalid, and <paramref name="throwIfInvalid" /> is true.</exception>
|
||||
/// <remarks>
|
||||
/// <para>When validation is exact, the combination must match the variation exactly. For instance, if the variation is Culture, then
|
||||
/// a culture is required. When validation is not strict, the combination must be equivalent, or more restrictive: if the variation is
|
||||
/// Culture, an invariant combination is ok.</para>
|
||||
/// <para>Basically, exact is for one content type, or one property type, and !exact is for "all property types" of one content type.</para>
|
||||
/// <para>Both <paramref name="culture"/> and <paramref name="segment"/> can be "*" to indicate "all of them".</para>
|
||||
/// <para>Both <paramref name="culture" /> and <paramref name="segment" /> can be "*" to indicate "all of them".</para>
|
||||
/// </remarks>
|
||||
/// <exception cref="NotSupportedException">Occurs when the combination is invalid, and <paramref name="throwIfInvalid"/> is true.</exception>
|
||||
public static bool ValidateVariation(this ContentVariation variation, string culture, string segment, bool exact, bool wildcards, bool throwIfInvalid)
|
||||
{
|
||||
culture = culture.NullOrWhiteSpaceAsNull();
|
||||
@@ -166,13 +302,14 @@ namespace Umbraco.Core
|
||||
if (variation.VariesByCulture())
|
||||
{
|
||||
// varies by culture
|
||||
// in exact mode, the culture cannot be null
|
||||
// in exact mode, the culture cannot be null
|
||||
if (exact && culture == null)
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Culture may not be null because culture variation is enabled.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -183,9 +320,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Culture \"{culture}\" is invalid because culture variation is disabled.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it does not vary by segment
|
||||
// the segment cannot have a value
|
||||
@@ -195,6 +333,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Segment \"{segment}\" is invalid because segment variation is disabled.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,81 +3,42 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods to enums.
|
||||
/// Provides extension methods to <see cref="Enum"/>.
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
// note:
|
||||
// - no need to HasFlagExact, that's basically an == test
|
||||
// - HasFlagAll cannot be named HasFlag because ext. methods never take priority over instance methods
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a flag enum has all the specified values.
|
||||
/// Determines whether all the flags/bits are set within the enum value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>True when all bits set in <paramref name="uses"/> are set in <paramref name="use"/>, though other bits may be set too.</para>
|
||||
/// <para>This is the behavior of the original <see cref="Enum.HasFlag"/> method.</para>
|
||||
/// </remarks>
|
||||
public static bool HasFlagAll<T>(this T use, T uses)
|
||||
/// <typeparam name="T">The enum type.</typeparam>
|
||||
/// <param name="value">The enum value.</param>
|
||||
/// <param name="flags">The flags.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if all the flags/bits are set within the enum value; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
[Obsolete("Use Enum.HasFlag() or bitwise operations (if performance is important) instead.")]
|
||||
public static bool HasFlagAll<T>(this T value, T flags)
|
||||
where T : Enum
|
||||
{
|
||||
var num = Convert.ToUInt64(use);
|
||||
var nums = Convert.ToUInt64(uses);
|
||||
|
||||
return (num & nums) == nums;
|
||||
return value.HasFlag(flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a flag enum has any of the specified values.
|
||||
/// Determines whether any of the flags/bits are set within the enum value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>True when at least one of the bits set in <paramref name="uses"/> is set in <paramref name="use"/>.</para>
|
||||
/// </remarks>
|
||||
public static bool HasFlagAny<T>(this T use, T uses)
|
||||
/// <typeparam name="T">The enum type.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="flags">The flags.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if any of the flags/bits are set within the enum value; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool HasFlagAny<T>(this T value, T flags)
|
||||
where T : Enum
|
||||
{
|
||||
var num = Convert.ToUInt64(use);
|
||||
var nums = Convert.ToUInt64(uses);
|
||||
var v = Convert.ToUInt64(value);
|
||||
var f = Convert.ToUInt64(flags);
|
||||
|
||||
return (num & nums) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a flag of the given input enum
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="input">Enum to set flag of</param>
|
||||
/// <param name="flag">Flag to set</param>
|
||||
/// <returns>A new enum with the flag set</returns>
|
||||
public static T SetFlag<T>(this T input, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
var i = Convert.ToUInt64(input);
|
||||
var f = Convert.ToUInt64(flag);
|
||||
|
||||
// bitwise OR to set flag f of enum i
|
||||
var result = i | f;
|
||||
|
||||
return (T)Enum.ToObject(typeof(T), result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsets a flag of the given input enum
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="input">Enum to unset flag of</param>
|
||||
/// <param name="flag">Flag to unset</param>
|
||||
/// <returns>A new enum with the flag unset</returns>
|
||||
public static T UnsetFlag<T>(this T input, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
var i = Convert.ToUInt64(input);
|
||||
var f = Convert.ToUInt64(flag);
|
||||
|
||||
// bitwise AND combined with bitwise complement to unset flag f of enum i
|
||||
var result = i & ~f;
|
||||
|
||||
return (T)Enum.ToObject(typeof(T), result);
|
||||
return (v & f) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,8 @@ namespace Umbraco.Core.Migrations.Install
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.Domains, Name = "Domains" });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.KeyValues, Name = "KeyValues" });
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.Languages, Name = "Languages" });
|
||||
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.MainDom, Name = "MainDom" });
|
||||
}
|
||||
|
||||
private void CreateContentTypeData()
|
||||
@@ -310,14 +312,27 @@ namespace Umbraco.Core.Migrations.Install
|
||||
private void CreateRelationTypeData()
|
||||
{
|
||||
var relationType = new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = Constants.ObjectTypes.Document, ParentObjectType = Constants.ObjectTypes.Document, Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName };
|
||||
relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid();
|
||||
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
|
||||
relationType = new RelationTypeDto { Id = 2, Alias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = Constants.ObjectTypes.Document, ParentObjectType = Constants.ObjectTypes.Document, Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName };
|
||||
relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid();
|
||||
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
|
||||
relationType = new RelationTypeDto { Id = 3, Alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, ChildObjectType = Constants.ObjectTypes.Media, ParentObjectType = Constants.ObjectTypes.Media, Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName };
|
||||
relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid();
|
||||
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
|
||||
|
||||
relationType = new RelationTypeDto { Id = 4, Alias = Constants.Conventions.RelationTypes.RelatedMediaAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Constants.Conventions.RelationTypes.RelatedMediaName };
|
||||
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
|
||||
|
||||
relationType = new RelationTypeDto { Id = 5, Alias = Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Constants.Conventions.RelationTypes.RelatedDocumentName };
|
||||
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
|
||||
_database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
|
||||
}
|
||||
|
||||
internal static Guid CreateUniqueRelationTypeId(string alias, string name)
|
||||
{
|
||||
return (alias + "____" + name).ToGuid();
|
||||
}
|
||||
|
||||
private void CreateKeyValueData()
|
||||
|
||||
@@ -183,9 +183,12 @@ namespace Umbraco.Core.Migrations.Upgrade
|
||||
To<RenameUserLoginDtoDateIndex>("{0372A42B-DECF-498D-B4D1-6379E907EB94}");
|
||||
To<FixContentNuCascade>("{5B1E0D93-F5A3-449B-84BA-65366B84E2D4}");
|
||||
|
||||
// to 8.6.0
|
||||
// to 8.6.0...
|
||||
To<UpdateRelationTypeTable>("{4759A294-9860-46BC-99F9-B4C975CAE580}");
|
||||
To<AddNewRelationTypes>("{0BC866BC-0665-487A-9913-0290BD0169AD}");
|
||||
To<AddPropertyTypeValidationMessageColumns>("{3D67D2C8-5E65-47D0-A9E1-DC2EE0779D6B}");
|
||||
To<MissingContentVersionsIndexes>("{EE288A91-531B-4995-8179-1D62D9AA3E2E}");
|
||||
To<AddMainDomLock>("{2AB29964-02A1-474D-BD6B-72148D2A53A2}");
|
||||
|
||||
//FINAL
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
|
||||
{
|
||||
public class AddMainDomLock : MigrationBase
|
||||
{
|
||||
public AddMainDomLock(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
Database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.MainDom, Name = "MainDom" });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Umbraco.Core.Migrations.Install;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures the new relation types are created
|
||||
/// </summary>
|
||||
public class AddNewRelationTypes : MigrationBase
|
||||
{
|
||||
public AddNewRelationTypes(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
CreateRelation(
|
||||
Constants.Conventions.RelationTypes.RelatedMediaAlias,
|
||||
Constants.Conventions.RelationTypes.RelatedMediaName);
|
||||
|
||||
CreateRelation(
|
||||
Constants.Conventions.RelationTypes.RelatedDocumentAlias,
|
||||
Constants.Conventions.RelationTypes.RelatedDocumentName);
|
||||
}
|
||||
|
||||
private void CreateRelation(string alias, string name)
|
||||
{
|
||||
var uniqueId = DatabaseDataCreator.CreateUniqueRelationTypeId(alias ,name); //this is the same as how it installs so everything is consistent
|
||||
Insert.IntoTable(Constants.DatabaseSchema.Tables.RelationType)
|
||||
.Row(new { typeUniqueId = uniqueId, dual = 0, name, alias })
|
||||
.Do();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
|
||||
{
|
||||
|
||||
public class AddPropertyTypeValidationMessageColumns : MigrationBase
|
||||
{
|
||||
public AddPropertyTypeValidationMessageColumns(IMigrationContext context)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_6_0
|
||||
{
|
||||
|
||||
public class UpdateRelationTypeTable : MigrationBase
|
||||
{
|
||||
public UpdateRelationTypeTable(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.RelationType).AlterColumn("parentObjectType").AsGuid().Nullable().Do();
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.RelationType).AlterColumn("childObjectType").AsGuid().Nullable().Do();
|
||||
|
||||
//TODO: We have to update this field to ensure it's not null, we can just copy across the name since that is not nullable
|
||||
|
||||
//drop index before we can alter the column
|
||||
if (IndexExists("IX_umbracoRelationType_alias"))
|
||||
Delete
|
||||
.Index("IX_umbracoRelationType_alias")
|
||||
.OnTable(Constants.DatabaseSchema.Tables.RelationType)
|
||||
.Do();
|
||||
//change the column to non nullable
|
||||
Alter.Table(Constants.DatabaseSchema.Tables.RelationType).AlterColumn("alias").AsString(100).NotNullable().Do();
|
||||
//re-create the index
|
||||
Create
|
||||
.Index("IX_umbracoRelationType_alias")
|
||||
.OnTable(Constants.DatabaseSchema.Tables.RelationType)
|
||||
.OnColumn("alias")
|
||||
.Ascending()
|
||||
.WithOptions().Unique().WithOptions().NonClustered()
|
||||
.Do();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Umbraco.Core.Models.Editors
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents an uploaded file for a property.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to track reference to other entities in a property value
|
||||
/// </summary>
|
||||
public struct UmbracoEntityReference : IEquatable<UmbracoEntityReference>
|
||||
{
|
||||
private static readonly UmbracoEntityReference _empty = new UmbracoEntityReference(Udi.UnknownTypeUdi.Instance, string.Empty);
|
||||
|
||||
public UmbracoEntityReference(Udi udi, string relationTypeAlias)
|
||||
{
|
||||
Udi = udi ?? throw new ArgumentNullException(nameof(udi));
|
||||
RelationTypeAlias = relationTypeAlias ?? throw new ArgumentNullException(nameof(relationTypeAlias));
|
||||
}
|
||||
|
||||
public UmbracoEntityReference(Udi udi)
|
||||
{
|
||||
Udi = udi ?? throw new ArgumentNullException(nameof(udi));
|
||||
|
||||
switch (udi.EntityType)
|
||||
{
|
||||
case Constants.UdiEntityType.Media:
|
||||
RelationTypeAlias = Constants.Conventions.RelationTypes.RelatedMediaAlias;
|
||||
break;
|
||||
default:
|
||||
RelationTypeAlias = Constants.Conventions.RelationTypes.RelatedDocumentAlias;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static UmbracoEntityReference Empty() => _empty;
|
||||
|
||||
public static bool IsEmpty(UmbracoEntityReference reference) => reference == Empty();
|
||||
|
||||
public Udi Udi { get; }
|
||||
public string RelationTypeAlias { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is UmbracoEntityReference reference && Equals(reference);
|
||||
}
|
||||
|
||||
public bool Equals(UmbracoEntityReference other)
|
||||
{
|
||||
return EqualityComparer<Udi>.Default.Equals(Udi, other.Udi) &&
|
||||
RelationTypeAlias == other.RelationTypeAlias;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = -487348478;
|
||||
hashCode = hashCode * -1521134295 + EqualityComparer<Udi>.Default.GetHashCode(Udi);
|
||||
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(RelationTypeAlias);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public static bool operator ==(UmbracoEntityReference left, UmbracoEntityReference right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(UmbracoEntityReference left, UmbracoEntityReference right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
/// Sets the parent entity.
|
||||
/// </summary>
|
||||
/// <remarks>Use this method to set the parent entity when the parent entity is known, but has not
|
||||
/// been persistent and does not yet have an identity. The parent identifier will we retrieved
|
||||
/// been persistent and does not yet have an identity. The parent identifier will be retrieved
|
||||
/// from the parent entity when needed. If the parent entity still does not have an entity by that
|
||||
/// time, an exception will be thrown by <see cref="ParentId"/> getter.</remarks>
|
||||
void SetParent(ITreeEntity parent);
|
||||
@@ -53,4 +53,4 @@
|
||||
/// </remarks>
|
||||
bool Trashed { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
namespace Umbraco.Core.Models.Entities
|
||||
{
|
||||
public class MemberEntitySlim : EntitySlim, IMemberEntitySlim
|
||||
public class MemberEntitySlim : ContentEntitySlim, IMemberEntitySlim
|
||||
{
|
||||
public string ContentTypeAlias { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ContentTypeIcon { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ContentTypeThumbnail { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
@@ -11,12 +12,18 @@ namespace Umbraco.Core.Models
|
||||
[DataMember]
|
||||
int ParentId { get; set; }
|
||||
|
||||
[DataMember]
|
||||
Guid ParentObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Child Id of the Relation (Destination)
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int ChildId { get; set; }
|
||||
|
||||
[DataMember]
|
||||
Guid ChildObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RelationType"/> for the Relation
|
||||
/// </summary>
|
||||
|
||||
@@ -29,13 +29,13 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember]
|
||||
Guid ParentObjectType { get; set; }
|
||||
Guid? ParentObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Childs object type id
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember]
|
||||
Guid ChildObjectType { get; set; }
|
||||
Guid? ChildObjectType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
public static UmbracoObjectTypes GetUmbracoObjectType(string name)
|
||||
{
|
||||
return (UmbracoObjectTypes) Enum.Parse(typeof (UmbracoObjectTypes), name, false);
|
||||
return (UmbracoObjectTypes) Enum.Parse(typeof (UmbracoObjectTypes), name, true);
|
||||
}
|
||||
|
||||
#region Guid object type utilities
|
||||
|
||||
@@ -17,13 +17,36 @@ namespace Umbraco.Core.Models
|
||||
private IRelationType _relationType;
|
||||
private string _comment;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for constructing the entity to be created
|
||||
/// </summary>
|
||||
/// <param name="parentId"></param>
|
||||
/// <param name="childId"></param>
|
||||
/// <param name="relationType"></param>
|
||||
public Relation(int parentId, int childId, IRelationType relationType)
|
||||
{
|
||||
_parentId = parentId;
|
||||
_childId = childId;
|
||||
_relationType = relationType;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for reconstructing the entity from the data source
|
||||
/// </summary>
|
||||
/// <param name="parentId"></param>
|
||||
/// <param name="childId"></param>
|
||||
/// <param name="parentObjectType"></param>
|
||||
/// <param name="childObjectType"></param>
|
||||
/// <param name="relationType"></param>
|
||||
public Relation(int parentId, int childId, Guid parentObjectType, Guid childObjectType, IRelationType relationType)
|
||||
{
|
||||
_parentId = parentId;
|
||||
_childId = childId;
|
||||
_relationType = relationType;
|
||||
ParentObjectType = parentObjectType;
|
||||
ChildObjectType = childObjectType;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Parent Id of the Relation (Source)
|
||||
@@ -35,6 +58,9 @@ namespace Umbraco.Core.Models
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _parentId, nameof(ParentId));
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public Guid ParentObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Child Id of the Relation (Destination)
|
||||
/// </summary>
|
||||
@@ -45,6 +71,9 @@ namespace Umbraco.Core.Models
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _childId, nameof(ChildId));
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public Guid ChildObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RelationType"/> for the Relation
|
||||
/// </summary>
|
||||
|
||||
@@ -14,28 +14,26 @@ namespace Umbraco.Core.Models
|
||||
private string _name;
|
||||
private string _alias;
|
||||
private bool _isBidrectional;
|
||||
private Guid _parentObjectType;
|
||||
private Guid _childObjectType;
|
||||
private Guid? _parentObjectType;
|
||||
private Guid? _childObjectType;
|
||||
|
||||
public RelationType(Guid childObjectType, Guid parentObjectType, string alias)
|
||||
//TODO: Should we put back the broken ctors with obsolete attributes?
|
||||
|
||||
public RelationType(string alias, string name)
|
||||
: this(name, alias, false, null, null)
|
||||
{
|
||||
if (alias == null) throw new ArgumentNullException(nameof(alias));
|
||||
if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(alias));
|
||||
}
|
||||
|
||||
_childObjectType = childObjectType;
|
||||
_parentObjectType = parentObjectType;
|
||||
public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType)
|
||||
{
|
||||
_name = name;
|
||||
_alias = alias;
|
||||
Name = _alias;
|
||||
_isBidrectional = isBidrectional;
|
||||
_parentObjectType = parentObjectType;
|
||||
_childObjectType = childObjectType;
|
||||
}
|
||||
|
||||
public RelationType(Guid childObjectType, Guid parentObjectType, string alias, string name)
|
||||
: this(childObjectType, parentObjectType, alias)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
|
||||
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the RelationType
|
||||
@@ -72,7 +70,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember]
|
||||
public Guid ParentObjectType
|
||||
public Guid? ParentObjectType
|
||||
{
|
||||
get => _parentObjectType;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _parentObjectType, nameof(ParentObjectType));
|
||||
@@ -83,7 +81,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember]
|
||||
public Guid ChildObjectType
|
||||
public Guid? ChildObjectType
|
||||
{
|
||||
get => _childObjectType;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _childObjectType, nameof(ChildObjectType));
|
||||
|
||||
@@ -8,6 +8,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class Locks
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IMainDom"/> lock
|
||||
/// </summary>
|
||||
public const int MainDom = -1000;
|
||||
|
||||
/// <summary>
|
||||
/// All servers.
|
||||
/// </summary>
|
||||
|
||||
@@ -34,5 +34,13 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
[Column("comment")]
|
||||
[Length(1000)]
|
||||
public string Comment { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
[Column("parentObjectType")]
|
||||
public Guid ParentObjectType { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
[Column("childObjectType")]
|
||||
public Guid ChildObjectType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
[ExplicitColumns]
|
||||
internal class RelationTypeDto
|
||||
{
|
||||
public const int NodeIdSeed = 4;
|
||||
public const int NodeIdSeed = 10;
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = NodeIdSeed)]
|
||||
@@ -23,17 +23,20 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
public bool Dual { get; set; }
|
||||
|
||||
[Column("parentObjectType")]
|
||||
public Guid ParentObjectType { get; set; }
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public Guid? ParentObjectType { get; set; }
|
||||
|
||||
[Column("childObjectType")]
|
||||
public Guid ChildObjectType { get; set; }
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public Guid? ChildObjectType { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Column("alias")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
[Length(100)]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
@@ -3,20 +3,11 @@ using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
internal class RelationFactory
|
||||
internal static class RelationFactory
|
||||
{
|
||||
private readonly IRelationType _relationType;
|
||||
|
||||
public RelationFactory(IRelationType relationType)
|
||||
public static IRelation BuildEntity(RelationDto dto, IRelationType relationType)
|
||||
{
|
||||
_relationType = relationType;
|
||||
}
|
||||
|
||||
#region Implementation of IEntityFactory<Relation,RelationDto>
|
||||
|
||||
public IRelation BuildEntity(RelationDto dto)
|
||||
{
|
||||
var entity = new Relation(dto.ParentId, dto.ChildId, _relationType);
|
||||
var entity = new Relation(dto.ParentId, dto.ChildId, dto.ParentObjectType, dto.ChildObjectType, relationType);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -37,7 +28,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
}
|
||||
}
|
||||
|
||||
public RelationDto BuildDto(IRelation entity)
|
||||
public static RelationDto BuildDto(IRelation entity)
|
||||
{
|
||||
var dto = new RelationDto
|
||||
{
|
||||
@@ -54,6 +45,5 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
return dto;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
public static IRelationType BuildEntity(RelationTypeDto dto)
|
||||
{
|
||||
var entity = new RelationType(dto.ChildObjectType, dto.ParentObjectType, dto.Alias);
|
||||
var entity = new RelationType(dto.Name, dto.Alias, dto.Dual, dto.ChildObjectType, dto.ParentObjectType);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -17,8 +17,6 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
entity.Id = dto.Id;
|
||||
entity.Key = dto.UniqueId;
|
||||
entity.IsBidirectional = dto.Dual;
|
||||
entity.Name = dto.Name;
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
entity.ResetDirtyProperties(false);
|
||||
|
||||
@@ -14,7 +14,21 @@ namespace Umbraco.Core.Persistence
|
||||
/// </summary>
|
||||
public static partial class NPocoDatabaseExtensions
|
||||
{
|
||||
// TODO: review NPoco native InsertBulk to replace the code below
|
||||
/// <summary>
|
||||
/// Configures NPoco's SqlBulkCopyHelper to use the correct SqlConnection and SqlTransaction instances from the underlying RetryDbConnection and ProfiledDbTransaction
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is required to use NPoco's own <see cref="Database.InsertBulk{T}(IEnumerable{T})" /> method because we use wrapped DbConnection and DbTransaction instances.
|
||||
/// NPoco's InsertBulk method only caters for efficient bulk inserting records for Sql Server, it does not cater for bulk inserting of records for
|
||||
/// any other database type and in which case will just insert records one at a time.
|
||||
/// NPoco's InsertBulk method also deals with updating the passed in entity's PK/ID once it's inserted whereas our own BulkInsertRecords methods
|
||||
/// do not handle this scenario.
|
||||
/// </remarks>
|
||||
public static void ConfigureNPocoBulkExtensions()
|
||||
{
|
||||
SqlBulkCopyHelper.SqlConnectionResolver = dbConn => GetTypedConnection<SqlConnection>(dbConn);
|
||||
SqlBulkCopyHelper.SqlTransactionResolver = dbTran => GetTypedTransaction<SqlTransaction>(dbTran);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulk-inserts records within a transaction.
|
||||
@@ -235,7 +249,7 @@ namespace Umbraco.Core.Persistence
|
||||
//we need to add column mappings here because otherwise columns will be matched by their order and if the order of them are different in the DB compared
|
||||
//to the order in which they are declared in the model then this will not work, so instead we will add column mappings by name so that this explicitly uses
|
||||
//the names instead of their ordering.
|
||||
foreach(var col in bulkReader.ColumnMappings)
|
||||
foreach (var col in bulkReader.ColumnMappings)
|
||||
{
|
||||
copy.ColumnMappings.Add(col.DestinationColumn, col.DestinationColumn);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using NPoco;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
@@ -15,10 +16,22 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IEntitySlim Get(int id, Guid objectTypeId);
|
||||
IEntitySlim Get(Guid key, Guid objectTypeId);
|
||||
|
||||
IEnumerable<IEntitySlim> GetAll(Guid objectType, params int[] ids);
|
||||
IEnumerable<IEntitySlim> GetAll(Guid objectType, params int[] ids);
|
||||
IEnumerable<IEntitySlim> GetAll(Guid objectType, params Guid[] keys);
|
||||
|
||||
/// <summary>
|
||||
/// Gets entities for a query
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query);
|
||||
|
||||
/// <summary>
|
||||
/// Gets entities for a query and a specific object type allowing the query to be slightly more optimized
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="objectType"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectType);
|
||||
|
||||
UmbracoObjectTypes GetObjectType(int id);
|
||||
@@ -30,7 +43,41 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
bool Exists(int id);
|
||||
bool Exists(Guid key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged entities for a query and a subset of object types
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="objectTypes"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="ordering"></param>
|
||||
/// <param name="sqlCustomization">
|
||||
/// A callback providing the ability to customize the generated SQL used to retrieve entities
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// A collection of mixed entity types which would be of type <see cref="IEntitySlim"/>, <see cref="IDocumentEntitySlim"/>, <see cref="IMediaEntitySlim"/>,
|
||||
/// <see cref="IMemberEntitySlim"/>
|
||||
/// </returns>
|
||||
IEnumerable<IEntitySlim> GetPagedResultsByQuery(
|
||||
IQuery<IUmbracoEntity> query, Guid[] objectTypes, long pageIndex, int pageSize, out long totalRecords,
|
||||
IQuery<IUmbracoEntity> filter, Ordering ordering, Action<Sql<ISqlContext>> sqlCustomization = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged entities for a query and a specific object type
|
||||
/// </summary>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="objectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="ordering"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IEntitySlim> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
IQuery<IUmbracoEntity> filter, Ordering ordering);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
using Umbraco.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IRelationRepository : IReadWriteQueryRepository<int, IRelation>
|
||||
{
|
||||
IEnumerable<IRelation> GetPagedRelationsByQuery(IQuery<IRelation> query, long pageIndex, int pageSize, out long totalRecords, Ordering ordering);
|
||||
|
||||
/// <summary>
|
||||
/// Persist multiple <see cref="IRelation"/> at once
|
||||
/// </summary>
|
||||
/// <param name="relations"></param>
|
||||
void Save(IEnumerable<IRelation> relations);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all relations for a parent for any specified relation type alias
|
||||
/// </summary>
|
||||
/// <param name="parentId"></param>
|
||||
/// <param name="relationTypeAliases">
|
||||
/// A list of relation types to match for deletion, if none are specified then all relations for this parent id are deleted
|
||||
/// </param>
|
||||
void DeleteByParent(int parentId, params string[] relationTypeAliases);
|
||||
|
||||
IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes);
|
||||
|
||||
IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
@@ -24,26 +25,48 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal sealed class ContentRepositoryBase
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// This is used for unit tests ONLY
|
||||
/// </summary>
|
||||
public static bool ThrowOnWarning = false;
|
||||
}
|
||||
|
||||
internal abstract class ContentRepositoryBase<TId, TEntity, TRepository> : NPocoRepositoryBase<TId, TEntity>, IContentRepository<TId, TEntity>
|
||||
where TEntity : class, IUmbracoEntity
|
||||
where TEntity : class, IContentBase
|
||||
where TRepository : class, IRepository
|
||||
{
|
||||
protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILanguageRepository languageRepository, ILogger logger)
|
||||
private readonly Lazy<PropertyEditorCollection> _propertyEditors;
|
||||
private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactories;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="scopeAccessor"></param>
|
||||
/// <param name="cache"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="languageRepository"></param>
|
||||
/// <param name="propertyEditors">
|
||||
/// Lazy property value collection - must be lazy because we have a circular dependency since some property editors require services, yet these services require property editors
|
||||
/// </param>
|
||||
protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger,
|
||||
ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository,
|
||||
Lazy<PropertyEditorCollection> propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
LanguageRepository = languageRepository;
|
||||
RelationRepository = relationRepository;
|
||||
RelationTypeRepository = relationTypeRepository;
|
||||
_propertyEditors = propertyEditors;
|
||||
_dataValueReferenceFactories = dataValueReferenceFactories;
|
||||
}
|
||||
|
||||
protected abstract TRepository This { get; }
|
||||
|
||||
protected ILanguageRepository LanguageRepository { get; }
|
||||
protected IRelationRepository RelationRepository { get; }
|
||||
protected IRelationTypeRepository RelationTypeRepository { get; }
|
||||
|
||||
protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // TODO: inject
|
||||
protected PropertyEditorCollection PropertyEditors => _propertyEditors.Value;
|
||||
|
||||
#region Versions
|
||||
|
||||
@@ -797,5 +820,56 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected void PersistRelations(TEntity entity)
|
||||
{
|
||||
// Get all references from our core built in DataEditors/Property Editors
|
||||
// Along with seeing if deverlopers want to collect additional references from the DataValueReferenceFactories collection
|
||||
var trackedRelations = new List<UmbracoEntityReference>();
|
||||
trackedRelations.AddRange(_dataValueReferenceFactories.GetAllReferences(entity.Properties, PropertyEditors));
|
||||
|
||||
//First delete all auto-relations for this entity
|
||||
RelationRepository.DeleteByParent(entity.Id, Constants.Conventions.RelationTypes.AutomaticRelationTypes);
|
||||
|
||||
if (trackedRelations.Count == 0) return;
|
||||
|
||||
trackedRelations = trackedRelations.Distinct().ToList();
|
||||
var udiToGuids = trackedRelations.Select(x => x.Udi as GuidUdi)
|
||||
.ToDictionary(x => (Udi)x, x => x.Guid);
|
||||
|
||||
//lookup in the DB all INT ids for the GUIDs and chuck into a dictionary
|
||||
var keyToIds = Database.Fetch<NodeIdKey>(Sql().Select<NodeDto>(x => x.NodeId, x => x.UniqueId).From<NodeDto>().WhereIn<NodeDto>(x => x.UniqueId, udiToGuids.Values))
|
||||
.ToDictionary(x => x.UniqueId, x => x.NodeId);
|
||||
|
||||
var allRelationTypes = RelationTypeRepository.GetMany(Array.Empty<int>())
|
||||
.ToDictionary(x => x.Alias, x => x);
|
||||
|
||||
var toSave = trackedRelations.Select(rel =>
|
||||
{
|
||||
if (!allRelationTypes.TryGetValue(rel.RelationTypeAlias, out var relationType))
|
||||
throw new InvalidOperationException($"The relation type {rel.RelationTypeAlias} does not exist");
|
||||
|
||||
if (!udiToGuids.TryGetValue(rel.Udi, out var guid))
|
||||
return null; // This shouldn't happen!
|
||||
|
||||
if (!keyToIds.TryGetValue(guid, out var id))
|
||||
return null; // This shouldn't happen!
|
||||
|
||||
return new Relation(entity.Id, id, relationType);
|
||||
}).WhereNotNull();
|
||||
|
||||
// Save bulk relations
|
||||
RelationRepository.Save(toSave);
|
||||
|
||||
}
|
||||
|
||||
private class NodeIdKey
|
||||
{
|
||||
[Column("id")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("uniqueId")]
|
||||
public Guid UniqueId { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
@@ -17,8 +18,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </remarks>
|
||||
internal class DocumentBlueprintRepository : DocumentRepository, IDocumentBlueprintRepository
|
||||
{
|
||||
public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository)
|
||||
public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger,
|
||||
IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository,
|
||||
Lazy<PropertyEditorCollection> propertyEditorCollection, DataValueReferenceFactoryCollection dataValueReferenceFactories)
|
||||
: base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditorCollection, dataValueReferenceFactories)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
@@ -29,8 +30,23 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly ContentByGuidReadRepository _contentByGuidReadRepository;
|
||||
private readonly IScopeAccessor _scopeAccessor;
|
||||
|
||||
public DocumentRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, appCaches, languageRepository, logger)
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="scopeAccessor"></param>
|
||||
/// <param name="appCaches"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="contentTypeRepository"></param>
|
||||
/// <param name="templateRepository"></param>
|
||||
/// <param name="tagRepository"></param>
|
||||
/// <param name="languageRepository"></param>
|
||||
/// <param name="propertyEditors">
|
||||
/// Lazy property value collection - must be lazy because we have a circular dependency since some property editors require services, yet these services require property editors
|
||||
/// </param>
|
||||
public DocumentRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger,
|
||||
IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository,
|
||||
Lazy<PropertyEditorCollection> propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories)
|
||||
: base(scopeAccessor, appCaches, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferenceFactories)
|
||||
{
|
||||
_contentTypeRepository = contentTypeRepository ?? throw new ArgumentNullException(nameof(contentTypeRepository));
|
||||
_templateRepository = templateRepository ?? throw new ArgumentNullException(nameof(templateRepository));
|
||||
@@ -484,6 +500,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
ClearEntityTags(entity, _tagRepository);
|
||||
}
|
||||
|
||||
PersistRelations(entity);
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
// troubleshooting
|
||||
@@ -687,6 +705,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
ClearEntityTags(entity, _tagRepository);
|
||||
}
|
||||
|
||||
PersistRelations(entity);
|
||||
|
||||
// TODO: note re. tags: explicitly unpublished entities have cleared tags, but masked or trashed entities *still* have tags in the db - so what?
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
@@ -35,21 +35,33 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
protected ISqlSyntaxProvider SqlSyntax => _scopeAccessor.AmbientScope.SqlContext.SqlSyntax;
|
||||
|
||||
#region Repository
|
||||
|
||||
// get a page of entities
|
||||
|
||||
public IEnumerable<IEntitySlim> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
IQuery<IUmbracoEntity> filter, Ordering ordering)
|
||||
{
|
||||
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
|
||||
var isMedia = objectType == Constants.ObjectTypes.Media;
|
||||
var isMember = objectType == Constants.ObjectTypes.Member;
|
||||
return GetPagedResultsByQuery(query, new[] { objectType }, pageIndex, pageSize, out totalRecords, filter, ordering);
|
||||
}
|
||||
|
||||
var sql = GetBaseWhere(isContent, isMedia, isMember, false, x =>
|
||||
// get a page of entities
|
||||
public IEnumerable<IEntitySlim> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid[] objectTypes, long pageIndex, int pageSize, out long totalRecords,
|
||||
IQuery<IUmbracoEntity> filter, Ordering ordering, Action<Sql<ISqlContext>> sqlCustomization = null)
|
||||
{
|
||||
var isContent = objectTypes.Any(objectType => objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint);
|
||||
var isMedia = objectTypes.Any(objectType => objectType == Constants.ObjectTypes.Media);
|
||||
var isMember = objectTypes.Any(objectType => objectType == Constants.ObjectTypes.Member);
|
||||
|
||||
var sql = GetBaseWhere(isContent, isMedia, isMember, false, s =>
|
||||
{
|
||||
if (filter == null) return;
|
||||
foreach (var filterClause in filter.GetWhereClauses())
|
||||
x.Where(filterClause.Item1, filterClause.Item2);
|
||||
}, objectType);
|
||||
sqlCustomization?.Invoke(s);
|
||||
|
||||
if (filter != null)
|
||||
{
|
||||
foreach (var filterClause in filter.GetWhereClauses())
|
||||
s.Where(filterClause.Item1, filterClause.Item2);
|
||||
}
|
||||
|
||||
|
||||
}, objectTypes);
|
||||
|
||||
ordering = ordering ?? Ordering.ByDefault();
|
||||
|
||||
@@ -70,35 +82,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
// for content we must query for ContentEntityDto entities to produce the correct culture variant entity names
|
||||
var pageIndexToFetch = pageIndex + 1;
|
||||
IEnumerable<BaseDto> dtos;
|
||||
if(isContent)
|
||||
{
|
||||
var page = Database.Page<ContentEntityDto>(pageIndexToFetch, pageSize, sql);
|
||||
dtos = page.Items;
|
||||
totalRecords = page.TotalItems;
|
||||
}
|
||||
else if (isMedia)
|
||||
{
|
||||
var page = Database.Page<MediaEntityDto>(pageIndexToFetch, pageSize, sql);
|
||||
dtos = page.Items;
|
||||
totalRecords = page.TotalItems;
|
||||
}
|
||||
else if (isMember)
|
||||
{
|
||||
var page = Database.Page<MemberEntityDto>(pageIndexToFetch, pageSize, sql);
|
||||
dtos = page.Items;
|
||||
totalRecords = page.TotalItems;
|
||||
}
|
||||
else
|
||||
{
|
||||
var page = Database.Page<BaseDto>(pageIndexToFetch, pageSize, sql);
|
||||
dtos = page.Items;
|
||||
totalRecords = page.TotalItems;
|
||||
}
|
||||
var page = Database.Page<GenericContentEntityDto>(pageIndexToFetch, pageSize, sql);
|
||||
dtos = page.Items;
|
||||
totalRecords = page.TotalItems;
|
||||
|
||||
var entities = dtos.Select(x => BuildEntity(isContent, isMedia, isMember, x)).ToArray();
|
||||
var entities = dtos.Select(BuildEntity).ToArray();
|
||||
|
||||
if (isContent)
|
||||
BuildVariants(entities.Cast<DocumentEntitySlim>());
|
||||
BuildVariants(entities.OfType<DocumentEntitySlim>());
|
||||
|
||||
return entities;
|
||||
}
|
||||
@@ -107,7 +97,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
var sql = GetBaseWhere(false, false, false, false, key);
|
||||
var dto = Database.FirstOrDefault<BaseDto>(sql);
|
||||
return dto == null ? null : BuildEntity(false, false, false, dto);
|
||||
return dto == null ? null : BuildEntity(dto);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +106,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
//isContent is going to return a 1:M result now with the variants so we need to do different things
|
||||
if (isContent)
|
||||
{
|
||||
var cdtos = Database.Fetch<ContentEntityDto>(sql);
|
||||
var cdtos = Database.Fetch<DocumentEntityDto>(sql);
|
||||
|
||||
return cdtos.Count == 0 ? null : BuildVariants(BuildDocumentEntity(cdtos[0]));
|
||||
}
|
||||
@@ -127,7 +117,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
if (dto == null) return null;
|
||||
|
||||
var entity = BuildEntity(false, isMedia, isMember, dto);
|
||||
var entity = BuildEntity(dto);
|
||||
|
||||
return entity;
|
||||
}
|
||||
@@ -146,7 +136,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
var sql = GetBaseWhere(false, false, false, false, id);
|
||||
var dto = Database.FirstOrDefault<BaseDto>(sql);
|
||||
return dto == null ? null : BuildEntity(false, false, false, dto);
|
||||
return dto == null ? null : BuildEntity(dto);
|
||||
}
|
||||
|
||||
public IEntitySlim Get(int id, Guid objectTypeId)
|
||||
@@ -178,7 +168,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
//isContent is going to return a 1:M result now with the variants so we need to do different things
|
||||
if (isContent)
|
||||
{
|
||||
var cdtos = Database.Fetch<ContentEntityDto>(sql);
|
||||
var cdtos = Database.Fetch<DocumentEntityDto>(sql);
|
||||
|
||||
return cdtos.Count == 0
|
||||
? Enumerable.Empty<IEntitySlim>()
|
||||
@@ -189,7 +179,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
? (IEnumerable<BaseDto>)Database.Fetch<MediaEntityDto>(sql)
|
||||
: Database.Fetch<BaseDto>(sql);
|
||||
|
||||
var entities = dtos.Select(x => BuildEntity(false, isMedia, isMember, x)).ToArray();
|
||||
var entities = dtos.Select(BuildEntity).ToArray();
|
||||
|
||||
return entities;
|
||||
}
|
||||
@@ -233,7 +223,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
var sql = translator.Translate();
|
||||
sql = AddGroupBy(false, false, false, sql, true);
|
||||
var dtos = Database.Fetch<BaseDto>(sql);
|
||||
return dtos.Select(x => BuildEntity(false, false, false, x)).ToList();
|
||||
return dtos.Select(BuildEntity).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectType)
|
||||
@@ -242,7 +232,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
var isMedia = objectType == Constants.ObjectTypes.Media;
|
||||
var isMember = objectType == Constants.ObjectTypes.Member;
|
||||
|
||||
var sql = GetBaseWhere(isContent, isMedia, isMember, false, null, objectType);
|
||||
var sql = GetBaseWhere(isContent, isMedia, isMember, false, null, new[] { objectType });
|
||||
|
||||
var translator = new SqlTranslator<IUmbracoEntity>(sql, query);
|
||||
sql = translator.Translate();
|
||||
@@ -356,14 +346,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
// gets the full sql for a given object type, with a given filter
|
||||
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, Action<Sql<ISqlContext>> filter)
|
||||
{
|
||||
var sql = GetBaseWhere(isContent, isMedia, isMember, false, filter, objectType);
|
||||
var sql = GetBaseWhere(isContent, isMedia, isMember, false, filter, new[] { objectType });
|
||||
return AddGroupBy(isContent, isMedia, isMember, sql, true);
|
||||
}
|
||||
|
||||
// gets the base SELECT + FROM [+ filter] sql
|
||||
// always from the 'current' content version
|
||||
protected Sql<ISqlContext> GetBase(bool isContent, bool isMedia, bool isMember, Action<Sql<ISqlContext>> filter, bool isCount = false)
|
||||
{
|
||||
{
|
||||
var sql = Sql();
|
||||
|
||||
if (isCount)
|
||||
@@ -401,15 +391,15 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (isContent || isMedia || isMember)
|
||||
{
|
||||
sql
|
||||
.InnerJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current)
|
||||
.InnerJoin<ContentDto>().On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId)
|
||||
.InnerJoin<ContentTypeDto>().On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId);
|
||||
.LeftJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current)
|
||||
.LeftJoin<ContentDto>().On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId)
|
||||
.LeftJoin<ContentTypeDto>().On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId);
|
||||
}
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
sql
|
||||
.InnerJoin<DocumentDto>().On<NodeDto, DocumentDto>((left, right) => left.NodeId == right.NodeId);
|
||||
.LeftJoin<DocumentDto>().On<NodeDto, DocumentDto>((left, right) => left.NodeId == right.NodeId);
|
||||
}
|
||||
|
||||
if (isMedia)
|
||||
@@ -433,10 +423,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
// gets the base SELECT + FROM [+ filter] + WHERE sql
|
||||
// for a given object type, with a given filter
|
||||
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Action<Sql<ISqlContext>> filter, Guid objectType)
|
||||
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Action<Sql<ISqlContext>> filter, Guid[] objectTypes)
|
||||
{
|
||||
return GetBase(isContent, isMedia, isMember, filter, isCount)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == objectType);
|
||||
var sql = GetBase(isContent, isMedia, isMember, filter, isCount);
|
||||
if (objectTypes.Length > 0)
|
||||
{
|
||||
sql.WhereIn<NodeDto>(x => x.NodeObjectType, objectTypes);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
// gets the base SELECT + FROM + WHERE sql
|
||||
@@ -510,8 +504,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (sql == null) throw new ArgumentNullException(nameof(sql));
|
||||
if (ordering == null) throw new ArgumentNullException(nameof(ordering));
|
||||
|
||||
// TODO: although this works for name, it probably doesn't work for others without an alias of some sort
|
||||
var orderBy = ordering.OrderBy;
|
||||
// TODO: although the default ordering string works for name, it wont work for others without a table or an alias of some sort
|
||||
// As more things are attempted to be sorted we'll prob have to add more expressions here
|
||||
string orderBy;
|
||||
switch (ordering.OrderBy.ToUpperInvariant())
|
||||
{
|
||||
case "PATH":
|
||||
orderBy = SqlSyntax.GetQuotedColumn(NodeDto.TableName, "path");
|
||||
break;
|
||||
|
||||
default:
|
||||
orderBy = ordering.OrderBy;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ordering.Direction == Direction.Ascending)
|
||||
sql.OrderBy(orderBy);
|
||||
@@ -524,9 +529,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
#region Classes
|
||||
|
||||
/// <summary>
|
||||
/// The DTO used to fetch results for a content item with its variation info
|
||||
/// The DTO used to fetch results for a generic content item which could be either a document, media or a member
|
||||
/// </summary>
|
||||
private class ContentEntityDto : BaseDto
|
||||
private class GenericContentEntityDto : DocumentEntityDto
|
||||
{
|
||||
public string MediaPath { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The DTO used to fetch results for a document item with its variation info
|
||||
/// </summary>
|
||||
private class DocumentEntityDto : BaseDto
|
||||
{
|
||||
public ContentVariation Variations { get; set; }
|
||||
|
||||
@@ -534,11 +547,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
public bool Edited { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The DTO used to fetch results for a media item with its media path info
|
||||
/// </summary>
|
||||
private class MediaEntityDto : BaseDto
|
||||
{
|
||||
public string MediaPath { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The DTO used to fetch results for a member item
|
||||
/// </summary>
|
||||
private class MemberEntityDto : BaseDto
|
||||
{
|
||||
}
|
||||
@@ -589,13 +608,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
#region Factory
|
||||
|
||||
private EntitySlim BuildEntity(bool isContent, bool isMedia, bool isMember, BaseDto dto)
|
||||
private EntitySlim BuildEntity(BaseDto dto)
|
||||
{
|
||||
if (isContent)
|
||||
if (dto.NodeObjectType == Constants.ObjectTypes.Document)
|
||||
return BuildDocumentEntity(dto);
|
||||
if (isMedia)
|
||||
if (dto.NodeObjectType == Constants.ObjectTypes.Media)
|
||||
return BuildMediaEntity(dto);
|
||||
if (isMember)
|
||||
if (dto.NodeObjectType == Constants.ObjectTypes.Member)
|
||||
return BuildMemberEntity(dto);
|
||||
|
||||
// EntitySlim does not track changes
|
||||
@@ -650,7 +669,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
var entity = new DocumentEntitySlim();
|
||||
BuildContentEntity(entity, dto);
|
||||
|
||||
if (dto is ContentEntityDto contentDto)
|
||||
if (dto is DocumentEntityDto contentDto)
|
||||
{
|
||||
// fill in the invariant info
|
||||
entity.Edited = contentDto.Edited;
|
||||
|
||||
@@ -12,6 +12,7 @@ using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
|
||||
@@ -27,8 +28,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly ITagRepository _tagRepository;
|
||||
private readonly MediaByGuidReadRepository _mediaByGuidReadRepository;
|
||||
|
||||
public MediaRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, languageRepository, logger)
|
||||
public MediaRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository,
|
||||
Lazy<PropertyEditorCollection> propertyEditorCollection, DataValueReferenceFactoryCollection dataValueReferenceFactories)
|
||||
: base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditorCollection, dataValueReferenceFactories)
|
||||
{
|
||||
_mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository));
|
||||
_tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository));
|
||||
@@ -287,6 +289,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
// set tags
|
||||
SetEntityTags(entity, _tagRepository);
|
||||
|
||||
PersistRelations(entity);
|
||||
|
||||
OnUowRefreshedEntity(new ScopedEntityEventArgs(AmbientScope, entity));
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
@@ -343,6 +347,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
SetEntityTags(entity, _tagRepository);
|
||||
|
||||
PersistRelations(entity);
|
||||
|
||||
OnUowRefreshedEntity(new ScopedEntityEventArgs(AmbientScope, entity));
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
|
||||
@@ -25,8 +26,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly ITagRepository _tagRepository;
|
||||
private readonly IMemberGroupRepository _memberGroupRepository;
|
||||
|
||||
public MemberRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, languageRepository, logger)
|
||||
public MemberRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger,
|
||||
IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository,
|
||||
Lazy<PropertyEditorCollection> propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories)
|
||||
: base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferenceFactories)
|
||||
{
|
||||
_memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository));
|
||||
_tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository));
|
||||
@@ -321,6 +324,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
SetEntityTags(entity, _tagRepository);
|
||||
|
||||
PersistRelations(entity);
|
||||
|
||||
OnUowRefreshedEntity(new ScopedEntityEventArgs(AmbientScope, entity));
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
@@ -386,6 +391,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
SetEntityTags(entity, _tagRepository);
|
||||
|
||||
PersistRelations(entity);
|
||||
|
||||
OnUowRefreshedEntity(new ScopedEntityEventArgs(AmbientScope, entity));
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
@@ -6,10 +6,14 @@ using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
@@ -19,11 +23,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal class RelationRepository : NPocoRepositoryBase<int, IRelation>, IRelationRepository
|
||||
{
|
||||
private readonly IRelationTypeRepository _relationTypeRepository;
|
||||
private readonly IEntityRepository _entityRepository;
|
||||
|
||||
public RelationRepository(IScopeAccessor scopeAccessor, ILogger logger, IRelationTypeRepository relationTypeRepository)
|
||||
public RelationRepository(IScopeAccessor scopeAccessor, ILogger logger, IRelationTypeRepository relationTypeRepository, IEntityRepository entityRepository)
|
||||
: base(scopeAccessor, AppCaches.NoCache, logger)
|
||||
{
|
||||
_relationTypeRepository = relationTypeRepository;
|
||||
_entityRepository = entityRepository;
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,Relation>
|
||||
@@ -39,10 +45,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
var relationType = _relationTypeRepository.Get(dto.RelationType);
|
||||
if (relationType == null)
|
||||
throw new Exception(string.Format("RelationType with Id: {0} doesn't exist", dto.RelationType));
|
||||
throw new InvalidOperationException(string.Format("RelationType with Id: {0} doesn't exist", dto.RelationType));
|
||||
|
||||
var factory = new RelationFactory(relationType);
|
||||
return DtoToEntity(dto, factory);
|
||||
return DtoToEntity(dto, relationType);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IRelation> PerformGetAll(params int[] ids)
|
||||
@@ -67,26 +72,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
private IEnumerable<IRelation> DtosToEntities(IEnumerable<RelationDto> dtos)
|
||||
{
|
||||
// in most cases, the relation type will be the same for all of them,
|
||||
// plus we've ordered the relations by type, so try to allocate as few
|
||||
// factories as possible - bearing in mind that relation types are cached
|
||||
RelationFactory factory = null;
|
||||
var relationTypeId = -1;
|
||||
//NOTE: This is N+1, BUT ALL relation types are cached so shouldn't matter
|
||||
|
||||
return dtos.Select(x =>
|
||||
{
|
||||
if (relationTypeId != x.RelationType)
|
||||
factory = new RelationFactory(_relationTypeRepository.Get(relationTypeId = x.RelationType));
|
||||
return DtoToEntity(x, factory);
|
||||
}).ToList();
|
||||
return dtos.Select(x => DtoToEntity(x, _relationTypeRepository.Get(x.RelationType))).ToList();
|
||||
}
|
||||
|
||||
private static IRelation DtoToEntity(RelationDto dto, RelationFactory factory)
|
||||
private static IRelation DtoToEntity(RelationDto dto, IRelationType relationType)
|
||||
{
|
||||
var entity = factory.BuildEntity(dto);
|
||||
var entity = RelationFactory.BuildEntity(dto, relationType);
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
((BeingDirtyBase)entity).ResetDirtyProperties(false);
|
||||
entity.ResetDirtyProperties(false);
|
||||
|
||||
return entity;
|
||||
}
|
||||
@@ -97,14 +93,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = Sql();
|
||||
if (isCount)
|
||||
{
|
||||
return Sql().SelectCount().From<RelationDto>();
|
||||
}
|
||||
|
||||
sql = isCount
|
||||
? sql.SelectCount()
|
||||
: sql.Select<RelationDto>();
|
||||
var sql = Sql().Select<RelationDto>()
|
||||
.AndSelect<NodeDto>("uchild", x => Alias(x.NodeObjectType, "childObjectType"))
|
||||
.AndSelect<NodeDto>("uparent", x => Alias(x.NodeObjectType, "parentObjectType"))
|
||||
.From<RelationDto>()
|
||||
.InnerJoin<NodeDto>("uchild").On<RelationDto, NodeDto>((rel, node) => rel.ChildId == node.NodeId, aliasRight: "uchild")
|
||||
.InnerJoin<NodeDto>("uparent").On<RelationDto, NodeDto>((rel, node) => rel.ParentId == node.NodeId, aliasRight: "uparent");
|
||||
|
||||
sql
|
||||
.From<RelationDto>();
|
||||
|
||||
return sql;
|
||||
}
|
||||
@@ -136,11 +136,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
entity.AddingEntity();
|
||||
|
||||
var factory = new RelationFactory(entity.RelationType);
|
||||
var dto = factory.BuildDto(entity);
|
||||
var dto = RelationFactory.BuildDto(entity);
|
||||
|
||||
var id = Convert.ToInt32(Database.Insert(dto));
|
||||
|
||||
entity.Id = id;
|
||||
PopulateObjectTypes(entity);
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
@@ -149,13 +150,192 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
entity.UpdatingEntity();
|
||||
|
||||
var factory = new RelationFactory(entity.RelationType);
|
||||
var dto = factory.BuildDto(entity);
|
||||
var dto = RelationFactory.BuildDto(entity);
|
||||
Database.Update(dto);
|
||||
|
||||
PopulateObjectTypes(entity);
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Used for joining the entity query with relations for the paging methods
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
private void SqlJoinRelations(Sql<ISqlContext> sql)
|
||||
{
|
||||
// add left joins for relation tables (this joins on both child or parent, so beware that this will normally return entities for
|
||||
// both sides of the relation type unless the IUmbracoEntity query passed in filters one side out).
|
||||
sql.LeftJoin<RelationDto>().On<NodeDto, RelationDto>((left, right) => left.NodeId == right.ChildId || left.NodeId == right.ParentId);
|
||||
sql.LeftJoin<RelationTypeDto>().On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id);
|
||||
}
|
||||
|
||||
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
|
||||
{
|
||||
// var contentObjectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member }
|
||||
// we could pass in the contentObjectTypes so that the entity repository sql is configured to do full entity lookups so that we get the full data
|
||||
// required to populate content, media or members, else we get the bare minimum data needed to populate an entity. BUT if we do this it
|
||||
// means that the SQL is less efficient and returns data that is probably not needed for what we need this lookup for. For the time being we
|
||||
// will just return the bare minimum entity data.
|
||||
|
||||
return _entityRepository.GetPagedResultsByQuery(Query<IUmbracoEntity>(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql =>
|
||||
{
|
||||
SqlJoinRelations(sql);
|
||||
|
||||
sql.Where<RelationDto>(rel => rel.ChildId == childId);
|
||||
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ParentId == childId || node.NodeId != childId);
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
|
||||
{
|
||||
// var contentObjectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member }
|
||||
// we could pass in the contentObjectTypes so that the entity repository sql is configured to do full entity lookups so that we get the full data
|
||||
// required to populate content, media or members, else we get the bare minimum data needed to populate an entity. BUT if we do this it
|
||||
// means that the SQL is less efficient and returns data that is probably not needed for what we need this lookup for. For the time being we
|
||||
// will just return the bare minimum entity data.
|
||||
|
||||
return _entityRepository.GetPagedResultsByQuery(Query<IUmbracoEntity>(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql =>
|
||||
{
|
||||
SqlJoinRelations(sql);
|
||||
|
||||
sql.Where<RelationDto>(rel => rel.ParentId == parentId);
|
||||
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ChildId == parentId || node.NodeId != parentId);
|
||||
});
|
||||
}
|
||||
|
||||
public void Save(IEnumerable<IRelation> relations)
|
||||
{
|
||||
foreach (var hasIdentityGroup in relations.GroupBy(r => r.HasIdentity))
|
||||
{
|
||||
if (hasIdentityGroup.Key)
|
||||
{
|
||||
// Do updates, we can't really do a bulk update so this is still a 1 by 1 operation
|
||||
// however we can bulk populate the object types. It might be possible to bulk update
|
||||
// with SQL but would be pretty ugly and we're not really too worried about that for perf,
|
||||
// it's the bulk inserts we care about.
|
||||
var asArray = hasIdentityGroup.ToArray();
|
||||
foreach (var relation in hasIdentityGroup)
|
||||
{
|
||||
relation.UpdatingEntity();
|
||||
var dto = RelationFactory.BuildDto(relation);
|
||||
Database.Update(dto);
|
||||
}
|
||||
PopulateObjectTypes(asArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do bulk inserts
|
||||
var entitiesAndDtos = hasIdentityGroup.ToDictionary(
|
||||
r => // key = entity
|
||||
{
|
||||
r.AddingEntity();
|
||||
return r;
|
||||
},
|
||||
RelationFactory.BuildDto); // value = DTO
|
||||
|
||||
// Use NPoco's own InsertBulk command which will automatically re-populate the new Ids on the entities, our own
|
||||
// BulkInsertRecords does not cater for this.
|
||||
Database.InsertBulk(entitiesAndDtos.Values);
|
||||
|
||||
// All dtos now have IDs assigned
|
||||
foreach (var de in entitiesAndDtos)
|
||||
{
|
||||
// re-assign ID to the entity
|
||||
de.Key.Id = de.Value.Id;
|
||||
}
|
||||
|
||||
PopulateObjectTypes(entitiesAndDtos.Keys.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IRelation> GetPagedRelationsByQuery(IQuery<IRelation> query, long pageIndex, int pageSize, out long totalRecords, Ordering ordering)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
|
||||
if (ordering == null || ordering.IsEmpty)
|
||||
ordering = Ordering.By(SqlSyntax.GetQuotedColumn(Constants.DatabaseSchema.Tables.Relation, "id"));
|
||||
|
||||
var translator = new SqlTranslator<IRelation>(sql, query);
|
||||
sql = translator.Translate();
|
||||
|
||||
// apply ordering
|
||||
ApplyOrdering(ref sql, ordering);
|
||||
|
||||
var pageIndexToFetch = pageIndex + 1;
|
||||
var page = Database.Page<RelationDto>(pageIndexToFetch, pageSize, sql);
|
||||
var dtos = page.Items;
|
||||
totalRecords = page.TotalItems;
|
||||
|
||||
var relTypes = _relationTypeRepository.GetMany(dtos.Select(x => x.RelationType).Distinct().ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
var result = dtos.Select(r =>
|
||||
{
|
||||
if (!relTypes.TryGetValue(r.RelationType, out var relType))
|
||||
throw new InvalidOperationException(string.Format("RelationType with Id: {0} doesn't exist", r.RelationType));
|
||||
return DtoToEntity(r, relType);
|
||||
}).ToList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public void DeleteByParent(int parentId, params string[] relationTypeAliases)
|
||||
{
|
||||
var subQuery = Sql().Select<RelationDto>(x => x.Id)
|
||||
.From<RelationDto>()
|
||||
.InnerJoin<RelationTypeDto>().On<RelationDto, RelationTypeDto>(x => x.RelationType, x => x.Id)
|
||||
.Where<RelationDto>(x => x.ParentId == parentId);
|
||||
|
||||
if (relationTypeAliases.Length > 0)
|
||||
{
|
||||
subQuery.WhereIn<RelationTypeDto>(x => x.Alias, relationTypeAliases);
|
||||
}
|
||||
|
||||
Database.Execute(Sql().Delete<RelationDto>().WhereIn<RelationDto>(x => x.Id, subQuery));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to populate the object types after insert/update
|
||||
/// </summary>
|
||||
/// <param name="entities"></param>
|
||||
private void PopulateObjectTypes(params IRelation[] entities)
|
||||
{
|
||||
var entityIds = entities.Select(x => x.ParentId).Concat(entities.Select(y => y.ChildId)).Distinct();
|
||||
|
||||
var nodes = Database.Fetch<NodeDto>(Sql().Select<NodeDto>().From<NodeDto>()
|
||||
.WhereIn<NodeDto>(x => x.NodeId, entityIds))
|
||||
.ToDictionary(x => x.NodeId, x => x.NodeObjectType);
|
||||
|
||||
foreach (var e in entities)
|
||||
{
|
||||
if (nodes.TryGetValue(e.ParentId, out var parentObjectType))
|
||||
{
|
||||
e.ParentObjectType = parentObjectType.GetValueOrDefault();
|
||||
}
|
||||
if (nodes.TryGetValue(e.ChildId, out var childObjectType))
|
||||
{
|
||||
e.ChildObjectType = childObjectType.GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyOrdering(ref Sql<ISqlContext> sql, Ordering ordering)
|
||||
{
|
||||
if (sql == null) throw new ArgumentNullException(nameof(sql));
|
||||
if (ordering == null) throw new ArgumentNullException(nameof(ordering));
|
||||
|
||||
// TODO: although this works for name, it probably doesn't work for others without an alias of some sort
|
||||
var orderBy = ordering.OrderBy;
|
||||
|
||||
if (ordering.Direction == Direction.Ascending)
|
||||
sql.OrderBy(orderBy);
|
||||
else
|
||||
sql.OrderByDescending(orderBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
protected override void PersistNewItem(IRelationType entity)
|
||||
{
|
||||
entity.AddingEntity();
|
||||
|
||||
|
||||
CheckNullObjectTypeValues(entity);
|
||||
|
||||
var dto = RelationTypeFactory.BuildDto(entity);
|
||||
|
||||
var id = Convert.ToInt32(Database.Insert(dto));
|
||||
@@ -146,7 +148,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
protected override void PersistUpdatedItem(IRelationType entity)
|
||||
{
|
||||
entity.UpdatingEntity();
|
||||
|
||||
|
||||
CheckNullObjectTypeValues(entity);
|
||||
|
||||
var dto = RelationTypeFactory.BuildDto(entity);
|
||||
Database.Update(dto);
|
||||
|
||||
@@ -154,5 +158,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void CheckNullObjectTypeValues(IRelationType entity)
|
||||
{
|
||||
if (entity.ParentObjectType.HasValue && entity.ParentObjectType == Guid.Empty)
|
||||
entity.ParentObjectType = null;
|
||||
if (entity.ChildObjectType.HasValue && entity.ChildObjectType == Guid.Empty)
|
||||
entity.ChildObjectType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,6 +250,11 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName)
|
||||
}
|
||||
|
||||
public override void WriteLock(IDatabase db, params int[] lockIds)
|
||||
{
|
||||
WriteLock(db, TimeSpan.FromMilliseconds(1800), lockIds);
|
||||
}
|
||||
|
||||
public void WriteLock(IDatabase db, TimeSpan timeout, params int[] lockIds)
|
||||
{
|
||||
// soon as we get Database, a transaction is started
|
||||
|
||||
@@ -260,7 +265,7 @@ where tbl.[name]=@0 and col.[name]=@1;", tableName, columnName)
|
||||
// *not* using a unique 'WHERE IN' query here because the *order* of lockIds is important to avoid deadlocks
|
||||
foreach (var lockId in lockIds)
|
||||
{
|
||||
db.Execute(@"SET LOCK_TIMEOUT 1800;");
|
||||
db.Execute($"SET LOCK_TIMEOUT {timeout.TotalMilliseconds};");
|
||||
var i = db.Execute(@"UPDATE umbracoLock WITH (REPEATABLEREAD) 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 ArgumentException($"LockObject with id={lockId} does not exist.");
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
/// </remarks>
|
||||
public static Sql GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery, WhereInType whereInType = WhereInType.In)
|
||||
{
|
||||
//TODO: This is no longer necessary since this used to be a specific requirement for MySql!
|
||||
// Now we can do a Delete<T> + sub query, see RelationRepository.DeleteByParent for example
|
||||
|
||||
return
|
||||
new Sql(string.Format(
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace Umbraco.Core.Persistence
|
||||
_commandRetryPolicy = commandRetryPolicy;
|
||||
|
||||
EnableSqlTrace = EnableSqlTraceDefault;
|
||||
|
||||
NPocoDatabaseExtensions.ConfigureNPocoBulkExtensions();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,6 +59,8 @@ namespace Umbraco.Core.Persistence
|
||||
_logger = logger;
|
||||
|
||||
EnableSqlTrace = EnableSqlTraceDefault;
|
||||
|
||||
NPocoDatabaseExtensions.ConfigureNPocoBulkExtensions();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public class DataEditor : IDataEditor
|
||||
{
|
||||
private IDictionary<string, object> _defaultConfiguration;
|
||||
private IDataValueEditor _dataValueEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataEditor"/> class.
|
||||
@@ -90,7 +91,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// simple enough for now.</para>
|
||||
/// </remarks>
|
||||
// TODO: point of that one? shouldn't we always configure?
|
||||
public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? CreateValueEditor();
|
||||
public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? (_dataValueEditor ?? (_dataValueEditor = CreateValueEditor()));
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
@@ -113,7 +114,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
return ExplicitValueEditor;
|
||||
|
||||
var editor = CreateValueEditor();
|
||||
((DataValueEditor) editor).Configuration = configuration; // TODO: casting is bad
|
||||
((DataValueEditor)editor).Configuration = configuration; // TODO: casting is bad
|
||||
return editor;
|
||||
}
|
||||
|
||||
@@ -163,7 +164,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
protected virtual IDataValueEditor CreateValueEditor()
|
||||
{
|
||||
if (Attribute == null)
|
||||
throw new InvalidOperationException("The editor does not specify a view.");
|
||||
throw new InvalidOperationException($"The editor is not attributed with {nameof(DataEditorAttribute)}");
|
||||
|
||||
return new DataValueEditor(Attribute);
|
||||
}
|
||||
@@ -175,7 +176,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
var editor = new ConfigurationEditor();
|
||||
// pass the default configuration if this is not a property value editor
|
||||
if((Type & EditorType.PropertyValue) == 0)
|
||||
if ((Type & EditorType.PropertyValue) == 0)
|
||||
{
|
||||
editor.DefaultConfiguration = _defaultConfiguration;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataValueReferenceFactory>
|
||||
{
|
||||
public DataValueReferenceFactoryCollection(IEnumerable<IDataValueReferenceFactory> items)
|
||||
: base(items)
|
||||
{ }
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetAllReferences(PropertyCollection properties, PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
var trackedRelations = new List<UmbracoEntityReference>();
|
||||
|
||||
foreach (var p in properties)
|
||||
{
|
||||
if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue;
|
||||
|
||||
//TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here
|
||||
if (!p.PropertyType.VariesByNothing()) continue;
|
||||
var val = p.GetValue(); // get the invariant value
|
||||
|
||||
var valueEditor = editor.GetValueEditor();
|
||||
if (valueEditor is IDataValueReference reference)
|
||||
{
|
||||
var refs = reference.GetReferences(val);
|
||||
trackedRelations.AddRange(refs);
|
||||
}
|
||||
|
||||
// Loop over collection that may be add to existing property editors
|
||||
// implementation of GetReferences in IDataValueReference.
|
||||
// Allows developers to add support for references by a
|
||||
// package /property editor that did not implement IDataValueReference themselves
|
||||
foreach (var item in this)
|
||||
{
|
||||
// Check if this value reference is for this datatype/editor
|
||||
// Then call it's GetReferences method - to see if the value stored
|
||||
// in the dataeditor/property has referecnes to media/content items
|
||||
if (item.IsForEditor(editor))
|
||||
trackedRelations.AddRange(item.GetDataValueReference().GetReferences(val));
|
||||
}
|
||||
}
|
||||
|
||||
return trackedRelations;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public class DataValueReferenceFactoryCollectionBuilder : OrderedCollectionBuilderBase<DataValueReferenceFactoryCollectionBuilder, DataValueReferenceFactoryCollection, IDataValueReferenceFactory>
|
||||
{
|
||||
protected override DataValueReferenceFactoryCollectionBuilder This => this;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents an editor for editing data values.
|
||||
/// </summary>
|
||||
@@ -63,8 +64,26 @@ namespace Umbraco.Core.PropertyEditors
|
||||
|
||||
// TODO: / deal with this when unplugging the xml cache
|
||||
// why property vs propertyType? services should be injected! etc...
|
||||
|
||||
/// <summary>
|
||||
/// Used for serializing an <see cref="IContent"/> item for packaging
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <param name="localizationService"></param>
|
||||
/// <param name="published"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<XElement> ConvertDbToXml(Property property, IDataTypeService dataTypeService, ILocalizationService localizationService, bool published);
|
||||
|
||||
/// <summary>
|
||||
/// Used for serializing an <see cref="IContent"/> item for packaging
|
||||
/// </summary>
|
||||
/// <param name="propertyType"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="dataTypeService"></param>
|
||||
/// <returns></returns>
|
||||
XNode ConvertDbToXml(PropertyType propertyType, object value, IDataTypeService dataTypeService);
|
||||
|
||||
string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolve references from <see cref="IDataValueEditor"/> values
|
||||
/// </summary>
|
||||
public interface IDataValueReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns any references contained in the value
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<UmbracoEntityReference> GetReferences(object value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public interface IDataValueReferenceFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the DataValueReference lookup supports a datatype (data editor).
|
||||
/// </summary>
|
||||
/// <param name="dataType">The datatype.</param>
|
||||
/// <returns>A value indicating whether the converter supports a datatype.</returns>
|
||||
bool IsForEditor(IDataEditor dataEditor);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IDataValueReference GetDataValueReference();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using Umbraco.Core.Manifest;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
|
||||
|
||||
public class PropertyEditorCollection : BuilderCollectionBase<IDataEditor>
|
||||
{
|
||||
public PropertyEditorCollection(DataEditorCollection dataEditors, ManifestParser manifestParser)
|
||||
@@ -27,4 +29,4 @@ namespace Umbraco.Core.PropertyEditors
|
||||
return editor != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Umbraco.Core.Runtime
|
||||
// register persistence mappers - required by database factory so needs to be done here
|
||||
// means the only place the collection can be modified is in a runtime - afterwards it
|
||||
// has been frozen and it is too late
|
||||
composition.WithCollectionBuilder<MapperCollectionBuilder>().AddCoreMappers();
|
||||
composition.Mappers().AddCoreMappers();
|
||||
|
||||
// register the scope provider
|
||||
composition.RegisterUnique<ScopeProvider>(); // implements both IScopeProvider and IScopeAccessor
|
||||
@@ -70,11 +70,15 @@ namespace Umbraco.Core.Runtime
|
||||
composition.ManifestFilters();
|
||||
|
||||
// properties and parameters derive from data editors
|
||||
composition.WithCollectionBuilder<DataEditorCollectionBuilder>()
|
||||
composition.DataEditors()
|
||||
.Add(() => composition.TypeLoader.GetDataEditors());
|
||||
composition.RegisterUnique<PropertyEditorCollection>();
|
||||
composition.RegisterUnique<ParameterEditorCollection>();
|
||||
|
||||
// Used to determine if a datatype/editor should be storing/tracking
|
||||
// references to media item/s
|
||||
composition.DataValueReferenceFactories();
|
||||
|
||||
// register a server registrar, by default it's the db registrar
|
||||
composition.RegisterUnique<IServerRegistrar>(f =>
|
||||
{
|
||||
@@ -101,13 +105,13 @@ namespace Umbraco.Core.Runtime
|
||||
factory.GetInstance<IGlobalSettings>(),
|
||||
true, new DatabaseServerMessengerOptions()));
|
||||
|
||||
composition.WithCollectionBuilder<CacheRefresherCollectionBuilder>()
|
||||
composition.CacheRefreshers()
|
||||
.Add(() => composition.TypeLoader.GetCacheRefreshers());
|
||||
|
||||
composition.WithCollectionBuilder<PackageActionCollectionBuilder>()
|
||||
composition.PackageActions()
|
||||
.Add(() => composition.TypeLoader.GetPackageActions());
|
||||
|
||||
composition.WithCollectionBuilder<PropertyValueConverterCollectionBuilder>()
|
||||
composition.PropertyValueConverters()
|
||||
.Append(composition.TypeLoader.GetTypes<IPropertyValueConverter>());
|
||||
|
||||
composition.RegisterUnique<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
|
||||
@@ -115,7 +119,7 @@ namespace Umbraco.Core.Runtime
|
||||
composition.RegisterUnique<IShortStringHelper>(factory
|
||||
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetInstance<IUmbracoSettingsSection>())));
|
||||
|
||||
composition.WithCollectionBuilder<UrlSegmentProviderCollectionBuilder>()
|
||||
composition.UrlSegmentProviders()
|
||||
.Append<DefaultUrlSegmentProvider>();
|
||||
|
||||
composition.RegisterUnique<IMigrationBuilder>(factory => new MigrationBuilder(factory));
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace Umbraco.Core.Runtime
|
||||
// TODO: remove this in netcore, this is purely backwards compat hacks with the empty ctor
|
||||
if (MainDom == null)
|
||||
{
|
||||
MainDom = new MainDom(Logger);
|
||||
MainDom = new MainDom(Logger, new MainDomSemaphoreLock(Logger));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
|
||||
// TODO: Can't change namespace due to breaking changes, change in netcore
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// An application-wide distributed lock
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Disposing releases the lock
|
||||
/// </remarks>
|
||||
public interface IMainDomLock : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Acquires an application-wide distributed lock
|
||||
/// </summary>
|
||||
/// <param name="millisecondsTimeout"></param>
|
||||
/// <returns>
|
||||
/// An awaitable boolean value which will be false if the elapsed millsecondsTimeout value is exceeded
|
||||
/// </returns>
|
||||
Task<bool> AcquireLockAsync(int millisecondsTimeout);
|
||||
|
||||
/// <summary>
|
||||
/// Wait on a background thread to receive a signal from another AppDomain
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task ListenAsync();
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,13 @@ using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
namespace Umbraco.Core
|
||||
namespace Umbraco.Core.Runtime
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Provides the full implementation of <see cref="IMainDom"/>.
|
||||
/// </summary>
|
||||
@@ -20,18 +23,11 @@ namespace Umbraco.Core
|
||||
#region Vars
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IMainDomLock _mainDomLock;
|
||||
|
||||
// our own lock for local consistency
|
||||
private object _locko = new object();
|
||||
|
||||
// async lock representing the main domain lock
|
||||
private readonly SystemLock _systemLock;
|
||||
private IDisposable _systemLocker;
|
||||
|
||||
// event wait handle used to notify current main domain that it should
|
||||
// release the lock because a new domain wants to be the main domain
|
||||
private readonly EventWaitHandle _signal;
|
||||
|
||||
private bool _isInitialized;
|
||||
// indicates whether...
|
||||
private bool _isMainDom; // we are the main domain
|
||||
@@ -40,39 +36,19 @@ namespace Umbraco.Core
|
||||
// actions to run before releasing the main domain
|
||||
private readonly List<KeyValuePair<int, Action>> _callbacks = new List<KeyValuePair<int, Action>>();
|
||||
|
||||
private const int LockTimeoutMilliseconds = 90000; // (1.5 * 60 * 1000) == 1 min 30 seconds
|
||||
private const int LockTimeoutMilliseconds = 40000; // 40 seconds
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
// initializes a new instance of MainDom
|
||||
public MainDom(ILogger logger)
|
||||
public MainDom(ILogger logger, IMainDomLock systemLock)
|
||||
{
|
||||
HostingEnvironment.RegisterObject(this);
|
||||
|
||||
_logger = logger;
|
||||
|
||||
// HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail
|
||||
var appId = HostingEnvironment.ApplicationID?.ReplaceNonAlphanumericChars(string.Empty) ?? string.Empty;
|
||||
|
||||
// combining with the physical path because if running on eg IIS Express,
|
||||
// two sites could have the same appId even though they are different.
|
||||
//
|
||||
// now what could still collide is... two sites, running in two different processes
|
||||
// and having the same appId, and running on the same app physical path
|
||||
//
|
||||
// we *cannot* use the process ID here because when an AppPool restarts it is
|
||||
// a new process for the same application path
|
||||
|
||||
var appPath = HostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty;
|
||||
var hash = (appId + ":::" + appPath).GenerateHash<SHA1>();
|
||||
|
||||
var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK";
|
||||
_systemLock = new SystemLock(lockName);
|
||||
|
||||
var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT";
|
||||
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
_mainDomLock = systemLock;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -141,13 +117,14 @@ namespace Umbraco.Core
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.Debug<MainDom>("Stopped ({SignalSource})", source);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// in any case...
|
||||
_isMainDom = false;
|
||||
_systemLocker?.Dispose();
|
||||
_mainDomLock.Dispose();
|
||||
_logger.Info<MainDom>("Released ({SignalSource})", source);
|
||||
}
|
||||
|
||||
@@ -167,36 +144,33 @@ namespace Umbraco.Core
|
||||
|
||||
_logger.Info<MainDom>("Acquiring.");
|
||||
|
||||
// signal other instances that we want the lock, then wait one the lock,
|
||||
// which may timeout, and this is accepted - see comments below
|
||||
// Get the lock
|
||||
var acquired = _mainDomLock.AcquireLockAsync(LockTimeoutMilliseconds).GetAwaiter().GetResult();
|
||||
|
||||
// signal, then wait for the lock, then make sure the event is
|
||||
// reset (maybe there was noone listening..)
|
||||
_signal.Set();
|
||||
if (!acquired)
|
||||
{
|
||||
_logger.Info<MainDom>("Cannot acquire (timeout).");
|
||||
|
||||
// if more than 1 instance reach that point, one will get the lock
|
||||
// and the other one will timeout, which is accepted
|
||||
// In previous versions we'd let a TimeoutException be thrown
|
||||
// and the appdomain would not start. We have the opportunity to allow it to
|
||||
// start without having MainDom? This would mean that it couldn't write
|
||||
// to nucache/examine and would only be ok if this was a super short lived appdomain.
|
||||
// maybe safer to just keep throwing in this case.
|
||||
|
||||
throw new TimeoutException("Cannot acquire MainDom");
|
||||
// return false;
|
||||
}
|
||||
|
||||
//This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset.
|
||||
try
|
||||
{
|
||||
_systemLocker = _systemLock.Lock(LockTimeoutMilliseconds);
|
||||
// Listen for the signal from another AppDomain coming online to release the lock
|
||||
_mainDomLock.ListenAsync().ContinueWith(_ => OnSignal("signal"));
|
||||
}
|
||||
finally
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
// we need to reset the event, because otherwise we would end up
|
||||
// signaling ourselves and committing suicide immediately.
|
||||
// only 1 instance can reach that point, but other instances may
|
||||
// have started and be trying to get the lock - they will timeout,
|
||||
// which is accepted
|
||||
|
||||
_signal.Reset();
|
||||
// the waiting task could be canceled if this appdomain is naturally shutting down, we'll just swallow this exception
|
||||
_logger.Warn<MainDom>(ex, ex.Message);
|
||||
}
|
||||
|
||||
//WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread
|
||||
|
||||
_signal.WaitOneAsync()
|
||||
.ContinueWith(_ => OnSignal("signal"));
|
||||
|
||||
_logger.Info<MainDom>("Acquired.");
|
||||
return true;
|
||||
@@ -205,6 +179,10 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current domain is the main domain.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The lazy initializer call will only call the Acquire callback when it's not been initialized, else it will just return
|
||||
/// the value from _isMainDom which means when we set _isMainDom to false again after being signaled, this will return false;
|
||||
/// </remarks>
|
||||
public bool IsMainDom => LazyInitializer.EnsureInitialized(ref _isMainDom, ref _isInitialized, ref _locko, () => Acquire());
|
||||
|
||||
// IRegisteredObject
|
||||
@@ -230,8 +208,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_signal?.Close();
|
||||
_signal?.Dispose();
|
||||
_mainDomLock.Dispose();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
@@ -244,5 +221,25 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static string GetMainDomId()
|
||||
{
|
||||
// HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail
|
||||
var appId = HostingEnvironment.ApplicationID?.ReplaceNonAlphanumericChars(string.Empty) ?? string.Empty;
|
||||
|
||||
// combining with the physical path because if running on eg IIS Express,
|
||||
// two sites could have the same appId even though they are different.
|
||||
//
|
||||
// now what could still collide is... two sites, running in two different processes
|
||||
// and having the same appId, and running on the same app physical path
|
||||
//
|
||||
// we *cannot* use the process ID here because when an AppPool restarts it is
|
||||
// a new process for the same application path
|
||||
|
||||
var appPath = HostingEnvironment.ApplicationPhysicalPath?.ToLowerInvariant() ?? string.Empty;
|
||||
var hash = (appId + ":::" + appPath).GenerateHash<SHA1>();
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses a system-wide Semaphore and EventWaitHandle to synchronize the current AppDomain
|
||||
/// </summary>
|
||||
internal class MainDomSemaphoreLock : IMainDomLock
|
||||
{
|
||||
private readonly SystemLock _systemLock;
|
||||
|
||||
// event wait handle used to notify current main domain that it should
|
||||
// release the lock because a new domain wants to be the main domain
|
||||
private readonly EventWaitHandle _signal;
|
||||
private readonly ILogger _logger;
|
||||
private IDisposable _lockRelease;
|
||||
|
||||
public MainDomSemaphoreLock(ILogger logger)
|
||||
{
|
||||
var lockName = "UMBRACO-" + MainDom.GetMainDomId() + "-MAINDOM-LCK";
|
||||
_systemLock = new SystemLock(lockName);
|
||||
|
||||
var eventName = "UMBRACO-" + MainDom.GetMainDomId() + "-MAINDOM-EVT";
|
||||
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
//WaitOneAsync (ext method) will wait for a signal without blocking the main thread, the waiting is done on a background thread
|
||||
public Task ListenAsync() => _signal.WaitOneAsync();
|
||||
|
||||
public Task<bool> AcquireLockAsync(int millisecondsTimeout)
|
||||
{
|
||||
// signal other instances that we want the lock, then wait on the lock,
|
||||
// which may timeout, and this is accepted - see comments below
|
||||
|
||||
// signal, then wait for the lock, then make sure the event is
|
||||
// reset (maybe there was noone listening..)
|
||||
_signal.Set();
|
||||
|
||||
// if more than 1 instance reach that point, one will get the lock
|
||||
// and the other one will timeout, which is accepted
|
||||
|
||||
//This can throw a TimeoutException - in which case should this be in a try/finally to ensure the signal is always reset.
|
||||
try
|
||||
{
|
||||
_lockRelease = _systemLock.Lock(millisecondsTimeout);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
catch (TimeoutException ex)
|
||||
{
|
||||
_logger.Error<MainDomSemaphoreLock>(ex);
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// we need to reset the event, because otherwise we would end up
|
||||
// signaling ourselves and committing suicide immediately.
|
||||
// only 1 instance can reach that point, but other instances may
|
||||
// have started and be trying to get the lock - they will timeout,
|
||||
// which is accepted
|
||||
|
||||
_signal.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_lockRelease?.Dispose();
|
||||
_signal.Close();
|
||||
_signal.Dispose();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Runtime
|
||||
{
|
||||
internal class SqlMainDomLock : IMainDomLock
|
||||
{
|
||||
private string _lockId;
|
||||
private const string MainDomKey = "Umbraco.Core.Runtime.SqlMainDom";
|
||||
private const string UpdatedSuffix = "_updated";
|
||||
private readonly ILogger _logger;
|
||||
private IUmbracoDatabase _db;
|
||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
private SqlServerSyntaxProvider _sqlServerSyntax = new SqlServerSyntaxProvider();
|
||||
private bool _mainDomChanging = false;
|
||||
private readonly UmbracoDatabaseFactory _dbFactory;
|
||||
private bool _hasError;
|
||||
private object _locker = new object();
|
||||
|
||||
public SqlMainDomLock(ILogger logger)
|
||||
{
|
||||
// unique id for our appdomain, this is more unique than the appdomain id which is just an INT counter to its safer
|
||||
_lockId = Guid.NewGuid().ToString();
|
||||
_logger = logger;
|
||||
_dbFactory = new UmbracoDatabaseFactory(
|
||||
Constants.System.UmbracoConnectionName,
|
||||
_logger,
|
||||
new Lazy<IMapperCollection>(() => new Persistence.Mappers.MapperCollection(Enumerable.Empty<BaseMapper>())));
|
||||
}
|
||||
|
||||
public async Task<bool> AcquireLockAsync(int millisecondsTimeout)
|
||||
{
|
||||
if (!(_dbFactory.SqlContext.SqlSyntax is SqlServerSyntaxProvider sqlServerSyntaxProvider))
|
||||
throw new NotSupportedException("SqlMainDomLock is only supported for Sql Server");
|
||||
|
||||
_sqlServerSyntax = sqlServerSyntaxProvider;
|
||||
|
||||
_logger.Debug<SqlMainDomLock>("Acquiring lock...");
|
||||
|
||||
var db = GetDatabase();
|
||||
|
||||
var tempId = Guid.NewGuid().ToString();
|
||||
|
||||
try
|
||||
{
|
||||
db.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
try
|
||||
{
|
||||
// wait to get a write lock
|
||||
_sqlServerSyntax.WriteLock(db, TimeSpan.FromMilliseconds(millisecondsTimeout), Constants.Locks.MainDom);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsLockTimeoutException(ex))
|
||||
{
|
||||
_logger.Error<SqlMainDomLock>(ex, "Sql timeout occurred, could not acquire MainDom.");
|
||||
_hasError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// unexpected (will be caught below)
|
||||
throw;
|
||||
}
|
||||
|
||||
var result = InsertLockRecord(tempId); //we change the row to a random Id to signal other MainDom to shutdown
|
||||
if (result == RecordPersistenceType.Insert)
|
||||
{
|
||||
// if we've inserted, then there was no MainDom so we can instantly acquire
|
||||
|
||||
// TODO: see the other TODO, could we just delete the row and that would indicate that we
|
||||
// are MainDom? then we don't leave any orphan rows behind.
|
||||
|
||||
InsertLockRecord(_lockId); // so update with our appdomain id
|
||||
_logger.Debug<SqlMainDomLock>("Acquired with ID {LockId}", _lockId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we've updated, this means there is an active MainDom, now we need to wait to
|
||||
// for the current MainDom to shutdown which also requires releasing our write lock
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResetDatabase();
|
||||
// unexpected
|
||||
_logger.Error<SqlMainDomLock>(ex, "Unexpected error, cannot acquire MainDom");
|
||||
_hasError = true;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
db?.CompleteTransaction();
|
||||
}
|
||||
|
||||
return await WaitForExistingAsync(tempId, millisecondsTimeout);
|
||||
}
|
||||
|
||||
public Task ListenAsync()
|
||||
{
|
||||
if (_hasError)
|
||||
{
|
||||
_logger.Warn<SqlMainDomLock>("Could not acquire MainDom, listening is canceled.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Create a long running task (dedicated thread)
|
||||
// to poll to check if we are still the MainDom registered in the DB
|
||||
return Task.Factory.StartNew(ListeningLoop, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
|
||||
}
|
||||
|
||||
private void ListeningLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// poll every 1 second
|
||||
Thread.Sleep(1000);
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
// If cancellation has been requested we will just exit. Depending on timing of the shutdown,
|
||||
// we will have already flagged _mainDomChanging = true, or we're shutting down faster than
|
||||
// the other MainDom is taking to startup. In this case the db row will just be deleted and the
|
||||
// new MainDom will just take over.
|
||||
if (_cancellationTokenSource.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
var db = GetDatabase();
|
||||
|
||||
try
|
||||
{
|
||||
db.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
// get a read lock
|
||||
_sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom);
|
||||
|
||||
// TODO: We could in theory just check if the main dom row doesn't exist, that could indicate that
|
||||
// we are still the maindom. An empty value might be better because then we won't have any orphan rows
|
||||
// if the app is terminated. Could that work?
|
||||
|
||||
if (!IsMainDomValue(_lockId))
|
||||
{
|
||||
// we are no longer main dom, another one has come online, exit
|
||||
_mainDomChanging = true;
|
||||
_logger.Debug<SqlMainDomLock>("Detected new booting application, releasing MainDom lock.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResetDatabase();
|
||||
// unexpected
|
||||
_logger.Error<SqlMainDomLock>(ex, "Unexpected error, listening is canceled.");
|
||||
_hasError = true;
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
db?.CompleteTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetDatabase()
|
||||
{
|
||||
if (_db.InTransaction)
|
||||
_db.AbortTransaction();
|
||||
_db.Dispose();
|
||||
_db = null;
|
||||
}
|
||||
|
||||
private IUmbracoDatabase GetDatabase()
|
||||
{
|
||||
if (_db != null)
|
||||
return _db;
|
||||
|
||||
_db = _dbFactory.CreateDatabase();
|
||||
return _db;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait for any existing MainDom to release so we can continue booting
|
||||
/// </summary>
|
||||
/// <param name="tempId"></param>
|
||||
/// <param name="millisecondsTimeout"></param>
|
||||
/// <returns></returns>
|
||||
private Task<bool> WaitForExistingAsync(string tempId, int millisecondsTimeout)
|
||||
{
|
||||
var updatedTempId = tempId + UpdatedSuffix;
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var db = GetDatabase();
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
while(true)
|
||||
{
|
||||
// poll very often, we need to take over as fast as we can
|
||||
Thread.Sleep(100);
|
||||
|
||||
try
|
||||
{
|
||||
db.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
// get a read lock
|
||||
_sqlServerSyntax.ReadLock(db, Constants.Locks.MainDom);
|
||||
|
||||
// the row
|
||||
var mainDomRows = db.Fetch<KeyValueDto>("SELECT * FROM umbracoKeyValue WHERE [key] = @key", new { key = MainDomKey });
|
||||
|
||||
if (mainDomRows.Count == 0 || mainDomRows[0].Value == updatedTempId)
|
||||
{
|
||||
// the other main dom has updated our record
|
||||
// Or the other maindom shutdown super fast and just deleted the record
|
||||
// which indicates that we
|
||||
// can acquire it and it has shutdown.
|
||||
|
||||
_sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom);
|
||||
|
||||
// so now we update the row with our appdomain id
|
||||
InsertLockRecord(_lockId);
|
||||
_logger.Debug<SqlMainDomLock>("Acquired with ID {LockId}", _lockId);
|
||||
return true;
|
||||
}
|
||||
else if (mainDomRows.Count == 1 && !mainDomRows[0].Value.StartsWith(tempId))
|
||||
{
|
||||
// in this case, the prefixed ID is different which means
|
||||
// another new AppDomain has come online and is wanting to take over. In that case, we will not
|
||||
// acquire.
|
||||
|
||||
_logger.Debug<SqlMainDomLock>("Cannot acquire, another booting application detected.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResetDatabase();
|
||||
|
||||
if (IsLockTimeoutException(ex))
|
||||
{
|
||||
_logger.Error<SqlMainDomLock>(ex, "Sql timeout occurred, waiting for existing MainDom is canceled.");
|
||||
_hasError = true;
|
||||
return false;
|
||||
}
|
||||
// unexpected
|
||||
_logger.Error<SqlMainDomLock>(ex, "Unexpected error, waiting for existing MainDom is canceled.");
|
||||
_hasError = true;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
db?.CompleteTransaction();
|
||||
}
|
||||
|
||||
if (watch.ElapsedMilliseconds >= millisecondsTimeout)
|
||||
{
|
||||
// if the timeout has elapsed, it either means that the other main dom is taking too long to shutdown,
|
||||
// or it could mean that the previous appdomain was terminated and didn't clear out the main dom SQL row
|
||||
// and it's just been left as an orphan row.
|
||||
// There's really know way of knowing unless we are constantly updating the row for the current maindom
|
||||
// which isn't ideal.
|
||||
// So... we're going to 'just' take over, if the writelock works then we'll assume we're ok
|
||||
|
||||
_logger.Debug<SqlMainDomLock>("Timeout elapsed, assuming orphan row, acquiring MainDom.");
|
||||
|
||||
try
|
||||
{
|
||||
db.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
_sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom);
|
||||
|
||||
// so now we update the row with our appdomain id
|
||||
InsertLockRecord(_lockId);
|
||||
_logger.Debug<SqlMainDomLock>("Acquired with ID {LockId}", _lockId);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResetDatabase();
|
||||
|
||||
if (IsLockTimeoutException(ex))
|
||||
{
|
||||
// something is wrong, we cannot acquire, not much we can do
|
||||
_logger.Error<SqlMainDomLock>(ex, "Sql timeout occurred, could not forcibly acquire MainDom.");
|
||||
_hasError = true;
|
||||
return false;
|
||||
}
|
||||
_logger.Error<SqlMainDomLock>(ex, "Unexpected error, could not forcibly acquire MainDom.");
|
||||
_hasError = true;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
db?.CompleteTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts or updates the key/value row
|
||||
/// </summary>
|
||||
private RecordPersistenceType InsertLockRecord(string id)
|
||||
{
|
||||
var db = GetDatabase();
|
||||
return db.InsertOrUpdate(new KeyValueDto
|
||||
{
|
||||
Key = MainDomKey,
|
||||
Value = id,
|
||||
Updated = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the DB row value is equals the value
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool IsMainDomValue(string val)
|
||||
{
|
||||
var db = GetDatabase();
|
||||
return db.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoKeyValue WHERE [key] = @key AND [value] = @val",
|
||||
new { key = MainDomKey, val = val }) == 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the exception is an SQL timeout
|
||||
/// </summary>
|
||||
/// <param name="exception"></param>
|
||||
/// <returns></returns>
|
||||
private bool IsLockTimeoutException(Exception exception) => exception is SqlException sqlException && sqlException.Number == 1222;
|
||||
|
||||
#region IDisposable Support
|
||||
private bool _disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
// immediately cancel all sub-tasks, we don't want them to keep querying
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource.Dispose();
|
||||
|
||||
var db = GetDatabase();
|
||||
try
|
||||
{
|
||||
db.BeginTransaction(IsolationLevel.ReadCommitted);
|
||||
|
||||
// get a write lock
|
||||
_sqlServerSyntax.WriteLock(db, Constants.Locks.MainDom);
|
||||
|
||||
// When we are disposed, it means we have released the MainDom lock
|
||||
// and called all MainDom release callbacks, in this case
|
||||
// if another maindom is actually coming online we need
|
||||
// to signal to the MainDom coming online that we have shutdown.
|
||||
// To do that, we update the existing main dom DB record with a suffixed "_updated" string.
|
||||
// Otherwise, if we are just shutting down, we want to just delete the row.
|
||||
if (_mainDomChanging)
|
||||
{
|
||||
_logger.Debug<SqlMainDomLock>("Releasing MainDom, updating row, new application is booting.");
|
||||
db.Execute($"UPDATE umbracoKeyValue SET [value] = [value] + '{UpdatedSuffix}' WHERE [key] = @key", new { key = MainDomKey });
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug<SqlMainDomLock>("Releasing MainDom, deleting row, application is shutting down.");
|
||||
db.Execute("DELETE FROM umbracoKeyValue WHERE [key] = @key", new { key = MainDomKey });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResetDatabase();
|
||||
_logger.Error<SqlMainDomLock>(ex, "Unexpected error during dipsose.");
|
||||
_hasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
db?.CompleteTransaction();
|
||||
ResetDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Scoping
|
||||
/// Provides scopes.
|
||||
/// </summary>
|
||||
public interface IScopeProvider
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an ambient scope.
|
||||
/// </summary>
|
||||
|
||||
@@ -8,136 +8,162 @@ namespace Umbraco.Core.Services
|
||||
public interface IRelationService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Relation"/> by its Id
|
||||
/// Gets a <see cref="IRelation"/> by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="Relation"/></param>
|
||||
/// <returns>A <see cref="Relation"/> object</returns>
|
||||
/// <param name="id">Id of the <see cref="IRelation"/></param>
|
||||
/// <returns>A <see cref="IRelation"/> object</returns>
|
||||
IRelation GetById(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RelationType"/> by its Id
|
||||
/// Gets a <see cref="IRelationType"/> by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="RelationType"/></param>
|
||||
/// <returns>A <see cref="RelationType"/> object</returns>
|
||||
/// <param name="id">Id of the <see cref="IRelationType"/></param>
|
||||
/// <returns>A <see cref="IRelationType"/> object</returns>
|
||||
IRelationType GetRelationTypeById(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RelationType"/> by its Id
|
||||
/// Gets a <see cref="IRelationType"/> by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="RelationType"/></param>
|
||||
/// <returns>A <see cref="RelationType"/> object</returns>
|
||||
/// <param name="id">Id of the <see cref="IRelationType"/></param>
|
||||
/// <returns>A <see cref="IRelationType"/> object</returns>
|
||||
IRelationType GetRelationTypeById(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RelationType"/> by its Alias
|
||||
/// Gets a <see cref="IRelationType"/> by its Alias
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of the <see cref="RelationType"/></param>
|
||||
/// <returns>A <see cref="RelationType"/> object</returns>
|
||||
/// <param name="alias">Alias of the <see cref="IRelationType"/></param>
|
||||
/// <returns>A <see cref="IRelationType"/> object</returns>
|
||||
IRelationType GetRelationTypeByAlias(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects
|
||||
/// Gets all <see cref="IRelation"/> objects
|
||||
/// </summary>
|
||||
/// <param name="ids">Optional array of integer ids to return relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetAllRelations(params int[] ids);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>
|
||||
/// Gets all <see cref="IRelation"/> objects by their <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType"><see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetAllRelationsByRelationType(RelationType relationType);
|
||||
/// <param name="relationType"><see cref="IRelation"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetAllRelationsByRelationType(IRelationType relationType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>'s Id
|
||||
/// Gets all <see cref="IRelation"/> objects by their <see cref="IRelationType"/>'s Id
|
||||
/// </summary>
|
||||
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <param name="relationTypeId">Id of the <see cref="IRelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetAllRelationsByRelationType(int relationTypeId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects
|
||||
/// Gets all <see cref="IRelation"/> objects
|
||||
/// </summary>
|
||||
/// <param name="ids">Optional array of integer ids to return relationtypes for</param>
|
||||
/// <returns>An enumerable list of <see cref="RelationType"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelationType> GetAllRelationTypes(params int[] ids);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their parent Id
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their parent Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the parent to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByParentId(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their parent entity
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their parent Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the parent to retrieve relations for</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByParentId(int id, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their parent entity
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent Entity to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByParent(IUmbracoEntity parent);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their parent entity
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their parent entity
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent Entity to retrieve relations for</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByParent(IUmbracoEntity parent, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child Id
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their child Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the child to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByChildId(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child Entity
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their child Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the child to retrieve relations for</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByChildId(int id, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their child Entity
|
||||
/// </summary>
|
||||
/// <param name="child">Child Entity to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByChild(IUmbracoEntity child);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child Entity
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their child Entity
|
||||
/// </summary>
|
||||
/// <param name="child">Child Entity to retrieve relations for</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByChild(IUmbracoEntity child, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child or parent Id.
|
||||
/// Gets a list of <see cref="IRelation"/> objects by their child or parent Id.
|
||||
/// Using this method will get you all relations regards of it being a child or parent relation.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the child or parent to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByParentOrChildId(int id);
|
||||
|
||||
IEnumerable<IRelation> GetByParentOrChildId(int id, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by the Name of the <see cref="RelationType"/>
|
||||
/// Gets a list of <see cref="IRelation"/> objects by the Name of the <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeName">Name of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <param name="relationTypeName">Name of the <see cref="IRelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByRelationTypeName(string relationTypeName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by the Alias of the <see cref="RelationType"/>
|
||||
/// Gets a list of <see cref="IRelation"/> objects by the Alias of the <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeAlias">Alias of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <param name="relationTypeAlias">Alias of the <see cref="IRelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByRelationTypeAlias(string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by the Id of the <see cref="RelationType"/>
|
||||
/// Gets a list of <see cref="IRelation"/> objects by the Id of the <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <param name="relationTypeId">Id of the <see cref="IRelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="IRelation"/> objects</returns>
|
||||
IEnumerable<IRelation> GetByRelationTypeId(int relationTypeId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged result of <see cref="IRelation"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeId"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalChildren"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering ordering = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Child object from a Relation as an <see cref="IUmbracoEntity"/>
|
||||
/// </summary>
|
||||
@@ -173,6 +199,26 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
|
||||
IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<IRelation> relations);
|
||||
|
||||
/// <summary>
|
||||
/// Returns paged parent entities for a related child id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalChildren"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes);
|
||||
|
||||
/// <summary>
|
||||
/// Returns paged child entities for a related parent id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalChildren"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Parent and Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
|
||||
/// </summary>
|
||||
@@ -186,7 +232,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="parentId">Id of the parent</param>
|
||||
/// <param name="childId">Id of the child</param>
|
||||
/// <param name="relationType">The type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <returns>The created <see cref="IRelation"/></returns>
|
||||
IRelation Relate(int parentId, int childId, IRelationType relationType);
|
||||
|
||||
/// <summary>
|
||||
@@ -195,7 +241,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="parent">Parent entity</param>
|
||||
/// <param name="child">Child entity</param>
|
||||
/// <param name="relationType">The type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <returns>The created <see cref="IRelation"/></returns>
|
||||
IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType);
|
||||
|
||||
/// <summary>
|
||||
@@ -204,7 +250,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="parentId">Id of the parent</param>
|
||||
/// <param name="childId">Id of the child</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <returns>The created <see cref="IRelation"/></returns>
|
||||
IRelation Relate(int parentId, int childId, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
@@ -213,14 +259,14 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="parent">Parent entity</param>
|
||||
/// <param name="child">Child entity</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <returns>The created <see cref="IRelation"/></returns>
|
||||
IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether any relations exists for the passed in <see cref="RelationType"/>.
|
||||
/// Checks whether any relations exists for the passed in <see cref="IRelationType"/>.
|
||||
/// </summary>
|
||||
/// <param name="relationType"><see cref="RelationType"/> to check for relations</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists for the given <see cref="RelationType"/>, otherwise <c>False</c></returns>
|
||||
/// <param name="relationType"><see cref="IRelationType"/> to check for relations</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists for the given <see cref="IRelationType"/>, otherwise <c>False</c></returns>
|
||||
bool HasRelations(IRelationType relationType);
|
||||
|
||||
/// <summary>
|
||||
@@ -265,33 +311,35 @@ namespace Umbraco.Core.Services
|
||||
bool AreRelated(int parentId, int childId, string relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a <see cref="Relation"/>
|
||||
/// Saves a <see cref="IRelation"/>
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to save</param>
|
||||
void Save(IRelation relation);
|
||||
|
||||
void Save(IEnumerable<IRelation> relations);
|
||||
|
||||
/// <summary>
|
||||
/// Saves a <see cref="RelationType"/>
|
||||
/// Saves a <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType">RelationType to Save</param>
|
||||
void Save(IRelationType relationType);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a <see cref="Relation"/>
|
||||
/// Deletes a <see cref="IRelation"/>
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to Delete</param>
|
||||
void Delete(IRelation relation);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a <see cref="RelationType"/>
|
||||
/// Deletes a <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType">RelationType to Delete</param>
|
||||
void Delete(IRelationType relationType);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all <see cref="Relation"/> objects based on the passed in <see cref="RelationType"/>
|
||||
/// Deletes all <see cref="IRelation"/> objects based on the passed in <see cref="IRelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType"><see cref="RelationType"/> to Delete Relations for</param>
|
||||
/// <param name="relationType"><see cref="IRelationType"/> to Delete Relations for</param>
|
||||
void DeleteRelationsOfType(IRelationType relationType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Relation"/> by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="Relation"/></param>
|
||||
/// <returns>A <see cref="Relation"/> object</returns>
|
||||
/// <inheritdoc />
|
||||
public IRelation GetById(int id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -38,11 +34,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RelationType"/> by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="RelationType"/></param>
|
||||
/// <returns>A <see cref="RelationType"/> object</returns>
|
||||
/// <inheritdoc />
|
||||
public IRelationType GetRelationTypeById(int id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -51,11 +43,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RelationType"/> by its Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="RelationType"/></param>
|
||||
/// <returns>A <see cref="RelationType"/> object</returns>
|
||||
/// <inheritdoc />
|
||||
public IRelationType GetRelationTypeById(Guid id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -64,25 +52,10 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RelationType"/> by its Alias
|
||||
/// </summary>
|
||||
/// <param name="alias">Alias of the <see cref="RelationType"/></param>
|
||||
/// <returns>A <see cref="RelationType"/> object</returns>
|
||||
public IRelationType GetRelationTypeByAlias(string alias)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IRelationType>().Where(x => x.Alias == alias);
|
||||
return _relationTypeRepository.Get(query).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IRelationType GetRelationTypeByAlias(string alias) => GetRelationType(alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects
|
||||
/// </summary>
|
||||
/// <param name="ids">Optional array of integer ids to return relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetAllRelations(params int[] ids)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -91,21 +64,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType"><see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetAllRelationsByRelationType(RelationType relationType)
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetAllRelationsByRelationType(IRelationType relationType)
|
||||
{
|
||||
return GetAllRelationsByRelationType(relationType.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects by their <see cref="RelationType"/>'s Id
|
||||
/// </summary>
|
||||
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetAllRelationsByRelationType(int relationTypeId)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -115,11 +80,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="Relation"/> objects
|
||||
/// </summary>
|
||||
/// <param name="ids">Optional array of integer ids to return relationtypes for</param>
|
||||
/// <returns>An enumerable list of <see cref="RelationType"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelationType> GetAllRelationTypes(params int[] ids)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -128,82 +89,65 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their parent Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the parent to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetByParentId(int id)
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentId(int id) => GetByParentId(id, null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentId(int id, string relationTypeAlias)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IRelation>().Where(x => x.ParentId == id);
|
||||
return _relationRepository.Get(query);
|
||||
if (relationTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
var qry1 = Query<IRelation>().Where(x => x.ParentId == id);
|
||||
return _relationRepository.Get(qry1);
|
||||
}
|
||||
|
||||
var relationType = GetRelationType(relationTypeAlias);
|
||||
if (relationType == null)
|
||||
return Enumerable.Empty<IRelation>();
|
||||
|
||||
var qry2 = Query<IRelation>().Where(x => x.ParentId == id && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(qry2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their parent entity
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent Entity to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetByParent(IUmbracoEntity parent)
|
||||
{
|
||||
return GetByParentId(parent.Id);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParent(IUmbracoEntity parent) => GetByParentId(parent.Id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their parent entity
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent Entity to retrieve relations for</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetByParent(IUmbracoEntity parent, string relationTypeAlias)
|
||||
{
|
||||
return GetByParent(parent).Where(relation => relation.RelationType.Alias == relationTypeAlias);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParent(IUmbracoEntity parent, string relationTypeAlias) => GetByParentId(parent.Id, relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child Id
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the child to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetByChildId(int id)
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByChildId(int id) => GetByChildId(id, null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByChildId(int id, string relationTypeAlias)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IRelation>().Where(x => x.ChildId == id);
|
||||
return _relationRepository.Get(query);
|
||||
if (relationTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
var qry1 = Query<IRelation>().Where(x => x.ChildId == id);
|
||||
return _relationRepository.Get(qry1);
|
||||
}
|
||||
|
||||
var relationType = GetRelationType(relationTypeAlias);
|
||||
if (relationType == null)
|
||||
return Enumerable.Empty<IRelation>();
|
||||
|
||||
var qry2 = Query<IRelation>().Where(x => x.ChildId == id && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(qry2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child Entity
|
||||
/// </summary>
|
||||
/// <param name="child">Child Entity to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetByChild(IUmbracoEntity child)
|
||||
{
|
||||
return GetByChildId(child.Id);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByChild(IUmbracoEntity child) => GetByChildId(child.Id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child Entity
|
||||
/// </summary>
|
||||
/// <param name="child">Child Entity to retrieve relations for</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to retrieve</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
public IEnumerable<IRelation> GetByChild(IUmbracoEntity child, string relationTypeAlias)
|
||||
{
|
||||
return GetByChild(child).Where(relation => relation.RelationType.Alias == relationTypeAlias);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByChild(IUmbracoEntity child, string relationTypeAlias) => GetByChildId(child.Id, relationTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by their child or parent Id.
|
||||
/// Using this method will get you all relations regards of it being a child or parent relation.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the child or parent to retrieve relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentOrChildId(int id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -217,8 +161,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var rtQuery = Query<IRelationType>().Where(x => x.Alias == relationTypeAlias);
|
||||
var relationType = _relationTypeRepository.Get(rtQuery).FirstOrDefault();
|
||||
var relationType = GetRelationType(relationTypeAlias);
|
||||
if (relationType == null)
|
||||
return Enumerable.Empty<IRelation>();
|
||||
|
||||
@@ -227,16 +170,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by the Name of the <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeName">Name of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByRelationTypeName(string relationTypeName)
|
||||
{
|
||||
List<int> relationTypeIds;
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
//This is a silly query - but i guess it's needed in case someone has more than one relation with the same Name (not alias), odd.
|
||||
var query = Query<IRelationType>().Where(x => x.Name == relationTypeName);
|
||||
var relationTypes = _relationTypeRepository.Get(query);
|
||||
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
|
||||
@@ -247,31 +187,17 @@ namespace Umbraco.Core.Services.Implement
|
||||
: GetRelationsByListOfTypeIds(relationTypeIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by the Alias of the <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeAlias">Alias of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByRelationTypeAlias(string relationTypeAlias)
|
||||
{
|
||||
List<int> relationTypeIds;
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IRelationType>().Where(x => x.Alias == relationTypeAlias);
|
||||
var relationTypes = _relationTypeRepository.Get(query);
|
||||
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
|
||||
}
|
||||
|
||||
return relationTypeIds.Count == 0
|
||||
var relationType = GetRelationType(relationTypeAlias);
|
||||
|
||||
return relationType == null
|
||||
? Enumerable.Empty<IRelation>()
|
||||
: GetRelationsByListOfTypeIds(relationTypeIds);
|
||||
: GetRelationsByListOfTypeIds(new[] { relationType.Id });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="Relation"/> objects by the Id of the <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationTypeId">Id of the <see cref="RelationType"/> to retrieve Relations for</param>
|
||||
/// <returns>An enumerable list of <see cref="Relation"/> objects</returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByRelationTypeId(int relationTypeId)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -281,37 +207,35 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Child object from a Relation as an <see cref="IUmbracoEntity"/>
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to retrieve child object from</param>
|
||||
/// <returns>An <see cref="IUmbracoEntity"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering ordering = null)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.GetPagedRelationsByQuery(query, pageIndex, pageSize, out totalRecords, ordering);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IUmbracoEntity GetChildEntityFromRelation(IRelation relation)
|
||||
{
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType);
|
||||
return _entityService.Get(relation.ChildId, objectType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Parent object from a Relation as an <see cref="IUmbracoEntity"/>
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to retrieve parent object from</param>
|
||||
/// <returns>An <see cref="IUmbracoEntity"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IUmbracoEntity GetParentEntityFromRelation(IRelation relation)
|
||||
{
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType);
|
||||
return _entityService.Get(relation.ParentId, objectType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Parent and Child objects from a Relation as a <see cref="Tuple"/>"/> with <see cref="IUmbracoEntity"/>.
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to retrieve parent and child object from</param>
|
||||
/// <returns>Returns a Tuple with Parent (item1) and Child (item2)</returns>
|
||||
/// <inheritdoc />
|
||||
public Tuple<IUmbracoEntity, IUmbracoEntity> GetEntitiesFromRelation(IRelation relation)
|
||||
{
|
||||
var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
|
||||
var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
|
||||
var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType);
|
||||
var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType);
|
||||
|
||||
var child = _entityService.Get(relation.ChildId, childObjectType);
|
||||
var parent = _entityService.Get(relation.ParentId, parentObjectType);
|
||||
@@ -319,45 +243,63 @@ namespace Umbraco.Core.Services.Implement
|
||||
return new Tuple<IUmbracoEntity, IUmbracoEntity>(parent, child);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
|
||||
/// </summary>
|
||||
/// <param name="relations">List of relations to retrieve child objects from</param>
|
||||
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IUmbracoEntity> GetChildEntitiesFromRelations(IEnumerable<IRelation> relations)
|
||||
{
|
||||
foreach (var relation in relations)
|
||||
// Trying to avoid full N+1 lookups, so we'll group by the object type and then use the GetAll
|
||||
// method to lookup batches of entities for each parent object type
|
||||
|
||||
foreach (var groupedRelations in relations.GroupBy(x => ObjectTypes.GetUmbracoObjectType(x.ChildObjectType)))
|
||||
{
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
|
||||
yield return _entityService.Get(relation.ChildId, objectType);
|
||||
var objectType = groupedRelations.Key;
|
||||
var ids = groupedRelations.Select(x => x.ChildId).ToArray();
|
||||
foreach (var e in _entityService.GetAll(objectType, ids))
|
||||
yield return e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Parent objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
|
||||
/// </summary>
|
||||
/// <param name="relations">List of relations to retrieve parent objects from</param>
|
||||
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<IRelation> relations)
|
||||
{
|
||||
foreach (var relation in relations)
|
||||
// Trying to avoid full N+1 lookups, so we'll group by the object type and then use the GetAll
|
||||
// method to lookup batches of entities for each parent object type
|
||||
|
||||
foreach (var groupedRelations in relations.GroupBy(x => ObjectTypes.GetUmbracoObjectType(x.ParentObjectType)))
|
||||
{
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
|
||||
yield return _entityService.Get(relation.ParentId, objectType);
|
||||
var objectType = groupedRelations.Key;
|
||||
var ids = groupedRelations.Select(x => x.ParentId).ToArray();
|
||||
foreach (var e in _entityService.GetAll(objectType, ids))
|
||||
yield return e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Parent and Child objects from a list of Relations as a list of <see cref="IUmbracoEntity"/> objects.
|
||||
/// </summary>
|
||||
/// <param name="relations">List of relations to retrieve parent and child objects from</param>
|
||||
/// <returns>An enumerable list of <see cref="Tuple"/> with <see cref="IUmbracoEntity"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _relationRepository.GetPagedParentEntitiesByChildId(id, pageIndex, pageSize, out totalChildren, entityTypes.Select(x => x.GetGuid()).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return _relationRepository.GetPagedChildEntitiesByParentId(id, pageIndex, pageSize, out totalChildren, entityTypes.Select(x => x.GetGuid()).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Tuple<IUmbracoEntity, IUmbracoEntity>> GetEntitiesFromRelations(IEnumerable<IRelation> relations)
|
||||
{
|
||||
//TODO: Argh! N+1
|
||||
|
||||
foreach (var relation in relations)
|
||||
{
|
||||
var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ChildObjectType);
|
||||
var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.RelationType.ParentObjectType);
|
||||
var childObjectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType);
|
||||
var parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType);
|
||||
|
||||
var child = _entityService.Get(relation.ChildId, childObjectType);
|
||||
var parent = _entityService.Get(relation.ParentId, parentObjectType);
|
||||
@@ -366,19 +308,15 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relates two objects by their entity Ids.
|
||||
/// </summary>
|
||||
/// <param name="parentId">Id of the parent</param>
|
||||
/// <param name="childId">Id of the child</param>
|
||||
/// <param name="relationType">The type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IRelation Relate(int parentId, int childId, IRelationType relationType)
|
||||
{
|
||||
// Ensure that the RelationType has an identity before using it to relate two entities
|
||||
if (relationType.HasIdentity == false)
|
||||
Save(relationType);
|
||||
|
||||
//TODO: We don't check if this exists first, it will throw some sort of data integrity exception if it already exists, is that ok?
|
||||
|
||||
var relation = new Relation(parentId, childId, relationType);
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -398,25 +336,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relates two objects that are based on the <see cref="IUmbracoEntity"/> interface.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent entity</param>
|
||||
/// <param name="child">Child entity</param>
|
||||
/// <param name="relationType">The type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType)
|
||||
{
|
||||
return Relate(parent.Id, child.Id, relationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relates two objects by their entity Ids.
|
||||
/// </summary>
|
||||
/// <param name="parentId">Id of the parent</param>
|
||||
/// <param name="childId">Id of the child</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IRelation Relate(int parentId, int childId, string relationTypeAlias)
|
||||
{
|
||||
var relationType = GetRelationTypeByAlias(relationTypeAlias);
|
||||
@@ -426,13 +352,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
return Relate(parentId, childId, relationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Relates two objects that are based on the <see cref="IUmbracoEntity"/> interface.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent entity</param>
|
||||
/// <param name="child">Child entity</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
|
||||
/// <returns>The created <see cref="Relation"/></returns>
|
||||
/// <inheritdoc />
|
||||
public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias)
|
||||
{
|
||||
var relationType = GetRelationTypeByAlias(relationTypeAlias);
|
||||
@@ -442,11 +362,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
return Relate(parent.Id, child.Id, relationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether any relations exists for the passed in <see cref="RelationType"/>.
|
||||
/// </summary>
|
||||
/// <param name="relationType"><see cref="RelationType"/> to check for relations</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists for the given <see cref="RelationType"/>, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool HasRelations(IRelationType relationType)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -456,11 +372,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether any relations exists for the passed in Id.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of an object to check relations for</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists with the given Id, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool IsRelated(int id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -470,12 +382,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related
|
||||
/// </summary>
|
||||
/// <param name="parentId">Id of the Parent relation</param>
|
||||
/// <param name="childId">Id of the Child relation</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists with the given Ids, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(int parentId, int childId)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -485,13 +392,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related with a given relation type alias
|
||||
/// </summary>
|
||||
/// <param name="parentId">Id of the Parent relation</param>
|
||||
/// <param name="childId">Id of the Child relation</param>
|
||||
/// <param name="relationTypeAlias">Alias of the relation type</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists with the given Ids and relation type, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(int parentId, int childId, string relationTypeAlias)
|
||||
{
|
||||
var relType = GetRelationTypeByAlias(relationTypeAlias);
|
||||
@@ -502,13 +403,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related with a given relation type
|
||||
/// </summary>
|
||||
/// <param name="parentId">Id of the Parent relation</param>
|
||||
/// <param name="childId">Id of the Child relation</param>
|
||||
/// <param name="relationType">Type of relation</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists with the given Ids and relation type, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(int parentId, int childId, IRelationType relationType)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
@@ -518,34 +413,20 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent entity</param>
|
||||
/// <param name="child">Child entity</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exist between the entities, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child)
|
||||
{
|
||||
return AreRelated(parent.Id, child.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent entity</param>
|
||||
/// <param name="child">Child entity</param>
|
||||
/// <param name="relationTypeAlias">Alias of the type of relation to create</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exist between the entities, otherwise <c>False</c></returns>
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias)
|
||||
{
|
||||
return AreRelated(parent.Id, child.Id, relationTypeAlias);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Saves a <see cref="Relation"/>
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to save</param>
|
||||
/// <inheritdoc />
|
||||
public void Save(IRelation relation)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -564,10 +445,25 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType">RelationType to Save</param>
|
||||
public void Save(IEnumerable<IRelation> relations)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IRelation>(relations);
|
||||
if (scope.Events.DispatchCancelable(SavingRelation, this, saveEventArgs))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Save(relations);
|
||||
scope.Complete();
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SavedRelation, this, saveEventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save(IRelationType relationType)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -586,10 +482,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a <see cref="Relation"/>
|
||||
/// </summary>
|
||||
/// <param name="relation">Relation to Delete</param>
|
||||
/// <inheritdoc />
|
||||
public void Delete(IRelation relation)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -608,10 +501,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType">RelationType to Delete</param>
|
||||
/// <inheritdoc />
|
||||
public void Delete(IRelationType relationType)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -630,10 +520,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all <see cref="Relation"/> objects based on the passed in <see cref="RelationType"/>
|
||||
/// </summary>
|
||||
/// <param name="relationType"><see cref="RelationType"/> to Delete Relations for</param>
|
||||
/// <inheritdoc />
|
||||
public void DeleteRelationsOfType(IRelationType relationType)
|
||||
{
|
||||
var relations = new List<IRelation>();
|
||||
@@ -642,6 +529,8 @@ namespace Umbraco.Core.Services.Implement
|
||||
var query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
relations.AddRange(_relationRepository.Get(query).ToList());
|
||||
|
||||
//TODO: N+1, we should be able to do this in a single call
|
||||
|
||||
foreach (var relation in relations)
|
||||
_relationRepository.Delete(relation);
|
||||
|
||||
@@ -653,6 +542,15 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private IRelationType GetRelationType(string relationTypeAlias)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var query = Query<IRelationType>().Where(x => x.Alias == relationTypeAlias);
|
||||
return _relationTypeRepository.Get(query).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IRelation> GetRelationsByListOfTypeIds(IEnumerable<int> relationTypeIds)
|
||||
{
|
||||
var relations = new List<IRelation>();
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace Umbraco.Core
|
||||
// just pick every service connectors - just making sure that not two of them
|
||||
// would register the same entity type, with different udi types (would not make
|
||||
// much sense anyways).
|
||||
var connectors = Current.TypeLoader.GetTypes<IServiceConnector>();
|
||||
var connectors = Current.HasFactory ? (Current.TypeLoader?.GetTypes<IServiceConnector>() ?? Enumerable.Empty<Type>()) : Enumerable.Empty<Type>();
|
||||
var result = new Dictionary<string, UdiType>();
|
||||
foreach (var connector in connectors)
|
||||
{
|
||||
@@ -368,7 +368,7 @@ namespace Umbraco.Core
|
||||
return (udi1 == udi2) == false;
|
||||
}
|
||||
|
||||
private class UnknownTypeUdi : Udi
|
||||
internal class UnknownTypeUdi : Udi
|
||||
{
|
||||
private UnknownTypeUdi()
|
||||
: base("unknown", "umb://unknown/")
|
||||
|
||||
@@ -128,6 +128,10 @@
|
||||
</Compile>
|
||||
-->
|
||||
<Compile Include="AssemblyExtensions.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_6_0\AddMainDomLock.cs" />
|
||||
<Compile Include="Runtime\IMainDomLock.cs" />
|
||||
<Compile Include="Runtime\MainDomSemaphoreLock.cs" />
|
||||
<Compile Include="Runtime\SqlMainDomLock.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_6_0\MissingContentVersionsIndexes.cs" />
|
||||
<Compile Include="SystemLock.cs" />
|
||||
<Compile Include="Attempt.cs" />
|
||||
@@ -252,6 +256,9 @@
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\RichTextPreValueMigrator.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\DropDownFlexiblePreValueMigrator.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\MergeDateAndDateTimePropertyEditor.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_6_0\AddNewRelationTypes.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_6_0\UpdateRelationTypeTable.cs" />
|
||||
<Compile Include="Models\Editors\UmbracoEntityReference.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentAppBadge.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentAppBadgeType.cs" />
|
||||
<Compile Include="Models\Entities\EntityExtensions.cs" />
|
||||
@@ -282,6 +289,10 @@
|
||||
<Compile Include="Models\PublishedContent\IPublishedContentType.cs" />
|
||||
<Compile Include="Models\PublishedContent\IPublishedPropertyType.cs" />
|
||||
<Compile Include="PropertyEditors\ConfigurationFieldsExtensions.cs" />
|
||||
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollection.cs" />
|
||||
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionBuilder.cs" />
|
||||
<Compile Include="PropertyEditors\IDataValueReference.cs" />
|
||||
<Compile Include="PropertyEditors\IDataValueReferenceFactory.cs" />
|
||||
<Compile Include="PropertyEditors\IDataEditorWithMediaPath.cs" />
|
||||
<Compile Include="PropertyEditors\IIgnoreUserStartNodesConfig.cs" />
|
||||
<Compile Include="PublishedContentExtensions.cs" />
|
||||
@@ -403,7 +414,7 @@
|
||||
<Compile Include="Events\ExportedMemberEventArgs.cs" />
|
||||
<Compile Include="Events\RolesEventArgs.cs" />
|
||||
<Compile Include="Events\UserGroupWithUsers.cs" />
|
||||
<Compile Include="IMainDom.cs" />
|
||||
<Compile Include="Runtime\IMainDom.cs" />
|
||||
<Compile Include="IO\IFileSystems.cs" />
|
||||
<Compile Include="IO\IMediaFileSystem.cs" />
|
||||
<Compile Include="GuidUtils.cs" />
|
||||
@@ -734,7 +745,7 @@
|
||||
<Compile Include="Logging\ProfilingLogger.cs" />
|
||||
<Compile Include="Logging\VoidProfiler.cs" />
|
||||
<Compile Include="Macros\MacroErrorBehaviour.cs" />
|
||||
<Compile Include="MainDom.cs" />
|
||||
<Compile Include="Runtime\MainDom.cs" />
|
||||
<Compile Include="Manifest\ManifestParser.cs" />
|
||||
<Compile Include="Manifest\ValueValidatorConverter.cs" />
|
||||
<Compile Include="Manifest\ManifestWatcher.cs" />
|
||||
@@ -1531,7 +1542,7 @@
|
||||
<Compile Include="TypeExtensions.cs" />
|
||||
<Compile Include="Udi.cs" />
|
||||
<Compile Include="UdiDefinitionAttribute.cs" />
|
||||
<Compile Include="UdiEntityType.cs" />
|
||||
<Compile Include="Contants-UdiEntityType.cs" />
|
||||
<Compile Include="UdiGetterExtensions.cs" />
|
||||
<Compile Include="UdiRange.cs" />
|
||||
<Compile Include="UdiType.cs" />
|
||||
|
||||
@@ -30,9 +30,8 @@ namespace Umbraco.TestData
|
||||
if (ct.Variations.VariesBySegment())
|
||||
return Content($"The document type {alias} already allows segments, nothing has been changed");
|
||||
|
||||
ct.Variations = ct.Variations.SetFlag(ContentVariation.Segment);
|
||||
|
||||
propType.Variations = propType.Variations.SetFlag(ContentVariation.Segment);
|
||||
ct.SetVariesBy(ContentVariation.Segment);
|
||||
propType.SetVariesBy(ContentVariation.Segment);
|
||||
|
||||
Services.ContentTypeService.Save(ct);
|
||||
return Content($"The document type {alias} and property type {propertyTypeAlias} now allows segments");
|
||||
@@ -50,7 +49,7 @@ namespace Umbraco.TestData
|
||||
if (!ct.VariesBySegment())
|
||||
return Content($"The document type {alias} does not allow segments, nothing has been changed");
|
||||
|
||||
ct.Variations = ct.Variations.UnsetFlag(ContentVariation.Segment);
|
||||
ct.SetVariesBy(ContentVariation.Segment, false);
|
||||
|
||||
Services.ContentTypeService.Save(ct);
|
||||
return Content($"The document type {alias} no longer allows segments");
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
var d = new SnapDictionary<int, string>();
|
||||
d.Test.CollectAuto = false;
|
||||
|
||||
|
||||
// gen 1
|
||||
d.Set(1, "one");
|
||||
Assert.AreEqual(1, d.Test.GetValues(1).Length);
|
||||
@@ -321,7 +321,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
var d = new SnapDictionary<int, string>();
|
||||
d.Test.CollectAuto = false;
|
||||
|
||||
|
||||
Assert.AreEqual(0, d.Test.GetValues(1).Length);
|
||||
|
||||
// gen 1
|
||||
@@ -416,7 +416,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
var d = new SnapDictionary<int, string>();
|
||||
d.Test.CollectAuto = false;
|
||||
|
||||
|
||||
// gen 1
|
||||
d.Set(1, "one");
|
||||
Assert.AreEqual(1, d.Test.GetValues(1).Length);
|
||||
@@ -578,7 +578,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
var d = new SnapDictionary<int, string>();
|
||||
d.Test.CollectAuto = false;
|
||||
|
||||
|
||||
d.Set(1, "one");
|
||||
d.Set(2, "two");
|
||||
|
||||
@@ -689,7 +689,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
// gen 3
|
||||
Assert.AreEqual(2, d.Test.GetValues(1).Length);
|
||||
d.Set(1, "ein");
|
||||
d.SetLocked(1, "ein");
|
||||
Assert.AreEqual(3, d.Test.GetValues(1).Length);
|
||||
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
@@ -727,31 +727,25 @@ namespace Umbraco.Tests.Cache
|
||||
using (var w1 = d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
Assert.AreEqual(1, t.LiveGen);
|
||||
Assert.AreEqual(1, t.WLocked);
|
||||
Assert.IsTrue(t.IsLocked);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
|
||||
using (var w2 = d.GetScopedWriteLock(scopeProvider))
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
Assert.AreEqual(1, t.LiveGen);
|
||||
Assert.AreEqual(2, t.WLocked);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
|
||||
Assert.AreNotSame(w1, w2); // get a new writer each time
|
||||
|
||||
d.Set(1, "one");
|
||||
|
||||
Assert.AreEqual(0, d.CreateSnapshot().Gen);
|
||||
}
|
||||
using (var w2 = d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
Assert.AreEqual(1, t.LiveGen);
|
||||
Assert.AreEqual(1, t.WLocked);
|
||||
Assert.IsTrue(t.IsLocked);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
|
||||
Assert.AreEqual(0, d.CreateSnapshot().Gen);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, t.LiveGen);
|
||||
Assert.AreEqual(0, t.WLocked);
|
||||
Assert.IsFalse(t.IsLocked);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
|
||||
Assert.AreEqual(1, d.CreateSnapshot().Gen);
|
||||
@@ -772,11 +766,14 @@ namespace Umbraco.Tests.Cache
|
||||
|
||||
using (var w1 = d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
// This one is interesting, although we don't allow recursive locks, since this is
|
||||
// using the same ScopeContext/key, the lock acquisition is only done once
|
||||
|
||||
using (var w2 = d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
Assert.AreSame(w1, w2);
|
||||
|
||||
d.Set(1, "one");
|
||||
d.SetLocked(1, "one");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,19 +794,16 @@ namespace Umbraco.Tests.Cache
|
||||
using (var w1 = d.GetScopedWriteLock(scopeProvider1))
|
||||
{
|
||||
Assert.AreEqual(1, t.LiveGen);
|
||||
Assert.AreEqual(1, t.WLocked);
|
||||
Assert.IsTrue(t.IsLocked);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
|
||||
using (var w2 = d.GetScopedWriteLock(scopeProvider2))
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
Assert.AreEqual(1, t.LiveGen);
|
||||
Assert.AreEqual(2, t.WLocked);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
using (var w2 = d.GetScopedWriteLock(scopeProvider2))
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
Assert.AreNotSame(w1, w2);
|
||||
|
||||
d.Set(1, "one");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,13 +842,13 @@ namespace Umbraco.Tests.Cache
|
||||
Assert.IsFalse(d.Test.NextGen);
|
||||
Assert.AreEqual("uno", s2.Get(1));
|
||||
|
||||
var scopeProvider = GetScopeProvider();
|
||||
|
||||
var scopeProvider = GetScopeProvider();
|
||||
using (d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
// gen 3
|
||||
Assert.AreEqual(2, d.Test.GetValues(1).Length);
|
||||
d.Set(1, "ein");
|
||||
d.SetLocked(1, "ein");
|
||||
Assert.AreEqual(3, d.Test.GetValues(1).Length);
|
||||
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
@@ -881,6 +875,7 @@ namespace Umbraco.Tests.Cache
|
||||
{
|
||||
var d = new SnapDictionary<int, string>();
|
||||
d.Test.CollectAuto = false;
|
||||
|
||||
|
||||
// gen 1
|
||||
d.Set(1, "one");
|
||||
@@ -894,12 +889,11 @@ namespace Umbraco.Tests.Cache
|
||||
Assert.AreEqual("uno", s2.Get(1));
|
||||
|
||||
var scopeProvider = GetScopeProvider();
|
||||
|
||||
using (d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
// creating a snapshot in a write-lock does NOT return the "current" content
|
||||
// it uses the previous snapshot, so new snapshot created only on release
|
||||
d.Set(1, "ein");
|
||||
d.SetLocked(1, "ein");
|
||||
var s3 = d.CreateSnapshot();
|
||||
Assert.AreEqual(2, s3.Gen);
|
||||
Assert.AreEqual("uno", s3.Get(1));
|
||||
@@ -934,12 +928,11 @@ namespace Umbraco.Tests.Cache
|
||||
|
||||
var scopeContext = new ScopeContext();
|
||||
var scopeProvider = GetScopeProvider(scopeContext);
|
||||
|
||||
using (d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
// creating a snapshot in a write-lock does NOT return the "current" content
|
||||
// it uses the previous snapshot, so new snapshot created only on release
|
||||
d.Set(1, "ein");
|
||||
d.SetLocked(1, "ein");
|
||||
var s3 = d.CreateSnapshot();
|
||||
Assert.AreEqual(2, s3.Gen);
|
||||
Assert.AreEqual("uno", s3.Get(1));
|
||||
@@ -967,7 +960,7 @@ namespace Umbraco.Tests.Cache
|
||||
var d = new SnapDictionary<int, string>();
|
||||
var t = d.Test;
|
||||
t.CollectAuto = false;
|
||||
|
||||
|
||||
// gen 1
|
||||
d.Set(1, "one");
|
||||
var s1 = d.CreateSnapshot();
|
||||
@@ -984,12 +977,11 @@ namespace Umbraco.Tests.Cache
|
||||
|
||||
var scopeContext = new ScopeContext();
|
||||
var scopeProvider = GetScopeProvider(scopeContext);
|
||||
|
||||
using (d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
// creating a snapshot in a write-lock does NOT return the "current" content
|
||||
// it uses the previous snapshot, so new snapshot created only on release
|
||||
d.Set(1, "ein");
|
||||
d.SetLocked(1, "ein");
|
||||
var s3 = d.CreateSnapshot();
|
||||
Assert.AreEqual(2, s3.Gen);
|
||||
Assert.AreEqual("uno", s3.Get(1));
|
||||
@@ -997,7 +989,7 @@ namespace Umbraco.Tests.Cache
|
||||
// we made some changes, so a next gen is required
|
||||
Assert.AreEqual(3, t.LiveGen);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
Assert.AreEqual(1, t.WLocked);
|
||||
Assert.IsTrue(t.IsLocked);
|
||||
|
||||
// but live snapshot contains changes
|
||||
var ls = t.LiveSnapshot;
|
||||
@@ -1008,7 +1000,7 @@ namespace Umbraco.Tests.Cache
|
||||
// nothing is committed until scope exits
|
||||
Assert.AreEqual(3, t.LiveGen);
|
||||
Assert.IsTrue(t.NextGen);
|
||||
Assert.AreEqual(1, t.WLocked);
|
||||
Assert.IsTrue(t.IsLocked);
|
||||
|
||||
// no changes until exit
|
||||
var s4 = d.CreateSnapshot();
|
||||
@@ -1020,7 +1012,7 @@ namespace Umbraco.Tests.Cache
|
||||
// now things have changed
|
||||
Assert.AreEqual(2, t.LiveGen);
|
||||
Assert.IsFalse(t.NextGen);
|
||||
Assert.AreEqual(0, t.WLocked);
|
||||
Assert.IsFalse(t.IsLocked);
|
||||
|
||||
// no changes since not completed
|
||||
var s5 = d.CreateSnapshot();
|
||||
@@ -1097,9 +1089,10 @@ namespace Umbraco.Tests.Cache
|
||||
// writer is scope contextual and scoped
|
||||
// when disposed, nothing happens
|
||||
// when the context exists, the writer is released
|
||||
|
||||
using (d.GetScopedWriteLock(scopeProvider))
|
||||
{
|
||||
d.Set(1, "ein");
|
||||
d.SetLocked(1, "ein");
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
@@ -1107,7 +1100,7 @@ namespace Umbraco.Tests.Cache
|
||||
}
|
||||
|
||||
// writer has not released
|
||||
Assert.AreEqual(1, d.Test.WLocked);
|
||||
Assert.IsTrue(d.Test.IsLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
|
||||
@@ -1118,7 +1111,7 @@ namespace Umbraco.Tests.Cache
|
||||
// panic!
|
||||
var s2 = d.CreateSnapshot();
|
||||
|
||||
Assert.AreEqual(1, d.Test.WLocked);
|
||||
Assert.IsTrue(d.Test.IsLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
@@ -1127,7 +1120,7 @@ namespace Umbraco.Tests.Cache
|
||||
// release writer
|
||||
scopeContext.ScopeExit(true);
|
||||
|
||||
Assert.AreEqual(0, d.Test.WLocked);
|
||||
Assert.IsFalse(d.Test.IsLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
@@ -1135,7 +1128,7 @@ namespace Umbraco.Tests.Cache
|
||||
|
||||
var s3 = d.CreateSnapshot();
|
||||
|
||||
Assert.AreEqual(0, d.Test.WLocked);
|
||||
Assert.IsFalse(d.Test.IsLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(3, d.Test.GenObj.Gen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
@@ -1150,4 +1143,45 @@ namespace Umbraco.Tests.Cache
|
||||
return scopeProvider;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for tests so that we don't have to wrap every Set/Clear call in locks
|
||||
/// </summary>
|
||||
public static class SnapDictionaryExtensions
|
||||
{
|
||||
internal static void Set<TKey, TValue>(this SnapDictionary<TKey, TValue> d, TKey key, TValue value)
|
||||
where TValue : class
|
||||
{
|
||||
using (d.GetScopedWriteLock(GetScopeProvider()))
|
||||
{
|
||||
d.SetLocked(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Clear<TKey, TValue>(this SnapDictionary<TKey, TValue> d)
|
||||
where TValue : class
|
||||
{
|
||||
using (d.GetScopedWriteLock(GetScopeProvider()))
|
||||
{
|
||||
d.ClearLocked();
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Clear<TKey, TValue>(this SnapDictionary<TKey, TValue> d, TKey key)
|
||||
where TValue : class
|
||||
{
|
||||
using (d.GetScopedWriteLock(GetScopeProvider()))
|
||||
{
|
||||
d.ClearLocked(key);
|
||||
}
|
||||
}
|
||||
|
||||
private static IScopeProvider GetScopeProvider()
|
||||
{
|
||||
var scopeProvider = Mock.Of<IScopeProvider>();
|
||||
Mock.Get(scopeProvider)
|
||||
.Setup(x => x.Context).Returns(() => null);
|
||||
return scopeProvider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Web.Trees;
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Trees;
|
||||
|
||||
namespace Umbraco.Tests.CoreThings
|
||||
{
|
||||
@@ -22,6 +23,7 @@ namespace Umbraco.Tests.CoreThings
|
||||
Assert.IsFalse(value.HasFlag(test));
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Dialog, true)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Main, false)]
|
||||
[TestCase(TreeUse.Dialog | TreeUse.Main, TreeUse.Dialog, true)]
|
||||
@@ -51,47 +53,5 @@ namespace Umbraco.Tests.CoreThings
|
||||
else
|
||||
Assert.IsFalse(value.HasFlagAny(test));
|
||||
}
|
||||
|
||||
[TestCase(TreeUse.None, TreeUse.None, TreeUse.None)]
|
||||
[TestCase(TreeUse.None, TreeUse.Main, TreeUse.Main)]
|
||||
[TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)]
|
||||
[TestCase(TreeUse.Main, TreeUse.Main, TreeUse.Main)]
|
||||
[TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog)]
|
||||
public void SetFlagTests(TreeUse value, TreeUse flag, TreeUse expected)
|
||||
{
|
||||
Assert.AreEqual(expected, value.SetFlag(flag));
|
||||
}
|
||||
|
||||
[TestCase(TreeUse.None, TreeUse.None, TreeUse.None)]
|
||||
[TestCase(TreeUse.None, TreeUse.Main, TreeUse.None)]
|
||||
[TestCase(TreeUse.None, TreeUse.Dialog, TreeUse.None)]
|
||||
[TestCase(TreeUse.None, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
|
||||
[TestCase(TreeUse.Main, TreeUse.None, TreeUse.Main)]
|
||||
[TestCase(TreeUse.Main, TreeUse.Main, TreeUse.None)]
|
||||
[TestCase(TreeUse.Main, TreeUse.Dialog, TreeUse.Main)]
|
||||
[TestCase(TreeUse.Main, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.None, TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Dialog, TreeUse.None)]
|
||||
[TestCase(TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.None, TreeUse.Main | TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main, TreeUse.Dialog)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Dialog, TreeUse.Main)]
|
||||
[TestCase(TreeUse.Main | TreeUse.Dialog, TreeUse.Main | TreeUse.Dialog, TreeUse.None)]
|
||||
public void UnsetFlagTests(TreeUse value, TreeUse flag, TreeUse expected)
|
||||
{
|
||||
Assert.AreEqual(expected, value.UnsetFlag(flag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Runtime;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Changes;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Tests.Models
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
var item = new Relation(9, 8, new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test")
|
||||
var item = new Relation(9, 8, new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid())
|
||||
{
|
||||
Id = 66
|
||||
})
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Tests.Models
|
||||
{
|
||||
var ss = new SerializationService(new JsonNetSerializer());
|
||||
|
||||
var item = new Relation(9, 8, new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test")
|
||||
var item = new Relation(9, 8, new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid())
|
||||
{
|
||||
Id = 66
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Tests.Models
|
||||
[Test]
|
||||
public void Can_Deep_Clone()
|
||||
{
|
||||
var item = new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test")
|
||||
var item = new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid())
|
||||
{
|
||||
Id = 66,
|
||||
CreateDate = DateTime.Now,
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Tests.Models
|
||||
{
|
||||
var ss = new SerializationService(new JsonNetSerializer());
|
||||
|
||||
var item = new RelationType(Guid.NewGuid(), Guid.NewGuid(), "test")
|
||||
var item = new RelationType("test", "test", false, Guid.NewGuid(), Guid.NewGuid())
|
||||
{
|
||||
Id = 66,
|
||||
CreateDate = DateTime.Now,
|
||||
|
||||
@@ -26,22 +26,22 @@ namespace Umbraco.Tests.Packaging
|
||||
public class PackageDataInstallationTests : TestWithSomeContentBase
|
||||
{
|
||||
[HideFromTypeFinder]
|
||||
[DataEditor("7e062c13-7c41-4ad9-b389-41d88aeef87c", "Editor1", "editor1")]
|
||||
public class Editor1 : DataEditor
|
||||
{
|
||||
public Editor1(ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
Alias = "7e062c13-7c41-4ad9-b389-41d88aeef87c";
|
||||
}
|
||||
}
|
||||
|
||||
[HideFromTypeFinder]
|
||||
[DataEditor("d15e1281-e456-4b24-aa86-1dda3e4299d5", "Editor2", "editor2")]
|
||||
public class Editor2 : DataEditor
|
||||
{
|
||||
public Editor2(ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
Alias = "d15e1281-e456-4b24-aa86-1dda3e4299d5";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -35,7 +36,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, AppCaches.Disabled);
|
||||
contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository, langRepository);
|
||||
var languageRepository = new LanguageRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var repository = new DocumentRepository(scopeAccessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(scopeAccessor);
|
||||
var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(scopeAccessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches);
|
||||
var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger);
|
||||
contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(scopeAccessor);
|
||||
var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
@@ -6,6 +7,7 @@ using NUnit.Framework;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -25,7 +27,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
languageRepository = new LanguageRepository(accessor, Core.Cache.AppCaches.Disabled, Logger);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, Core.Cache.AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
var domainRepository = new DomainRepository(accessor, Core.Cache.AppCaches.Disabled, Logger);
|
||||
return domainRepository;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Tests.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
[TestFixture]
|
||||
[UmbracoTest(Mapper = true, Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class EntityRepositoryTest : TestWithDatabaseBase
|
||||
{
|
||||
|
||||
private EntityRepository CreateRepository(IScopeAccessor scopeAccessor)
|
||||
{
|
||||
var entityRepository = new EntityRepository(scopeAccessor);
|
||||
return entityRepository;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Paged_Mixed_Entities_By_Ids()
|
||||
{
|
||||
//Create content
|
||||
|
||||
var createdContent = new List<IContent>();
|
||||
var contentType = MockedContentTypes.CreateBasicContentType("blah");
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var c1 = MockedContent.CreateBasicContent(contentType);
|
||||
ServiceContext.ContentService.Save(c1);
|
||||
createdContent.Add(c1);
|
||||
}
|
||||
|
||||
//Create media
|
||||
|
||||
var createdMedia = new List<IMedia>();
|
||||
var imageType = MockedContentTypes.CreateImageMediaType("myImage");
|
||||
ServiceContext.MediaTypeService.Save(imageType);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var c1 = MockedMedia.CreateMediaImage(imageType, -1);
|
||||
ServiceContext.MediaService.Save(c1);
|
||||
createdMedia.Add(c1);
|
||||
}
|
||||
|
||||
// Create members
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType("simple");
|
||||
ServiceContext.MemberTypeService.Save(memberType);
|
||||
var createdMembers = MockedMember.CreateSimpleMember(memberType, 10).ToList();
|
||||
ServiceContext.MemberService.Save(createdMembers);
|
||||
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repo = CreateRepository((IScopeAccessor)provider);
|
||||
|
||||
var ids = createdContent.Select(x => x.Id).Concat(createdMedia.Select(x => x.Id)).Concat(createdMembers.Select(x => x.Id));
|
||||
|
||||
var objectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member };
|
||||
|
||||
var query = SqlContext.Query<IUmbracoEntity>()
|
||||
.WhereIn(e => e.Id, ids);
|
||||
|
||||
var entities = repo.GetPagedResultsByQuery(query, objectTypes, 0, 20, out var totalRecords, null, null).ToList();
|
||||
|
||||
Assert.AreEqual(20, entities.Count);
|
||||
Assert.AreEqual(30, totalRecords);
|
||||
|
||||
//add the next page
|
||||
entities.AddRange(repo.GetPagedResultsByQuery(query, objectTypes, 1, 20, out totalRecords, null, null));
|
||||
|
||||
Assert.AreEqual(30, entities.Count);
|
||||
Assert.AreEqual(30, totalRecords);
|
||||
|
||||
var contentEntities = entities.OfType<IDocumentEntitySlim>().ToList();
|
||||
var mediaEntities = entities.OfType<IMediaEntitySlim>().ToList();
|
||||
var memberEntities = entities.OfType<IMemberEntitySlim>().ToList();
|
||||
|
||||
Assert.AreEqual(10, contentEntities.Count);
|
||||
Assert.AreEqual(10, mediaEntities.Count);
|
||||
Assert.AreEqual(10, memberEntities.Count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
@@ -41,7 +42,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger);
|
||||
mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository);
|
||||
var tagRepository = new TagRepository(scopeAccessor, appCaches, Logger);
|
||||
var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>());
|
||||
var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(scopeAccessor);
|
||||
var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -35,7 +36,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
memberTypeRepository = new MemberTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
memberGroupRepository = new MemberGroupRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var tagRepo = new TagRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of<ILanguageRepository>());
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of<ILanguageRepository>(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -310,7 +311,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches, Logger);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -6,6 +7,7 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
@@ -31,7 +33,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
var accessor = (IScopeAccessor) provider;
|
||||
relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Mock.Of<ILogger>());
|
||||
var repository = new RelationRepository(accessor, Mock.Of<ILogger>(), relationTypeRepository);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var repository = new RelationRepository(accessor, Mock.Of<ILogger>(), relationTypeRepository, entityRepository);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -168,6 +171,156 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Paged_Parent_Entities_By_Child_Id()
|
||||
{
|
||||
CreateTestDataForPagingTests(out var createdContent, out var createdMembers, out var createdMedia);
|
||||
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider, out var relationTypeRepository);
|
||||
|
||||
// Get parent entities for child id
|
||||
var parents = repository.GetPagedParentEntitiesByChildId(createdMedia[0].Id, 0, 11, out var totalRecords).ToList();
|
||||
Assert.AreEqual(20, totalRecords);
|
||||
Assert.AreEqual(11, parents.Count);
|
||||
|
||||
//add the next page
|
||||
parents.AddRange(repository.GetPagedParentEntitiesByChildId(createdMedia[0].Id, 1, 11, out totalRecords));
|
||||
Assert.AreEqual(20, totalRecords);
|
||||
Assert.AreEqual(20, parents.Count);
|
||||
|
||||
var contentEntities = parents.OfType<IDocumentEntitySlim>().ToList();
|
||||
var mediaEntities = parents.OfType<IMediaEntitySlim>().ToList();
|
||||
var memberEntities = parents.OfType<IMemberEntitySlim>().ToList();
|
||||
|
||||
Assert.AreEqual(10, contentEntities.Count);
|
||||
Assert.AreEqual(0, mediaEntities.Count);
|
||||
Assert.AreEqual(10, memberEntities.Count);
|
||||
|
||||
//only of a certain type
|
||||
parents.AddRange(repository.GetPagedParentEntitiesByChildId(createdMedia[0].Id, 0, 100, out totalRecords, UmbracoObjectTypes.Document.GetGuid()));
|
||||
Assert.AreEqual(10, totalRecords);
|
||||
|
||||
parents.AddRange(repository.GetPagedParentEntitiesByChildId(createdMedia[0].Id, 0, 100, out totalRecords, UmbracoObjectTypes.Member.GetGuid()));
|
||||
Assert.AreEqual(10, totalRecords);
|
||||
|
||||
parents.AddRange(repository.GetPagedParentEntitiesByChildId(createdMedia[0].Id, 0, 100, out totalRecords, UmbracoObjectTypes.Media.GetGuid()));
|
||||
Assert.AreEqual(0, totalRecords);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Paged_Parent_Child_Entities_With_Same_Entity_Relation()
|
||||
{
|
||||
//Create a media item and create a relationship between itself (parent -> child)
|
||||
var imageType = MockedContentTypes.CreateImageMediaType("myImage");
|
||||
ServiceContext.MediaTypeService.Save(imageType);
|
||||
var media = MockedMedia.CreateMediaImage(imageType, -1);
|
||||
ServiceContext.MediaService.Save(media);
|
||||
var relType = ServiceContext.RelationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelatedMediaAlias);
|
||||
ServiceContext.RelationService.Relate(media.Id, media.Id, relType);
|
||||
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider, out var relationTypeRepository);
|
||||
|
||||
// Get parent entities for child id
|
||||
var parents = repository.GetPagedParentEntitiesByChildId(media.Id, 0, 10, out var totalRecords).ToList();
|
||||
Assert.AreEqual(1, totalRecords);
|
||||
Assert.AreEqual(1, parents.Count);
|
||||
|
||||
// Get child entities for parent id
|
||||
var children = repository.GetPagedChildEntitiesByParentId(media.Id, 0, 10, out totalRecords).ToList();
|
||||
Assert.AreEqual(1, totalRecords);
|
||||
Assert.AreEqual(1, children.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Paged_Child_Entities_By_Parent_Id()
|
||||
{
|
||||
CreateTestDataForPagingTests(out var createdContent, out var createdMembers, out var createdMedia);
|
||||
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var repository = CreateRepository(provider, out var relationTypeRepository);
|
||||
|
||||
// Get parent entities for child id
|
||||
var parents = repository.GetPagedChildEntitiesByParentId(createdContent[0].Id, 0, 6, out var totalRecords).ToList();
|
||||
Assert.AreEqual(10, totalRecords);
|
||||
Assert.AreEqual(6, parents.Count);
|
||||
|
||||
//add the next page
|
||||
parents.AddRange(repository.GetPagedChildEntitiesByParentId(createdContent[0].Id, 1, 6, out totalRecords));
|
||||
Assert.AreEqual(10, totalRecords);
|
||||
Assert.AreEqual(10, parents.Count);
|
||||
|
||||
var contentEntities = parents.OfType<IDocumentEntitySlim>().ToList();
|
||||
var mediaEntities = parents.OfType<IMediaEntitySlim>().ToList();
|
||||
var memberEntities = parents.OfType<IMemberEntitySlim>().ToList();
|
||||
|
||||
Assert.AreEqual(0, contentEntities.Count);
|
||||
Assert.AreEqual(10, mediaEntities.Count);
|
||||
Assert.AreEqual(0, memberEntities.Count);
|
||||
|
||||
//only of a certain type
|
||||
parents.AddRange(repository.GetPagedChildEntitiesByParentId(createdContent[0].Id, 0, 100, out totalRecords, UmbracoObjectTypes.Media.GetGuid()));
|
||||
Assert.AreEqual(10, totalRecords);
|
||||
|
||||
parents.AddRange(repository.GetPagedChildEntitiesByParentId(createdMembers[0].Id, 0, 100, out totalRecords, UmbracoObjectTypes.Media.GetGuid()));
|
||||
Assert.AreEqual(10, totalRecords);
|
||||
|
||||
parents.AddRange(repository.GetPagedChildEntitiesByParentId(createdContent[0].Id, 0, 100, out totalRecords, UmbracoObjectTypes.Member.GetGuid()));
|
||||
Assert.AreEqual(0, totalRecords);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTestDataForPagingTests(out List<IContent> createdContent, out List<IMember> createdMembers, out List<IMedia> createdMedia)
|
||||
{
|
||||
//Create content
|
||||
createdContent = new List<IContent>();
|
||||
var contentType = MockedContentTypes.CreateBasicContentType("blah");
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var c1 = MockedContent.CreateBasicContent(contentType);
|
||||
ServiceContext.ContentService.Save(c1);
|
||||
createdContent.Add(c1);
|
||||
}
|
||||
|
||||
//Create media
|
||||
createdMedia = new List<IMedia>();
|
||||
var imageType = MockedContentTypes.CreateImageMediaType("myImage");
|
||||
ServiceContext.MediaTypeService.Save(imageType);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var c1 = MockedMedia.CreateMediaImage(imageType, -1);
|
||||
ServiceContext.MediaService.Save(c1);
|
||||
createdMedia.Add(c1);
|
||||
}
|
||||
|
||||
// Create members
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType("simple");
|
||||
ServiceContext.MemberTypeService.Save(memberType);
|
||||
createdMembers = MockedMember.CreateSimpleMember(memberType, 10).ToList();
|
||||
ServiceContext.MemberService.Save(createdMembers);
|
||||
|
||||
var relType = ServiceContext.RelationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelatedMediaAlias);
|
||||
|
||||
// Relate content to media
|
||||
foreach (var content in createdContent)
|
||||
foreach (var media in createdMedia)
|
||||
ServiceContext.RelationService.Relate(content.Id, media.Id, relType);
|
||||
// Relate members to media
|
||||
foreach (var member in createdMembers)
|
||||
foreach (var media in createdMedia)
|
||||
ServiceContext.RelationService.Relate(member.Id, media.Id, relType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Perform_Exists_On_RelationRepository()
|
||||
{
|
||||
@@ -260,14 +413,24 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
public void CreateTestData()
|
||||
{
|
||||
var relateContent = new RelationType(Constants.ObjectTypes.Document, new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), "relateContentOnCopy") { IsBidirectional = true, Name = "Relate Content on Copy" };
|
||||
var relateContentType = new RelationType(Constants.ObjectTypes.DocumentType, new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB"), "relateContentTypeOnCopy") { IsBidirectional = true, Name = "Relate ContentType on Copy" };
|
||||
var relateContent = new RelationType(
|
||||
"Relate Content on Copy", "relateContentOnCopy", true,
|
||||
Constants.ObjectTypes.Document,
|
||||
new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"));
|
||||
|
||||
var relateContentType = new RelationType("Relate ContentType on Copy",
|
||||
"relateContentTypeOnCopy",
|
||||
true,
|
||||
Constants.ObjectTypes.DocumentType,
|
||||
new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB"));
|
||||
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var relationTypeRepository = new RelationTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of<ILogger>());
|
||||
var relationRepository = new RelationRepository((IScopeAccessor) provider, Mock.Of<ILogger>(), relationTypeRepository);
|
||||
var accessor = (IScopeAccessor)provider;
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Mock.Of<ILogger>());
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Mock.Of<ILogger>(), relationTypeRepository, entityRepository);
|
||||
|
||||
relationTypeRepository.Save(relateContent);
|
||||
relationTypeRepository.Save(relateContentType);
|
||||
|
||||
@@ -42,9 +42,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var repository = CreateRepository(provider);
|
||||
|
||||
// Act
|
||||
var relateMemberToContent = new RelationType(Constants.ObjectTypes.Member,
|
||||
Constants.ObjectTypes.Document,
|
||||
"relateMemberToContent") { IsBidirectional = true, Name = "Relate Member to Content" };
|
||||
var relateMemberToContent = new RelationType("Relate Member to Content", "relateMemberToContent", true, Constants.ObjectTypes.Member, Constants.ObjectTypes.Document);
|
||||
|
||||
repository.Save(relateMemberToContent);
|
||||
|
||||
@@ -135,7 +133,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
Assert.That(relationTypes, Is.Not.Null);
|
||||
Assert.That(relationTypes.Any(), Is.True);
|
||||
Assert.That(relationTypes.Any(x => x == null), Is.False);
|
||||
Assert.That(relationTypes.Count(), Is.EqualTo(5));
|
||||
Assert.That(relationTypes.Count(), Is.EqualTo(7));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,8 +224,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
public void CreateTestData()
|
||||
{
|
||||
var relateContent = new RelationType(Constants.ObjectTypes.Document, new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), "relateContentOnCopy") { IsBidirectional = true, Name = "Relate Content on Copy" };
|
||||
var relateContentType = new RelationType(Constants.ObjectTypes.DocumentType, new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB"), "relateContentTypeOnCopy") { IsBidirectional = true, Name = "Relate ContentType on Copy" };
|
||||
var relateContent = new RelationType("Relate Content on Copy", "relateContentOnCopy", true, Constants.ObjectTypes.Document, new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"));
|
||||
var relateContentType = new RelationType("Relate ContentType on Copy", "relateContentTypeOnCopy", true, Constants.ObjectTypes.DocumentType, new Guid("A2CB7800-F571-4787-9638-BC48539A0EFB"));
|
||||
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
@@ -7,6 +8,7 @@ using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -74,7 +76,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
// create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -104,7 +106,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -143,7 +145,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -185,7 +187,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -225,7 +227,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -261,7 +263,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -305,7 +307,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -349,7 +351,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -394,7 +396,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -429,7 +431,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -469,7 +471,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -513,7 +515,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -557,7 +559,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -601,7 +603,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -646,7 +648,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
var mediaRepository = CreateMediaRepository(provider, out var mediaTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
@@ -703,7 +705,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
var mediaRepository = CreateMediaRepository(provider, out var mediaTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
@@ -755,7 +757,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("test", "Test");
|
||||
@@ -791,7 +793,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
var mediaRepository = CreateMediaRepository(provider, out var mediaTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
@@ -871,7 +873,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (ScopeProvider.CreateScope())
|
||||
{
|
||||
var contentRepository = CreateContentRepository(provider, out var contentTypeRepository);
|
||||
var contentRepository = CreateDocumentRepository(provider, out var contentTypeRepository);
|
||||
var mediaRepository = CreateMediaRepository(provider, out var mediaTypeRepository);
|
||||
|
||||
//create data to relate to
|
||||
@@ -950,7 +952,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
return new TagRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
}
|
||||
|
||||
private DocumentRepository CreateContentRepository(IScopeProvider provider, out ContentTypeRepository contentTypeRepository)
|
||||
private DocumentRepository CreateDocumentRepository(IScopeProvider provider, out ContentTypeRepository contentTypeRepository)
|
||||
{
|
||||
var accessor = (IScopeAccessor) provider;
|
||||
var templateRepository = new TemplateRepository(accessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
@@ -958,7 +960,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -970,7 +977,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger);
|
||||
mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>());
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -237,11 +238,16 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
var templateRepository = CreateRepository(ScopeProvider);
|
||||
|
||||
var tagRepository = new TagRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger);
|
||||
var tagRepository = new TagRepository(ScopeProvider, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(ScopeProvider, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger);
|
||||
var contentTypeRepository = new ContentTypeRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var contentRepo = new DocumentRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var languageRepository = new LanguageRepository(ScopeProvider, AppCaches.Disabled, Logger);
|
||||
var contentTypeRepository = new ContentTypeRepository(ScopeProvider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(ScopeProvider, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(ScopeProvider);
|
||||
var relationRepository = new RelationRepository(ScopeProvider, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var contentRepo = new DocumentRepository(ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage");
|
||||
ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType!
|
||||
|
||||
@@ -14,6 +14,8 @@ using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
@@ -29,7 +31,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches, Logger);
|
||||
mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches, Mock.Of<ILogger>(), commonRepository, languageRepository);
|
||||
var tagRepository = new TagRepository(accessor, AppCaches, Mock.Of<ILogger>());
|
||||
var repository = new MediaRepository(accessor, AppCaches, Mock.Of<ILogger>(), mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>());
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new MediaRepository(accessor, AppCaches, Mock.Of<ILogger>(), mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>(), relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -47,7 +54,12 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches, Logger);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ using Umbraco.Web.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class ImageCropperTest
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.PublishedContent;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web;
|
||||
@@ -33,7 +34,7 @@ namespace Umbraco.Tests.Published
|
||||
var proflog = new ProfilingLogger(logger, profiler);
|
||||
|
||||
PropertyEditorCollection editors = null;
|
||||
var editor = new NestedContentPropertyEditor(logger, new Lazy<PropertyEditorCollection>(() => editors));
|
||||
var editor = new NestedContentPropertyEditor(logger, new Lazy<PropertyEditorCollection>(() => editors), Mock.Of<IDataTypeService>(), Mock.Of<IContentTypeService>());
|
||||
editors = new PropertyEditorCollection(new DataEditorCollection(new DataEditor[] { editor }));
|
||||
|
||||
var dataType1 = new DataType(editor)
|
||||
|
||||
@@ -918,6 +918,14 @@ namespace Umbraco.Tests.PublishedContent
|
||||
var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null);
|
||||
_snapshotAccessor.PublishedSnapshot = snapshot;
|
||||
|
||||
var snapshotService = (PublishedSnapshotService)_snapshotService;
|
||||
var contentStore = snapshotService.GetContentStore();
|
||||
|
||||
var parentNodes = contentStore.Test.GetValues(1);
|
||||
var parentNode = parentNodes[0];
|
||||
AssertLinkedNode(parentNode.contentNode, -1, -1, 2, 4, 6);
|
||||
Assert.AreEqual(1, parentNode.gen);
|
||||
|
||||
var documents = snapshot.Content.GetAtRoot().ToArray();
|
||||
AssertDocuments(documents, "N1", "N2", "N3");
|
||||
|
||||
@@ -934,6 +942,15 @@ namespace Umbraco.Tests.PublishedContent
|
||||
new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshNode),
|
||||
}, out _, out _);
|
||||
|
||||
parentNodes = contentStore.Test.GetValues(1);
|
||||
Assert.AreEqual(2, parentNodes.Length);
|
||||
parentNode = parentNodes[1]; // get the first gen
|
||||
AssertLinkedNode(parentNode.contentNode, -1, -1, 2, 4, 6); // the structure should have remained the same
|
||||
Assert.AreEqual(1, parentNode.gen);
|
||||
parentNode = parentNodes[0]; // get the latest gen
|
||||
AssertLinkedNode(parentNode.contentNode, -1, -1, 2, 4, 6); // the structure should have remained the same
|
||||
Assert.AreEqual(2, parentNode.gen);
|
||||
|
||||
documents = snapshot.Content.GetAtRoot().ToArray();
|
||||
AssertDocuments(documents, "N1", "N2", "N3");
|
||||
|
||||
@@ -942,6 +959,8 @@ namespace Umbraco.Tests.PublishedContent
|
||||
|
||||
documents = snapshot.Content.GetById(2).Children().ToArray();
|
||||
AssertDocuments(documents, "N9", "N8", "N7");
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -11,6 +11,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Templates;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
@@ -38,9 +39,14 @@ namespace Umbraco.Tests.PublishedContent
|
||||
base.Initialize();
|
||||
|
||||
var converters = Factory.GetInstance<PropertyValueConverterCollection>();
|
||||
var umbracoContextAccessor = Mock.Of<IUmbracoContextAccessor>();
|
||||
var logger = Mock.Of<ILogger>();
|
||||
|
||||
var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor);
|
||||
var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, Mock.Of<IMediaService>(), Mock.Of<IContentTypeBaseServiceProvider>());
|
||||
var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor);
|
||||
var dataTypeService = new TestObjects.TestDataTypeService(
|
||||
new DataType(new RichTextPropertyEditor(Mock.Of<ILogger>(), Mock.Of<IMediaService>(), Mock.Of<IContentTypeBaseServiceProvider>(), Mock.Of<IUmbracoContextAccessor>())) { Id = 1 });
|
||||
new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, localLinkParser, pastedImages)) { Id = 1 });
|
||||
|
||||
var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of<IPublishedModelFactory>(), converters, dataTypeService);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web.Models.PublishedContent;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Web.Templates;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
@@ -45,11 +46,14 @@ namespace Umbraco.Tests.PublishedContent
|
||||
var mediaService = Mock.Of<IMediaService>();
|
||||
var contentTypeBaseServiceProvider = Mock.Of<IContentTypeBaseServiceProvider>();
|
||||
var umbracoContextAccessor = Mock.Of<IUmbracoContextAccessor>();
|
||||
var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor);
|
||||
var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider);
|
||||
var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor);
|
||||
|
||||
var dataTypeService = new TestObjects.TestDataTypeService(
|
||||
new DataType(new VoidEditor(logger)) { Id = 1 },
|
||||
new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 },
|
||||
new DataType(new RichTextPropertyEditor(logger, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor)) { Id = 1002 },
|
||||
new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, linkParser, pastedImages)) { Id = 1002 },
|
||||
new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 },
|
||||
new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 },
|
||||
new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 });
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
@@ -39,6 +40,23 @@ namespace Umbraco.Tests.Services
|
||||
Composition.Register<IProfiler, TestProfiler>();
|
||||
}
|
||||
|
||||
private DocumentRepository CreateDocumentRepository(IScopeProvider provider)
|
||||
{
|
||||
var accessor = (IScopeAccessor)provider;
|
||||
var tRepository = new TemplateRepository(accessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepo = new TagRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, tRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var ctRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Profiler()
|
||||
{
|
||||
@@ -163,12 +181,7 @@ namespace Umbraco.Tests.Services
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var tRepository = new TemplateRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepo = new TagRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository);
|
||||
var repository = CreateDocumentRepository(provider);
|
||||
|
||||
// Act
|
||||
Stopwatch watch = Stopwatch.StartNew();
|
||||
@@ -197,12 +210,7 @@ namespace Umbraco.Tests.Services
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var tRepository = new TemplateRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepo = new TagRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository);
|
||||
var repository = CreateDocumentRepository(provider);
|
||||
|
||||
// Act
|
||||
Stopwatch watch = Stopwatch.StartNew();
|
||||
@@ -229,12 +237,7 @@ namespace Umbraco.Tests.Services
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var tRepository = new TemplateRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepo = new TagRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository((IScopeAccessor) provider, tRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger);
|
||||
var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository);
|
||||
var repository = CreateDocumentRepository(provider);
|
||||
|
||||
// Act
|
||||
var contents = repository.GetMany();
|
||||
@@ -264,12 +267,7 @@ namespace Umbraco.Tests.Services
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
using (var scope = provider.CreateScope())
|
||||
{
|
||||
var tRepository = new TemplateRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepo = new TagRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger);
|
||||
var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository);
|
||||
var repository = CreateDocumentRepository(provider);
|
||||
|
||||
// Act
|
||||
var contents = repository.GetMany();
|
||||
|
||||
@@ -448,6 +448,47 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(content.HasIdentity, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Automatically_Track_Relations()
|
||||
{
|
||||
var mt = MockedContentTypes.CreateSimpleMediaType("testMediaType", "Test Media Type");
|
||||
ServiceContext.MediaTypeService.Save(mt);
|
||||
var m1 = MockedMedia.CreateSimpleMedia(mt, "hello 1", -1);
|
||||
var m2 = MockedMedia.CreateSimpleMedia(mt, "hello 1", -1);
|
||||
ServiceContext.MediaService.Save(m1);
|
||||
ServiceContext.MediaService.Save(m2);
|
||||
|
||||
var ct = MockedContentTypes.CreateTextPageContentType("richTextTest");
|
||||
ct.AllowedTemplates = Enumerable.Empty<ITemplate>();
|
||||
|
||||
ServiceContext.ContentTypeService.Save(ct);
|
||||
|
||||
var c1 = MockedContent.CreateTextpageContent(ct, "my content 1", -1);
|
||||
ServiceContext.ContentService.Save(c1);
|
||||
|
||||
var c2 = MockedContent.CreateTextpageContent(ct, "my content 2", -1);
|
||||
|
||||
//'bodyText' is a property with a RTE property editor which we knows tracks relations
|
||||
c2.Properties["bodyText"].SetValue(@"<p>
|
||||
<img src='/media/12312.jpg' data-udi='umb://media/" + m1.Key.ToString("N") + @"' />
|
||||
</p><p><img src='/media/234234.jpg' data-udi=""umb://media/" + m2.Key.ToString("N") + @""" />
|
||||
</p>
|
||||
<p>
|
||||
<a href=""{locallink:umb://document/" + c1.Key.ToString("N") + @"}"">hello</a>
|
||||
</p>");
|
||||
|
||||
ServiceContext.ContentService.Save(c2);
|
||||
|
||||
var relations = ServiceContext.RelationService.GetByParentId(c2.Id).ToList();
|
||||
Assert.AreEqual(3, relations.Count);
|
||||
Assert.AreEqual(Constants.Conventions.RelationTypes.RelatedMediaAlias, relations[0].RelationType.Alias);
|
||||
Assert.AreEqual(m1.Id, relations[0].ChildId);
|
||||
Assert.AreEqual(Constants.Conventions.RelationTypes.RelatedMediaAlias, relations[1].RelationType.Alias);
|
||||
Assert.AreEqual(m2.Id, relations[1].ChildId);
|
||||
Assert.AreEqual(Constants.Conventions.RelationTypes.RelatedDocumentAlias, relations[2].RelationType.Alias);
|
||||
Assert.AreEqual(c1.Id, relations[2].ChildId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Create_Content_Without_Explicitly_Set_User()
|
||||
{
|
||||
@@ -1778,7 +1819,7 @@ namespace Umbraco.Tests.Services
|
||||
admin.StartContentIds = new[] {content1.Id};
|
||||
ServiceContext.UserService.Save(admin);
|
||||
|
||||
ServiceContext.RelationService.Save(new RelationType(Constants.ObjectTypes.Document, Constants.ObjectTypes.Document, "test"));
|
||||
ServiceContext.RelationService.Save(new RelationType("test", "test", false, Constants.ObjectTypes.Document, Constants.ObjectTypes.Document));
|
||||
Assert.IsNotNull(ServiceContext.RelationService.Relate(content1, content2, "test"));
|
||||
|
||||
ServiceContext.PublicAccessService.Save(new PublicAccessEntry(content1, content2, content2, new List<PublicAccessRule>
|
||||
@@ -3166,7 +3207,12 @@ namespace Umbraco.Tests.Services
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var entityRepository = new EntityRepository(accessor);
|
||||
var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository, entityRepository);
|
||||
var propertyEditors = new Lazy<PropertyEditorCollection>(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty<IDataEditor>())));
|
||||
var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences);
|
||||
return repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,247 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Tests.Testing;
|
||||
|
||||
namespace Umbraco.Tests.Services
|
||||
{
|
||||
[TestFixture]
|
||||
[Apartment(ApartmentState.STA)]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class RelationServiceTests : TestWithSomeContentBase
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Get_Paged_Relations_By_Relation_Type()
|
||||
{
|
||||
//Create content
|
||||
var createdContent = new List<IContent>();
|
||||
var contentType = MockedContentTypes.CreateBasicContentType("blah");
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var c1 = MockedContent.CreateBasicContent(contentType);
|
||||
ServiceContext.ContentService.Save(c1);
|
||||
createdContent.Add(c1);
|
||||
}
|
||||
|
||||
//Create media
|
||||
var createdMedia = new List<IMedia>();
|
||||
var imageType = MockedContentTypes.CreateImageMediaType("myImage");
|
||||
ServiceContext.MediaTypeService.Save(imageType);
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var c1 = MockedMedia.CreateMediaImage(imageType, -1);
|
||||
ServiceContext.MediaService.Save(c1);
|
||||
createdMedia.Add(c1);
|
||||
}
|
||||
|
||||
var relType = ServiceContext.RelationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelatedMediaAlias);
|
||||
|
||||
// Relate content to media
|
||||
foreach (var content in createdContent)
|
||||
foreach (var media in createdMedia)
|
||||
ServiceContext.RelationService.Relate(content.Id, media.Id, relType);
|
||||
|
||||
var paged = ServiceContext.RelationService.GetPagedByRelationTypeId(relType.Id, 0, 51, out var totalRecs).ToList();
|
||||
|
||||
Assert.AreEqual(100, totalRecs);
|
||||
Assert.AreEqual(51, paged.Count);
|
||||
|
||||
//next page
|
||||
paged.AddRange(ServiceContext.RelationService.GetPagedByRelationTypeId(relType.Id, 1, 51, out totalRecs));
|
||||
|
||||
Assert.AreEqual(100, totalRecs);
|
||||
Assert.AreEqual(100, paged.Count);
|
||||
|
||||
Assert.IsTrue(createdContent.Select(x => x.Id).ContainsAll(paged.Select(x => x.ParentId)));
|
||||
Assert.IsTrue(createdMedia.Select(x => x.Id).ContainsAll(paged.Select(x => x.ChildId)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Return_List_Of_Content_Items_Where_Media_Item_Referenced()
|
||||
{
|
||||
var mt = MockedContentTypes.CreateSimpleMediaType("testMediaType", "Test Media Type");
|
||||
ServiceContext.MediaTypeService.Save(mt);
|
||||
var m1 = MockedMedia.CreateSimpleMedia(mt, "hello 1", -1);
|
||||
ServiceContext.MediaService.Save(m1);
|
||||
|
||||
var ct = MockedContentTypes.CreateTextPageContentType("richTextTest");
|
||||
ct.AllowedTemplates = Enumerable.Empty<ITemplate>();
|
||||
ServiceContext.ContentTypeService.Save(ct);
|
||||
|
||||
void createContentWithMediaRefs()
|
||||
{
|
||||
var content = MockedContent.CreateTextpageContent(ct, "my content 2", -1);
|
||||
//'bodyText' is a property with a RTE property editor which we knows automatically tracks relations
|
||||
content.Properties["bodyText"].SetValue(@"<p>
|
||||
<img src='/media/12312.jpg' data-udi='umb://media/" + m1.Key.ToString("N") + @"' />
|
||||
</p>");
|
||||
ServiceContext.ContentService.Save(content);
|
||||
}
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
createContentWithMediaRefs(); //create 6 content items referencing the same media
|
||||
|
||||
var relations = ServiceContext.RelationService.GetByChildId(m1.Id, Constants.Conventions.RelationTypes.RelatedMediaAlias).ToList();
|
||||
Assert.AreEqual(6, relations.Count);
|
||||
|
||||
var entities = ServiceContext.RelationService.GetParentEntitiesFromRelations(relations).ToList();
|
||||
Assert.AreEqual(6, entities.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Create_RelationType_Without_Name()
|
||||
{
|
||||
var rs = ServiceContext.RelationService;
|
||||
var rt = new RelationType(Constants.ObjectTypes.Document, Constants.ObjectTypes.Document, "repeatedEventOccurence");
|
||||
IRelationType rt = new RelationType("Test", "repeatedEventOccurence", false, Constants.ObjectTypes.Document, Constants.ObjectTypes.Media);
|
||||
|
||||
Assert.DoesNotThrow(() => rs.Save(rt));
|
||||
|
||||
Assert.AreEqual(rt.Name, "repeatedEventOccurence");
|
||||
//re-get
|
||||
rt = ServiceContext.RelationService.GetRelationTypeById(rt.Id);
|
||||
|
||||
Assert.AreEqual("Test", rt.Name);
|
||||
Assert.AreEqual("repeatedEventOccurence", rt.Alias);
|
||||
Assert.AreEqual(false, rt.IsBidirectional);
|
||||
Assert.AreEqual(Constants.ObjectTypes.Document, rt.ChildObjectType.Value);
|
||||
Assert.AreEqual(Constants.ObjectTypes.Media, rt.ParentObjectType.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Relation_Type_Without_Object_Types()
|
||||
{
|
||||
var rs = ServiceContext.RelationService;
|
||||
IRelationType rt = new RelationType("repeatedEventOccurence", "repeatedEventOccurence", false, null, null);
|
||||
|
||||
Assert.DoesNotThrow(() => rs.Save(rt));
|
||||
|
||||
//re-get
|
||||
rt = ServiceContext.RelationService.GetRelationTypeById(rt.Id);
|
||||
|
||||
Assert.IsNull(rt.ChildObjectType);
|
||||
Assert.IsNull(rt.ParentObjectType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Relation_Returns_Parent_Child_Object_Types_When_Creating()
|
||||
{
|
||||
var r = CreateAndSaveRelation("Test", "test");
|
||||
|
||||
Assert.AreEqual(Constants.ObjectTypes.Document, r.ParentObjectType);
|
||||
Assert.AreEqual(Constants.ObjectTypes.Media, r.ChildObjectType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Relation_Returns_Parent_Child_Object_Types_When_Getting()
|
||||
{
|
||||
var r = CreateAndSaveRelation("Test", "test");
|
||||
|
||||
// re-get
|
||||
r = ServiceContext.RelationService.GetById(r.Id);
|
||||
|
||||
Assert.AreEqual(Constants.ObjectTypes.Document, r.ParentObjectType);
|
||||
Assert.AreEqual(Constants.ObjectTypes.Media, r.ChildObjectType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Insert_Bulk_Relations()
|
||||
{
|
||||
var rs = ServiceContext.RelationService;
|
||||
|
||||
var newRelations = CreateRelations(10);
|
||||
|
||||
Assert.IsTrue(newRelations.All(x => !x.HasIdentity));
|
||||
|
||||
ServiceContext.RelationService.Save(newRelations);
|
||||
|
||||
Assert.IsTrue(newRelations.All(x => x.HasIdentity));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Update_Bulk_Relations()
|
||||
{
|
||||
var rs = ServiceContext.RelationService;
|
||||
|
||||
var date = DateTime.Now.AddDays(-10);
|
||||
var newRelations = CreateRelations(10);
|
||||
foreach (var r in newRelations)
|
||||
{
|
||||
r.CreateDate = date;
|
||||
r.UpdateDate = date;
|
||||
}
|
||||
|
||||
//insert
|
||||
ServiceContext.RelationService.Save(newRelations);
|
||||
Assert.IsTrue(newRelations.All(x => x.UpdateDate == date));
|
||||
|
||||
var newDate = DateTime.Now.AddDays(-5);
|
||||
foreach (var r in newRelations)
|
||||
r.UpdateDate = newDate;
|
||||
|
||||
//update
|
||||
ServiceContext.RelationService.Save(newRelations);
|
||||
Assert.IsTrue(newRelations.All(x => x.UpdateDate == newDate));
|
||||
}
|
||||
|
||||
private IRelation CreateAndSaveRelation(string name, string alias)
|
||||
{
|
||||
var rs = ServiceContext.RelationService;
|
||||
var rt = new RelationType(name, alias, false, null, null);
|
||||
rs.Save(rt);
|
||||
|
||||
var ct = MockedContentTypes.CreateBasicContentType();
|
||||
ServiceContext.ContentTypeService.Save(ct);
|
||||
|
||||
var mt = MockedContentTypes.CreateImageMediaType("img");
|
||||
ServiceContext.MediaTypeService.Save(mt);
|
||||
|
||||
var c1 = MockedContent.CreateBasicContent(ct);
|
||||
var c2 = MockedMedia.CreateMediaImage(mt, -1);
|
||||
ServiceContext.ContentService.Save(c1);
|
||||
ServiceContext.MediaService.Save(c2);
|
||||
|
||||
var r = new Relation(c1.Id, c2.Id, rt);
|
||||
ServiceContext.RelationService.Save(r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a bunch of content/media items return relation objects for them (unsaved)
|
||||
/// </summary>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IRelation> CreateRelations(int count)
|
||||
{
|
||||
var rs = ServiceContext.RelationService;
|
||||
var rtName = Guid.NewGuid().ToString();
|
||||
var rt = new RelationType(rtName, rtName, false, null, null);
|
||||
rs.Save(rt);
|
||||
|
||||
var ct = MockedContentTypes.CreateBasicContentType();
|
||||
ServiceContext.ContentTypeService.Save(ct);
|
||||
|
||||
var mt = MockedContentTypes.CreateImageMediaType("img");
|
||||
ServiceContext.MediaTypeService.Save(mt);
|
||||
|
||||
return Enumerable.Range(1, count).Select(index =>
|
||||
{
|
||||
var c1 = MockedContent.CreateBasicContent(ct);
|
||||
var c2 = MockedMedia.CreateMediaImage(mt, -1);
|
||||
ServiceContext.ContentService.Save(c1);
|
||||
ServiceContext.MediaService.Save(c2);
|
||||
|
||||
return new Relation(c1.Id, c2.Id, rt);
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
//TODO: Create a relation for entities of the wrong Entity Type (GUID) based on the Relation Type's defined parent/child object types
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
using Umbraco.Core.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.Testing.Objects.Accessors;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Tests.Testing.Objects;
|
||||
using System.Web;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Tests.Templates
|
||||
{
|
||||
|
||||
|
||||
[TestFixture]
|
||||
public class HtmlImageSourceParserTests
|
||||
{
|
||||
[Test]
|
||||
public void Returns_Udis_From_Data_Udi_Html_Attributes()
|
||||
{
|
||||
var input = @"<p>
|
||||
<div>
|
||||
<img src='/media/12312.jpg' data-udi='umb://media/D4B18427A1544721B09AC7692F35C264' />
|
||||
</div>
|
||||
</p><p><img src='/media/234234.jpg' data-udi=""umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"" /></p>";
|
||||
|
||||
var logger = Mock.Of<ILogger>();
|
||||
var umbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor);
|
||||
|
||||
var result = imageSourceParser.FindUdisFromDataAttributes(input).ToList();
|
||||
Assert.AreEqual(2, result.Count);
|
||||
Assert.AreEqual(Udi.Parse("umb://media/D4B18427A1544721B09AC7692F35C264"), result[0]);
|
||||
Assert.AreEqual(Udi.Parse("umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"), result[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Remove_Image_Sources()
|
||||
{
|
||||
var logger = Mock.Of<ILogger>();
|
||||
var umbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor);
|
||||
|
||||
var result = imageSourceParser.RemoveImageSources(@"<p>
|
||||
<div>
|
||||
<img src=""/media/12354/test.jpg"" />
|
||||
</div></p>
|
||||
<p>
|
||||
<div><img src=""/media/987645/test.jpg"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
|
||||
</p>");
|
||||
|
||||
Assert.AreEqual(@"<p>
|
||||
<div>
|
||||
<img src=""/media/12354/test.jpg"" />
|
||||
</div></p>
|
||||
<p>
|
||||
<div><img src="""" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
|
||||
</p>", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ensure_Image_Sources()
|
||||
{
|
||||
//setup a mock url provider which we'll use for testing
|
||||
|
||||
var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing);
|
||||
var media = new Mock<IPublishedContent>();
|
||||
media.Setup(x => x.ContentType).Returns(mediaType);
|
||||
var mediaUrlProvider = new Mock<IMediaUrlProvider>();
|
||||
mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<string>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns(UrlInfo.Url("/media/1001/my-image.jpg"));
|
||||
|
||||
var umbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
|
||||
var umbracoContextFactory = TestUmbracoContextFactory.Create(
|
||||
mediaUrlProvider: mediaUrlProvider.Object,
|
||||
umbracoContextAccessor: umbracoContextAccessor);
|
||||
|
||||
using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>()))
|
||||
{
|
||||
var mediaCache = Mock.Get(reference.UmbracoContext.Media);
|
||||
mediaCache.Setup(x => x.GetById(It.IsAny<Guid>())).Returns(media.Object);
|
||||
|
||||
var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor);
|
||||
|
||||
var result = imageSourceParser.EnsureImageSources(@"<p>
|
||||
<div>
|
||||
<img src="""" />
|
||||
</div></p>
|
||||
<p>
|
||||
<div><img src="""" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
|
||||
</p>
|
||||
<p>
|
||||
<div><img src=""?width=100"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
|
||||
</p>");
|
||||
|
||||
Assert.AreEqual(@"<p>
|
||||
<div>
|
||||
<img src="""" />
|
||||
</div></p>
|
||||
<p>
|
||||
<div><img src=""/media/1001/my-image.jpg"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
|
||||
</p>
|
||||
<p>
|
||||
<div><img src=""/media/1001/my-image.jpg?width=100"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
|
||||
</p>", result);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Tests.Testing.Objects;
|
||||
using Umbraco.Tests.Testing.Objects.Accessors;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Templates;
|
||||
|
||||
namespace Umbraco.Tests.Templates
|
||||
{
|
||||
[TestFixture]
|
||||
public class HtmlLocalLinkParserTests
|
||||
{
|
||||
[Test]
|
||||
public void Returns_Udis_From_LocalLinks()
|
||||
{
|
||||
var input = @"<p>
|
||||
<div>
|
||||
<img src='/media/12312.jpg' data-udi='umb://media/D4B18427A1544721B09AC7692F35C264' />
|
||||
<a href=""{locallink:umb://document/C093961595094900AAF9170DDE6AD442}"">hello</a>
|
||||
</div>
|
||||
</p><p><img src='/media/234234.jpg' data-udi=""umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"" />
|
||||
<a href=""{locallink:umb://document-type/2D692FCB070B4CDA92FB6883FDBFD6E2}"">hello</a>
|
||||
</p>";
|
||||
|
||||
var umbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
var parser = new HtmlLocalLinkParser(umbracoContextAccessor);
|
||||
|
||||
var result = parser.FindUdisFromLocalLinks(input).ToList();
|
||||
|
||||
Assert.AreEqual(2, result.Count);
|
||||
Assert.AreEqual(Udi.Parse("umb://document/C093961595094900AAF9170DDE6AD442"), result[0]);
|
||||
Assert.AreEqual(Udi.Parse("umb://document-type/2D692FCB070B4CDA92FB6883FDBFD6E2"), result[1]);
|
||||
}
|
||||
|
||||
[TestCase("", "")]
|
||||
[TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://document/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://media/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/media/1001/my-image.jpg\" world ")]
|
||||
//this one has an invalid char so won't match
|
||||
[TestCase("hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")]
|
||||
public void ParseLocalLinks(string input, string result)
|
||||
{
|
||||
//setup a mock url provider which we'll use for testing
|
||||
var contentUrlProvider = new Mock<IUrlProvider>();
|
||||
contentUrlProvider
|
||||
.Setup(x => x.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns(UrlInfo.Url("/my-test-url"));
|
||||
var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing);
|
||||
var publishedContent = new Mock<IPublishedContent>();
|
||||
publishedContent.Setup(x => x.Id).Returns(1234);
|
||||
publishedContent.Setup(x => x.ContentType).Returns(contentType);
|
||||
|
||||
var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing);
|
||||
var media = new Mock<IPublishedContent>();
|
||||
media.Setup(x => x.ContentType).Returns(mediaType);
|
||||
var mediaUrlProvider = new Mock<IMediaUrlProvider>();
|
||||
mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<string>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns(UrlInfo.Url("/media/1001/my-image.jpg"));
|
||||
|
||||
var umbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
|
||||
var umbracoContextFactory = TestUmbracoContextFactory.Create(
|
||||
urlProvider: contentUrlProvider.Object,
|
||||
mediaUrlProvider: mediaUrlProvider.Object,
|
||||
umbracoContextAccessor: umbracoContextAccessor);
|
||||
|
||||
using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>()))
|
||||
{
|
||||
var contentCache = Mock.Get(reference.UmbracoContext.Content);
|
||||
contentCache.Setup(x => x.GetById(It.IsAny<int>())).Returns(publishedContent.Object);
|
||||
contentCache.Setup(x => x.GetById(It.IsAny<Guid>())).Returns(publishedContent.Object);
|
||||
|
||||
var mediaCache = Mock.Get(reference.UmbracoContext.Media);
|
||||
mediaCache.Setup(x => x.GetById(It.IsAny<int>())).Returns(media.Object);
|
||||
mediaCache.Setup(x => x.GetById(It.IsAny<Guid>())).Returns(media.Object);
|
||||
|
||||
var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor);
|
||||
|
||||
var output = linkParser.EnsureInternalLinks(input);
|
||||
|
||||
Assert.AreEqual(result, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PublishedCache.NuCache;
|
||||
using Umbraco.Web.PublishedCache.NuCache.DataSource;
|
||||
|
||||
namespace Umbraco.Tests.Testing.Objects
|
||||
{
|
||||
|
||||
internal class TestDataSource : IDataSource
|
||||
{
|
||||
public TestDataSource(params ContentNodeKit[] kits)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
using Moq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.Testing.Objects.Accessors;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Tests.Testing.Objects
|
||||
{
|
||||
/// <summary>
|
||||
/// Simplify creating test UmbracoContext's
|
||||
/// </summary>
|
||||
public class TestUmbracoContextFactory
|
||||
{
|
||||
public static IUmbracoContextFactory Create(IGlobalSettings globalSettings = null, IUrlProvider urlProvider = null,
|
||||
IMediaUrlProvider mediaUrlProvider = null,
|
||||
IUmbracoContextAccessor umbracoContextAccessor = null)
|
||||
{
|
||||
if (globalSettings == null) globalSettings = SettingsForTests.GenerateMockGlobalSettings();
|
||||
if (urlProvider == null) urlProvider = Mock.Of<IUrlProvider>();
|
||||
if (mediaUrlProvider == null) mediaUrlProvider = Mock.Of<IMediaUrlProvider>();
|
||||
if (umbracoContextAccessor == null) umbracoContextAccessor = new TestUmbracoContextAccessor();
|
||||
|
||||
var contentCache = new Mock<IPublishedContentCache>();
|
||||
var mediaCache = new Mock<IPublishedMediaCache>();
|
||||
var snapshot = new Mock<IPublishedSnapshot>();
|
||||
snapshot.Setup(x => x.Content).Returns(contentCache.Object);
|
||||
snapshot.Setup(x => x.Media).Returns(mediaCache.Object);
|
||||
var snapshotService = new Mock<IPublishedSnapshotService>();
|
||||
snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny<string>())).Returns(snapshot.Object);
|
||||
|
||||
var umbracoContextFactory = new UmbracoContextFactory(
|
||||
umbracoContextAccessor,
|
||||
snapshotService.Object,
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
|
||||
globalSettings,
|
||||
new UrlProviderCollection(new[] { urlProvider }),
|
||||
new MediaUrlProviderCollection(new[] { mediaUrlProvider }),
|
||||
Mock.Of<IUserService>());
|
||||
|
||||
return umbracoContextFactory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ using Umbraco.Web.Composing.CompositionExtensions;
|
||||
using Umbraco.Web.Sections;
|
||||
using Current = Umbraco.Core.Composing.Current;
|
||||
using FileSystems = Umbraco.Core.IO.FileSystems;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
@@ -215,6 +217,9 @@ namespace Umbraco.Tests.Testing
|
||||
Composition.RegisterUnique(_ => Umbraco.Web.Composing.Current.UmbracoContextAccessor);
|
||||
Composition.RegisterUnique<IPublishedRouter, PublishedRouter>();
|
||||
Composition.WithCollectionBuilder<ContentFinderCollectionBuilder>();
|
||||
|
||||
Composition.DataValueReferenceFactories();
|
||||
|
||||
Composition.RegisterUnique<IContentLastChanceFinder, TestLastChanceFinder>();
|
||||
Composition.RegisterUnique<IVariationContextAccessor, TestVariationContextAccessor>();
|
||||
Composition.RegisterUnique<IPublishedSnapshotAccessor, TestPublishedSnapshotAccessor>();
|
||||
@@ -230,6 +235,11 @@ namespace Umbraco.Tests.Testing
|
||||
.Append<TranslationSection>();
|
||||
Composition.RegisterUnique<ISectionService, SectionService>();
|
||||
|
||||
Composition.RegisterUnique<HtmlLocalLinkParser>();
|
||||
Composition.RegisterUnique<HtmlUrlParser>();
|
||||
Composition.RegisterUnique<HtmlImageSourceParser>();
|
||||
Composition.RegisterUnique<RichTextEditorPastedImages>();
|
||||
|
||||
}
|
||||
|
||||
protected virtual void ComposeMisc()
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
<Compile Include="Models\VariationTests.cs" />
|
||||
<Compile Include="Persistence\Mappers\MapperTestBase.cs" />
|
||||
<Compile Include="Persistence\Repositories\DocumentRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
|
||||
<Compile Include="PublishedContent\NuCacheChildrenTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentLanguageVariantTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
|
||||
@@ -163,6 +164,7 @@
|
||||
<Compile Include="Services\MemberGroupServiceTests.cs" />
|
||||
<Compile Include="Services\MediaTypeServiceTests.cs" />
|
||||
<Compile Include="Services\PropertyValidationServiceTests.cs" />
|
||||
<Compile Include="Templates\HtmlLocalLinkParserTests.cs" />
|
||||
<Compile Include="TestHelpers\RandomIdRamDirectory.cs" />
|
||||
<Compile Include="Testing\Objects\TestDataSource.cs" />
|
||||
<Compile Include="Published\PublishedSnapshotTestObjects.cs" />
|
||||
@@ -216,6 +218,7 @@
|
||||
<Compile Include="Testing\Objects\Accessors\TestVariationContextAccessor.cs" />
|
||||
<Compile Include="Testing\ContentBaseExtensions.cs" />
|
||||
<Compile Include="Testing\Objects\Accessors\TestDefaultCultureAccessor.cs" />
|
||||
<Compile Include="Testing\Objects\TestUmbracoContextFactory.cs" />
|
||||
<Compile Include="Testing\TestDatabase.cs" />
|
||||
<Compile Include="Testing\TestingTests\NUnitTests.cs" />
|
||||
<Compile Include="Persistence\LocksTests.cs" />
|
||||
@@ -257,6 +260,7 @@
|
||||
<Compile Include="Web\Controllers\UsersControllerTests.cs" />
|
||||
<Compile Include="Web\HealthChecks\HealthCheckResultsTests.cs" />
|
||||
<Compile Include="Web\HttpCookieExtensionsTests.cs" />
|
||||
<Compile Include="Templates\HtmlImageSourceParserTests.cs" />
|
||||
<Compile Include="Web\Mvc\HtmlStringUtilitiesTests.cs" />
|
||||
<Compile Include="Web\ModelStateExtensionsTests.cs" />
|
||||
<Compile Include="Web\Mvc\RenderIndexActionSelectorAttributeTests.cs" />
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
@@ -9,20 +7,16 @@ using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.Testing.Objects.Accessors;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Tests.Web
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class TemplateUtilitiesTests
|
||||
{
|
||||
@@ -59,67 +53,6 @@ namespace Umbraco.Tests.Web
|
||||
Current.Reset();
|
||||
}
|
||||
|
||||
[TestCase("", "")]
|
||||
[TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://document/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://media/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/media/1001/my-image.jpg\" world ")]
|
||||
//this one has an invalid char so won't match
|
||||
[TestCase("hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
|
||||
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")]
|
||||
public void ParseLocalLinks(string input, string result)
|
||||
{
|
||||
var serviceCtxMock = new TestObjects(null).GetServiceContextMock();
|
||||
|
||||
//setup a mock entity service from the service context to return an integer for a GUID
|
||||
var entityService = Mock.Get(serviceCtxMock.EntityService);
|
||||
//entityService.Setup(x => x.GetId(It.IsAny<Guid>(), It.IsAny<UmbracoObjectTypes>()))
|
||||
// .Returns((Guid id, UmbracoObjectTypes objType) =>
|
||||
// {
|
||||
// return Attempt.Succeed(1234);
|
||||
// });
|
||||
|
||||
//setup a mock url provider which we'll use for testing
|
||||
var testUrlProvider = new Mock<IUrlProvider>();
|
||||
testUrlProvider
|
||||
.Setup(x => x.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string>(), It.IsAny<Uri>()))
|
||||
.Returns((UmbracoContext umbCtx, IPublishedContent content, UrlMode mode, string culture, Uri url) => UrlInfo.Url("/my-test-url"));
|
||||
|
||||
var globalSettings = SettingsForTests.GenerateMockGlobalSettings();
|
||||
|
||||
var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty<string>(), Enumerable.Empty<PublishedPropertyType>(), ContentVariation.Nothing);
|
||||
var publishedContent = Mock.Of<IPublishedContent>();
|
||||
Mock.Get(publishedContent).Setup(x => x.Id).Returns(1234);
|
||||
Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType);
|
||||
var contentCache = Mock.Of<IPublishedContentCache>();
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<int>())).Returns(publishedContent);
|
||||
Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny<Guid>())).Returns(publishedContent);
|
||||
var snapshot = Mock.Of<IPublishedSnapshot>();
|
||||
Mock.Get(snapshot).Setup(x => x.Content).Returns(contentCache);
|
||||
var snapshotService = Mock.Of<IPublishedSnapshotService>();
|
||||
Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny<string>())).Returns(snapshot);
|
||||
var media = Mock.Of<IPublishedContent>();
|
||||
Mock.Get(media).Setup(x => x.Url).Returns("/media/1001/my-image.jpg");
|
||||
var mediaCache = Mock.Of<IPublishedMediaCache>();
|
||||
Mock.Get(mediaCache).Setup(x => x.GetById(It.IsAny<Guid>())).Returns(media);
|
||||
|
||||
var umbracoContextFactory = new UmbracoContextFactory(
|
||||
Umbraco.Web.Composing.Current.UmbracoContextAccessor,
|
||||
snapshotService,
|
||||
new TestVariationContextAccessor(),
|
||||
new TestDefaultCultureAccessor(),
|
||||
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
|
||||
globalSettings,
|
||||
new UrlProviderCollection(new[] { testUrlProvider.Object }),
|
||||
new MediaUrlProviderCollection(Enumerable.Empty<IMediaUrlProvider>()),
|
||||
Mock.Of<IUserService>());
|
||||
|
||||
using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>()))
|
||||
{
|
||||
var output = TemplateUtilities.ParseInternalLinks(input, reference.UmbracoContext.UrlProvider, mediaCache);
|
||||
|
||||
Assert.AreEqual(result, output);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,6 +791,7 @@
|
||||
$scope.content.variants[i].expireDate = model.variants[i].expireDate;
|
||||
$scope.content.variants[i].releaseDateFormatted = model.variants[i].releaseDateFormatted;
|
||||
$scope.content.variants[i].expireDateFormatted = model.variants[i].expireDateFormatted;
|
||||
$scope.content.variants[i].save = model.variants[i].save;
|
||||
}
|
||||
|
||||
model.submitButtonState = "busy";
|
||||
|
||||
+69
-1
@@ -1,13 +1,30 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function MediaNodeInfoDirective($timeout, $location, eventsService, userService, dateHelper, editorService, mediaHelper) {
|
||||
function MediaNodeInfoDirective($timeout, $location, eventsService, userService, dateHelper, editorService, mediaHelper, mediaResource, $q) {
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
var evts = [];
|
||||
|
||||
scope.allowChangeMediaType = false;
|
||||
scope.loading = true;
|
||||
|
||||
scope.changeContentPageNumber = changeContentPageNumber;
|
||||
scope.contentOptions = {};
|
||||
scope.contentOptions.entityType = "DOCUMENT";
|
||||
scope.hasContentReferences = false;
|
||||
|
||||
scope.changeMediaPageNumber = changeMediaPageNumber;
|
||||
scope.mediaOptions = {};
|
||||
scope.mediaOptions.entityType = "MEDIA";
|
||||
scope.hasMediaReferences = false;
|
||||
|
||||
scope.changeMemberPageNumber = changeMemberPageNumber;
|
||||
scope.memberOptions = {};
|
||||
scope.memberOptions.entityType = "MEMBER";
|
||||
scope.hasMemberReferences = false;
|
||||
|
||||
|
||||
function onInit() {
|
||||
|
||||
@@ -94,6 +111,45 @@
|
||||
setMediaExtension();
|
||||
});
|
||||
|
||||
function changeContentPageNumber(pageNumber) {
|
||||
scope.contentOptions.pageNumber = pageNumber;
|
||||
loadContentRelations();
|
||||
}
|
||||
|
||||
function changeMediaPageNumber(pageNumber) {
|
||||
scope.mediaOptions.pageNumber = pageNumber;
|
||||
loadMediaRelations();
|
||||
}
|
||||
|
||||
function changeMemberPageNumber(pageNumber) {
|
||||
scope.memberOptions.pageNumber = pageNumber;
|
||||
loadMemberRelations();
|
||||
}
|
||||
|
||||
function loadContentRelations() {
|
||||
return mediaResource.getPagedReferences(scope.node.id, scope.contentOptions)
|
||||
.then(function (data) {
|
||||
scope.contentReferences = data;
|
||||
scope.hasContentReferences = data.items.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function loadMediaRelations() {
|
||||
return mediaResource.getPagedReferences(scope.node.id, scope.mediaOptions)
|
||||
.then(function (data) {
|
||||
scope.mediaReferences = data;
|
||||
scope.hasMediaReferences = data.items.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function loadMemberRelations() {
|
||||
return mediaResource.getPagedReferences(scope.node.id, scope.memberOptions)
|
||||
.then(function (data) {
|
||||
scope.memberReferences = data;
|
||||
scope.hasMemberReferences = data.items.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
//ensure to unregister from all events!
|
||||
scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
@@ -102,6 +158,18 @@
|
||||
});
|
||||
|
||||
onInit();
|
||||
|
||||
// load media type references when the 'info' tab is first activated/switched to
|
||||
evts.push(eventsService.on("app.tabChange", function (event, args) {
|
||||
$timeout(function () {
|
||||
if (args.alias === "umbInfo") {
|
||||
|
||||
$q.all([loadContentRelations(), loadMediaRelations(), loadMemberRelations()]).then(function () {
|
||||
scope.loading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
var directive = {
|
||||
|
||||
@@ -552,6 +552,36 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
"Search",
|
||||
args)),
|
||||
'Failed to retrieve media items for search: ' + query);
|
||||
},
|
||||
|
||||
getPagedReferences: function (id, options) {
|
||||
|
||||
var defaults = {
|
||||
pageSize: 25,
|
||||
pageNumber: 1,
|
||||
entityType: "DOCUMENT"
|
||||
};
|
||||
if (options === undefined) {
|
||||
options = {};
|
||||
}
|
||||
//overwrite the defaults if there are any specified
|
||||
angular.extend(defaults, options);
|
||||
//now copy back to the options we will use
|
||||
options = defaults;
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"mediaApiBaseUrl",
|
||||
"GetPagedReferences",
|
||||
{
|
||||
id: id,
|
||||
entityType: options.entityType,
|
||||
pageNumber: options.pageNumber,
|
||||
pageSize: options.pageSize
|
||||
}
|
||||
)),
|
||||
"Failed to retrieve usages for media of id " + id);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user