Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b686dc6362 | |||
| ab0c4d2700 | |||
| 8b8a8c0ccb | |||
| 2f84e76952 | |||
| 12a30d3ffd | |||
| 7e1f90fb3b | |||
| feb5567621 | |||
| ab3202b560 | |||
| e10ce234db | |||
| 84554101f2 | |||
| 8a3b2e038c | |||
| 296d7964ae | |||
| ed5effd190 | |||
| 57e58c6cf7 | |||
| 34e45abcc7 | |||
| b3497011e8 | |||
| a398881fa1 | |||
| 2cf3a7831c | |||
| 0138091a6c | |||
| 6c0fb2f849 | |||
| e495faf83c | |||
| 8721423f40 | |||
| 49b3351c72 | |||
| faef449750 | |||
| 4a24064783 | |||
| 0ab84fc443 | |||
| fca74ad8d7 | |||
| 7bc7dba86d | |||
| ca75a25802 | |||
| ece31733a9 | |||
| 28a8027179 | |||
| a97604d6c4 | |||
| 308f929f7b | |||
| c8d7df1515 | |||
| 2c795662d2 | |||
| 8a43e3a87e | |||
| 210e43fcb0 | |||
| beb8c7ac7c | |||
| 2f8979bbc3 | |||
| a8ed7f2c17 | |||
| 1c6bf55e5d | |||
| c246044ac7 | |||
| 5126521d37 | |||
| 27223738c6 | |||
| 968463912a | |||
| 05c6b856f9 | |||
| 6fe3089207 | |||
| 06b136fdf6 | |||
| c6246b5404 | |||
| 3062f40ef5 | |||
| dcd0d38419 | |||
| c01a8f8f13 | |||
| da71a94c39 | |||
| f6413af3f4 | |||
| 440e649d6d | |||
| 47dcd37d1d | |||
| d60fc63e78 | |||
| b0c4042e87 | |||
| da525513a3 | |||
| 2ac5e61e44 | |||
| 101a8b6c78 | |||
| f7a10e3a15 | |||
| 4e0dd728a8 | |||
| 320ccf8105 | |||
| 62f924a757 | |||
| c47a08b553 | |||
| de9241bcf5 | |||
| 7c03fe3c1f | |||
| 82eb241119 | |||
| 1073e6257c | |||
| bc3e4847fe | |||
| 1a7251909c | |||
| 61a18297d9 | |||
| bdd413f9ee | |||
| fd8ca35e63 | |||
| fdd9aee773 | |||
| 3c2a92d3b6 | |||
| cf49e6160a | |||
| ec93030e21 | |||
| 1d49c8626e | |||
| 89bb94aa1a | |||
| 03a4b75176 | |||
| 943b54dcf7 | |||
| 39bd18ec1a | |||
| e93ddff46b | |||
| 3144b51c91 | |||
| e92f68d040 | |||
| 469054e9b3 | |||
| d17022d87d | |||
| 6865629090 | |||
| a4749f201d | |||
| ad90264ef1 | |||
| 9ba7e7742a | |||
| 6473f600c7 | |||
| 6329dfa629 | |||
| 2cd0173534 | |||
| a0bcfeddcc | |||
| c4b5483d8c | |||
| 7557c584d8 |
@@ -12,7 +12,7 @@ thoroughly. Then, proceed by filling out the rest of the details in the issue
|
||||
template below. The more details you can give us, the easier it will be for us
|
||||
to determine the cause of a problem.
|
||||
|
||||
See: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md
|
||||
See: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.github/CONTRIBUTING.md
|
||||
|
||||
-->
|
||||
|
||||
|
||||
+2
-2
@@ -18,5 +18,5 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("8.0.0")]
|
||||
|
||||
// these are FYI and changed automatically
|
||||
[assembly: AssemblyFileVersion("8.1.2")]
|
||||
[assembly: AssemblyInformationalVersion("8.1.2")]
|
||||
[assembly: AssemblyFileVersion("8.2.0")]
|
||||
[assembly: AssemblyInformationalVersion("8.2.0")]
|
||||
|
||||
@@ -12,62 +12,31 @@
|
||||
/// or MS.DI, PerDependency in Autofac.</remarks>
|
||||
Transient,
|
||||
|
||||
// TODO: We need to fix this up, currently LightInject is the only one that behaves differently from all other containers.
|
||||
// ... the simple fix would be to map this to PerScopeLifetime in LI but need to wait on a response here https://github.com/seesharper/LightInject/issues/494#issuecomment-518942625
|
||||
//
|
||||
// we use it for controllers, httpContextBase and other request scoped objects: MembershpHelper, TagQuery, UmbracoTreeSearcher and ISearchableTree
|
||||
// - so that they are automatically disposed at the end of the scope (ie request)
|
||||
// - not sure they should not be simply 'scoped'?
|
||||
|
||||
/// <summary>
|
||||
/// One unique instance per request.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Any instance created with this lifetime will be disposed at the end of a request.
|
||||
/// </para>
|
||||
/// Corresponds to
|
||||
/// <para>
|
||||
/// PerRequestLifeTime in LightInject - means transient but disposed at the end of the current web request.
|
||||
/// see: https://github.com/seesharper/LightInject/issues/494#issuecomment-518493262
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Scoped in MS.DI - means one per web request.
|
||||
/// see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes</para>
|
||||
/// <para>
|
||||
/// InstancePerRequest in Autofac - means one per web request.
|
||||
/// see https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-request
|
||||
/// But "Behind the scenes, though, it’s still just instance per matching lifetime scope."
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// LifestylePerWebRequest in Castle Windsor - means one per web request.
|
||||
/// see https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-part-7-lifestyles.md#the-perwebrequest-lifestyle
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
// TODO: review lifetimes for LightInject vs other containers
|
||||
// currently, corresponds to 'Request' in LightInject which is 'Transient + disposed by Scope'
|
||||
// but NOT (in LightInject) a per-web-request lifetime, more a TransientScoped
|
||||
//
|
||||
// we use it for controllers, httpContextBase and umbracoContext
|
||||
// - so that they are automatically disposed at the end of the scope (ie request)
|
||||
// - not sure they should not be simply 'scoped'?
|
||||
//
|
||||
// Castle has an extra PerWebRequest something, and others use scope
|
||||
// what about Request before first request ie during application startup?
|
||||
// see http://blog.ploeh.dk/2009/11/17/UsingCastleWindsor'sPerWebRequestlifestylewithASP.NETMVConIIS7/
|
||||
// Castle ends up requiring a special scope manager too
|
||||
// see https://groups.google.com/forum/#!topic/castle-project-users/1E2W9LVIYR4
|
||||
//
|
||||
// but maybe also - why are we requiring scoped services at startup?
|
||||
Request,
|
||||
|
||||
/// <summary>
|
||||
/// One unique instance per scope.
|
||||
/// One unique instance per container scope.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Any instance created with this lifetime will be disposed at the end of the current scope.
|
||||
/// </para>
|
||||
/// Corresponds to
|
||||
/// <para>PerScopeLifetime in LightInject (when in a request, means one per web request)</para>
|
||||
/// <para>
|
||||
/// Scoped in MS.DI (when in a request, means one per web request)
|
||||
/// see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes</para>
|
||||
/// <para>
|
||||
/// InstancePerLifetimeScope in Autofac (when in a request, means one per web request)
|
||||
/// see https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-lifetime-scope
|
||||
/// Also note that Autofac's InstancePerRequest is the same as this, see https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-request
|
||||
/// it says "Behind the scenes, though, it’s still just instance per matching lifetime scope."
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// LifestyleScoped in Castle Windsor
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <remarks>Corresponds to Scope in LightInject, Scoped in MS.DI
|
||||
/// or Castle Windsor, PerLifetimeScope in Autofac.</remarks>
|
||||
Scope,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -203,6 +203,24 @@ namespace Umbraco.Core
|
||||
/// <remarks>Must be a valid <see cref="ValueTypes"/> value.</remarks>
|
||||
public const string DataValueType = "umbracoDataValueType";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines Umbraco's built-in property editor groups.
|
||||
/// </summary>
|
||||
public static class Groups
|
||||
{
|
||||
public const string Common = "Common";
|
||||
|
||||
public const string Lists = "Lists";
|
||||
|
||||
public const string Media = "Media";
|
||||
|
||||
public const string People = "People";
|
||||
|
||||
public const string Pickers = "Pickers";
|
||||
|
||||
public const string RichContent = "Rich Content";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal exception that in theory should never ben thrown, it is only thrown in circumstances that should never happen
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
internal class PanicException : Exception
|
||||
{
|
||||
public PanicException()
|
||||
{
|
||||
}
|
||||
|
||||
public PanicException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public PanicException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected PanicException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Mapping
|
||||
{
|
||||
@@ -260,7 +259,7 @@ namespace Umbraco.Core.Mapping
|
||||
if (typeof(TTarget).IsArray)
|
||||
{
|
||||
var elementType = typeof(TTarget).GetElementType();
|
||||
if (elementType == null) throw new PanicException("elementType == null which should never occur");
|
||||
if (elementType == null) throw new Exception("panic");
|
||||
var targetArray = Array.CreateInstance(elementType, targetList.Count);
|
||||
targetList.CopyTo(targetArray, 0);
|
||||
target = targetArray;
|
||||
@@ -383,7 +382,7 @@ namespace Umbraco.Core.Mapping
|
||||
{
|
||||
if (type.IsArray) return type.GetElementType();
|
||||
if (type.IsGenericType) return type.GenericTypeArguments[0];
|
||||
throw new PanicException($"Could not get enumerable or array type from {type}");
|
||||
throw new Exception("panic");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
|
||||
{
|
||||
@@ -21,7 +20,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
|
||||
case "Umbraco.NoEdit":
|
||||
return Constants.PropertyEditors.Aliases.Label;
|
||||
default:
|
||||
throw new PanicException($"The alias {editorAlias} is not supported");
|
||||
throw new Exception("panic");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,13 +222,7 @@ namespace Umbraco.Core.Models
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if the culture is already unpublished
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
public static bool UnpublishCulture(this IContent content, string culture = "*")
|
||||
public static void UnpublishCulture(this IContent content, string culture = "*")
|
||||
{
|
||||
culture = culture.NullOrWhiteSpaceAsNull();
|
||||
|
||||
@@ -236,31 +230,16 @@ namespace Umbraco.Core.Models
|
||||
if (!content.ContentType.SupportsPropertyVariation(culture, "*", true))
|
||||
throw new NotSupportedException($"Culture \"{culture}\" is not supported by content type \"{content.ContentType.Alias}\" with variation \"{content.ContentType.Variations}\".");
|
||||
|
||||
|
||||
var keepProcessing = true;
|
||||
|
||||
if (culture == "*")
|
||||
{
|
||||
// all cultures
|
||||
if (culture == "*") // all cultures
|
||||
content.ClearPublishInfos();
|
||||
}
|
||||
else
|
||||
{
|
||||
// one single culture
|
||||
keepProcessing = content.ClearPublishInfo(culture);
|
||||
}
|
||||
|
||||
else // one single culture
|
||||
content.ClearPublishInfo(culture);
|
||||
|
||||
if (keepProcessing)
|
||||
{
|
||||
// property.PublishValues only publishes what is valid, variation-wise
|
||||
foreach (var property in content.Properties)
|
||||
property.UnpublishValues(culture);
|
||||
// property.PublishValues only publishes what is valid, variation-wise
|
||||
foreach (var property in content.Properties)
|
||||
property.UnpublishValues(culture);
|
||||
|
||||
content.PublishedState = PublishedState.Publishing;
|
||||
}
|
||||
|
||||
return keepProcessing;
|
||||
content.PublishedState = PublishedState.Publishing;
|
||||
}
|
||||
|
||||
public static void ClearPublishInfos(this IContent content)
|
||||
@@ -268,24 +247,15 @@ namespace Umbraco.Core.Models
|
||||
content.PublishCultureInfos = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if the culture is already unpublished
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ClearPublishInfo(this IContent content, string culture)
|
||||
public static void ClearPublishInfo(this IContent content, string culture)
|
||||
{
|
||||
if (culture.IsNullOrWhiteSpace())
|
||||
throw new ArgumentNullOrEmptyException(nameof(culture));
|
||||
|
||||
var removed = content.PublishCultureInfos.Remove(culture);
|
||||
if (removed)
|
||||
{
|
||||
// set the culture to be dirty - it's been modified
|
||||
content.TouchCulture(culture);
|
||||
}
|
||||
return removed;
|
||||
content.PublishCultureInfos.Remove(culture);
|
||||
|
||||
// set the culture to be dirty - it's been modified
|
||||
content.TouchCulture(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Umbraco.Core.Models
|
||||
PropertyGroupCollection PropertyGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all local property types all local property groups or ungrouped.
|
||||
/// Gets all local property types belonging to a group, across all local property groups.
|
||||
/// </summary>
|
||||
IEnumerable<PropertyType> PropertyTypes { get; }
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Models
|
||||
/// Gets of sets the alias of the property type.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Alias
|
||||
public virtual string Alias
|
||||
{
|
||||
get => _alias;
|
||||
set => SetPropertyValueAndDetectChanges(SanitizeAlias(value), ref _alias, nameof(Alias));
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
return type;
|
||||
var def = type.GetGenericTypeDefinition();
|
||||
if (def == null)
|
||||
throw new PanicException($"The type {type} has not generic type definition");
|
||||
throw new InvalidOperationException("panic");
|
||||
|
||||
var args = type.GetGenericArguments().Select(x => Map(x, modelTypes, true)).ToArray();
|
||||
return def.MakeGenericType(args);
|
||||
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
return type.FullName;
|
||||
var def = type.GetGenericTypeDefinition();
|
||||
if (def == null)
|
||||
throw new PanicException($"The type {type} has not generic type definition");
|
||||
throw new InvalidOperationException("panic");
|
||||
|
||||
var args = type.GetGenericArguments().Select(x => MapToName(x, map, true)).ToArray();
|
||||
var defFullName = def.FullName.Substring(0, def.FullName.IndexOf('`'));
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
public static IEnumerable<Property> BuildEntities(PropertyType[] propertyTypes, IReadOnlyCollection<PropertyDataDto> dtos, int publishedVersionId, ILanguageRepository languageRepository)
|
||||
{
|
||||
var properties = new List<Property>();
|
||||
var xdtos = dtos.GroupBy(x => x.PropertyTypeId).ToDictionary(x => x.Key, x => (IEnumerable<PropertyDataDto>)x);
|
||||
var xdtos = dtos.GroupBy(x => x.PropertyTypeId).ToDictionary(x => x.Key, x => (IEnumerable<PropertyDataDto>) x);
|
||||
|
||||
foreach (var propertyType in propertyTypes)
|
||||
{
|
||||
@@ -104,14 +104,10 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
/// <param name="properties">The properties to map</param>
|
||||
/// <param name="languageRepository"></param>
|
||||
/// <param name="edited">out parameter indicating that one or more properties have been edited</param>
|
||||
/// <param name="editedCultures">
|
||||
/// Out parameter containing a collection of edited cultures when the contentVariation varies by culture.
|
||||
/// The value of this will be used to populate the edited cultures in the umbracoDocumentCultureVariation table.
|
||||
/// </param>
|
||||
/// <param name="editedCultures">out parameter containing a collection of edited cultures when the contentVariation varies by culture</param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<PropertyDataDto> BuildDtos(ContentVariation contentVariation, int currentVersionId, int publishedVersionId, IEnumerable<Property> properties,
|
||||
ILanguageRepository languageRepository, out bool edited,
|
||||
out HashSet<string> editedCultures)
|
||||
ILanguageRepository languageRepository, out bool edited, out HashSet<string> editedCultures)
|
||||
{
|
||||
var propertyDataDtos = new List<PropertyDataDto>();
|
||||
edited = false;
|
||||
@@ -134,9 +130,6 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
// publishing = deal with edit and published values
|
||||
foreach (var propertyValue in property.Values)
|
||||
{
|
||||
var isInvariantValue = propertyValue.Culture == null;
|
||||
var isCultureValue = propertyValue.Culture != null && propertyValue.Segment == null;
|
||||
|
||||
// deal with published value
|
||||
if (propertyValue.PublishedValue != null && publishedVersionId > 0)
|
||||
propertyDataDtos.Add(BuildDto(publishedVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.PublishedValue));
|
||||
@@ -145,36 +138,26 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
if (propertyValue.EditedValue != null)
|
||||
propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue));
|
||||
|
||||
// property.Values will contain ALL of it's values, both variant and invariant which will be populated if the
|
||||
// administrator has previously changed the property type to be variant vs invariant.
|
||||
// We need to check for this scenario here because otherwise the editedCultures and edited flags
|
||||
// will end up incorrectly set in the umbracoDocumentCultureVariation table so here we need to
|
||||
// only process edited cultures based on the current value type and how the property varies.
|
||||
// The above logic will still persist the currently saved property value for each culture in case the admin
|
||||
// decides to swap the property's variance again, in which case the edited flag will be recalculated.
|
||||
|
||||
if (property.PropertyType.VariesByCulture() && isInvariantValue || !property.PropertyType.VariesByCulture() && isCultureValue)
|
||||
continue;
|
||||
|
||||
// use explicit equals here, else object comparison fails at comparing eg strings
|
||||
var sameValues = propertyValue.PublishedValue == null ? propertyValue.EditedValue == null : propertyValue.PublishedValue.Equals(propertyValue.EditedValue);
|
||||
|
||||
edited |= !sameValues;
|
||||
|
||||
if (entityVariesByCulture && !sameValues)
|
||||
if (entityVariesByCulture // cultures can be edited, ie CultureNeutral is supported
|
||||
&& propertyValue.Culture != null && propertyValue.Segment == null // and value is CultureNeutral
|
||||
&& !sameValues) // and edited and published are different
|
||||
{
|
||||
if (isCultureValue)
|
||||
{
|
||||
editedCultures.Add(propertyValue.Culture); // report culture as edited
|
||||
}
|
||||
else if (isInvariantValue)
|
||||
{
|
||||
// flag culture as edited if it contains an edited invariant property
|
||||
if (defaultCulture == null)
|
||||
defaultCulture = languageRepository.GetDefaultIsoCode();
|
||||
editedCultures.Add(propertyValue.Culture); // report culture as edited
|
||||
}
|
||||
|
||||
editedCultures.Add(defaultCulture);
|
||||
}
|
||||
// flag culture as edited if it contains an edited invariant property
|
||||
if (propertyValue.Culture == null //invariant property
|
||||
&& !sameValues // and edited and published are different
|
||||
&& entityVariesByCulture) //only when the entity is variant
|
||||
{
|
||||
if (defaultCulture == null)
|
||||
defaultCulture = languageRepository.GetDefaultIsoCode();
|
||||
|
||||
editedCultures.Add(defaultCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,7 +167,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
// not publishing = only deal with edit values
|
||||
if (propertyValue.EditedValue != null)
|
||||
propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue));
|
||||
propertyDataDtos.Add(BuildDto(currentVersionId, property, languageRepository.GetIdByIsoCode(propertyValue.Culture), propertyValue.Segment, propertyValue.EditedValue));
|
||||
}
|
||||
edited = true;
|
||||
}
|
||||
|
||||
@@ -512,16 +512,31 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
foreach (var a in allPropertyDataDtos)
|
||||
a.PropertyTypeDto = indexedPropertyTypeDtos[a.PropertyTypeId];
|
||||
|
||||
// prefetch configuration for tag properties
|
||||
var tagEditors = new Dictionary<string, TagConfiguration>();
|
||||
foreach (var propertyTypeDto in indexedPropertyTypeDtos.Values)
|
||||
{
|
||||
var editorAlias = propertyTypeDto.DataTypeDto.EditorAlias;
|
||||
var editorAttribute = PropertyEditors[editorAlias].GetTagAttribute();
|
||||
if (editorAttribute == null) continue;
|
||||
var tagConfigurationSource = propertyTypeDto.DataTypeDto.Configuration;
|
||||
var tagConfiguration = string.IsNullOrWhiteSpace(tagConfigurationSource)
|
||||
? new TagConfiguration()
|
||||
: JsonConvert.DeserializeObject<TagConfiguration>(tagConfigurationSource);
|
||||
if (tagConfiguration.Delimiter == default) tagConfiguration.Delimiter = editorAttribute.Delimiter;
|
||||
tagEditors[editorAlias] = tagConfiguration;
|
||||
}
|
||||
|
||||
// now we have
|
||||
// - the definitions
|
||||
// - all property data dtos
|
||||
// - tag editors (Actually ... no we don't since i removed that code, but we don't need them anyways it seems)
|
||||
// - tag editors
|
||||
// and we need to build the proper property collections
|
||||
|
||||
return GetPropertyCollections(temps, allPropertyDataDtos);
|
||||
return GetPropertyCollections(temps, allPropertyDataDtos, tagEditors);
|
||||
}
|
||||
|
||||
private IDictionary<int, PropertyCollection> GetPropertyCollections<T>(List<TempContent<T>> temps, IEnumerable<PropertyDataDto> allPropertyDataDtos)
|
||||
private IDictionary<int, PropertyCollection> GetPropertyCollections<T>(List<TempContent<T>> temps, IEnumerable<PropertyDataDto> allPropertyDataDtos, Dictionary<string, TagConfiguration> tagConfigurations)
|
||||
where T : class, IContentBase
|
||||
{
|
||||
var result = new Dictionary<int, PropertyCollection>();
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
@@ -91,7 +90,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
contentType = ContentTypeFactory.BuildContentTypeEntity(contentTypeDto);
|
||||
else if (contentTypeDto.NodeDto.NodeObjectType == Constants.ObjectTypes.MemberType)
|
||||
contentType = ContentTypeFactory.BuildMemberTypeEntity(contentTypeDto);
|
||||
else throw new PanicException($"The node object type {contentTypeDto.NodeDto.NodeObjectType} is not supported");
|
||||
else throw new Exception("panic");
|
||||
contentTypes.Add(contentType.Id, contentType);
|
||||
|
||||
// map allowed content types
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
@@ -19,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class ContentTypeRepository : ContentTypeRepositoryBase<IContentType>, IContentTypeRepository
|
||||
{
|
||||
public ContentTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, logger, commonRepository, languageRepository)
|
||||
public ContentTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
|
||||
: base(scopeAccessor, cache, logger, commonRepository)
|
||||
{ }
|
||||
|
||||
protected override bool SupportsPublishing => ContentType.SupportsPublishingConst;
|
||||
@@ -57,7 +56,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
// the cache policy will always want everything
|
||||
// even GetMany(ids) gets everything and filters afterwards
|
||||
if (ids.Any()) throw new PanicException("There can be no ids specified");
|
||||
if (ids.Any()) throw new Exception("panic");
|
||||
return CommonRepository.GetAllTypes().OfType<IContentType>();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
@@ -27,15 +26,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal abstract class ContentTypeRepositoryBase<TEntity> : NPocoRepositoryBase<int, TEntity>, IReadRepository<Guid, TEntity>
|
||||
where TEntity : class, IContentTypeComposition
|
||||
{
|
||||
protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository)
|
||||
protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
CommonRepository = commonRepository;
|
||||
LanguageRepository = languageRepository;
|
||||
}
|
||||
|
||||
protected IContentTypeCommonRepository CommonRepository { get; }
|
||||
protected ILanguageRepository LanguageRepository { get; }
|
||||
|
||||
protected abstract bool SupportsPublishing { get; }
|
||||
|
||||
public IEnumerable<MoveEventInfo<TEntity>> Move(TEntity moving, EntityContainer container)
|
||||
@@ -100,8 +98,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
protected void PersistNewBaseContentType(IContentTypeComposition entity)
|
||||
{
|
||||
ValidateVariations(entity);
|
||||
|
||||
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
|
||||
|
||||
//Cannot add a duplicate content type
|
||||
@@ -167,11 +163,11 @@ AND umbracoNode.nodeObjectType = @objectType",
|
||||
foreach (var allowedContentType in entity.AllowedContentTypes)
|
||||
{
|
||||
Database.Insert(new ContentTypeAllowedContentTypeDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
AllowedId = allowedContentType.Id.Value,
|
||||
SortOrder = allowedContentType.SortOrder
|
||||
});
|
||||
{
|
||||
Id = entity.Id,
|
||||
AllowedId = allowedContentType.Id.Value,
|
||||
SortOrder = allowedContentType.SortOrder
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -218,8 +214,6 @@ AND umbracoNode.nodeObjectType = @objectType",
|
||||
|
||||
protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
|
||||
{
|
||||
ValidateVariations(entity);
|
||||
|
||||
var dto = ContentTypeFactory.BuildContentTypeDto(entity);
|
||||
|
||||
// ensure the alias is not used already
|
||||
@@ -376,7 +370,7 @@ AND umbracoNode.id <> @id",
|
||||
foreach (var propertyGroup in entity.PropertyGroups)
|
||||
{
|
||||
// insert or update group
|
||||
var groupDto = PropertyGroupFactory.BuildGroupDto(propertyGroup, entity.Id);
|
||||
var groupDto = PropertyGroupFactory.BuildGroupDto(propertyGroup,entity.Id);
|
||||
var groupId = propertyGroup.HasIdentity
|
||||
? Database.Update(groupDto)
|
||||
: Convert.ToInt32(Database.Insert(groupDto));
|
||||
@@ -394,7 +388,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
//check if the content type variation has been changed
|
||||
var contentTypeVariationDirty = entity.IsPropertyDirty("Variations");
|
||||
var oldContentTypeVariation = (ContentVariation)dtoPk.Variations;
|
||||
var oldContentTypeVariation = (ContentVariation) dtoPk.Variations;
|
||||
var newContentTypeVariation = entity.Variations;
|
||||
var contentTypeVariationChanging = contentTypeVariationDirty && oldContentTypeVariation != newContentTypeVariation;
|
||||
if (contentTypeVariationChanging)
|
||||
@@ -455,7 +449,7 @@ AND umbracoNode.id <> @id",
|
||||
// via composition, with their original variations (ie not filtered by this
|
||||
// content type variations - we need this true value to make decisions.
|
||||
|
||||
foreach (var propertyType in ((ContentTypeCompositionBase)entity).RawComposedPropertyTypes)
|
||||
foreach (var propertyType in ((ContentTypeCompositionBase) entity).RawComposedPropertyTypes)
|
||||
{
|
||||
if (propertyType.VariesBySegment() || newContentTypeVariation.VariesBySegment())
|
||||
throw new NotSupportedException(); // TODO: support this
|
||||
@@ -524,25 +518,6 @@ AND umbracoNode.id <> @id",
|
||||
CommonRepository.ClearCache(); // always
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that no property types are flagged for a variance that is not supported by the content type itself
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
private void ValidateVariations(IContentTypeComposition entity)
|
||||
{
|
||||
//if the entity does not vary at all, then the property cannot have a variance value greater than it
|
||||
if (entity.Variations == ContentVariation.Nothing)
|
||||
{
|
||||
foreach (var prop in entity.PropertyTypes)
|
||||
{
|
||||
if (prop.IsPropertyDirty(nameof(prop.Variations)) && prop.Variations > entity.Variations)
|
||||
throw new InvalidOperationException($"The property {prop.Alias} cannot have variations of {prop.Variations} with the content type variations of {entity.Variations}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IEnumerable<IContentTypeComposition> GetImpactedContentTypes(IContentTypeComposition contentType, IEnumerable<IContentTypeComposition> all)
|
||||
{
|
||||
var impact = new List<IContentTypeComposition>();
|
||||
@@ -550,12 +525,12 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
var tree = new Dictionary<int, List<IContentTypeComposition>>();
|
||||
foreach (var x in all)
|
||||
foreach (var y in x.ContentTypeComposition)
|
||||
{
|
||||
if (!tree.TryGetValue(y.Id, out var list))
|
||||
list = tree[y.Id] = new List<IContentTypeComposition>();
|
||||
list.Add(x);
|
||||
}
|
||||
foreach (var y in x.ContentTypeComposition)
|
||||
{
|
||||
if (!tree.TryGetValue(y.Id, out var list))
|
||||
list = tree[y.Id] = new List<IContentTypeComposition>();
|
||||
list.Add(x);
|
||||
}
|
||||
|
||||
var nset = new List<IContentTypeComposition>();
|
||||
do
|
||||
@@ -597,7 +572,7 @@ AND umbracoNode.id <> @id",
|
||||
// new property type, ignore
|
||||
if (!oldVariations.TryGetValue(propertyType.Id, out var oldVariationB))
|
||||
continue;
|
||||
var oldVariation = (ContentVariation)oldVariationB; // NPoco cannot fetch directly
|
||||
var oldVariation = (ContentVariation) oldVariationB; // NPoco cannot fetch directly
|
||||
|
||||
// only those property types that *actually* changed
|
||||
var newVariation = propertyType.Variations;
|
||||
@@ -661,7 +636,7 @@ AND umbracoNode.id <> @id",
|
||||
var impactedL = impacted.Select(x => x.Id).ToList();
|
||||
|
||||
//Group by the "To" variation so we can bulk update in the correct batches
|
||||
foreach (var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation))
|
||||
foreach(var grouping in propertyTypeChanges.GroupBy(x => x.Value.ToVariation))
|
||||
{
|
||||
var propertyTypeIds = grouping.Select(x => x.Key).ToList();
|
||||
var toVariation = grouping.Key;
|
||||
@@ -671,12 +646,10 @@ AND umbracoNode.id <> @id",
|
||||
case ContentVariation.Culture:
|
||||
CopyPropertyData(null, defaultLanguageId, propertyTypeIds, impactedL);
|
||||
CopyTagData(null, defaultLanguageId, propertyTypeIds, impactedL);
|
||||
RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL);
|
||||
break;
|
||||
case ContentVariation.Nothing:
|
||||
CopyPropertyData(defaultLanguageId, null, propertyTypeIds, impactedL);
|
||||
CopyTagData(defaultLanguageId, null, propertyTypeIds, impactedL);
|
||||
RenormalizeDocumentEditedFlags(propertyTypeIds, impactedL);
|
||||
break;
|
||||
case ContentVariation.CultureAndSegment:
|
||||
case ContentVariation.Segment:
|
||||
@@ -990,205 +963,6 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
Database.Execute(sqlDelete);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-normalizes the edited value in the umbracoDocumentCultureVariation and umbracoDocument table when variations are changed
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeIds"></param>
|
||||
/// <param name="contentTypeIds"></param>
|
||||
/// <remarks>
|
||||
/// If this is not done, then in some cases the "edited" value for a particular culture for a document will remain true when it should be false
|
||||
/// if the property was changed to invariant. In order to do this we need to recalculate this value based on the values stored for each
|
||||
/// property, culture and current/published version.
|
||||
/// </remarks>
|
||||
private void RenormalizeDocumentEditedFlags(IReadOnlyCollection<int> propertyTypeIds, IReadOnlyCollection<int> contentTypeIds = null)
|
||||
{
|
||||
var defaultLang = LanguageRepository.GetDefaultId();
|
||||
|
||||
//This will build up a query to get the property values of both the current and the published version so that we can check
|
||||
//based on the current variance of each item to see if it's 'edited' value should be true/false.
|
||||
|
||||
var whereInArgsCount = propertyTypeIds.Count + (contentTypeIds?.Count ?? 0);
|
||||
if (whereInArgsCount > 2000)
|
||||
throw new NotSupportedException("Too many property/content types.");
|
||||
|
||||
var propertySql = Sql()
|
||||
.Select<PropertyDataDto>()
|
||||
.AndSelect<ContentVersionDto>(x => x.NodeId, x => x.Current)
|
||||
.AndSelect<DocumentVersionDto>(x => x.Published)
|
||||
.AndSelect<PropertyTypeDto>(x => x.Variations)
|
||||
.From<PropertyDataDto>()
|
||||
.InnerJoin<ContentVersionDto>().On<ContentVersionDto, PropertyDataDto>((left, right) => left.Id == right.VersionId)
|
||||
.InnerJoin<PropertyTypeDto>().On<PropertyTypeDto, PropertyDataDto>((left, right) => left.Id == right.PropertyTypeId);
|
||||
|
||||
if (contentTypeIds != null)
|
||||
{
|
||||
propertySql.InnerJoin<ContentDto>().On<ContentDto, ContentVersionDto>((c, cversion) => c.NodeId == cversion.NodeId);
|
||||
}
|
||||
|
||||
propertySql.LeftJoin<DocumentVersionDto>().On<DocumentVersionDto, ContentVersionDto>((docversion, cversion) => cversion.Id == docversion.Id)
|
||||
.Where<DocumentVersionDto, ContentVersionDto>((docversion, cversion) => cversion.Current || docversion.Published)
|
||||
.WhereIn<PropertyDataDto>(x => x.PropertyTypeId, propertyTypeIds);
|
||||
|
||||
if (contentTypeIds != null)
|
||||
{
|
||||
propertySql.WhereIn<ContentDto>(x => x.ContentTypeId, contentTypeIds);
|
||||
}
|
||||
|
||||
propertySql
|
||||
.OrderBy<ContentVersionDto>(x => x.NodeId)
|
||||
.OrderBy<PropertyDataDto>(x => x.PropertyTypeId, x => x.LanguageId, x => x.VersionId);
|
||||
|
||||
//keep track of this node/lang to mark or unmark a culture as edited
|
||||
var editedLanguageVersions = new Dictionary<(int nodeId, int? langId), bool>();
|
||||
//keep track of which node to mark or unmark as edited
|
||||
var editedDocument = new Dictionary<int, bool>();
|
||||
var nodeId = -1;
|
||||
var propertyTypeId = -1;
|
||||
|
||||
PropertyValueVersionDto pubRow = null;
|
||||
|
||||
//This is a reader (Query), we are not fetching this all into memory so we cannot make any changes during this iteration, we are just collecting data.
|
||||
//Published data will always come before Current data based on the version id sort.
|
||||
//There will only be one published row (max) and one current row per property.
|
||||
foreach (var row in Database.Query<PropertyValueVersionDto>(propertySql))
|
||||
{
|
||||
//make sure to reset on each node/property change
|
||||
if (nodeId != row.NodeId || propertyTypeId != row.PropertyTypeId)
|
||||
{
|
||||
nodeId = row.NodeId;
|
||||
propertyTypeId = row.PropertyTypeId;
|
||||
pubRow = null;
|
||||
}
|
||||
|
||||
if (row.Published)
|
||||
pubRow = row;
|
||||
|
||||
if (row.Current)
|
||||
{
|
||||
var propVariations = (ContentVariation)row.Variations;
|
||||
|
||||
//if this prop doesn't vary but the row has a lang assigned or vice versa, flag this as not edited
|
||||
if (!propVariations.VariesByCulture() && row.LanguageId.HasValue
|
||||
|| propVariations.VariesByCulture() && !row.LanguageId.HasValue)
|
||||
{
|
||||
//Flag this as not edited for this node/lang if the key doesn't exist
|
||||
if (!editedLanguageVersions.TryGetValue((row.NodeId, row.LanguageId), out _))
|
||||
editedLanguageVersions.Add((row.NodeId, row.LanguageId), false);
|
||||
|
||||
//mark as false if the item doesn't exist, else coerce to true
|
||||
editedDocument[row.NodeId] = editedDocument.TryGetValue(row.NodeId, out var edited) ? (edited |= false) : false;
|
||||
}
|
||||
else if (pubRow == null)
|
||||
{
|
||||
//this would mean that that this property is 'edited' since there is no published version
|
||||
editedLanguageVersions[(row.NodeId, row.LanguageId)] = true;
|
||||
editedDocument[row.NodeId] = true;
|
||||
}
|
||||
//compare the property values, if they differ from versions then flag the current version as edited
|
||||
else if (IsPropertyValueChanged(pubRow, row))
|
||||
{
|
||||
//Here we would check if the property is invariant, in which case the edited language should be indicated by the default lang
|
||||
editedLanguageVersions[(row.NodeId, !propVariations.VariesByCulture() ? defaultLang : row.LanguageId)] = true;
|
||||
editedDocument[row.NodeId] = true;
|
||||
}
|
||||
|
||||
//reset
|
||||
pubRow = null;
|
||||
}
|
||||
}
|
||||
|
||||
//lookup all matching rows in umbracoDocumentCultureVariation
|
||||
var docCultureVariationsToUpdate = editedLanguageVersions.InGroupsOf(2000)
|
||||
.SelectMany(_ => Database.Fetch<DocumentCultureVariationDto>(
|
||||
Sql().Select<DocumentCultureVariationDto>().From<DocumentCultureVariationDto>()
|
||||
.WhereIn<DocumentCultureVariationDto>(x => x.LanguageId, editedLanguageVersions.Keys.Select(x => x.langId).ToList())
|
||||
.WhereIn<DocumentCultureVariationDto>(x => x.NodeId, editedLanguageVersions.Keys.Select(x => x.nodeId))))
|
||||
//convert to dictionary with the same key type
|
||||
.ToDictionary(x => (x.NodeId, (int?)x.LanguageId), x => x);
|
||||
|
||||
var toUpdate = new List<DocumentCultureVariationDto>();
|
||||
foreach (var ev in editedLanguageVersions)
|
||||
{
|
||||
if (docCultureVariationsToUpdate.TryGetValue(ev.Key, out var docVariations))
|
||||
{
|
||||
//check if it needs updating
|
||||
if (docVariations.Edited != ev.Value)
|
||||
{
|
||||
docVariations.Edited = ev.Value;
|
||||
toUpdate.Add(docVariations);
|
||||
}
|
||||
}
|
||||
else if (ev.Key.langId.HasValue)
|
||||
{
|
||||
//This should never happen! If a property culture is flagged as edited then the culture must exist at the document level
|
||||
throw new PanicException($"The existing DocumentCultureVariationDto was not found for node {ev.Key.nodeId} and language {ev.Key.langId}");
|
||||
}
|
||||
}
|
||||
|
||||
//Now bulk update the table DocumentCultureVariationDto, once for edited = true, another for edited = false
|
||||
foreach (var editValue in toUpdate.GroupBy(x => x.Edited))
|
||||
{
|
||||
Database.Execute(Sql().Update<DocumentCultureVariationDto>(u => u.Set(x => x.Edited, editValue.Key))
|
||||
.WhereIn<DocumentCultureVariationDto>(x => x.Id, editValue.Select(x => x.Id)));
|
||||
}
|
||||
|
||||
//Now bulk update the umbracoDocument table
|
||||
foreach (var editValue in editedDocument.GroupBy(x => x.Value))
|
||||
{
|
||||
Database.Execute(Sql().Update<DocumentDto>(u => u.Set(x => x.Edited, editValue.Key))
|
||||
.WhereIn<DocumentDto>(x => x.NodeId, editValue.Select(x => x.Key)));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsPropertyValueChanged(PropertyValueVersionDto pubRow, PropertyValueVersionDto row)
|
||||
{
|
||||
return !pubRow.TextValue.IsNullOrWhiteSpace() && pubRow.TextValue != row.TextValue
|
||||
|| !pubRow.VarcharValue.IsNullOrWhiteSpace() && pubRow.VarcharValue != row.VarcharValue
|
||||
|| pubRow.DateValue.HasValue && pubRow.DateValue != row.DateValue
|
||||
|| pubRow.DecimalValue.HasValue && pubRow.DecimalValue != row.DecimalValue
|
||||
|| pubRow.IntValue.HasValue && pubRow.IntValue != row.IntValue;
|
||||
}
|
||||
|
||||
private class NameCompareDto
|
||||
{
|
||||
public int NodeId { get; set; }
|
||||
public int CurrentVersion { get; set; }
|
||||
public int LanguageId { get; set; }
|
||||
public string CurrentName { get; set; }
|
||||
public string PublishedName { get; set; }
|
||||
public int? PublishedVersion { get; set; }
|
||||
public int Id { get; set; } // the Id of the DocumentCultureVariationDto
|
||||
public bool Edited { get; set; }
|
||||
}
|
||||
|
||||
private class PropertyValueVersionDto
|
||||
{
|
||||
public int VersionId { get; set; }
|
||||
public int PropertyTypeId { get; set; }
|
||||
public int? LanguageId { get; set; }
|
||||
public string Segment { get; set; }
|
||||
public int? IntValue { get; set; }
|
||||
|
||||
private decimal? _decimalValue;
|
||||
[Column("decimalValue")]
|
||||
public decimal? DecimalValue
|
||||
{
|
||||
get => _decimalValue;
|
||||
set => _decimalValue = value?.Normalize();
|
||||
}
|
||||
|
||||
public DateTime? DateValue { get; set; }
|
||||
public string VarcharValue { get; set; }
|
||||
public string TextValue { get; set; }
|
||||
|
||||
public int NodeId { get; set; }
|
||||
public bool Current { get; set; }
|
||||
public bool Published { get; set; }
|
||||
|
||||
public byte Variations { get; set; }
|
||||
}
|
||||
|
||||
private void DeletePropertyType(int contentTypeId, int propertyTypeId)
|
||||
|
||||
@@ -386,7 +386,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.BulkInsertRecords(GetContentVariationDtos(entity, publishing));
|
||||
|
||||
// insert document variations
|
||||
Database.BulkInsertRecords(GetDocumentVariationDtos(entity, editedCultures));
|
||||
Database.BulkInsertRecords(GetDocumentVariationDtos(entity, publishing, editedCultures));
|
||||
}
|
||||
|
||||
// refresh content
|
||||
@@ -511,7 +511,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
entity.VersionId = documentVersionDto.Id = contentVersionDto.Id; // get the new id
|
||||
|
||||
documentVersionDto.Published = false; // non-published version
|
||||
Database.Insert(documentVersionDto);
|
||||
Database.Insert(documentVersionDto);
|
||||
}
|
||||
|
||||
// replace the property data (rather than updating)
|
||||
@@ -571,7 +571,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.BulkInsertRecords(GetContentVariationDtos(entity, publishing));
|
||||
|
||||
// insert document variations
|
||||
Database.BulkInsertRecords(GetDocumentVariationDtos(entity, editedCultures));
|
||||
Database.BulkInsertRecords(GetDocumentVariationDtos(entity, publishing, editedCultures));
|
||||
}
|
||||
|
||||
// refresh content
|
||||
@@ -1297,28 +1297,25 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<DocumentCultureVariationDto> GetDocumentVariationDtos(IContent content, HashSet<string> editedCultures)
|
||||
private IEnumerable<DocumentCultureVariationDto> GetDocumentVariationDtos(IContent content, bool publishing, HashSet<string> editedCultures)
|
||||
{
|
||||
var allCultures = content.AvailableCultures.Union(content.PublishedCultures); // union = distinct
|
||||
foreach (var culture in allCultures)
|
||||
{
|
||||
var dto = new DocumentCultureVariationDto
|
||||
yield return new DocumentCultureVariationDto
|
||||
{
|
||||
NodeId = content.Id,
|
||||
LanguageId = LanguageRepository.GetIdByIsoCode(culture) ?? throw new InvalidOperationException("Not a valid culture."),
|
||||
Culture = culture,
|
||||
|
||||
Name = content.GetCultureName(culture) ?? content.GetPublishName(culture),
|
||||
|
||||
// note: can't use IsCultureEdited at that point - hasn't been updated yet - see PersistUpdatedItem
|
||||
|
||||
Available = content.IsCultureAvailable(culture),
|
||||
Published = content.IsCulturePublished(culture),
|
||||
// note: can't use IsCultureEdited at that point - hasn't been updated yet - see PersistUpdatedItem
|
||||
Edited = content.IsCultureAvailable(culture) &&
|
||||
(!content.IsCulturePublished(culture) || (editedCultures != null && editedCultures.Contains(culture)))
|
||||
};
|
||||
|
||||
yield return dto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ContentVariation
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
@@ -18,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class MediaTypeRepository : ContentTypeRepositoryBase<IMediaType>, IMediaTypeRepository
|
||||
{
|
||||
public MediaTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, logger, commonRepository, languageRepository)
|
||||
public MediaTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
|
||||
: base(scopeAccessor, cache, logger, commonRepository)
|
||||
{ }
|
||||
|
||||
protected override bool SupportsPublishing => MediaType.SupportsPublishingConst;
|
||||
@@ -51,7 +50,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
// the cache policy will always want everything
|
||||
// even GetMany(ids) gets everything and filters afterwards
|
||||
if (ids.Any()) throw new PanicException("There can be no ids specified");
|
||||
if (ids.Any()) throw new Exception("panic");
|
||||
return CommonRepository.GetAllTypes().OfType<IMediaType>();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
@@ -19,8 +18,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class MemberTypeRepository : ContentTypeRepositoryBase<IMemberType>, IMemberTypeRepository
|
||||
{
|
||||
public MemberTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, logger, commonRepository, languageRepository)
|
||||
public MemberTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IContentTypeCommonRepository commonRepository)
|
||||
: base(scopeAccessor, cache, logger, commonRepository)
|
||||
{ }
|
||||
|
||||
protected override bool SupportsPublishing => MemberType.SupportsPublishingConst;
|
||||
@@ -58,7 +57,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
// the cache policy will always want everything
|
||||
// even GetMany(ids) gets everything and filters afterwards
|
||||
if (ids.Any()) throw new PanicException("There can be no ids specified");
|
||||
if (ids.Any()) throw new Exception("panic");
|
||||
return CommonRepository.GetAllTypes().OfType<IMemberType>();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
// defaults
|
||||
Type = type;
|
||||
Icon = Constants.Icons.PropertyEditor;
|
||||
Group = "common";
|
||||
Group = Constants.PropertyEditors.Groups.Common;
|
||||
|
||||
// assign properties based on the attribute, if it is found
|
||||
Attribute = GetType().GetCustomAttribute<DataEditorAttribute>(false);
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// Gets or sets an optional group.
|
||||
/// </summary>
|
||||
/// <remarks>The group can be used for example to group the editors by category.</remarks>
|
||||
public string Group { get; set; } = "common";
|
||||
public string Group { get; set; } = Constants.PropertyEditors.Groups.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value editor is deprecated.
|
||||
|
||||
@@ -5,7 +5,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a property editor for label properties.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Label, "Label", "readonlyvalue", Icon = "icon-readonly")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Label,
|
||||
"Label",
|
||||
"readonlyvalue",
|
||||
Icon = "icon-readonly")]
|
||||
public class LabelPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -55,7 +55,6 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <summary>
|
||||
/// Gets the type of the dynamic configuration provider.
|
||||
/// </summary>
|
||||
//TODO: This is not used and should be implemented in a nicer way, see https://github.com/umbraco/Umbraco-CMS/issues/6017#issuecomment-516253562
|
||||
public Type TagsConfigurationProviderType { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,8 +886,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
@@ -896,13 +894,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
// if culture is '*', then publish them all (including variants)
|
||||
|
||||
//this will create the correct culture impact even if culture is * or null
|
||||
var impact = CultureImpact.Create(culture, IsDefaultCulture(allLangs, culture), content);
|
||||
var impact = CultureImpact.Create(culture, _languageRepository.IsDefault(culture), content);
|
||||
|
||||
// publish the culture(s)
|
||||
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
|
||||
content.PublishCulture(impact);
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -923,8 +921,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
@@ -932,23 +928,25 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
var varies = content.ContentType.VariesByCulture();
|
||||
|
||||
if (cultures.Length == 0 && !varies)
|
||||
if (cultures.Length == 0)
|
||||
{
|
||||
//no cultures specified and doesn't vary, so publish it, else nothing to publish
|
||||
return SaveAndPublish(content, userId: userId, raiseEvents: raiseEvents);
|
||||
return !varies
|
||||
? SaveAndPublish(content, userId: userId, raiseEvents: raiseEvents)
|
||||
: new PublishResult(PublishResultType.FailedPublishNothingToPublish, evtMsgs, content);
|
||||
}
|
||||
|
||||
if (cultures.Any(x => x == null || x == "*"))
|
||||
throw new InvalidOperationException("Only valid cultures are allowed to be used in this method, wildcards or nulls are not allowed");
|
||||
|
||||
var impacts = cultures.Select(x => CultureImpact.Explicit(x, IsDefaultCulture(allLangs, x)));
|
||||
var impacts = cultures.Select(x => CultureImpact.Explicit(x, _languageRepository.IsDefault(x)));
|
||||
|
||||
// publish the culture(s)
|
||||
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
|
||||
foreach (var impact in impacts)
|
||||
content.PublishCulture(impact);
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -988,8 +986,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
@@ -997,39 +993,26 @@ namespace Umbraco.Core.Services.Implement
|
||||
// all cultures = unpublish whole
|
||||
if (culture == "*" || (!content.ContentType.VariesByCulture() && culture == null))
|
||||
{
|
||||
// It's important to understand that when the document varies by culture but the "*" is used,
|
||||
// we are just unpublishing the whole document but leaving all of the culture's as-is. This is expected
|
||||
// because we don't want to actually unpublish every culture and then the document, we just want everything
|
||||
// to be non-routable so that when it's re-published all variants were as they were.
|
||||
|
||||
content.PublishedState = PublishedState.Unpublishing;
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unpublish the culture, this will change the document state to Publishing! ... which is expected because this will
|
||||
// essentially be re-publishing the document with the requested culture removed.
|
||||
// The call to CommitDocumentChangesInternal will perform all the checks like if this is a mandatory culture or the last culture being unpublished
|
||||
// and will then unpublish the document accordingly.
|
||||
// If the result of this is false it means there was no culture to unpublish (i.e. it was already unpublished or it did not exist)
|
||||
var removed = content.UnpublishCulture(culture);
|
||||
// If the culture we want to unpublish was already unpublished, nothing to do.
|
||||
// To check for that we need to lookup the persisted content item
|
||||
var persisted = content.HasIdentity ? GetById(content.Id) : null;
|
||||
|
||||
//save and publish any changes
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId);
|
||||
|
||||
scope.Complete();
|
||||
|
||||
// In one case the result will be PublishStatusType.FailedPublishNothingToPublish which means that no cultures
|
||||
// were specified to be published which will be the case when removed is false. In that case
|
||||
// we want to swap the result type to PublishResultType.SuccessUnpublishAlready (that was the expectation before).
|
||||
if (result.Result == PublishResultType.FailedPublishNothingToPublish && !removed)
|
||||
if (persisted != null && !persisted.IsCulturePublished(culture))
|
||||
return new PublishResult(PublishResultType.SuccessUnpublishAlready, evtMsgs, content);
|
||||
|
||||
return result;
|
||||
// unpublish the culture
|
||||
content.UnpublishCulture(culture);
|
||||
}
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1064,35 +1047,15 @@ namespace Umbraco.Core.Services.Implement
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a lot of business logic cases for how the document should be persisted
|
||||
/// </summary>
|
||||
/// <param name="scope"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="saveEventArgs"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <param name="branchOne"></param>
|
||||
/// <param name="branchRoot"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Business logic cases such: as unpublishing a mandatory culture, or unpublishing the last culture, checking for pending scheduled publishing, etc... is dealt with in this method.
|
||||
/// There is quite a lot of cases to take into account along with logic that needs to deal with scheduled saving/publishing, branch saving/publishing, etc...
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content,
|
||||
ContentSavingEventArgs saveEventArgs, IReadOnlyCollection<ILanguage> allLangs,
|
||||
int userId = Constants.Security.SuperUserId,
|
||||
bool raiseEvents = true, bool branchOne = false, bool branchRoot = false)
|
||||
ContentSavingEventArgs saveEventArgs,
|
||||
int userId = Constants.Security.SuperUserId, bool raiseEvents = true, bool branchOne = false, bool branchRoot = false)
|
||||
{
|
||||
if (scope == null) throw new ArgumentNullException(nameof(scope));
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
@@ -1107,8 +1070,8 @@ namespace Umbraco.Core.Services.Implement
|
||||
if (content.PublishedState != PublishedState.Publishing && content.PublishedState != PublishedState.Unpublishing)
|
||||
content.PublishedState = PublishedState.Publishing;
|
||||
|
||||
// State here is either Publishing or Unpublishing
|
||||
// Publishing to unpublish a culture may end up unpublishing everything so these flags can be flipped later
|
||||
// state here is either Publishing or Unpublishing
|
||||
// (even though, Publishing to unpublish a culture may end up unpublishing everything)
|
||||
var publishing = content.PublishedState == PublishedState.Publishing;
|
||||
var unpublishing = content.PublishedState == PublishedState.Unpublishing;
|
||||
|
||||
@@ -1125,18 +1088,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
var changeType = isNew ? TreeChangeTypes.RefreshNode : TreeChangeTypes.RefreshBranch;
|
||||
var previouslyPublished = content.HasIdentity && content.Published;
|
||||
|
||||
//inline method to persist the document with the documentRepository since this logic could be called a couple times below
|
||||
void SaveDocument(IContent c)
|
||||
{
|
||||
// save, always
|
||||
if (c.HasIdentity == false)
|
||||
c.CreatorId = userId;
|
||||
c.WriterId = userId;
|
||||
|
||||
// saving does NOT change the published version, unless PublishedState is Publishing or Unpublishing
|
||||
_documentRepository.Save(c);
|
||||
}
|
||||
|
||||
if (publishing)
|
||||
{
|
||||
//determine cultures publishing/unpublishing which will be based on previous calls to content.PublishCulture and ClearPublishInfo
|
||||
@@ -1146,25 +1097,11 @@ namespace Umbraco.Core.Services.Implement
|
||||
: null;
|
||||
|
||||
// ensure that the document can be published, and publish handling events, business rules, etc
|
||||
publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, saveEventArgs, allLangs);
|
||||
publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, saveEventArgs);
|
||||
if (publishResult.Success)
|
||||
{
|
||||
// note: StrategyPublish flips the PublishedState to Publishing!
|
||||
publishResult = StrategyPublish(content, culturesPublishing, culturesUnpublishing, evtMsgs);
|
||||
|
||||
//check if a culture has been unpublished and if there are no cultures left, and then unpublish document as a whole
|
||||
if (publishResult.Result == PublishResultType.SuccessUnpublishCulture && content.PublishCultureInfos.Count == 0)
|
||||
{
|
||||
// This is a special case! We are unpublishing the last culture and to persist that we need to re-publish without any cultures
|
||||
// so the state needs to remain Publishing to do that. However, we then also need to unpublish the document and to do that
|
||||
// the state needs to be Unpublishing and it cannot be both. This state is used within the documentRepository to know how to
|
||||
// persist certain things. So before proceeding below, we need to save the Publishing state to publish no cultures, then we can
|
||||
// mark the document for Unpublishing.
|
||||
SaveDocument(content);
|
||||
|
||||
//set the flag to unpublish and continue
|
||||
unpublishing = content.Published; // if not published yet, nothing to do
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1225,8 +1162,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
//Persist the document
|
||||
SaveDocument(content);
|
||||
// save, always
|
||||
if (content.HasIdentity == false)
|
||||
content.CreatorId = userId;
|
||||
content.WriterId = userId;
|
||||
|
||||
// saving does NOT change the published version, unless PublishedState is Publishing or Unpublishing
|
||||
_documentRepository.Save(content);
|
||||
|
||||
// raise the Saved event, always
|
||||
if (raiseEvents)
|
||||
@@ -1244,34 +1186,17 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
if (culturesUnpublishing != null)
|
||||
{
|
||||
// This will mean that that we unpublished a mandatory culture or we unpublished the last culture.
|
||||
|
||||
var langs = string.Join(", ", allLangs
|
||||
//If we are here, it means we tried unpublishing a culture but it was mandatory so now everything is unpublished
|
||||
var langs = string.Join(", ", _languageRepository.GetMany()
|
||||
.Where(x => culturesUnpublishing.InvariantContains(x.IsoCode))
|
||||
.Select(x => x.CultureName));
|
||||
Audit(AuditType.UnpublishVariant, userId, content.Id, $"Unpublished languages: {langs}", langs);
|
||||
|
||||
if (publishResult == null)
|
||||
throw new PanicException("publishResult == null - should not happen");
|
||||
|
||||
switch(publishResult.Result)
|
||||
{
|
||||
case PublishResultType.FailedPublishMandatoryCultureMissing:
|
||||
//occurs when a mandatory culture was unpublished (which means we tried publishing the document without a mandatory culture)
|
||||
|
||||
//log that the whole content item has been unpublished due to mandatory culture unpublished
|
||||
Audit(AuditType.Unpublish, userId, content.Id, "Unpublished (mandatory language unpublished)");
|
||||
return new PublishResult(PublishResultType.SuccessUnpublishMandatoryCulture, evtMsgs, content);
|
||||
case PublishResultType.SuccessUnpublishCulture:
|
||||
//occurs when the last culture is unpublished
|
||||
|
||||
Audit(AuditType.Unpublish, userId, content.Id, "Unpublished (last language unpublished)");
|
||||
return new PublishResult(PublishResultType.SuccessUnpublishLastCulture, evtMsgs, content);
|
||||
}
|
||||
|
||||
//log that the whole content item has been unpublished due to mandatory culture unpublished
|
||||
Audit(AuditType.Unpublish, userId, content.Id, "Unpublished (mandatory language unpublished)");
|
||||
}
|
||||
else
|
||||
Audit(AuditType.Unpublish, userId, content.Id);
|
||||
|
||||
Audit(AuditType.Unpublish, userId, content.Id);
|
||||
return new PublishResult(PublishResultType.SuccessUnpublish, evtMsgs, content);
|
||||
}
|
||||
|
||||
@@ -1311,7 +1236,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
case PublishResultType.SuccessPublishCulture:
|
||||
if (culturesPublishing != null)
|
||||
{
|
||||
var langs = string.Join(", ", allLangs
|
||||
var langs = string.Join(", ", _languageRepository.GetMany()
|
||||
.Where(x => culturesPublishing.InvariantContains(x.IsoCode))
|
||||
.Select(x => x.CultureName));
|
||||
Audit(AuditType.PublishVariant, userId, content.Id, $"Published languages: {langs}", langs);
|
||||
@@ -1320,7 +1245,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
case PublishResultType.SuccessUnpublishCulture:
|
||||
if (culturesUnpublishing != null)
|
||||
{
|
||||
var langs = string.Join(", ", allLangs
|
||||
var langs = string.Join(", ", _languageRepository.GetMany()
|
||||
.Where(x => culturesUnpublishing.InvariantContains(x.IsoCode))
|
||||
.Select(x => x.CultureName));
|
||||
Audit(AuditType.UnpublishVariant, userId, content.Id, $"Unpublished languages: {langs}", langs);
|
||||
@@ -1334,14 +1259,14 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
// should not happen
|
||||
if (branchOne && !branchRoot)
|
||||
throw new PanicException("branchOne && !branchRoot - should not happen");
|
||||
throw new Exception("panic");
|
||||
|
||||
//if publishing didn't happen or if it has failed, we still need to log which cultures were saved
|
||||
if (!branchOne && (publishResult == null || !publishResult.Success))
|
||||
{
|
||||
if (culturesChanging != null)
|
||||
{
|
||||
var langs = string.Join(", ", allLangs
|
||||
var langs = string.Join(", ", _languageRepository.GetMany()
|
||||
.Where(x => culturesChanging.InvariantContains(x.IsoCode))
|
||||
.Select(x => x.CultureName));
|
||||
Audit(AuditType.SaveVariant, userId, content.Id, $"Saved languages: {langs}", langs);
|
||||
@@ -1372,8 +1297,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
foreach (var d in _documentRepository.GetContentForRelease(date))
|
||||
{
|
||||
PublishResult result;
|
||||
@@ -1402,7 +1325,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
//publish the culture values and validate the property values, if validation fails, log the invalid properties so the develeper has an idea of what has failed
|
||||
Property[] invalidProperties = null;
|
||||
var impact = CultureImpact.Explicit(culture, IsDefaultCulture(allLangs, culture));
|
||||
var impact = CultureImpact.Explicit(culture, _languageRepository.IsDefault(culture));
|
||||
var tryPublish = d.PublishCulture(impact) && _propertyValidationService.Value.IsPropertyDataValid(d, out invalidProperties, impact);
|
||||
if (invalidProperties != null && invalidProperties.Length > 0)
|
||||
Logger.Warn<ContentService>("Scheduled publishing will fail for document {DocumentId} and culture {Culture} because of invalid properties {InvalidProperties}",
|
||||
@@ -1417,7 +1340,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
else if (!publishing)
|
||||
result = new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, d);
|
||||
else
|
||||
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs, d.WriterId);
|
||||
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, d.WriterId);
|
||||
|
||||
|
||||
if (result.Success == false)
|
||||
@@ -1467,7 +1390,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
d.UnpublishCulture(c);
|
||||
}
|
||||
|
||||
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs, d.WriterId);
|
||||
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, d.WriterId);
|
||||
if (result.Success == false)
|
||||
Logger.Error<ContentService>(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result);
|
||||
yield return result;
|
||||
@@ -1493,7 +1416,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
// utility 'PublishCultures' func used by SaveAndPublishBranch
|
||||
private bool SaveAndPublishBranch_PublishCultures(IContent content, HashSet<string> culturesToPublish, IReadOnlyCollection<ILanguage> allLangs)
|
||||
private bool SaveAndPublishBranch_PublishCultures(IContent content, HashSet<string> culturesToPublish)
|
||||
{
|
||||
//TODO: This does not support being able to return invalid property details to bubble up to the UI
|
||||
|
||||
@@ -1503,7 +1426,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
return culturesToPublish.All(culture =>
|
||||
{
|
||||
var impact = CultureImpact.Create(culture, IsDefaultCulture(allLangs, culture), content);
|
||||
var impact = CultureImpact.Create(culture, _languageRepository.IsDefault(culture), content);
|
||||
return content.PublishCulture(impact) && _propertyValidationService.Value.IsPropertyDataValid(content, out _, impact);
|
||||
});
|
||||
}
|
||||
@@ -1612,7 +1535,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
internal IEnumerable<PublishResult> SaveAndPublishBranch(IContent document, bool force,
|
||||
Func<IContent, HashSet<string>> shouldPublish,
|
||||
Func<IContent, HashSet<string>, IReadOnlyCollection<ILanguage>, bool> publishCultures,
|
||||
Func<IContent, HashSet<string>, bool> publishCultures,
|
||||
int userId = Constants.Security.SuperUserId)
|
||||
{
|
||||
if (shouldPublish == null) throw new ArgumentNullException(nameof(shouldPublish));
|
||||
@@ -1626,8 +1549,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
scope.WriteLock(Constants.Locks.ContentTree);
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
if (!document.HasIdentity)
|
||||
throw new InvalidOperationException("Cannot not branch-publish a new document.");
|
||||
|
||||
@@ -1636,7 +1557,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
throw new InvalidOperationException("Cannot mix PublishCulture and SaveAndPublishBranch.");
|
||||
|
||||
// deal with the branch root - if it fails, abort
|
||||
var result = SaveAndPublishBranchItem(scope, document, shouldPublish, publishCultures, true, publishedDocuments, evtMsgs, userId, allLangs);
|
||||
var result = SaveAndPublishBranchItem(scope, document, shouldPublish, publishCultures, true, publishedDocuments, evtMsgs, userId);
|
||||
if (result != null)
|
||||
{
|
||||
results.Add(result);
|
||||
@@ -1667,7 +1588,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
// no need to check path here, parent has to be published here
|
||||
result = SaveAndPublishBranchItem(scope, d, shouldPublish, publishCultures, false, publishedDocuments, evtMsgs, userId, allLangs);
|
||||
result = SaveAndPublishBranchItem(scope, d, shouldPublish, publishCultures, false, publishedDocuments, evtMsgs, userId);
|
||||
if (result != null)
|
||||
{
|
||||
results.Add(result);
|
||||
@@ -1699,10 +1620,10 @@ namespace Umbraco.Core.Services.Implement
|
||||
// publishValues: a function publishing values (using the appropriate PublishCulture calls)
|
||||
private PublishResult SaveAndPublishBranchItem(IScope scope, IContent document,
|
||||
Func<IContent, HashSet<string>> shouldPublish,
|
||||
Func<IContent, HashSet<string>, IReadOnlyCollection<ILanguage>, bool> publishCultures,
|
||||
Func<IContent, HashSet<string>, bool> publishCultures,
|
||||
bool isRoot,
|
||||
ICollection<IContent> publishedDocuments,
|
||||
EventMessages evtMsgs, int userId, IReadOnlyCollection<ILanguage> allLangs)
|
||||
EventMessages evtMsgs, int userId)
|
||||
{
|
||||
var culturesToPublish = shouldPublish(document);
|
||||
if (culturesToPublish == null) // null = do not include
|
||||
@@ -1715,13 +1636,13 @@ namespace Umbraco.Core.Services.Implement
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document);
|
||||
|
||||
// publish & check if values are valid
|
||||
if (!publishCultures(document, culturesToPublish, allLangs))
|
||||
if (!publishCultures(document, culturesToPublish))
|
||||
{
|
||||
//TODO: Based on this callback behavior there is no way to know which properties may have been invalid if this failed, see other results of FailedPublishContentInvalid
|
||||
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, document);
|
||||
}
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, document, saveEventArgs, allLangs, userId, branchOne: true, branchRoot: isRoot);
|
||||
var result = CommitDocumentChangesInternal(scope, document, saveEventArgs, userId, branchOne: true, branchRoot: isRoot);
|
||||
if (result.Success)
|
||||
publishedDocuments.Add(document);
|
||||
return result;
|
||||
@@ -2422,9 +2343,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.Document), message, parameters));
|
||||
}
|
||||
|
||||
private bool IsDefaultCulture(IReadOnlyCollection<ILanguage> langs, string culture) => langs.Any(x => x.IsDefault && x.IsoCode.InvariantEquals(culture));
|
||||
private bool IsMandatoryCulture(IReadOnlyCollection<ILanguage> langs, string culture) => langs.Any(x => x.IsMandatory && x.IsoCode.InvariantEquals(culture));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
@@ -2579,9 +2497,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <param name="culturesPublishing"></param>
|
||||
/// <param name="savingEventArgs"></param>
|
||||
/// <returns></returns>
|
||||
private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList<string> culturesPublishing,
|
||||
IReadOnlyCollection<string> culturesUnpublishing, EventMessages evtMsgs, ContentSavingEventArgs savingEventArgs,
|
||||
IReadOnlyCollection<ILanguage> allLangs)
|
||||
private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList<string> culturesPublishing, IReadOnlyCollection<string> culturesUnpublishing, EventMessages evtMsgs, ContentSavingEventArgs savingEventArgs)
|
||||
{
|
||||
// raise Publishing event
|
||||
if (scope.Events.DispatchCancelable(Publishing, this, savingEventArgs.ToContentPublishingEventArgs()))
|
||||
@@ -2594,7 +2510,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
|
||||
var impactsToPublish = culturesPublishing == null
|
||||
? new[] {CultureImpact.Invariant} //if it's null it's invariant
|
||||
: culturesPublishing.Select(x => CultureImpact.Explicit(x, allLangs.Any(lang => lang.IsoCode.InvariantEquals(x) && lang.IsMandatory))).ToArray();
|
||||
: culturesPublishing.Select(x => CultureImpact.Explicit(x, _languageRepository.IsDefault(x))).ToArray();
|
||||
|
||||
// publish the culture(s)
|
||||
if (!impactsToPublish.All(content.PublishCulture))
|
||||
@@ -2615,17 +2531,11 @@ namespace Umbraco.Core.Services.Implement
|
||||
if (culturesPublishing == null)
|
||||
throw new InvalidOperationException("Internal error, variesByCulture but culturesPublishing is null.");
|
||||
|
||||
if (content.Published && culturesPublishing.Count == 0 && culturesUnpublishing.Count == 0)
|
||||
{
|
||||
// no published cultures = cannot be published
|
||||
// This will occur if for example, a culture that is already unpublished is sent to be unpublished again, or vice versa, in that case
|
||||
// there will be nothing to publish/unpublish.
|
||||
if (content.Published && culturesPublishing.Count == 0 && culturesUnpublishing.Count == 0) // no published cultures = cannot be published
|
||||
return new PublishResult(PublishResultType.FailedPublishNothingToPublish, evtMsgs, content);
|
||||
}
|
||||
|
||||
|
||||
// missing mandatory culture = cannot be published
|
||||
var mandatoryCultures = allLangs.Where(x => x.IsMandatory).Select(x => x.IsoCode);
|
||||
var mandatoryCultures = _languageRepository.GetMany().Where(x => x.IsMandatory).Select(x => x.IsoCode);
|
||||
var mandatoryMissing = mandatoryCultures.Any(x => !content.PublishedCultures.Contains(x, StringComparer.OrdinalIgnoreCase));
|
||||
if (mandatoryMissing)
|
||||
return new PublishResult(PublishResultType.FailedPublishMandatoryCultureMissing, evtMsgs, content);
|
||||
@@ -2766,7 +2676,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
var attempt = new PublishResult(PublishResultType.SuccessUnpublish, evtMsgs, content);
|
||||
|
||||
//TODO: What is this check?? we just created this attempt and of course it is Success?!
|
||||
if (attempt.Success == false)
|
||||
return attempt;
|
||||
|
||||
|
||||
@@ -49,11 +49,6 @@
|
||||
/// </summary>
|
||||
SuccessUnpublishMandatoryCulture = 6,
|
||||
|
||||
/// <summary>
|
||||
/// The specified document culture was unpublished, and was the last published culture in the document, therefore the document itself was unpublished.
|
||||
/// </summary>
|
||||
SuccessUnpublishLastCulture = 8,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Success - Mixed
|
||||
@@ -118,9 +113,9 @@
|
||||
FailedPublishContentInvalid = FailedPublish | 8,
|
||||
|
||||
/// <summary>
|
||||
/// The document could not be published because it has no publishing flags or values or if its a variant document, no cultures were specified to be published.
|
||||
/// The document could not be published because it has no publishing flags or values.
|
||||
/// </summary>
|
||||
FailedPublishNothingToPublish = FailedPublish | 9,
|
||||
FailedPublishNothingToPublish = FailedPublish | 9, // TODO: in ContentService.StrategyCanPublish - weird
|
||||
|
||||
/// <summary>
|
||||
/// The document could not be published because some mandatory cultures are missing.
|
||||
|
||||
@@ -204,7 +204,6 @@
|
||||
<Compile Include="CompositionExtensions_Essentials.cs" />
|
||||
<Compile Include="CompositionExtensions_FileSystems.cs" />
|
||||
<Compile Include="CompositionExtensions_Uniques.cs" />
|
||||
<Compile Include="Exceptions\PanicException.cs" />
|
||||
<Compile Include="FactoryExtensions.cs" />
|
||||
<Compile Include="Composing\RegisterFactory.cs" />
|
||||
<Compile Include="Composing\Current.cs" />
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Umbraco.Examine
|
||||
{"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate
|
||||
{"nodeName", (PublishedValuesOnly //Always add invariant nodeName
|
||||
? c.PublishName?.Yield()
|
||||
: c.Name?.Yield()) ?? Enumerable.Empty<string>()},
|
||||
: c?.Name.Yield()) ?? Enumerable.Empty<string>()},
|
||||
{"urlName", urlValue?.Yield() ?? Enumerable.Empty<string>()}, //Always add invariant urlName
|
||||
{"path", c.Path?.Yield() ?? Enumerable.Empty<string>()},
|
||||
{"nodeType", c.ContentType.Id.ToString().Yield() ?? Enumerable.Empty<string>()},
|
||||
|
||||
@@ -28,31 +28,6 @@ namespace Umbraco.Examine
|
||||
/// </remarks>
|
||||
internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled);
|
||||
|
||||
private static volatile bool _isUnlocked = false;
|
||||
private static readonly object IsUnlockedLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks all Lucene based indexes registered with the <see cref="IExamineManager"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Indexing rebuilding can occur on a normal boot if the indexes are empty or on a cold boot by the database server messenger. Before
|
||||
/// either of these happens, we need to configure the indexes.
|
||||
/// </remarks>
|
||||
internal static void EnsureUnlocked(this IExamineManager examineManager, IMainDom mainDom, ILogger logger)
|
||||
{
|
||||
if (!mainDom.IsMainDom) return;
|
||||
if (_isUnlocked) return;
|
||||
|
||||
lock (IsUnlockedLocker)
|
||||
{
|
||||
//double check
|
||||
if (_isUnlocked) return;
|
||||
|
||||
_isUnlocked = true;
|
||||
examineManager.UnlockLuceneIndexes(logger);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression
|
||||
|
||||
/// <summary>
|
||||
@@ -127,7 +102,7 @@ namespace Umbraco.Examine
|
||||
/// <remarks>
|
||||
/// This is not thread safe, use with care
|
||||
/// </remarks>
|
||||
internal static void UnlockLuceneIndexes(this IExamineManager examineManager, ILogger logger)
|
||||
internal static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing)
|
||||
{
|
||||
foreach (var luceneIndexer in examineManager.Indexes.OfType<LuceneIndex>())
|
||||
{
|
||||
@@ -136,6 +111,8 @@ namespace Umbraco.Examine
|
||||
//that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems.
|
||||
luceneIndexer.WaitForIndexQueueOnShutdown = false;
|
||||
|
||||
if (disableExamineIndexing) continue; //exit if not enabled, we don't need to unlock them if we're not maindom
|
||||
|
||||
//we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that
|
||||
//the indexes are not operational unless MainDom is true
|
||||
var dir = luceneIndexer.GetLuceneDirectory();
|
||||
|
||||
@@ -5,8 +5,7 @@ using System.Threading.Tasks;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Examine
|
||||
{
|
||||
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility to rebuild all indexes ensuring minimal data queries
|
||||
/// </summary>
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Examine.LuceneEngine.Providers;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Lucene.Net.Store;
|
||||
using Umbraco.Core.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Examine
|
||||
{
|
||||
public class LuceneIndexDiagnostics : IIndexDiagnostics
|
||||
{
|
||||
public LuceneIndexDiagnostics(LuceneIndex index, ILogger logger)
|
||||
{
|
||||
Index = index;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public LuceneIndex Index { get; }
|
||||
public ILogger Logger { get; }
|
||||
|
||||
public int DocumentCount
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return Index.GetIndexDocumentCount();
|
||||
}
|
||||
catch (AlreadyClosedException)
|
||||
{
|
||||
Logger.Warn(typeof(UmbracoContentIndex), "Cannot get GetIndexDocumentCount, the writer is already closed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int FieldCount
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return Index.GetIndexFieldCount();
|
||||
}
|
||||
catch (AlreadyClosedException)
|
||||
{
|
||||
Logger.Warn(typeof(UmbracoContentIndex), "Cannot get GetIndexFieldCount, the writer is already closed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Attempt<string> IsHealthy()
|
||||
{
|
||||
var isHealthy = Index.IsHealthy(out var indexError);
|
||||
return isHealthy ? Attempt<string>.Succeed() : Attempt.Fail(indexError.Message);
|
||||
}
|
||||
|
||||
public virtual IReadOnlyDictionary<string, object> Metadata
|
||||
{
|
||||
get
|
||||
{
|
||||
var luceneDir = Index.GetLuceneDirectory();
|
||||
var d = new Dictionary<string, object>
|
||||
{
|
||||
[nameof(UmbracoExamineIndex.CommitCount)] = Index.CommitCount,
|
||||
[nameof(UmbracoExamineIndex.DefaultAnalyzer)] = Index.DefaultAnalyzer.GetType().Name,
|
||||
["LuceneDirectory"] = luceneDir.GetType().Name
|
||||
};
|
||||
|
||||
if (luceneDir is FSDirectory fsDir)
|
||||
{
|
||||
d[nameof(UmbracoExamineIndex.LuceneIndexFolder)] = fsDir.Directory.ToString().ToLowerInvariant().TrimStart(IOHelper.MapPath(SystemDirectories.Root).ToLowerInvariant()).Replace("\\", "/").EnsureStartsWith('/');
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,6 @@
|
||||
<Compile Include="IPublishedContentValueSetBuilder.cs" />
|
||||
<Compile Include="IUmbracoIndex.cs" />
|
||||
<Compile Include="IValueSetBuilder.cs" />
|
||||
<Compile Include="LuceneIndexDiagnostics.cs" />
|
||||
<Compile Include="MediaIndexPopulator.cs" />
|
||||
<Compile Include="MediaValueSetBuilder.cs" />
|
||||
<Compile Include="MemberIndexPopulator.cs" />
|
||||
|
||||
@@ -7,24 +7,73 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Examine
|
||||
{
|
||||
public class UmbracoExamineIndexDiagnostics : LuceneIndexDiagnostics
|
||||
public class UmbracoExamineIndexDiagnostics : IIndexDiagnostics
|
||||
{
|
||||
private readonly UmbracoExamineIndex _index;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public UmbracoExamineIndexDiagnostics(UmbracoExamineIndex index, ILogger logger)
|
||||
: base(index, logger)
|
||||
{
|
||||
_index = index;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override IReadOnlyDictionary<string, object> Metadata
|
||||
public int DocumentCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var d = base.Metadata.ToDictionary(x => x.Key, x => x.Value);
|
||||
try
|
||||
{
|
||||
return _index.GetIndexDocumentCount();
|
||||
}
|
||||
catch (AlreadyClosedException)
|
||||
{
|
||||
_logger.Warn(typeof(UmbracoContentIndex), "Cannot get GetIndexDocumentCount, the writer is already closed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d[nameof(UmbracoExamineIndex.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler;
|
||||
d[nameof(UmbracoExamineIndex.PublishedValuesOnly)] = _index.PublishedValuesOnly;
|
||||
public int FieldCount
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return _index.GetIndexFieldCount();
|
||||
}
|
||||
catch (AlreadyClosedException)
|
||||
{
|
||||
_logger.Warn(typeof(UmbracoContentIndex), "Cannot get GetIndexFieldCount, the writer is already closed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Attempt<string> IsHealthy()
|
||||
{
|
||||
var isHealthy = _index.IsHealthy(out var indexError);
|
||||
return isHealthy ? Attempt<string>.Succeed() : Attempt.Fail(indexError.Message);
|
||||
}
|
||||
|
||||
public virtual IReadOnlyDictionary<string, object> Metadata
|
||||
{
|
||||
get
|
||||
{
|
||||
var d = new Dictionary<string, object>
|
||||
{
|
||||
[nameof(UmbracoExamineIndex.CommitCount)] = _index.CommitCount,
|
||||
[nameof(UmbracoExamineIndex.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType().Name,
|
||||
["LuceneDirectory"] = _index.GetLuceneDirectory().GetType().Name,
|
||||
[nameof(UmbracoExamineIndex.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler,
|
||||
[nameof(UmbracoExamineIndex.LuceneIndexFolder)] =
|
||||
_index.LuceneIndexFolder == null
|
||||
? string.Empty
|
||||
: _index.LuceneIndexFolder.ToString().ToLowerInvariant().TrimStart(IOHelper.MapPath(SystemDirectories.Root).ToLowerInvariant()).Replace("\\", "/").EnsureStartsWith('/'),
|
||||
[nameof(UmbracoExamineIndex.PublishedValuesOnly)] = _index.PublishedValuesOnly,
|
||||
//There's too much info here
|
||||
//[nameof(UmbracoExamineIndexer.FieldDefinitionCollection)] = _index.FieldDefinitionCollection,
|
||||
};
|
||||
|
||||
if (_index.ValueSetValidator is ValueSetValidator vsv)
|
||||
{
|
||||
|
||||
@@ -29,11 +29,10 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
private DocumentRepository CreateRepository(IScopeAccessor scopeAccessor, out ContentTypeRepository contentTypeRepository)
|
||||
{
|
||||
var langRepository = new LanguageRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var templateRepository = new TemplateRepository(scopeAccessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, AppCaches.Disabled);
|
||||
contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository, langRepository);
|
||||
contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
var languageRepository = new LanguageRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var repository = new DocumentRepository(scopeAccessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository);
|
||||
return repository;
|
||||
@@ -41,10 +40,9 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
private ContentTypeRepository CreateRepository(IScopeAccessor scopeAccessor)
|
||||
{
|
||||
var langRepository = new LanguageRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var templateRepository = new TemplateRepository(scopeAccessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, AppCaches.Disabled);
|
||||
var contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository, langRepository);
|
||||
var contentTypeRepository = new ContentTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
return contentTypeRepository;
|
||||
}
|
||||
|
||||
@@ -52,8 +50,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
var templateRepository = new TemplateRepository(scopeAccessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, AppCaches.Disabled);
|
||||
var langRepository = new LanguageRepository(scopeAccessor, AppCaches.Disabled, Logger);
|
||||
var contentTypeRepository = new MediaTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository, langRepository);
|
||||
var contentTypeRepository = new MediaTypeRepository(scopeAccessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
return contentTypeRepository;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
templateRepository = new TemplateRepository(scopeAccessor, appCaches, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(scopeAccessor, appCaches, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches);
|
||||
contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, Logger, commonRepository);
|
||||
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);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var templateRepository = new TemplateRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(accessor, Core.Cache.AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, commonRepository);
|
||||
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 domainRepository = new DomainRepository(accessor, Core.Cache.AppCaches.Disabled, Logger);
|
||||
return domainRepository;
|
||||
|
||||
@@ -38,8 +38,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
var templateRepository = new TemplateRepository(scopeAccessor, appCaches, Logger, TestObjects.GetFileSystemsMock());
|
||||
var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches);
|
||||
var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger);
|
||||
mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository);
|
||||
mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, Logger, commonRepository);
|
||||
var tagRepository = new TagRepository(scopeAccessor, appCaches, Logger);
|
||||
var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>());
|
||||
return repository;
|
||||
|
||||
@@ -23,8 +23,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var cacheHelper = AppCaches.Disabled;
|
||||
var templateRepository = new TemplateRepository((IScopeAccessor)provider, cacheHelper, Logger, TestObjects.GetFileSystemsMock());
|
||||
var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches, Logger);
|
||||
return new MediaTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
return new MediaTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository);
|
||||
}
|
||||
|
||||
private EntityContainerRepository CreateContainerRepository(IScopeProvider provider)
|
||||
|
||||
@@ -31,8 +31,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var accessor = (IScopeAccessor) provider;
|
||||
var templateRepository = Mock.Of<ITemplateRepository>();
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger);
|
||||
memberTypeRepository = new MemberTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository);
|
||||
memberTypeRepository = new MemberTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
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>());
|
||||
|
||||
@@ -24,8 +24,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
var templateRepository = Mock.Of<ITemplateRepository>();
|
||||
var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Mock.Of<ILogger>());
|
||||
return new MemberTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of<ILogger>(), commonRepository, languageRepository);
|
||||
return new MemberTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of<ILogger>(), commonRepository);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -308,8 +308,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var templateRepository = new TemplateRepository(accessor, AppCaches, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(accessor, AppCaches, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository);
|
||||
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);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -956,8 +956,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var templateRepository = new TemplateRepository(accessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
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);
|
||||
return repository;
|
||||
}
|
||||
@@ -968,8 +968,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var templateRepository = new TemplateRepository(accessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(accessor, AppCaches.Disabled, Logger);
|
||||
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);
|
||||
mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>());
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -239,8 +239,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
var tagRepository = new TagRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(ScopeProvider, templateRepository, AppCaches);
|
||||
var contentTypeRepository = new ContentTypeRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, commonRepository);
|
||||
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 contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage");
|
||||
|
||||
@@ -26,8 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var accessor = (IScopeAccessor) provider;
|
||||
var templateRepository = new TemplateRepository(accessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
var languageRepository = new LanguageRepository(accessor, AppCaches, Logger);
|
||||
mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches, Mock.Of<ILogger>(), commonRepository, languageRepository);
|
||||
mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches, Mock.Of<ILogger>(), commonRepository);
|
||||
var tagRepository = new TagRepository(accessor, AppCaches, Mock.Of<ILogger>());
|
||||
var repository = new MediaRepository(accessor, AppCaches, Mock.Of<ILogger>(), mediaTypeRepository, tagRepository, Mock.Of<ILanguageRepository>());
|
||||
return repository;
|
||||
@@ -45,8 +44,8 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
templateRepository = new TemplateRepository(accessor, AppCaches, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(accessor, AppCaches, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository);
|
||||
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);
|
||||
return repository;
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ namespace Umbraco.Tests.Services
|
||||
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 ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository);
|
||||
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);
|
||||
|
||||
// Act
|
||||
@@ -200,8 +200,8 @@ namespace Umbraco.Tests.Services
|
||||
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 ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository);
|
||||
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);
|
||||
|
||||
// Act
|
||||
@@ -232,8 +232,8 @@ namespace Umbraco.Tests.Services
|
||||
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 ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository);
|
||||
|
||||
// Act
|
||||
@@ -267,8 +267,8 @@ namespace Umbraco.Tests.Services
|
||||
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 ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository);
|
||||
var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger);
|
||||
var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository);
|
||||
|
||||
// Act
|
||||
|
||||
@@ -717,8 +717,21 @@ namespace Umbraco.Tests.Services
|
||||
[Test]
|
||||
public void Can_Unpublish_Content_Variation()
|
||||
{
|
||||
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr, out var contentType);
|
||||
// Arrange
|
||||
|
||||
var langUk = new Language("en-GB") { IsDefault = true };
|
||||
var langFr = new Language("fr-FR");
|
||||
|
||||
ServiceContext.LocalizationService.Save(langFr);
|
||||
ServiceContext.LocalizationService.Save(langUk);
|
||||
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
IContent content = new Content("content", Constants.System.Root, contentType);
|
||||
content.SetCultureName("content-fr", langFr.IsoCode);
|
||||
content.SetCultureName("content-en", langUk.IsoCode);
|
||||
content.PublishCulture(CultureImpact.Explicit(langFr.IsoCode, langFr.IsDefault));
|
||||
content.PublishCulture(CultureImpact.Explicit(langUk.IsoCode, langUk.IsDefault));
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
@@ -748,185 +761,6 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Publish_Culture_After_Last_Culture_Unpublished()
|
||||
{
|
||||
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr, out var contentType);
|
||||
|
||||
var published = ServiceContext.ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
var unpublished = ServiceContext.ContentService.Unpublish(content, langUk.IsoCode); //first culture
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
unpublished = ServiceContext.ContentService.Unpublish(content, langFr.IsoCode); //last culture
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishLastCulture, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
published = ServiceContext.ContentService.SaveAndPublish(content, langUk.IsoCode);
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
|
||||
content = ServiceContext.ContentService.GetById(content.Id); //reget
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public void Unpublish_All_Cultures_Has_Unpublished_State()
|
||||
{
|
||||
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr, out var contentType);
|
||||
|
||||
var published = ServiceContext.ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsTrue(published.Success);
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
|
||||
var unpublished = ServiceContext.ContentService.Unpublish(content, langFr.IsoCode); //first culture
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState); //still published
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
|
||||
unpublished = ServiceContext.ContentService.Unpublish(content, langUk.IsoCode); //last culture
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishLastCulture, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.AreEqual(PublishedState.Unpublished, content.PublishedState); //the last culture was unpublished so the document should also reflect this
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
Assert.AreEqual(PublishedState.Unpublished, content.PublishedState); //just double checking
|
||||
Assert.IsFalse(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unpublishing_Mandatory_Language_Unpublishes_Document()
|
||||
{
|
||||
var langUk = new Language("en-GB") { IsDefault = true, IsMandatory = true };
|
||||
var langFr = new Language("fr-FR");
|
||||
|
||||
ServiceContext.LocalizationService.Save(langFr);
|
||||
ServiceContext.LocalizationService.Save(langUk);
|
||||
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
IContent content = new Content("content", Constants.System.Root, contentType);
|
||||
content.SetCultureName("content-fr", langFr.IsoCode);
|
||||
content.SetCultureName("content-en", langUk.IsoCode);
|
||||
|
||||
var published = ServiceContext.ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsTrue(published.Success);
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
var unpublished = ServiceContext.ContentService.Unpublish(content, langUk.IsoCode); //unpublish mandatory lang
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishMandatoryCulture, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode)); //remains published
|
||||
Assert.AreEqual(PublishedState.Unpublished, content.PublishedState);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Unpublishing_Already_Unpublished_Culture()
|
||||
{
|
||||
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr, out var contentType);
|
||||
|
||||
var published = ServiceContext.ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsTrue(published.Success);
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
var unpublished = ServiceContext.ContentService.Unpublish(content, langUk.IsoCode);
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishCulture, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
//Change some data since Unpublish should always Save
|
||||
content.SetCultureName("content-en-updated", langUk.IsoCode);
|
||||
|
||||
unpublished = ServiceContext.ContentService.Unpublish(content, langUk.IsoCode); //unpublish again
|
||||
Assert.IsTrue(unpublished.Success);
|
||||
Assert.AreEqual(PublishResultType.SuccessUnpublishAlready, unpublished.Result);
|
||||
Assert.IsFalse(content.IsCulturePublished(langUk.IsoCode));
|
||||
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
//ensure that even though the culture was already unpublished that the data was still persisted
|
||||
Assert.AreEqual("content-en-updated", content.GetCultureName(langUk.IsoCode));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Publishing_No_Cultures_Still_Saves()
|
||||
{
|
||||
var content = CreateEnglishAndFrenchDocument(out var langUk, out var langFr, out var contentType);
|
||||
|
||||
var published = ServiceContext.ContentService.SaveAndPublish(content, new[] { langFr.IsoCode, langUk.IsoCode });
|
||||
Assert.IsTrue(content.IsCulturePublished(langFr.IsoCode));
|
||||
Assert.IsTrue(content.IsCulturePublished(langUk.IsoCode));
|
||||
Assert.IsTrue(published.Success);
|
||||
Assert.AreEqual(PublishedState.Published, content.PublishedState);
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
|
||||
//Change some data since SaveAndPublish should always Save
|
||||
content.SetCultureName("content-en-updated", langUk.IsoCode);
|
||||
|
||||
var saved = ServiceContext.ContentService.SaveAndPublish(content, new string [] { }); //save without cultures
|
||||
Assert.AreEqual(PublishResultType.FailedPublishNothingToPublish, saved.Result);
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
//ensure that even though nothing was published that the data was still persisted
|
||||
Assert.AreEqual("content-en-updated", content.GetCultureName(langUk.IsoCode));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Pending_Invariant_Property_Changes_Affect_Default_Language_Edited_State()
|
||||
{
|
||||
@@ -977,7 +811,17 @@ namespace Umbraco.Tests.Services
|
||||
[Test]
|
||||
public void Can_Publish_Content_Variation_And_Detect_Changed_Cultures()
|
||||
{
|
||||
CreateEnglishAndFrenchDocumentType(out var langUk, out var langFr, out var contentType);
|
||||
// Arrange
|
||||
|
||||
var langGB = new Language("en-GB") { IsDefault = true };
|
||||
var langFr = new Language("fr-FR");
|
||||
|
||||
ServiceContext.LocalizationService.Save(langFr);
|
||||
ServiceContext.LocalizationService.Save(langGB);
|
||||
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
IContent content = new Content("content", Constants.System.Root, contentType);
|
||||
content.SetCultureName("content-fr", langFr.IsoCode);
|
||||
@@ -988,8 +832,8 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
//re-get
|
||||
content = ServiceContext.ContentService.GetById(content.Id);
|
||||
content.SetCultureName("content-en", langUk.IsoCode);
|
||||
published = ServiceContext.ContentService.SaveAndPublish(content, langUk.IsoCode);
|
||||
content.SetCultureName("content-en", langGB.IsoCode);
|
||||
published = ServiceContext.ContentService.SaveAndPublish(content, langGB.IsoCode);
|
||||
//audit log will only show that english was published
|
||||
lastLog = ServiceContext.AuditService.GetLogs(content.Id).Last();
|
||||
Assert.AreEqual($"Published languages: English (United Kingdom)", lastLog.Comment);
|
||||
@@ -3164,33 +3008,10 @@ namespace Umbraco.Tests.Services
|
||||
var templateRepository = new TemplateRepository(accessor, AppCaches.Disabled, Logger, TestObjects.GetFileSystemsMock());
|
||||
var tagRepository = new TagRepository(accessor, AppCaches.Disabled, Logger);
|
||||
var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches);
|
||||
contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository);
|
||||
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);
|
||||
return repository;
|
||||
}
|
||||
|
||||
private void CreateEnglishAndFrenchDocumentType(out Language langUk, out Language langFr, out ContentType contentType)
|
||||
{
|
||||
langUk = new Language("en-GB") { IsDefault = true };
|
||||
langFr = new Language("fr-FR");
|
||||
ServiceContext.LocalizationService.Save(langFr);
|
||||
ServiceContext.LocalizationService.Save(langUk);
|
||||
|
||||
contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
}
|
||||
|
||||
private IContent CreateEnglishAndFrenchDocument(out Language langUk, out Language langFr, out ContentType contentType)
|
||||
{
|
||||
CreateEnglishAndFrenchDocumentType(out langUk, out langFr, out contentType);
|
||||
|
||||
IContent content = new Content("content", Constants.System.Root, contentType);
|
||||
content.SetCultureName("content-fr", langFr.IsoCode);
|
||||
content.SetCultureName("content-en", langUk.IsoCode);
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,360 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsTrue(contentType.IsElement);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_Variation_Clears_Redirects()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("test");
|
||||
ServiceContext.ContentTypeService.Save(contentType2);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.Name = "Hello1";
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
IContent doc2 = MockedContent.CreateBasicContent(contentType2);
|
||||
ServiceContext.ContentService.Save(doc2);
|
||||
|
||||
ServiceContext.RedirectUrlService.Register("hello/world", doc.Key);
|
||||
ServiceContext.RedirectUrlService.Register("hello2/world2", doc2.Key);
|
||||
|
||||
Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count());
|
||||
Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc2.Key).Count());
|
||||
|
||||
//change variation
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
Assert.AreEqual(0, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count());
|
||||
Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc2.Key).Count());
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_From_Invariant_Variant()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.Name = "Hello1";
|
||||
doc.SetValue("title", "hello world");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
Assert.AreEqual("Hello1", doc.Name);
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
|
||||
//change the content type to be variant, we will also update the name here to detect the copy changes
|
||||
doc.Name = "Hello2";
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello2", doc.GetCultureName("en-US"));
|
||||
Assert.AreEqual("hello world", doc.GetValue("title")); //We are not checking against en-US here because properties will remain invariant
|
||||
|
||||
//change back property type to be invariant, we will also update the name here to detect the copy changes
|
||||
doc.SetCultureName("Hello3", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello3", doc.Name);
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_From_Variant_Invariant()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Hello1", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
Assert.AreEqual("Hello1", doc.GetCultureName("en-US"));
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
|
||||
//change the content type to be invariant, we will also update the name here to detect the copy changes
|
||||
doc.SetCultureName("Hello2", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello2", doc.Name);
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
|
||||
//change back property type to be variant, we will also update the name here to detect the copy changes
|
||||
doc.Name = "Hello3";
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
//at this stage all property types were switched to invariant so even though the variant value
|
||||
//exists it will not be returned because the property type is invariant,
|
||||
//so this check proves that null will be returned
|
||||
Assert.IsNull(doc.GetValue("title", "en-US"));
|
||||
|
||||
//we can now switch the property type to be variant and the value can be returned again
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello3", doc.GetCultureName("en-US"));
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_Invariant_Variant()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.Name = "Home";
|
||||
doc.SetValue("title", "hello world");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
|
||||
//change the property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
|
||||
//change back property type to be invariant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_Variant_Invariant()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
|
||||
//change the property type to be invariant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
|
||||
//change back property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_Variant_Invariant_On_A_Composition()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//compose this from the other one
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("test");
|
||||
contentType2.Variations = ContentVariation.Culture;
|
||||
contentType2.AddContentType(contentType);
|
||||
ServiceContext.ContentTypeService.Save(contentType2);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
IContent doc2 = MockedContent.CreateBasicContent(contentType2);
|
||||
doc2.SetCultureName("Home", "en-US");
|
||||
doc2.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc2);
|
||||
|
||||
//change the property type to be invariant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.AreEqual("hello world", doc2.GetValue("title"));
|
||||
|
||||
//change back property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
Assert.AreEqual("hello world", doc2.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_From_Variant_Invariant_On_A_Composition()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var contentCollection = new PropertyTypeCollection(true);
|
||||
contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "title",
|
||||
Name = "Title",
|
||||
Description = "",
|
||||
Mandatory = false,
|
||||
SortOrder = 1,
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
});
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//compose this from the other one
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("test");
|
||||
contentType2.Variations = ContentVariation.Culture;
|
||||
contentType2.AddContentType(contentType);
|
||||
ServiceContext.ContentTypeService.Save(contentType2);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
IContent doc2 = MockedContent.CreateBasicContent(contentType2);
|
||||
doc2.SetCultureName("Home", "en-US");
|
||||
doc2.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc2);
|
||||
|
||||
//change the content type to be invariant
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.AreEqual("hello world", doc2.GetValue("title"));
|
||||
|
||||
//change back content type to be variant
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
//this will be null because the doc type was changed back to variant but it's property types don't get changed back
|
||||
Assert.IsNull(doc.GetValue("title", "en-US"));
|
||||
Assert.IsNull(doc2.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_Content_Type_With_Hierarchy_Of_Content_Items_Moves_Orphaned_Content_To_Recycle_Bin()
|
||||
|
||||
@@ -17,7 +17,6 @@ using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Strings;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using Umbraco.Web.PublishedCache.NuCache;
|
||||
@@ -107,346 +106,44 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_Variation_Clears_Redirects()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Nothing));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("test");
|
||||
ServiceContext.ContentTypeService.Save(contentType2);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.Name = "Hello1";
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
IContent doc2 = MockedContent.CreateBasicContent(contentType2);
|
||||
ServiceContext.ContentService.Save(doc2);
|
||||
|
||||
ServiceContext.RedirectUrlService.Register("hello/world", doc.Key);
|
||||
ServiceContext.RedirectUrlService.Register("hello2/world2", doc2.Key);
|
||||
|
||||
Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count());
|
||||
Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc2.Key).Count());
|
||||
|
||||
//change variation
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
Assert.AreEqual(0, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc.Key).Count());
|
||||
Assert.AreEqual(1, ServiceContext.RedirectUrlService.GetContentRedirectUrls(doc2.Key).Count());
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_From_Invariant_Variant()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Nothing));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.Name = "Hello1";
|
||||
doc.SetValue("title", "hello world");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello1", doc.Name);
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsFalse (doc.IsCultureEdited("en-US"));
|
||||
|
||||
//change the content type to be variant, we will also update the name here to detect the copy changes
|
||||
doc.Name = "Hello2";
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello2", doc.GetCultureName("en-US"));
|
||||
Assert.AreEqual("hello world", doc.GetValue("title")); //We are not checking against en-US here because properties will remain invariant
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US"));
|
||||
|
||||
//change back property type to be invariant, we will also update the name here to detect the copy changes
|
||||
doc.SetCultureName("Hello3", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello3", doc.Name);
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsFalse(doc.IsCultureEdited("en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_From_Variant_Invariant()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Culture));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Hello1", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
Assert.AreEqual("Hello1", doc.GetCultureName("en-US"));
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US"));
|
||||
|
||||
//change the content type to be invariant, we will also update the name here to detect the copy changes
|
||||
doc.SetCultureName("Hello2", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello2", doc.Name);
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsFalse(doc.IsCultureEdited("en-US"));
|
||||
|
||||
//change back property type to be variant, we will also update the name here to detect the copy changes
|
||||
doc.Name = "Hello3";
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
//at this stage all property types were switched to invariant so even though the variant value
|
||||
//exists it will not be returned because the property type is invariant,
|
||||
//so this check proves that null will be returned
|
||||
Assert.IsNull(doc.GetValue("title", "en-US"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US")); // this is true because the name change is copied to the default language
|
||||
|
||||
//we can now switch the property type to be variant and the value can be returned again
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("Hello3", doc.GetCultureName("en-US"));
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_To_Variant_On_Invariant_Content_Type()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Nothing));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//change the property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
|
||||
//Cannot change a property type to be variant if the content type itself is not variant
|
||||
Assert.Throws<InvalidOperationException>(() => ServiceContext.ContentTypeService.Save(contentType));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_Invariant_Variant()
|
||||
{
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Nothing));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US")); //invariant prop changes show up on default lang
|
||||
Assert.IsTrue(doc.Edited);
|
||||
|
||||
//change the property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US"));
|
||||
Assert.IsTrue(doc.Edited);
|
||||
|
||||
//change back property type to be invariant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.IsTrue(doc.IsCultureEdited("en-US")); //invariant prop changes show up on default lang
|
||||
Assert.IsTrue(doc.Edited);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_Variant_Invariant()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Culture));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
|
||||
//change the property type to be invariant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
|
||||
//change back property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Type_From_Variant_Invariant_On_A_Composition()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Culture));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//compose this from the other one
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("test");
|
||||
contentType2.Variations = ContentVariation.Culture;
|
||||
contentType2.AddContentType(contentType);
|
||||
ServiceContext.ContentTypeService.Save(contentType2);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
IContent doc2 = MockedContent.CreateBasicContent(contentType2);
|
||||
doc2.SetCultureName("Home", "en-US");
|
||||
doc2.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc2);
|
||||
|
||||
//change the property type to be invariant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.AreEqual("hello world", doc2.GetValue("title"));
|
||||
|
||||
//change back property type to be variant
|
||||
contentType.PropertyTypes.First().Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title", "en-US"));
|
||||
Assert.AreEqual("hello world", doc2.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Content_Type_From_Variant_Invariant_On_A_Composition()
|
||||
{
|
||||
//create content type with a property type that varies by culture
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
var properties = CreatePropertyCollection(("title", ContentVariation.Culture));
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
//compose this from the other one
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("test");
|
||||
contentType2.Variations = ContentVariation.Culture;
|
||||
contentType2.AddContentType(contentType);
|
||||
ServiceContext.ContentTypeService.Save(contentType2);
|
||||
|
||||
//create some content of this content type
|
||||
IContent doc = MockedContent.CreateBasicContent(contentType);
|
||||
doc.SetCultureName("Home", "en-US");
|
||||
doc.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc);
|
||||
|
||||
IContent doc2 = MockedContent.CreateBasicContent(contentType2);
|
||||
doc2.SetCultureName("Home", "en-US");
|
||||
doc2.SetValue("title", "hello world", "en-US");
|
||||
ServiceContext.ContentService.Save(doc2);
|
||||
|
||||
//change the content type to be invariant
|
||||
contentType.Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
Assert.AreEqual("hello world", doc.GetValue("title"));
|
||||
Assert.AreEqual("hello world", doc2.GetValue("title"));
|
||||
|
||||
//change back content type to be variant
|
||||
contentType.Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
doc = ServiceContext.ContentService.GetById(doc.Id); //re-get
|
||||
doc2 = ServiceContext.ContentService.GetById(doc2.Id); //re-get
|
||||
|
||||
//this will be null because the doc type was changed back to variant but it's property types don't get changed back
|
||||
Assert.IsNull(doc.GetValue("title", "en-US"));
|
||||
Assert.IsNull(doc2.GetValue("title", "en-US"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Variations_SimpleContentType_VariantToInvariantAndBack()
|
||||
{
|
||||
// one simple content type, variant, with both variant and invariant properties
|
||||
// can change it to invariant and back
|
||||
|
||||
CreateFrenchAndEnglishLangs();
|
||||
var languageEn = new Language("en") { IsDefault = true };
|
||||
ServiceContext.LocalizationService.Save(languageEn);
|
||||
var languageFr = new Language("fr");
|
||||
ServiceContext.LocalizationService.Save(languageFr);
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Culture);
|
||||
var contentType = new ContentType(-1)
|
||||
{
|
||||
Alias = "contentType",
|
||||
Name = "contentType",
|
||||
Variations = ContentVariation.Culture
|
||||
};
|
||||
|
||||
var properties = CreatePropertyCollection(
|
||||
("value1", ContentVariation.Culture),
|
||||
("value2", ContentVariation.Nothing));
|
||||
var properties = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value1", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value1",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
},
|
||||
new PropertyType("value2", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value2",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
var document = (IContent)new Content("document", -1, contentType);
|
||||
var document = (IContent) new Content("document", -1, contentType);
|
||||
document.SetCultureName("doc1en", "en");
|
||||
document.SetCultureName("doc1fr", "fr");
|
||||
document.SetValue("value1", "v1en", "en");
|
||||
@@ -529,11 +226,28 @@ namespace Umbraco.Tests.Services
|
||||
var languageFr = new Language("fr");
|
||||
ServiceContext.LocalizationService.Save(languageFr);
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Nothing);
|
||||
var contentType = new ContentType(-1)
|
||||
{
|
||||
Alias = "contentType",
|
||||
Name = "contentType",
|
||||
Variations = ContentVariation.Nothing
|
||||
};
|
||||
|
||||
var properties = CreatePropertyCollection(
|
||||
("value1", ContentVariation.Nothing),
|
||||
("value2", ContentVariation.Nothing));
|
||||
var properties = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value1", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value1",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
},
|
||||
new PropertyType("value2", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value2",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
@@ -612,13 +326,33 @@ namespace Umbraco.Tests.Services
|
||||
// one simple content type, variant, with both variant and invariant properties
|
||||
// can change an invariant property to variant and back
|
||||
|
||||
CreateFrenchAndEnglishLangs();
|
||||
var languageEn = new Language("en") { IsDefault = true };
|
||||
ServiceContext.LocalizationService.Save(languageEn);
|
||||
var languageFr = new Language("fr");
|
||||
ServiceContext.LocalizationService.Save(languageFr);
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Culture);
|
||||
var contentType = new ContentType(-1)
|
||||
{
|
||||
Alias = "contentType",
|
||||
Name = "contentType",
|
||||
Variations = ContentVariation.Culture
|
||||
};
|
||||
|
||||
var properties = CreatePropertyCollection(
|
||||
("value1", ContentVariation.Culture),
|
||||
("value2", ContentVariation.Nothing));
|
||||
var properties = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value1", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value1",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
},
|
||||
new PropertyType("value2", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value2",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
@@ -695,185 +429,6 @@ namespace Umbraco.Tests.Services
|
||||
"{'properties':{'value1':[{'culture':'en','seg':'','val':'v1en'},{'culture':'fr','seg':'','val':'v1fr'}],'value2':[{'culture':'en','seg':'','val':'v2'}]},'cultureData':");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Variations_From_Variant_To_Invariant_And_Ensure_Edited_Values_Are_Renormalized()
|
||||
{
|
||||
// one simple content type, variant, with both variant and invariant properties
|
||||
// can change an invariant property to variant and back
|
||||
|
||||
CreateFrenchAndEnglishLangs();
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Culture);
|
||||
|
||||
var properties = CreatePropertyCollection(("value1", ContentVariation.Culture));
|
||||
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
var document = (IContent)new Content("document", -1, contentType);
|
||||
document.SetCultureName("doc1en", "en");
|
||||
document.SetCultureName("doc1fr", "fr");
|
||||
document.SetValue("value1", "v1en-init", "en");
|
||||
document.SetValue("value1", "v1fr-init", "fr");
|
||||
ServiceContext.ContentService.SaveAndPublish(document); //all values are published which means the document is not 'edited'
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.IsFalse(document.IsCultureEdited("en"));
|
||||
Assert.IsFalse(document.IsCultureEdited("fr"));
|
||||
Assert.IsFalse(document.Edited);
|
||||
|
||||
document.SetValue("value1", "v1en", "en"); //change the property culture value, so now this culture will be edited
|
||||
document.SetValue("value1", "v1fr", "fr"); //change the property culture value, so now this culture will be edited
|
||||
ServiceContext.ContentService.Save(document);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("doc1en", document.Name);
|
||||
Assert.AreEqual("doc1en", document.GetCultureName("en"));
|
||||
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
|
||||
Assert.AreEqual("v1en", document.GetValue("value1", "en"));
|
||||
Assert.AreEqual("v1en-init", document.GetValue("value1", "en", published: true));
|
||||
Assert.AreEqual("v1fr", document.GetValue("value1", "fr"));
|
||||
Assert.AreEqual("v1fr-init", document.GetValue("value1", "fr", published: true));
|
||||
Assert.IsTrue(document.IsCultureEdited("en")); //This will be true because the edited value isn't the same as the published value
|
||||
Assert.IsTrue(document.IsCultureEdited("fr")); //This will be true because the edited value isn't the same as the published value
|
||||
Assert.IsTrue(document.Edited);
|
||||
|
||||
// switch property type to Invariant
|
||||
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType); //This is going to have to re-normalize the "Edited" flag
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.IsTrue(document.IsCultureEdited("en")); //This will remain true because there is now a pending change for the invariant property data which is flagged under the default lang
|
||||
Assert.IsFalse(document.IsCultureEdited("fr")); //This will be false because nothing has changed for this culture and the property no longer reflects variant changes
|
||||
Assert.IsTrue(document.Edited);
|
||||
|
||||
//update the invariant value and publish
|
||||
document.SetValue("value1", "v1inv");
|
||||
ServiceContext.ContentService.SaveAndPublish(document);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("doc1en", document.Name);
|
||||
Assert.AreEqual("doc1en", document.GetCultureName("en"));
|
||||
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
|
||||
Assert.IsNull(document.GetValue("value1", "en")); //The values are there but the business logic returns null
|
||||
Assert.IsNull(document.GetValue("value1", "fr")); //The values are there but the business logic returns null
|
||||
Assert.IsNull(document.GetValue("value1", "en", published: true)); //The values are there but the business logic returns null
|
||||
Assert.IsNull(document.GetValue("value1", "fr", published: true)); //The values are there but the business logic returns null
|
||||
Assert.AreEqual("v1inv", document.GetValue("value1"));
|
||||
Assert.AreEqual("v1inv", document.GetValue("value1", published: true));
|
||||
Assert.IsFalse(document.IsCultureEdited("en")); //This returns false, everything is published
|
||||
Assert.IsFalse(document.IsCultureEdited("fr")); //This will be false because nothing has changed for this culture and the property no longer reflects variant changes
|
||||
Assert.IsFalse(document.Edited);
|
||||
|
||||
// switch property back to Culture
|
||||
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("v1inv", document.GetValue("value1", "en")); //The invariant property value gets copied over to the default language
|
||||
Assert.AreEqual("v1inv", document.GetValue("value1", "en", published: true));
|
||||
Assert.AreEqual("v1fr", document.GetValue("value1", "fr")); //values are still retained
|
||||
Assert.AreEqual("v1fr-init", document.GetValue("value1", "fr", published: true)); //values are still retained
|
||||
Assert.IsFalse(document.IsCultureEdited("en")); //The invariant published AND edited values are copied over to the default language
|
||||
Assert.IsTrue(document.IsCultureEdited("fr")); //The previously existing french values are there and there is no published value
|
||||
Assert.IsTrue(document.Edited); //Will be flagged edited again because the french culture had pending changes
|
||||
|
||||
// publish again
|
||||
document.SetValue("value1", "v1en2", "en"); //update the value now that it's variant again
|
||||
document.SetValue("value1", "v1fr2", "fr"); //update the value now that it's variant again
|
||||
ServiceContext.ContentService.SaveAndPublish(document);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("doc1en", document.Name);
|
||||
Assert.AreEqual("doc1en", document.GetCultureName("en"));
|
||||
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
|
||||
Assert.AreEqual("v1en2", document.GetValue("value1", "en"));
|
||||
Assert.AreEqual("v1fr2", document.GetValue("value1", "fr"));
|
||||
Assert.IsNull(document.GetValue("value1")); //The value is there but the business logic returns null
|
||||
Assert.IsFalse(document.IsCultureEdited("en")); //This returns false, the variant property value has been published
|
||||
Assert.IsFalse(document.IsCultureEdited("fr")); //This returns false, the variant property value has been published
|
||||
Assert.IsFalse(document.Edited);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Property_Variations_From_Invariant_To_Variant_And_Ensure_Edited_Values_Are_Renormalized()
|
||||
{
|
||||
// one simple content type, variant, with both variant and invariant properties
|
||||
// can change an invariant property to variant and back
|
||||
|
||||
CreateFrenchAndEnglishLangs();
|
||||
|
||||
var contentType = CreateContentType(ContentVariation.Culture);
|
||||
|
||||
var properties = CreatePropertyCollection(("value1", ContentVariation.Nothing));
|
||||
|
||||
contentType.PropertyGroups.Add(new PropertyGroup(properties) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
var document = (IContent)new Content("document", -1, contentType);
|
||||
document.SetCultureName("doc1en", "en");
|
||||
document.SetCultureName("doc1fr", "fr");
|
||||
document.SetValue("value1", "v1en-init");
|
||||
ServiceContext.ContentService.SaveAndPublish(document); //all values are published which means the document is not 'edited'
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.IsFalse(document.IsCultureEdited("en"));
|
||||
Assert.IsFalse(document.IsCultureEdited("fr"));
|
||||
Assert.IsFalse(document.Edited);
|
||||
|
||||
document.SetValue("value1", "v1en"); //change the property value, so now the invariant (default) culture will be edited
|
||||
ServiceContext.ContentService.Save(document);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("doc1en", document.Name);
|
||||
Assert.AreEqual("doc1en", document.GetCultureName("en"));
|
||||
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
|
||||
Assert.AreEqual("v1en", document.GetValue("value1"));
|
||||
Assert.AreEqual("v1en-init", document.GetValue("value1", published: true));
|
||||
Assert.IsTrue(document.IsCultureEdited("en")); //This is true because the invariant property reflects changes on the default lang
|
||||
Assert.IsFalse(document.IsCultureEdited("fr"));
|
||||
Assert.IsTrue(document.Edited);
|
||||
|
||||
// switch property type to Culture
|
||||
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Culture;
|
||||
ServiceContext.ContentTypeService.Save(contentType); //This is going to have to re-normalize the "Edited" flag
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.IsTrue(document.IsCultureEdited("en")); //Remains true
|
||||
Assert.IsFalse(document.IsCultureEdited("fr")); //False because no french property has ever been edited
|
||||
Assert.IsTrue(document.Edited);
|
||||
|
||||
//update the culture value and publish
|
||||
document.SetValue("value1", "v1en2", "en");
|
||||
ServiceContext.ContentService.SaveAndPublish(document);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("doc1en", document.Name);
|
||||
Assert.AreEqual("doc1en", document.GetCultureName("en"));
|
||||
Assert.AreEqual("doc1fr", document.GetCultureName("fr"));
|
||||
Assert.IsNull(document.GetValue("value1")); //The values are there but the business logic returns null
|
||||
Assert.IsNull(document.GetValue("value1", published: true)); //The values are there but the business logic returns null
|
||||
Assert.AreEqual("v1en2", document.GetValue("value1", "en"));
|
||||
Assert.AreEqual("v1en2", document.GetValue("value1", "en", published: true));
|
||||
Assert.IsFalse(document.IsCultureEdited("en")); //This returns false, everything is published
|
||||
Assert.IsFalse(document.IsCultureEdited("fr")); //False because no french property has ever been edited
|
||||
Assert.IsFalse(document.Edited);
|
||||
|
||||
// switch property back to Invariant
|
||||
contentType.PropertyTypes.First(x => x.Alias == "value1").Variations = ContentVariation.Nothing;
|
||||
ServiceContext.ContentTypeService.Save(contentType);
|
||||
|
||||
document = ServiceContext.ContentService.GetById(document.Id);
|
||||
Assert.AreEqual("v1en2", document.GetValue("value1")); //The variant property value gets copied over to the invariant
|
||||
Assert.AreEqual("v1en2", document.GetValue("value1", published: true));
|
||||
Assert.IsNull(document.GetValue("value1", "fr")); //The values are there but the business logic returns null
|
||||
Assert.IsNull(document.GetValue("value1", "fr", published: true)); //The values are there but the business logic returns null
|
||||
Assert.IsFalse(document.IsCultureEdited("en")); //The variant published AND edited values are copied over to the invariant
|
||||
Assert.IsFalse(document.IsCultureEdited("fr"));
|
||||
Assert.IsFalse(document.Edited);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Change_Variations_ComposedContentType_1()
|
||||
{
|
||||
@@ -882,22 +437,59 @@ namespace Umbraco.Tests.Services
|
||||
// can change the composing content type to invariant and back
|
||||
// can change the composed content type to invariant and back
|
||||
|
||||
CreateFrenchAndEnglishLangs();
|
||||
var languageEn = new Language("en") { IsDefault = true };
|
||||
ServiceContext.LocalizationService.Save(languageEn);
|
||||
var languageFr = new Language("fr");
|
||||
ServiceContext.LocalizationService.Save(languageFr);
|
||||
|
||||
var composing = CreateContentType(ContentVariation.Culture, "composing");
|
||||
var composing = new ContentType(-1)
|
||||
{
|
||||
Alias = "composing",
|
||||
Name = "composing",
|
||||
Variations = ContentVariation.Culture
|
||||
};
|
||||
|
||||
var properties1 = CreatePropertyCollection(
|
||||
("value11", ContentVariation.Culture),
|
||||
("value12", ContentVariation.Nothing));
|
||||
var properties1 = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value11", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value11",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
},
|
||||
new PropertyType("value12", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value12",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
composing.PropertyGroups.Add(new PropertyGroup(properties1) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(composing);
|
||||
|
||||
var composed = CreateContentType(ContentVariation.Culture, "composed");
|
||||
var composed = new ContentType(-1)
|
||||
{
|
||||
Alias = "composed",
|
||||
Name = "composed",
|
||||
Variations = ContentVariation.Culture
|
||||
};
|
||||
|
||||
var properties2 = CreatePropertyCollection(
|
||||
("value21", ContentVariation.Culture),
|
||||
("value22", ContentVariation.Nothing));
|
||||
var properties2 = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value21", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value21",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
},
|
||||
new PropertyType("value22", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value22",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
composed.PropertyGroups.Add(new PropertyGroup(properties2) { Name = "Content" });
|
||||
composed.AddContentType(composing);
|
||||
@@ -977,32 +569,86 @@ namespace Umbraco.Tests.Services
|
||||
// can change the composing content type to invariant and back
|
||||
// can change the variant composed content type to invariant and back
|
||||
|
||||
CreateFrenchAndEnglishLangs();
|
||||
var languageEn = new Language("en") { IsDefault = true };
|
||||
ServiceContext.LocalizationService.Save(languageEn);
|
||||
var languageFr = new Language("fr");
|
||||
ServiceContext.LocalizationService.Save(languageFr);
|
||||
|
||||
var composing = CreateContentType(ContentVariation.Culture, "composing");
|
||||
var composing = new ContentType(-1)
|
||||
{
|
||||
Alias = "composing",
|
||||
Name = "composing",
|
||||
Variations = ContentVariation.Culture
|
||||
};
|
||||
|
||||
var properties1 = CreatePropertyCollection(
|
||||
("value11", ContentVariation.Culture),
|
||||
("value12", ContentVariation.Nothing));
|
||||
var properties1 = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value11", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value11",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
},
|
||||
new PropertyType("value12", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value12",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
composing.PropertyGroups.Add(new PropertyGroup(properties1) { Name = "Content" });
|
||||
ServiceContext.ContentTypeService.Save(composing);
|
||||
|
||||
var composed1 = CreateContentType(ContentVariation.Culture, "composed1");
|
||||
var composed1 = new ContentType(-1)
|
||||
{
|
||||
Alias = "composed1",
|
||||
Name = "composed1",
|
||||
Variations = ContentVariation.Culture
|
||||
};
|
||||
|
||||
var properties2 = CreatePropertyCollection(
|
||||
("value21", ContentVariation.Culture),
|
||||
("value22", ContentVariation.Nothing));
|
||||
var properties2 = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value21", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value21",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Culture
|
||||
},
|
||||
new PropertyType("value22", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value22",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
composed1.PropertyGroups.Add(new PropertyGroup(properties2) { Name = "Content" });
|
||||
composed1.AddContentType(composing);
|
||||
ServiceContext.ContentTypeService.Save(composed1);
|
||||
|
||||
var composed2 = CreateContentType(ContentVariation.Nothing, "composed2");
|
||||
var composed2 = new ContentType(-1)
|
||||
{
|
||||
Alias = "composed2",
|
||||
Name = "composed2",
|
||||
Variations = ContentVariation.Nothing
|
||||
};
|
||||
|
||||
var properties3 = CreatePropertyCollection(
|
||||
("value31", ContentVariation.Nothing),
|
||||
("value32", ContentVariation.Nothing));
|
||||
var properties3 = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType("value31", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value31",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
},
|
||||
new PropertyType("value32", ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = "value32",
|
||||
DataTypeId = -88,
|
||||
Variations = ContentVariation.Nothing
|
||||
}
|
||||
};
|
||||
|
||||
composed2.PropertyGroups.Add(new PropertyGroup(properties3) { Name = "Content" });
|
||||
composed2.AddContentType(composing);
|
||||
@@ -1108,35 +754,5 @@ namespace Umbraco.Tests.Services
|
||||
AssertJsonStartsWith(document2.Id,
|
||||
"{'properties':{'value11':[{'culture':'','seg':'','val':'v11'}],'value12':[{'culture':'','seg':'','val':'v12'}],'value31':[{'culture':'','seg':'','val':'v31'}],'value32':[{'culture':'','seg':'','val':'v32'}]},'cultureData':");
|
||||
}
|
||||
|
||||
private void CreateFrenchAndEnglishLangs()
|
||||
{
|
||||
var languageEn = new Language("en") { IsDefault = true };
|
||||
ServiceContext.LocalizationService.Save(languageEn);
|
||||
var languageFr = new Language("fr");
|
||||
ServiceContext.LocalizationService.Save(languageFr);
|
||||
}
|
||||
|
||||
private IContentType CreateContentType(ContentVariation variance, string alias = "contentType") => new ContentType(-1)
|
||||
{
|
||||
Alias = alias,
|
||||
Name = alias,
|
||||
Variations = variance
|
||||
};
|
||||
|
||||
private PropertyTypeCollection CreatePropertyCollection(params (string alias, ContentVariation variance)[] props)
|
||||
{
|
||||
var propertyCollection = new PropertyTypeCollection(true);
|
||||
|
||||
foreach (var (alias, variance) in props)
|
||||
propertyCollection.Add(new PropertyType(alias, ValueStorageType.Ntext)
|
||||
{
|
||||
Alias = alias,
|
||||
DataTypeId = -88,
|
||||
Variations = variance
|
||||
});
|
||||
|
||||
return propertyCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
@@ -30,7 +29,7 @@ namespace Umbraco.Tests.Testing
|
||||
var methodName = test.MethodName;
|
||||
var type = Type.GetType(typeName, true);
|
||||
if (type == null)
|
||||
throw new PanicException($"Could not resolve the type from type name {typeName}"); // makes no sense
|
||||
throw new Exception("panic"); // makes no sense
|
||||
var methodInfo = type.GetMethod(methodName); // what about overloads?
|
||||
var options = GetTestOptions<TOptions>(methodInfo);
|
||||
return options;
|
||||
@@ -54,7 +53,7 @@ namespace Umbraco.Tests.Testing
|
||||
{
|
||||
if (other == null) throw new ArgumentNullException(nameof(other));
|
||||
if (!(Merge((TestOptionAttributeBase) other) is TOptions merged))
|
||||
throw new PanicException("Could not merge test options");
|
||||
throw new Exception("panic");
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -1,7 +1,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService) {
|
||||
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService, entityResource) {
|
||||
|
||||
function link(scope) {
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
scope.allowChangeDocumentType = false;
|
||||
scope.allowChangeTemplate = false;
|
||||
scope.allTemplates = [];
|
||||
|
||||
function onInit() {
|
||||
entityResource.getAll("Template").then(function (templates) {
|
||||
scope.allTemplates = templates;
|
||||
});
|
||||
|
||||
// set currentVariant
|
||||
scope.currentVariant = _.find(scope.node.variants, (v) => v.active);
|
||||
@@ -158,8 +162,12 @@
|
||||
}
|
||||
|
||||
scope.openTemplate = function () {
|
||||
var template = _.findWhere(scope.allTemplates, {alias: scope.node.template})
|
||||
if (!template) {
|
||||
return;
|
||||
}
|
||||
var templateEditor = {
|
||||
id: scope.node.templateId,
|
||||
id: template.id,
|
||||
submit: function (model) {
|
||||
editorService.close();
|
||||
},
|
||||
|
||||
-2
@@ -47,7 +47,6 @@
|
||||
function setSortingOptions() {
|
||||
|
||||
scope.sortableOptionsGroup = {
|
||||
axis: 'y',
|
||||
distance: 10,
|
||||
tolerance: "pointer",
|
||||
opacity: 0.7,
|
||||
@@ -66,7 +65,6 @@
|
||||
};
|
||||
|
||||
scope.sortableOptionsProperty = {
|
||||
axis: 'y',
|
||||
distance: 10,
|
||||
tolerance: "pointer",
|
||||
connectWith: ".umb-group-builder__properties",
|
||||
|
||||
+82
-120
@@ -4,7 +4,7 @@
|
||||
@restrict A
|
||||
|
||||
@description
|
||||
Use this directive make an element sticky and follow the page when scrolling.
|
||||
Use this directive make an element sticky and follow the page when scrolling. `umb-sticky-bar--active` class is applied when the element is stuck
|
||||
|
||||
<h3>Markup example</h3>
|
||||
<pre>
|
||||
@@ -12,140 +12,102 @@ Use this directive make an element sticky and follow the page when scrolling.
|
||||
|
||||
<div
|
||||
class="my-sticky-bar"
|
||||
umb-sticky-bar
|
||||
scrollable-container=".container">
|
||||
umb-sticky-bar>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
<h3>CSS example</h3>
|
||||
<pre>
|
||||
.my-sticky-bar {
|
||||
padding: 15px 0;
|
||||
background: #000000;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.my-sticky-bar.-umb-sticky-bar {
|
||||
top: 100px;
|
||||
}
|
||||
</pre>
|
||||
|
||||
@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element.
|
||||
**/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function StickyBarDirective($rootScope) {
|
||||
function StickyBarDirective() {
|
||||
|
||||
/**
|
||||
On initial load, the intersector fires if the grid editor is in the viewport
|
||||
This flag is used to suppress the setClass behaviour on the initial load
|
||||
**/
|
||||
var initial = true;
|
||||
|
||||
/**
|
||||
Toggle `umb-sticky-bar--active` class on the sticky-bar element
|
||||
**/
|
||||
function setClass(addClass, current) {
|
||||
if (!initial) {
|
||||
current.classList.toggle('umb-sticky-bar--active', addClass);
|
||||
} else {
|
||||
initial = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Inserts two elements in the umbStickyBar parent element
|
||||
These are used by the IntersectionObserve to calculate scroll position
|
||||
**/
|
||||
function addSentinels(current) {
|
||||
['-top', '-bottom'].forEach(s => {
|
||||
const sentinel = document.createElement('div');
|
||||
sentinel.classList.add('umb-sticky-sentinel', s);
|
||||
current.parentElement.appendChild(sentinel);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
Calls into setClass when the footer sentinel enters/exits the bottom of the container
|
||||
Container is the parent element of the umbStickyBar element
|
||||
**/
|
||||
function observeFooter(current, container) {
|
||||
const observer = new IntersectionObserver((records, observer) => {
|
||||
let [target, rootBounds, intersected] = [records[0].boundingClientRect, records[0].rootBounds, records[0].intersectionRatio === 1];
|
||||
|
||||
if (target.bottom > rootBounds.top && intersected) {
|
||||
setClass(true, current);
|
||||
}
|
||||
if (target.top < rootBounds.top && target.bottom < rootBounds.bottom) {
|
||||
setClass(false, current);
|
||||
}
|
||||
}, {
|
||||
threshold: [1],
|
||||
root: container
|
||||
});
|
||||
|
||||
observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-bottom'));
|
||||
}
|
||||
|
||||
/**
|
||||
Calls into setClass when the header sentinel enters/exits the top of the container
|
||||
Container is the parent element of the umbStickyBar element
|
||||
**/
|
||||
function observeHeader(current, container) {
|
||||
const observer = new IntersectionObserver((records, observer) => {
|
||||
let [target, rootBounds] = [records[0].boundingClientRect, records[0].rootBounds];
|
||||
|
||||
if (target.bottom < rootBounds.top) {
|
||||
setClass(true, current);
|
||||
}
|
||||
|
||||
if (target.bottom >= rootBounds.top && target.bottom < rootBounds.bottom) {
|
||||
setClass(false, current);
|
||||
}
|
||||
}, {
|
||||
threshold: [0],
|
||||
root: container
|
||||
});
|
||||
|
||||
observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-top'));
|
||||
}
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
var bar = $(el);
|
||||
var scrollableContainer = null;
|
||||
var clonedBar = null;
|
||||
var cloneIsMade = false;
|
||||
let current = el[0];
|
||||
let container = current.closest('[data-element="editor-container"]');
|
||||
|
||||
function activate() {
|
||||
|
||||
if (bar.parents(".umb-property").length > 1) {
|
||||
bar.addClass("nested");
|
||||
return;
|
||||
}
|
||||
|
||||
if (attr.scrollableContainer) {
|
||||
scrollableContainer = bar.closest(attr.scrollableContainer);
|
||||
} else {
|
||||
scrollableContainer = $(window);
|
||||
}
|
||||
|
||||
scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
|
||||
$(window).on('resize.umbStickyBar', determineVisibility);
|
||||
|
||||
scope.$on('$destroy', function () {
|
||||
scrollableContainer.off('.umbStickyBar');
|
||||
$(window).off('.umbStickyBar');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function determineVisibility() {
|
||||
|
||||
var barTop = bar[0].offsetTop;
|
||||
var scrollTop = scrollableContainer.scrollTop();
|
||||
|
||||
if (scrollTop > barTop) {
|
||||
|
||||
if (!cloneIsMade) {
|
||||
|
||||
createClone();
|
||||
|
||||
clonedBar.css({
|
||||
'visibility': 'visible'
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
calculateSize();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (cloneIsMade) {
|
||||
|
||||
//remove cloned element (switched places with original on creation)
|
||||
bar.remove();
|
||||
bar = clonedBar;
|
||||
clonedBar = null;
|
||||
|
||||
bar.removeClass('-umb-sticky-bar');
|
||||
bar.css({
|
||||
position: 'relative',
|
||||
'width': 'auto',
|
||||
'height': 'auto',
|
||||
'z-index': 'auto',
|
||||
'visibility': 'visible'
|
||||
});
|
||||
|
||||
cloneIsMade = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function calculateSize() {
|
||||
var width = bar.innerWidth();
|
||||
clonedBar.css({
|
||||
width: width + 10 // + 10 (5*2) because we need to add border to avoid seeing the shadow beneath. Look at the CSS.
|
||||
});
|
||||
}
|
||||
|
||||
function createClone() {
|
||||
//switch place with cloned element, to keep binding intact
|
||||
clonedBar = bar;
|
||||
bar = clonedBar.clone();
|
||||
clonedBar.after(bar);
|
||||
clonedBar.addClass('-umb-sticky-bar');
|
||||
clonedBar.css({
|
||||
'position': 'fixed',
|
||||
// if you change this z-index value, make sure the sticky editor sub headers do not
|
||||
// clash with umb-dropdown (e.g. the content actions dropdown in content list view)
|
||||
'z-index': 99,
|
||||
'visibility': 'hidden'
|
||||
});
|
||||
|
||||
cloneIsMade = true;
|
||||
calculateSize();
|
||||
|
||||
}
|
||||
|
||||
activate();
|
||||
addSentinels(current);
|
||||
|
||||
observeHeader(current, container);
|
||||
observeFooter(current, container);
|
||||
}
|
||||
|
||||
var directive = {
|
||||
|
||||
@@ -133,7 +133,7 @@ function entityResource($q, $http, umbRequestHelper) {
|
||||
* ##usage
|
||||
* <pre>
|
||||
* //get media by id
|
||||
* entityResource.getEntityById(0, "Media")
|
||||
* entityResource.getById(0, "Media")
|
||||
* .then(function(ent) {
|
||||
* var myDoc = ent;
|
||||
* alert('its here!');
|
||||
@@ -172,7 +172,7 @@ function entityResource($q, $http, umbRequestHelper) {
|
||||
umbRequestHelper.getApiUrl(
|
||||
"entityApiBaseUrl",
|
||||
"GetUrlAndAnchors",
|
||||
{ id: id })),
|
||||
[{ id: id }])),
|
||||
'Failed to retrieve url and anchors data for id ' + id);
|
||||
},
|
||||
|
||||
@@ -204,7 +204,7 @@ function entityResource($q, $http, umbRequestHelper) {
|
||||
* ##usage
|
||||
* <pre>
|
||||
* //Get templates for ids
|
||||
* entityResource.getEntitiesByIds( [1234,2526,28262], "Template")
|
||||
* entityResource.getByIds( [1234,2526,28262], "Template")
|
||||
* .then(function(templateArray) {
|
||||
* var myDoc = contentArray;
|
||||
* alert('they are here!');
|
||||
|
||||
@@ -261,7 +261,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
unbindKeyboardShortcuts();
|
||||
|
||||
// set flag so we know when the editor is open in "infinie mode"
|
||||
// set flag so we know when the editor is open in "infinite mode"
|
||||
editor.infiniteMode = true;
|
||||
|
||||
editors.push(editor);
|
||||
|
||||
+23
-6
@@ -32,15 +32,32 @@
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.umb-editor-sub-header.-umb-sticky-bar {
|
||||
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
|
||||
[umb-sticky-bar] {
|
||||
transition: box-shadow 240ms;
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
top: calc(@appHeaderHeight + @editorHeaderHeight);
|
||||
position:sticky;
|
||||
z-index: 99;
|
||||
|
||||
.umb-editor--infinityMode & {
|
||||
top: calc(@editorHeaderHeight);
|
||||
&.umb-sticky-bar--active {
|
||||
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
|
||||
}
|
||||
}
|
||||
|
||||
.umb-sticky-sentinel {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
|
||||
&.-top {
|
||||
top:0px;
|
||||
height:1px;
|
||||
}
|
||||
|
||||
&.-bottom {
|
||||
bottom:50px;
|
||||
height:10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// --------------------------------------------------
|
||||
.umb-property-editor {
|
||||
width: 100%;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.umb-property-editor-tiny {
|
||||
@@ -165,8 +166,6 @@
|
||||
.sp-replacer {
|
||||
display: inline-flex;
|
||||
margin-right: 18px;
|
||||
border: solid 1px @gray-8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
+2
-2
@@ -59,10 +59,10 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
|
||||
if (dialogOptions.currentTarget) {
|
||||
// clone the current target so we don't accidentally update the caller's model while manipulating $scope.model.target
|
||||
$scope.model.target = angular.copy(dialogOptions.currentTarget);
|
||||
//if we have a node ID, we fetch the current node to build the form data
|
||||
// if we have a node ID, we fetch the current node to build the form data
|
||||
if ($scope.model.target.id || $scope.model.target.udi) {
|
||||
|
||||
//will be either a udi or an int
|
||||
// will be either a udi or an int
|
||||
var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id;
|
||||
|
||||
if ($scope.model.target.udi) {
|
||||
|
||||
@@ -53,14 +53,14 @@
|
||||
<form ng-if="login.linkedProviderKey == undefined" method="POST" name="externalLoginForm"
|
||||
action="{{externalLinkLoginFormAction}}" id="oauthloginform" name="oauthloginform">
|
||||
<input type="hidden" name="provider" value="{{login.authType}}" />
|
||||
<button class="btn btn-block btn-social"
|
||||
ng-class="login.properties.SocialStyle"
|
||||
id="{{login.authType}}"
|
||||
onclick="document.forms.oauthloginform.submit();">
|
||||
<button class="btn btn-block btn-social"
|
||||
ng-class="login.properties.SocialStyle"
|
||||
id="{{login.authType}}"
|
||||
onclick="document.forms.oauthloginform.submit();">
|
||||
|
||||
<i class="fa" ng-class="login.properties.SocialIcon"></i>
|
||||
<localize key="defaultdialogs_linkYour">Link your</localize> {{login.caption}} <localize key="defaultdialogs_account">account</localize>
|
||||
</button>
|
||||
<i class="fa" ng-class="login.properties.SocialIcon"></i>
|
||||
<localize key="defaultdialogs_linkYour">Link your</localize> {{login.caption}} <localize key="defaultdialogs_account">account</localize>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<button ng-if="login.linkedProviderKey != undefined"
|
||||
@@ -71,7 +71,7 @@
|
||||
name="provider"
|
||||
value="{{login.authType}}">
|
||||
<i class="fa" ng-class="login.properties.SocialIcon"></i>
|
||||
<localize key="defaultdialogs_unLinkYour">Un-link your</localize> {{login.caption}} <localize key="defaultdialogs_account">account</localize>
|
||||
<localize key="defaultdialogs_unLinkYour">Un-link your</localize> {{login.caption}} <localize key="defaultdialogs_account">account</localize>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
id="{{login.authType}}" name="provider" value="{{login.authType}}"
|
||||
title="Log in using your {{login.caption}} account">
|
||||
<i class="fa" ng-class="login.properties.SocialIcon"></i>
|
||||
<localize key="login_signInWith">Sign in with</localize> {{login.caption}}
|
||||
<localize key="login_signInWith">Sign in with</localize> {{login.caption}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
<div
|
||||
class="umb-editor-sub-header umb-editor-sub-header--{{appearance}}"
|
||||
umb-sticky-bar
|
||||
scrollable-container=".umb-editor-container"
|
||||
ng-transclude>
|
||||
</div>
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
vm.numberOfErrors = 0;
|
||||
vm.commonLogMessages = [];
|
||||
vm.commonLogMessagesCount = 10;
|
||||
vm.dateRangeLabel = "";
|
||||
|
||||
// ChartJS Options - for count/overview of log distribution
|
||||
vm.logTypeLabels = ["Info", "Debug", "Warning", "Error", "Critical"];
|
||||
vm.logTypeLabels = ["Debug", "Info", "Warning", "Error", "Fatal"];
|
||||
vm.logTypeData = [0, 0, 0, 0, 0];
|
||||
vm.logTypeColors = [ '#dcdcdc', '#97bbcd', '#46bfbd', '#fdb45c', '#f7464a'];
|
||||
vm.logTypeColors = ['#eaddd5', '#2bc37c', '#3544b1', '#ff9412', '#d42054'];
|
||||
vm.chartOptions = {
|
||||
legend: {
|
||||
display: true,
|
||||
@@ -23,35 +24,42 @@
|
||||
};
|
||||
|
||||
let querystring = $location.search();
|
||||
if(querystring.startDate){
|
||||
if (querystring.startDate) {
|
||||
vm.startDate = querystring.startDate;
|
||||
}else{
|
||||
vm.dateRangeLabel = getDateRangeLabel("Selected Time Period");
|
||||
} else {
|
||||
vm.startDate = new Date(Date.now());
|
||||
vm.startDate.setDate(vm.startDate.getDate()-1);
|
||||
vm.startDate.setDate(vm.startDate.getDate() - 1);
|
||||
vm.startDate = vm.startDate.toIsoDateString();
|
||||
vm.dateRangeLabel = getDateRangeLabel("Today");
|
||||
}
|
||||
|
||||
if(querystring.endDate){
|
||||
if (querystring.endDate) {
|
||||
vm.endDate = querystring.endDate;
|
||||
}else{
|
||||
|
||||
if (querystring.endDate === querystring.startDate) {
|
||||
vm.dateRangeLabel = getDateRangeLabel("Selected Date");
|
||||
}
|
||||
} else {
|
||||
vm.endDate = new Date(Date.now()).toIsoDateString();
|
||||
}
|
||||
vm.period = [vm.startDate, vm.endDate];
|
||||
|
||||
vm.period = [vm.startDate, vm.endDate];
|
||||
|
||||
//functions
|
||||
vm.searchLogQuery = searchLogQuery;
|
||||
vm.findMessageTemplate = findMessageTemplate;
|
||||
|
||||
vm.searchErrors = searchErrors;
|
||||
|
||||
function preFlightCheck(){
|
||||
vm.loading = true;
|
||||
//Do our pre-flight check (to see if we can view logs)
|
||||
//IE the log file is NOT too big such as 1GB & crash the site
|
||||
logViewerResource.canViewLogs(vm.startDate, vm.endDate).then(function(result){
|
||||
logViewerResource.canViewLogs(vm.startDate, vm.endDate).then(function (result) {
|
||||
vm.loading = false;
|
||||
vm.canLoadLogs = result;
|
||||
|
||||
if(result){
|
||||
if (result) {
|
||||
//Can view logs - so initalise
|
||||
init();
|
||||
}
|
||||
@@ -62,39 +70,39 @@
|
||||
function init() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
|
||||
var savedSearches = logViewerResource.getSavedSearches().then(function (data) {
|
||||
vm.searches = data;
|
||||
},
|
||||
// fallback to some defaults if error from API response
|
||||
function () {
|
||||
vm.searches = [
|
||||
{
|
||||
"name": "Find all logs where the Level is NOT Verbose and NOT Debug",
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
vm.searches = data;
|
||||
},
|
||||
// fallback to some defaults if error from API response
|
||||
function () {
|
||||
vm.searches = [
|
||||
{
|
||||
"name": "Find all logs where the Level is NOT Verbose and NOT Debug",
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration'",
|
||||
"query": "Has(Duration)"
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration'",
|
||||
"query": "Has(Duration)"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
|
||||
"query": "Has(Duration) and Duration > 1000"
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
|
||||
"query": "Has(Duration) and Duration > 1000"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that are from the namespace 'Umbraco.Core'",
|
||||
"query": "StartsWith(SourceContext, 'Umbraco.Core')"
|
||||
{
|
||||
"name": "Find all logs that are from the namespace 'Umbraco.Core'",
|
||||
"query": "StartsWith(SourceContext, 'Umbraco.Core')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that use a specific log message template",
|
||||
"query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
|
||||
{
|
||||
"name": "Find all logs that use a specific log message template",
|
||||
"query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
var numOfErrors = logViewerResource.getNumberOfErrors(vm.startDate, vm.endDate).then(function (data) {
|
||||
vm.numberOfErrors = data;
|
||||
@@ -102,41 +110,56 @@
|
||||
|
||||
var logCounts = logViewerResource.getLogLevelCounts(vm.startDate, vm.endDate).then(function (data) {
|
||||
vm.logTypeData = [];
|
||||
vm.logTypeData.push(data.Information);
|
||||
vm.logTypeData.push(data.Debug);
|
||||
vm.logTypeData.push(data.Warning);
|
||||
vm.logTypeData.push(data.Error);
|
||||
vm.logTypeData.push(data.Fatal);
|
||||
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
const index = vm.logTypeLabels.findIndex(x => key.startsWith(x));
|
||||
if (index > -1) {
|
||||
vm.logTypeData[index] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var commonMsgs = logViewerResource.getMessageTemplates(vm.startDate, vm.endDate).then(function(data){
|
||||
var commonMsgs = logViewerResource.getMessageTemplates(vm.startDate, vm.endDate).then(function (data) {
|
||||
vm.commonLogMessages = data;
|
||||
});
|
||||
|
||||
//Set loading indicatior to false when these 3 queries complete
|
||||
$q.all([savedSearches, numOfErrors, logCounts, commonMsgs]).then(function(data) {
|
||||
//Set loading indicator to false when these 3 queries complete
|
||||
$q.all([savedSearches, numOfErrors, logCounts, commonMsgs]).then(function () {
|
||||
vm.loading = false;
|
||||
});
|
||||
|
||||
$timeout(function () {
|
||||
navigationService.syncTree({ tree: "logViewer", path: "-1" });
|
||||
navigationService.syncTree({
|
||||
tree: "logViewer",
|
||||
path: "-1"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function searchLogQuery(logQuery){
|
||||
$location.path("/settings/logViewer/search").search({lq: logQuery, startDate: vm.startDate, endDate: vm.endDate});
|
||||
function searchLogQuery(logQuery) {
|
||||
$location.path("/settings/logViewer/search").search({
|
||||
lq: logQuery,
|
||||
startDate: vm.startDate,
|
||||
endDate: vm.endDate
|
||||
});
|
||||
}
|
||||
|
||||
function findMessageTemplate(template){
|
||||
function findMessageTemplate(template) {
|
||||
var logQuery = "@MessageTemplate='" + template.MessageTemplate + "'";
|
||||
searchLogQuery(logQuery);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getDateRangeLabel(suffix) {
|
||||
return "Log Overview for " + suffix;
|
||||
}
|
||||
|
||||
function searchErrors(){
|
||||
var logQuery = "@Level='Fatal' or @Level='Error' or Has(@Exception)";
|
||||
searchLogQuery(logQuery);
|
||||
}
|
||||
|
||||
preFlightCheck();
|
||||
|
||||
|
||||
/////////////////////
|
||||
|
||||
vm.config = {
|
||||
@@ -147,20 +170,21 @@
|
||||
maxDate: "today",
|
||||
conjunction: " to "
|
||||
};
|
||||
|
||||
vm.dateRangeChange = function(selectedDates, dateStr, instance) {
|
||||
|
||||
if(selectedDates.length > 0){
|
||||
vm.startDate = selectedDates[0].toIsoDateString();
|
||||
vm.endDate = selectedDates[selectedDates.length-1].toIsoDateString(); // Take the last date as end
|
||||
|
||||
if(vm.startDate === vm.endDate){
|
||||
vm.period = [vm.startDate];
|
||||
}else{
|
||||
vm.period = [vm.startDate, vm.endDate];
|
||||
}
|
||||
|
||||
preFlightCheck();
|
||||
vm.dateRangeChange = function (selectedDates, dateStr, instance) {
|
||||
|
||||
if (selectedDates.length > 0) {
|
||||
|
||||
// Update view by re-requesting route with updated querystring.
|
||||
// By doing this we make sure the URL matches the selected time period, aiding sharing the link.
|
||||
// Also resolves a minor layout issue where the " to " conjunction between the selected dates
|
||||
// is collapsed to a comma.
|
||||
const startDate = selectedDates[0].toIsoDateString();
|
||||
const endDate = selectedDates[selectedDates.length - 1].toIsoDateString(); // Take the last date as end
|
||||
$location.path("/settings/logViewer/overview").search({
|
||||
startDate: startDate,
|
||||
endDate: endDate
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<umb-editor-view footer="false">
|
||||
|
||||
<umb-editor-header
|
||||
name="'Log Overview for Today'"
|
||||
name="vm.dateRangeLabel"
|
||||
name-locked="true"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
@@ -74,17 +74,16 @@
|
||||
<umb-box>
|
||||
<umb-box-header title="Time Period"></umb-box-header>
|
||||
|
||||
<umb-flatpickr
|
||||
class="datepicker"
|
||||
ng-model="vm.period"
|
||||
options="vm.config"
|
||||
on-close="vm.dateRangeChange(selectedDates, dateStr, instance)">
|
||||
<umb-flatpickr class="datepicker"
|
||||
ng-model="vm.period"
|
||||
options="vm.config"
|
||||
on-close="vm.dateRangeChange(selectedDates, dateStr, instance)">
|
||||
</umb-flatpickr>
|
||||
</umb-box>
|
||||
|
||||
<div ng-show=" vm.canLoadLogs">
|
||||
<!-- No of Errors -->
|
||||
<umb-box ng-click="vm.searchLogQuery('Has(@Exception)')" style="cursor:pointer;">
|
||||
<umb-box ng-click="vm.searchErrors()" style="cursor:pointer;">
|
||||
<umb-box-header title="Number of Errors"></umb-box-header>
|
||||
<umb-box-content class="block-form" style="font-size: 40px; font-weight:900; text-align:center; color:#fe6561;">
|
||||
{{ vm.numberOfErrors }}
|
||||
|
||||
@@ -11,26 +11,28 @@
|
||||
vm.showBackButton = true;
|
||||
vm.page = {};
|
||||
|
||||
// this array is also used to map the logTypeColor param onto the log items
|
||||
// in setLogTypeColors()
|
||||
vm.logLevels = [
|
||||
{
|
||||
name: 'Verbose',
|
||||
logTypeColor: 'gray'
|
||||
logTypeColor: ''
|
||||
},
|
||||
{
|
||||
name: 'Debug',
|
||||
logTypeColor: 'secondary'
|
||||
logTypeColor: 'gray'
|
||||
},
|
||||
{
|
||||
name: 'Information',
|
||||
logTypeColor: 'primary'
|
||||
logTypeColor: 'success'
|
||||
},
|
||||
{
|
||||
name: 'Warning',
|
||||
logTypeColor: 'warning'
|
||||
logTypeColor: 'primary'
|
||||
},
|
||||
{
|
||||
name: 'Error',
|
||||
logTypeColor: 'danger'
|
||||
logTypeColor: 'warning'
|
||||
},
|
||||
{
|
||||
name: 'Fatal',
|
||||
@@ -118,7 +120,7 @@
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
},
|
||||
{
|
||||
@@ -173,25 +175,8 @@
|
||||
}
|
||||
|
||||
function setLogTypeColor(logItems) {
|
||||
angular.forEach(logItems, function (log) {
|
||||
switch (log.Level) {
|
||||
case "Information":
|
||||
log.logTypeColor = "primary";
|
||||
break;
|
||||
case "Debug":
|
||||
log.logTypeColor = "secondary";
|
||||
break;
|
||||
case "Warning":
|
||||
log.logTypeColor = "warning";
|
||||
break;
|
||||
case "Fatal":
|
||||
case "Error":
|
||||
log.logTypeColor = "danger";
|
||||
break;
|
||||
default:
|
||||
log.logTypeColor = "gray";
|
||||
}
|
||||
});
|
||||
logItems.forEach(logItem =>
|
||||
logItem.logTypeColor = vm.logLevels.find(x => x.name === logItem.Level).logTypeColor);
|
||||
}
|
||||
|
||||
function getFilterName(array) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//TODO: What is this file? Is it used?? I don't think so
|
||||
var uSkyGridConfig = [
|
||||
{
|
||||
|
||||
|
||||
+14
@@ -15,6 +15,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
$scope.canAdd = function () {
|
||||
return !$scope.model.docTypes || !$scope.model.value || $scope.model.value.length < $scope.model.docTypes.length;
|
||||
}
|
||||
|
||||
$scope.remove = function (index) {
|
||||
$scope.model.value.splice(index, 1);
|
||||
}
|
||||
@@ -57,10 +61,20 @@
|
||||
|
||||
ncResources.getContentTypes().then(function (docTypes) {
|
||||
$scope.model.docTypes = docTypes;
|
||||
|
||||
// Count doctype name occurrences
|
||||
var docTypeNameOccurrences = _.countBy(docTypes, 'name');
|
||||
|
||||
// Populate document type tab dictionary
|
||||
// And append alias to name if multiple doctypes have the same name
|
||||
docTypes.forEach(function (value) {
|
||||
$scope.docTypeTabs[value.alias] = value.tabs;
|
||||
|
||||
value.displayName = value.name;
|
||||
|
||||
if (docTypeNameOccurrences[value.name] > 1) {
|
||||
value.displayName += " (" + value.alias + ")";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+5
-5
@@ -23,7 +23,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<select id="{{model.alias}}_doctype_select"
|
||||
ng-options="dt.alias as dt.name for dt in selectableDocTypesFor(config) | orderBy: 'name'"
|
||||
ng-options="dt.alias as dt.displayName for dt in selectableDocTypesFor(config) | orderBy: 'name'"
|
||||
ng-model="config.ncAlias" required></select>
|
||||
</td>
|
||||
<td>
|
||||
@@ -35,17 +35,17 @@
|
||||
<input type="text" ng-model="config.nameTemplate" />
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-danger" ng-click="remove($index)">
|
||||
<button type="button" class="btn btn-danger" ng-click="remove($index)">
|
||||
<localize key="general_delete">Delete</localize>
|
||||
</a>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<a class="btn" ng-click="add()">
|
||||
<button type="button" class="btn" ng-click="add()" ng-disabled="!canAdd()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
<i class="icon icon-help-alt medium umb-nested-content__help-icon" ng-click="showHelpText = !showHelpText"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -345,9 +345,9 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>8120</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>8200</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:8120</IISUrl>
|
||||
<IISUrl>http://localhost:8200</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
<add key="serilog:write-to:File.restrictedToMinimumLevel" value="Debug" />
|
||||
<add key="serilog:write-to:File.retainedFileCountLimit" value="32" />--> <!-- Number of log files to keep (or remove value to keep all files) -->
|
||||
<!--<add key="serilog:write-to:File.rollingInterval" value="Day" />--> <!-- Create a new log file every Minute/Hour/Day/Month/Year/infinite -->
|
||||
<!--<add key="serilog:write-to:File.outputTemplate" value="{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}" /> -->
|
||||
|
||||
<!-- Filters all above sink's to use this expression -->
|
||||
<!-- Common use case is to include SourceType starting with your own namespace -->
|
||||
<!--
|
||||
<add key="serilog:using:FilterExpressions" value="Serilog.Filters.Expressions" />
|
||||
<add key="serilog:filter:ByIncluding.expression" value="StartsWith(SourceContext, 'Umbraco.Core')" />
|
||||
<add key="serilog:filter:ByIncludingOnly.expression" value="StartsWith(SourceContext, 'Umbraco.Core')" />
|
||||
-->
|
||||
|
||||
</appSettings>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Cache
|
||||
@@ -8,11 +9,13 @@ namespace Umbraco.Web.Cache
|
||||
public sealed class TemplateCacheRefresher : CacheRefresherBase<TemplateCacheRefresher>
|
||||
{
|
||||
private readonly IdkMap _idkMap;
|
||||
private readonly IContentTypeCommonRepository _contentTypeCommonRepository;
|
||||
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap)
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository)
|
||||
: base(appCaches)
|
||||
{
|
||||
_idkMap = idkMap;
|
||||
_contentTypeCommonRepository = contentTypeCommonRepository;
|
||||
}
|
||||
|
||||
#region Define
|
||||
@@ -45,6 +48,7 @@ namespace Umbraco.Web.Cache
|
||||
// it has an associated template.
|
||||
ClearAllIsolatedCacheByEntityType<IContent>();
|
||||
ClearAllIsolatedCacheByEntityType<IContentType>();
|
||||
_contentTypeCommonRepository.ClearCache();
|
||||
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Umbraco.Web.Editors
|
||||
//id is passed in eventually we'll probably want to support GUID + Udi too
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPagedChildren", "id", typeof(int), typeof(string)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPath", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetUrlAndAnchors", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[]))));
|
||||
}
|
||||
@@ -288,7 +289,16 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public UrlAndAnchors GetUrlAndAnchors([FromUri]int id, [FromUri]string culture = "*")
|
||||
public UrlAndAnchors GetUrlAndAnchors(Udi id, string culture = "*")
|
||||
{
|
||||
var intId = Services.EntityService.GetId(id);
|
||||
if (!intId.Success)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
return GetUrlAndAnchors(intId.Result, culture);
|
||||
}
|
||||
[HttpGet]
|
||||
public UrlAndAnchors GetUrlAndAnchors(int id, string culture = "*")
|
||||
{
|
||||
var url = UmbracoContext.UrlProvider.GetUrl(id);
|
||||
var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id, culture);
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Umbraco.Web.Editors
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if the index has been rebuilt
|
||||
@@ -250,7 +250,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
private void Indexer_IndexOperationComplete(object sender, EventArgs e)
|
||||
{
|
||||
var indexer = (LuceneIndex)sender;
|
||||
var indexer = (IIndex)sender;
|
||||
|
||||
_logger.Debug<ExamineManagementController>("Logging operation completed for index {IndexName}", indexer.Name);
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
_logger
|
||||
.Info<ExamineManagementController
|
||||
>($"Rebuilding index '{indexer.Name}' done, {indexer.CommitCount} items committed (can differ from the number of items in the index)");
|
||||
>($"Rebuilding index '{indexer.Name}' done.");
|
||||
|
||||
var cacheKey = "temp_indexing_op_" + indexer.Name;
|
||||
_runtimeCache.Clear(cacheKey);
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace Umbraco.Web.Editors
|
||||
[PluginController("UmbracoApi")]
|
||||
public class LogViewerController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private ILogViewer _logViewer;
|
||||
private readonly ILogViewer _logViewer;
|
||||
|
||||
public LogViewerController(ILogViewer logViewer)
|
||||
{
|
||||
_logViewer = logViewer;
|
||||
_logViewer = logViewer ?? throw new ArgumentNullException(nameof(logViewer));
|
||||
}
|
||||
|
||||
private bool CanViewLogs(LogTimePeriod logTimePeriod)
|
||||
@@ -91,8 +91,6 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var direction = orderDirection == "Descending" ? Direction.Descending : Direction.Ascending;
|
||||
|
||||
|
||||
|
||||
return _logViewer.GetLogs(logTimePeriod, filterExpression: filterExpression, pageNumber: pageNumber, orderDirection: direction, logLevels: logLevels);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +223,6 @@ namespace Umbraco.Web
|
||||
_method = method;
|
||||
_controllerName = controllerName;
|
||||
_encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
@@ -578,7 +577,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
udiType = Constants.UdiEntityType.DocumentType;
|
||||
break;
|
||||
default:
|
||||
throw new PanicException($"Source is of type {source.GetType()} which isn't supported here");
|
||||
throw new Exception("panic");
|
||||
}
|
||||
|
||||
return Udi.Create(udiType, source.Key);
|
||||
|
||||
+1
-11
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.2.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
@@ -50,15 +50,5 @@ namespace Umbraco.Web.Properties {
|
||||
return ((string)(this["test"]));
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("https://our.umbraco.com/umbraco/webservices/api/repository.asmx")]
|
||||
public string umbraco_org_umbraco_our_Repository {
|
||||
get {
|
||||
return ((string)(this["umbraco_org_umbraco_our_Repository"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,5 @@
|
||||
<Setting Name="test" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">Something</Value>
|
||||
</Setting>
|
||||
<Setting Name="umbraco_org_umbraco_our_Repository" Type="(Web Service URL)" Scope="Application">
|
||||
<Value Profile="(Default)">https://our.umbraco.com/umbraco/webservices/api/repository.asmx</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -8,7 +8,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// A property editor to allow multiple checkbox selection of pre-defined items.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.CheckBoxList, "Checkbox list", "checkboxlist", Icon="icon-bulleted-list", Group="lists")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.CheckBoxList,
|
||||
"Checkbox list",
|
||||
"checkboxlist",
|
||||
Icon = "icon-bulleted-list",
|
||||
Group = Constants.PropertyEditors.Groups.Lists)]
|
||||
public class CheckBoxListPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
@@ -4,7 +4,12 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ColorPicker, "Color Picker", "colorpicker", Icon="icon-colorpicker", Group="Pickers")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ColorPicker,
|
||||
"Color Picker",
|
||||
"colorpicker",
|
||||
Icon = "icon-colorpicker",
|
||||
Group = Constants.PropertyEditors.Groups.Pickers)]
|
||||
public class ColorPickerPropertyEditor : DataEditor
|
||||
{
|
||||
public ColorPickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -8,7 +8,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Content property editor that stores UDI
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ContentPicker, EditorType.PropertyValue | EditorType.MacroParameter, "Content Picker", "contentpicker", ValueType = ValueTypes.String, Group = "Pickers")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ContentPicker,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Content Picker",
|
||||
"contentpicker",
|
||||
ValueType = ValueTypes.String,
|
||||
Group = Constants.PropertyEditors.Groups.Pickers)]
|
||||
public class ContentPickerPropertyEditor : DataEditor
|
||||
{
|
||||
public ContentPickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -7,7 +7,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a date and time property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.DateTime, "Date/Time", "datepicker", ValueType = ValueTypes.DateTime, Icon="icon-time")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.DateTime,
|
||||
"Date/Time",
|
||||
"datepicker",
|
||||
ValueType = ValueTypes.DateTime,
|
||||
Icon = "icon-time")]
|
||||
public class DateTimePropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -8,7 +8,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a decimal property and parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Decimal, EditorType.PropertyValue | EditorType.MacroParameter, "Decimal", "decimal", ValueType = ValueTypes.Decimal)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Decimal,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Decimal",
|
||||
"decimal",
|
||||
ValueType = ValueTypes.Decimal)]
|
||||
public class DecimalPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,12 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.DropDownListFlexible, "Dropdown", "dropdownFlexible", Group = "lists", Icon = "icon-indent")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.DropDownListFlexible,
|
||||
"Dropdown",
|
||||
"dropdownFlexible",
|
||||
Group = Constants.PropertyEditors.Groups.Lists,
|
||||
Icon = "icon-indent")]
|
||||
public class DropDownFlexiblePropertyEditor : DataEditor
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
@@ -5,7 +5,12 @@ using Umbraco.Core.PropertyEditors.Validators;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.EmailAddress, EditorType.PropertyValue | EditorType.MacroParameter, "Email address", "email", Icon="icon-message")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.EmailAddress,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Email address",
|
||||
"email",
|
||||
Icon = "icon-message")]
|
||||
public class EmailAddressPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -12,7 +12,12 @@ using Umbraco.Web.Media;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.UploadField, "File upload", "fileupload", Icon = "icon-download-alt", Group = "media")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.UploadField,
|
||||
"File upload",
|
||||
"fileupload",
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-download-alt")]
|
||||
public class FileUploadPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
|
||||
@@ -12,7 +12,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a grid property and parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Grid, "Grid layout", "grid", HideLabel = true, ValueType = ValueTypes.Json, Group="rich content", Icon="icon-layout")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Grid,
|
||||
"Grid layout",
|
||||
"grid",
|
||||
HideLabel = true,
|
||||
ValueType = ValueTypes.Json,
|
||||
Icon = "icon-layout",
|
||||
Group = Constants.PropertyEditors.Groups.RichContent)]
|
||||
public class GridPropertyEditor : DataEditor
|
||||
{
|
||||
public GridPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -19,7 +19,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents an image cropper property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ImageCropper, "Image Cropper", "imagecropper", ValueType = ValueTypes.Json, HideLabel = false, Group="media", Icon="icon-crop")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ImageCropper,
|
||||
"Image Cropper",
|
||||
"imagecropper",
|
||||
ValueType = ValueTypes.Json,
|
||||
HideLabel = false,
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-crop")]
|
||||
public class ImageCropperPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
|
||||
@@ -8,7 +8,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents an integer property and parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Integer, EditorType.PropertyValue | EditorType.MacroParameter, "Numeric", "integer", ValueType = ValueTypes.Integer)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Integer,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Numeric",
|
||||
"integer",
|
||||
ValueType = ValueTypes.Integer)]
|
||||
public class IntegerPropertyEditor : DataEditor
|
||||
{
|
||||
public IntegerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -8,7 +8,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a list-view editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ListView, "List view", "listview", HideLabel = true, Group = "lists", Icon = Constants.Icons.ListView)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ListView,
|
||||
"List view",
|
||||
"listview",
|
||||
HideLabel = true,
|
||||
Group = Constants.PropertyEditors.Groups.Lists,
|
||||
Icon = Constants.Icons.ListView)]
|
||||
public class ListViewPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,14 @@ using Umbraco.Core.PropertyEditors;
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
// TODO: MacroContainerPropertyEditor is deprecated, but what's the alternative?
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MacroContainer, "(Obsolete) Macro Picker", "macrocontainer", ValueType = ValueTypes.Text, Group = "rich content", Icon = Constants.Icons.Macro, IsDeprecated = true)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MacroContainer,
|
||||
"(Obsolete) Macro Picker",
|
||||
"macrocontainer",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.RichContent,
|
||||
Icon = Constants.Icons.Macro,
|
||||
IsDeprecated = true)]
|
||||
public class MacroContainerPropertyEditor : DataEditor
|
||||
{
|
||||
public MacroContainerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -7,7 +7,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a markdown editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MarkdownEditor, "Markdown editor", "markdowneditor", ValueType = ValueTypes.Text, Icon="icon-code", Group="rich content")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MarkdownEditor,
|
||||
"Markdown editor",
|
||||
"markdowneditor",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.RichContent,
|
||||
Icon = "icon-code")]
|
||||
public class MarkdownPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -7,8 +7,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a media picker property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MediaPicker, EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Media Picker", "mediapicker", ValueType = ValueTypes.Text, Group = "media", Icon = Constants.Icons.MediaImage)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MediaPicker,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Media Picker",
|
||||
"mediapicker",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = Constants.Icons.MediaImage)]
|
||||
public class MediaPickerPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,13 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MemberGroupPicker, "Member Group Picker", "membergrouppicker", ValueType = ValueTypes.Text, Group = "People", Icon = Constants.Icons.MemberGroup)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MemberGroupPicker,
|
||||
"Member Group Picker",
|
||||
"membergrouppicker",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.People,
|
||||
Icon = Constants.Icons.MemberGroup)]
|
||||
public class MemberGroupPickerPropertyEditor : DataEditor
|
||||
{
|
||||
public MemberGroupPickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -4,7 +4,13 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MemberPicker, "Member Picker", "memberpicker", ValueType = ValueTypes.String, Group = "People", Icon = Constants.Icons.Member)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MemberPicker,
|
||||
"Member Picker",
|
||||
"memberpicker",
|
||||
ValueType = ValueTypes.String,
|
||||
Group = Constants.PropertyEditors.Groups.People,
|
||||
Icon = Constants.Icons.Member)]
|
||||
public class MemberPickerPropertyEditor : DataEditor
|
||||
{
|
||||
public MemberPickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -4,7 +4,13 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MultiNodeTreePicker, "Multinode Treepicker", "contentpicker", ValueType = ValueTypes.Text, Group = "pickers", Icon = "icon-page-add")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MultiNodeTreePicker,
|
||||
"Multinode Treepicker",
|
||||
"contentpicker",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.Pickers,
|
||||
Icon = "icon-page-add")]
|
||||
public class MultiNodeTreePickerPropertyEditor : DataEditor
|
||||
{
|
||||
public MultiNodeTreePickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -7,7 +7,14 @@ using Umbraco.Web.PublishedCache;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MultiUrlPicker, EditorType.PropertyValue, "Multi Url Picker", "multiurlpicker", ValueType = ValueTypes.Json, Group = "pickers", Icon = "icon-link")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MultiUrlPicker,
|
||||
EditorType.PropertyValue,
|
||||
"Multi Url Picker",
|
||||
"multiurlpicker",
|
||||
ValueType = ValueTypes.Json,
|
||||
Group = Constants.PropertyEditors.Groups.Pickers,
|
||||
Icon = "icon-link")]
|
||||
public class MultiUrlPickerPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
@@ -14,7 +13,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a multiple text string property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MultipleTextstring, "Repeatable textstrings", "multipletextbox", ValueType = ValueTypes.Text, Icon="icon-ordered-list", Group="lists")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MultipleTextstring,
|
||||
"Repeatable textstrings",
|
||||
"multipletextbox",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.Lists,
|
||||
Icon = "icon-ordered-list")]
|
||||
public class MultipleTextStringPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
@@ -57,7 +62,7 @@ namespace Umbraco.Web.PropertyEditors
|
||||
}
|
||||
|
||||
if (!(editorValue.DataTypeConfiguration is MultipleTextStringConfiguration config))
|
||||
throw new PanicException($"editorValue.DataTypeConfiguration is {editorValue.DataTypeConfiguration.GetType()} but must be {typeof(MultipleTextStringConfiguration)}");
|
||||
throw new Exception("panic");
|
||||
var max = config.Maximum;
|
||||
|
||||
//The legacy property editor saved this data as new line delimited! strange but we have to maintain that.
|
||||
|
||||
@@ -18,7 +18,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a nested content property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.NestedContent, "Nested Content", "nestedcontent", ValueType = "JSON", Group = "lists", Icon = "icon-thumbnail-list")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.NestedContent,
|
||||
"Nested Content",
|
||||
"nestedcontent",
|
||||
ValueType = ValueTypes.Json,
|
||||
Group = Constants.PropertyEditors.Groups.Lists,
|
||||
Icon = "icon-thumbnail-list")]
|
||||
public class NestedContentPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly Lazy<PropertyEditorCollection> _propertyEditors;
|
||||
|
||||
@@ -6,7 +6,11 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors
|
||||
/// <summary>
|
||||
/// Represents a content type parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor("contentType", EditorType.MacroParameter, "Content Type Picker", "entitypicker")]
|
||||
[DataEditor(
|
||||
"contentType",
|
||||
EditorType.MacroParameter,
|
||||
"Content Type Picker",
|
||||
"entitypicker")]
|
||||
public class ContentTypeParameterEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
+5
-1
@@ -7,7 +7,11 @@ namespace Umbraco.Web.PropertyEditors.ParameterEditors
|
||||
/// <summary>
|
||||
/// Represents a parameter editor of some sort.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MultiNodeTreePicker, EditorType.MacroParameter, "Multiple Content Picker", "contentpicker")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MultiNodeTreePicker,
|
||||
EditorType.MacroParameter,
|
||||
"Multiple Content Picker",
|
||||
"contentpicker")]
|
||||
public class MultipleContentPickerParameterEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
+5
-1
@@ -3,7 +3,11 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors.ParameterEditors
|
||||
{
|
||||
[DataEditor("contentTypeMultiple", EditorType.MacroParameter, "Multiple Content Type Picker", "entitypicker")]
|
||||
[DataEditor(
|
||||
"contentTypeMultiple",
|
||||
EditorType.MacroParameter,
|
||||
"Multiple Content Type Picker",
|
||||
"entitypicker")]
|
||||
public class MultipleContentTypeParameterEditor : DataEditor
|
||||
{
|
||||
public MultipleContentTypeParameterEditor(ILogger logger)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user