diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs
index e7dcf7cd8c..f358c0f881 100644
--- a/src/Umbraco.Core/Cache/CacheKeys.cs
+++ b/src/Umbraco.Core/Cache/CacheKeys.cs
@@ -1,41 +1,15 @@
-using Umbraco.Core.CodeAnnotations;
-
-namespace Umbraco.Core.Cache
+namespace Umbraco.Core.Cache
{
///
/// Constants storing cache keys used in caching
///
public static class CacheKeys
{
- public const string ApplicationTreeCacheKey = "ApplicationTreeCache";
- public const string ApplicationsCacheKey = "ApplicationCache";
+ public const string ApplicationTreeCacheKey = "ApplicationTreeCache"; // used by ApplicationTreeService
+ public const string ApplicationsCacheKey = "ApplicationCache"; // used by SectionService
- [UmbracoWillObsolete("This cache key is only used for the legacy 'library' caching, remove in v8")]
- public const string MediaCacheKey = "UL_GetMedia";
-
- [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
- public const string MacroCacheKey = "UmbracoMacroCache";
+ public const string TemplateFrontEndCacheKey = "template"; // fixme usage?
- public const string MacroContentCacheKey = "macroContent_"; // for macro contents
-
- [UmbracoWillObsolete("This cache key is only used for legacy 'library' member caching, remove in v8")]
- public const string MemberLibraryCacheKey = "UL_GetMember";
-
- [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
- public const string MemberBusinessLogicCacheKey = "MemberCacheItem_";
-
- [UmbracoWillObsolete("This cache key is only used for legacy template business logic caching, remove in v8")]
- public const string TemplateFrontEndCacheKey = "template";
-
- public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
-
- [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
- public const string ContentTypeCacheKey = "UmbracoContentType";
-
- [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
- public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
-
- public const string IdToKeyCacheKey = "UI2K__";
- public const string KeyToIdCacheKey = "UK2I__";
+ public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
}
}
diff --git a/src/Umbraco.Core/CodeAnnotations/UmbracoExperimentalFeatureAttribute.cs b/src/Umbraco.Core/CodeAnnotations/UmbracoExperimentalFeatureAttribute.cs
deleted file mode 100644
index 74be6ab397..0000000000
--- a/src/Umbraco.Core/CodeAnnotations/UmbracoExperimentalFeatureAttribute.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using System;
-
-namespace Umbraco.Core.CodeAnnotations
-{
- ///
- /// Marks the program elements that Umbraco is experimenting with and could become public.
- ///
- ///
- /// Indicates that Umbraco is experimenting with code that potentially could become
- /// public, but we're not sure, and the code is not stable and can be refactored at any time.
- /// The issue tracker should contain more details, discussion, and planning.
- ///
- [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
- internal sealed class UmbracoExperimentalFeatureAttribute : Attribute
- {
- ///
- /// Initializes a new instance of the class with a description.
- ///
- /// The text string that describes what is intended.
- public UmbracoExperimentalFeatureAttribute(string description)
- {
-
- }
-
- ///
- /// Initializes a new instance of the class with a tracker url and a description.
- ///
- /// The url of a tracker issue containing more details, discussion, and planning.
- /// The text string that describes what is intended.
- public UmbracoExperimentalFeatureAttribute(string trackerUrl, string description)
- {
-
- }
- }
-}
diff --git a/src/Umbraco.Core/CodeAnnotations/UmbracoProposedPublicAttribute.cs b/src/Umbraco.Core/CodeAnnotations/UmbracoProposedPublicAttribute.cs
deleted file mode 100644
index 1a65dc036e..0000000000
--- a/src/Umbraco.Core/CodeAnnotations/UmbracoProposedPublicAttribute.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-
-namespace Umbraco.Core.CodeAnnotations
-{
- ///
- /// Marks the program elements that Umbraco is considering making public.
- ///
- ///
- /// Indicates that Umbraco considers making the (currently internal) program element public
- /// at some point in the future, but the decision is not fully made yet and is still pending reviews.
- /// Note that it is not a commitment to make the program element public. It may not ever become public.
- /// The issue tracker should contain more details, discussion, and planning.
- ///
- [AttributeUsage(AttributeTargets.All, AllowMultiple=false, Inherited=false)]
- internal sealed class UmbracoProposedPublicAttribute : Attribute
- {
- ///
- /// Initializes a new instance of the class with a description.
- ///
- /// The text string that describes what is intended.
- public UmbracoProposedPublicAttribute(string description)
- {
-
- }
-
- ///
- /// Initializes a new instance of the class with a tracker url and a description.
- ///
- /// The url of a tracker issue containing more details, discussion, and planning.
- /// The text string that describes what is intended.
- public UmbracoProposedPublicAttribute(string trackerUrl, string description)
- {
-
- }
- }
-}
diff --git a/src/Umbraco.Core/CodeAnnotations/UmbracoWillObsoleteAttribute.cs b/src/Umbraco.Core/CodeAnnotations/UmbracoWillObsoleteAttribute.cs
deleted file mode 100644
index 7babe71469..0000000000
--- a/src/Umbraco.Core/CodeAnnotations/UmbracoWillObsoleteAttribute.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Umbraco.Core.CodeAnnotations
-{
- ///
- /// Marks the program elements that Umbraco will obsolete.
- ///
- ///
- /// Indicates that Umbraco will obsolete the program element at some point in the future, but we do not want to
- /// explicitely mark it [Obsolete] yet to avoid warning messages showing when developers compile Umbraco.
- ///
- [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
- internal sealed class UmbracoWillObsoleteAttribute : Attribute
- {
- ///
- /// Initializes a new instance of the class with a description.
- ///
- /// The text string that describes what is intended.
- public UmbracoWillObsoleteAttribute(string description)
- {
-
- }
-
- ///
- /// Initializes a new instance of the class with a tracker url and a description.
- ///
- /// The url of a tracker issue containing more details, discussion, and planning.
- /// The text string that describes what is intended.
- public UmbracoWillObsoleteAttribute(string trackerUrl, string description)
- {
-
- }
- }
-}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
index 49f679d1f4..e019f9a5d1 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
@@ -194,17 +194,19 @@ namespace Umbraco.Core.Models.PublishedContent
}
///
- /// Determines whether a source value is an actual value, or not a value.
+ /// Determines whether a value is an actual value, or not a value.
///
/// Used by property.HasValue and, for instance, in fallback scenarios.
- public bool IsValue(object value)
+ public bool? IsValue(object value, PropertyValueLevel level)
{
- // if we have a converter, use the converter,
- // otherwise use the old magic null & string comparisons
+ if (!_initialized) Initialize();
- return _converter == null
- ? value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false)
- : _converter.IsValue(value);
+ // if we have a converter, use the converter
+ if (_converter != null)
+ return _converter.IsValue(value, level);
+
+ // otherwise use the old magic null & string comparisons
+ return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
}
///
diff --git a/src/Umbraco.Core/Persistence/SqlTemplates.cs b/src/Umbraco.Core/Persistence/SqlTemplates.cs
index 06c985232e..021f3a4670 100644
--- a/src/Umbraco.Core/Persistence/SqlTemplates.cs
+++ b/src/Umbraco.Core/Persistence/SqlTemplates.cs
@@ -1,12 +1,12 @@
using System;
-using System.Collections.Generic;
+using System.Collections.Concurrent;
using NPoco;
namespace Umbraco.Core.Persistence
{
public class SqlTemplates
{
- private readonly Dictionary _templates = new Dictionary();
+ private readonly ConcurrentDictionary _templates = new ConcurrentDictionary();
private readonly ISqlContext _sqlContext;
public SqlTemplates(ISqlContext sqlContext)
@@ -22,9 +22,13 @@ namespace Umbraco.Core.Persistence
public SqlTemplate Get(string key, Func, Sql> sqlBuilder)
{
- if (_templates.TryGetValue(key, out var template)) return template;
- var sql = sqlBuilder(new Sql(_sqlContext));
- return _templates[key] = new SqlTemplate(_sqlContext, sql.SQL, sql.Arguments);
+ SqlTemplate CreateTemplate(string _)
+ {
+ var sql = sqlBuilder(new Sql(_sqlContext));
+ return new SqlTemplate(_sqlContext, sql.SQL, sql.Arguments);
+ }
+
+ return _templates.GetOrAdd(key, CreateTemplate);
}
}
}
diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs
index 53851f8653..258febe813 100644
--- a/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs
+++ b/src/Umbraco.Core/PropertyEditors/IPropertyValueConverter.cs
@@ -18,9 +18,16 @@ namespace Umbraco.Core.PropertyEditors
bool IsConverter(PublishedPropertyType propertyType);
///
- /// Determines whether a source value is an actual value, or not a value.
+ /// Determines whether a value is an actual value, or not a value.
///
- bool IsValue(object value);
+ ///
+ /// Called for Source, Inter and Object levels, until one does not return null.
+ /// Can return true (is a value), false (is not a value), or null to indicate that it
+ /// cannot be determined at the specified level. For instance, if source is a string that
+ /// could contain JSON, the decision could be made on the intermediate value. Or, if it is
+ /// a picker, it could be made on the object value (the actual picked object).
+ ///
+ bool? IsValue(object value, PropertyValueLevel level);
///
/// Gets the type of values returned by the converter.
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs
index 9a82446edd..4c20016318 100644
--- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs
+++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterBase.cs
@@ -11,8 +11,25 @@ namespace Umbraco.Core.PropertyEditors
public virtual bool IsConverter(PublishedPropertyType propertyType)
=> false;
- public bool IsValue(object value)
- => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
+ public virtual bool? IsValue(object value, PropertyValueLevel level)
+ {
+ switch (level)
+ {
+ case PropertyValueLevel.Source:
+ return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
+ default:
+ throw new NotSupportedException($"Invalid level: {level}.");
+ }
+ }
+
+ public virtual bool HasValue(IPublishedProperty property, string culture, string segment)
+ {
+ // the default implementation uses the old magic null & string comparisons,
+ // other implementations may be more clever, and/or test the final converted object values
+ // fixme - cannot access the intermediate value here?
+ var value = property.GetSourceValue(culture, segment);
+ return value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
+ }
public virtual Type GetPropertyValueType(PublishedPropertyType propertyType)
=> typeof (object);
diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs
new file mode 100644
index 0000000000..956ce03b30
--- /dev/null
+++ b/src/Umbraco.Core/PropertyEditors/PropertyValueLevel.cs
@@ -0,0 +1,23 @@
+namespace Umbraco.Core.PropertyEditors
+{
+ ///
+ /// Indicates the level of a value.
+ ///
+ public enum PropertyValueLevel
+ {
+ ///
+ /// The source value, i.e. what is in the database.
+ ///
+ Source,
+
+ ///
+ /// The conversion intermediate value.
+ ///
+ Inter,
+
+ ///
+ /// The converted value.
+ ///
+ Object
+ }
+}
diff --git a/src/Umbraco.Core/StringAliasCaseType.cs b/src/Umbraco.Core/StringAliasCaseType.cs
deleted file mode 100644
index 328382e1c7..0000000000
--- a/src/Umbraco.Core/StringAliasCaseType.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Umbraco.Core.CodeAnnotations;
-
-namespace Umbraco.Core
-{
- [UmbracoWillObsolete("This enumeration should be removed. Use Umbraco.Core.Strings.CleanStringType instead.")]
- public enum StringAliasCaseType
- {
- PascalCase,
- CamelCase,
- Unchanged
- }
-}
diff --git a/src/Umbraco.Core/Strings/StringAliasCaseTypeExtensions.cs b/src/Umbraco.Core/Strings/StringAliasCaseTypeExtensions.cs
deleted file mode 100644
index ce0b0f0c7a..0000000000
--- a/src/Umbraco.Core/Strings/StringAliasCaseTypeExtensions.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-namespace Umbraco.Core.Strings
-{
- ///
- /// Provides extension methods to StringAliasCaseType to facilitate migration to CleanStringType.
- ///
- public static class StringAliasCaseTypeExtensions
- {
- ///
- /// Gets the CleanStringType value corresponding to the StringAliasCaseType value.
- ///
- /// The value.
- /// A CleanStringType value corresponding to the StringAliasCaseType value.
- public static CleanStringType ToCleanStringType(this StringAliasCaseType aliasCaseType)
- {
- switch (aliasCaseType)
- {
- case StringAliasCaseType.PascalCase:
- return CleanStringType.PascalCase;
- case StringAliasCaseType.CamelCase:
- return CleanStringType.CamelCase;
- //case StringAliasCaseType.Unchanged:
- default:
- return CleanStringType.Unchanged;
- }
- }
- }
-}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 359f69d7f0..9ad9b9e2c5 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -128,11 +128,8 @@
-
-
-
@@ -404,6 +401,7 @@
+
@@ -1437,7 +1435,6 @@
-
@@ -1449,7 +1446,6 @@
-
diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs
index 39cb37f160..edc4face17 100644
--- a/src/Umbraco.Tests/Published/ConvertersTests.cs
+++ b/src/Umbraco.Tests/Published/ConvertersTests.cs
@@ -43,12 +43,30 @@ namespace Umbraco.Tests.Published
var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false);
Assert.AreEqual(1234, element1.Value("prop1"));
+
+ // 'null' would be considered a 'missing' value by the default, magic logic
+ var e = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", null } }, false);
+ Assert.IsFalse(e.HasValue("prop1"));
+
+ // '0' would not - it's a valid integer - but the converter knows better
+ e = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "0" } }, false);
+ Assert.IsFalse(e.HasValue("prop1"));
}
private class SimpleConverter1 : IPropertyValueConverter
{
- public bool IsValue(object value)
- => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
+ public bool? IsValue(object value, PropertyValueLevel level)
+ {
+ switch (level)
+ {
+ case PropertyValueLevel.Source:
+ return null;
+ case PropertyValueLevel.Inter:
+ return value is int ivalue && ivalue != 0;
+ default:
+ throw new NotSupportedException($"Invalid level: {level}.");
+ }
+ }
public bool IsConverter(PublishedPropertyType propertyType)
=> propertyType.EditorAlias.InvariantEquals("Umbraco.Void");
@@ -120,7 +138,7 @@ namespace Umbraco.Tests.Published
_cacheLevel = cacheLevel;
}
- public bool IsValue(object value)
+ public bool? IsValue(object value, PropertyValueLevel level)
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
public bool IsConverter(PublishedPropertyType propertyType)
diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs
index 617429d205..33a595626e 100644
--- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs
+++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs
@@ -210,7 +210,7 @@ namespace Umbraco.Tests.Published
public int SourceConverts { get; private set; }
public int InterConverts { get; private set; }
- public bool IsValue(object value)
+ public bool? IsValue(object value, PropertyValueLevel level)
=> value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false);
public bool IsConverter(PublishedPropertyType propertyType)
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js
index d69777464c..cec9cf191e 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js
@@ -3,17 +3,17 @@
* @name umbraco.resources.contentTypeResource
* @description Loads in data for content types
**/
-function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
+function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter, localizationService, notificationsService) {
return {
getCount: function () {
return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "contentTypeApiBaseUrl",
- "GetCount")),
- 'Failed to retrieve count');
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "contentTypeApiBaseUrl",
+ "GetCount")),
+ 'Failed to retrieve count');
},
getAvailableCompositeContentTypes: function (contentTypeId, filterContentTypes, filterPropertyTypes) {
@@ -31,32 +31,32 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
};
return umbRequestHelper.resourcePromise(
- $http.post(
- umbRequestHelper.getApiUrl(
- "contentTypeApiBaseUrl",
- "GetAvailableCompositeContentTypes"),
- query),
- 'Failed to retrieve data for content type id ' + contentTypeId);
+ $http.post(
+ umbRequestHelper.getApiUrl(
+ "contentTypeApiBaseUrl",
+ "GetAvailableCompositeContentTypes"),
+ query),
+ 'Failed to retrieve data for content type id ' + contentTypeId);
},
- /**
- * @ngdoc method
- * @name umbraco.resources.contentTypeResource#getWhereCompositionIsUsedInContentTypes
- * @methodOf umbraco.resources.contentTypeResource
- *
- * @description
- * Returns a list of content types which use a specific composition with a given id
- *
- * ##usage
- *
- * @param {Int} contentTypeId id of the composition content type to retrieve the list of the content types where it has been used
- * @returns {Promise} resourcePromise object.
- *
- */
+ /**
+ * @ngdoc method
+ * @name umbraco.resources.contentTypeResource#getWhereCompositionIsUsedInContentTypes
+ * @methodOf umbraco.resources.contentTypeResource
+ *
+ * @description
+ * Returns a list of content types which use a specific composition with a given id
+ *
+ * ##usage
+ *