Merge branch 'temp8' into temp8-di2690

This commit is contained in:
Stephan
2018-07-20 11:31:35 +02:00
22 changed files with 143 additions and 267 deletions

View File

@@ -1,41 +1,15 @@
using Umbraco.Core.CodeAnnotations;
namespace Umbraco.Core.Cache
namespace Umbraco.Core.Cache
{
/// <summary>
/// Constants storing cache keys used in caching
/// </summary>
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
}
}

View File

@@ -1,35 +0,0 @@
using System;
namespace Umbraco.Core.CodeAnnotations
{
/// <summary>
/// Marks the program elements that Umbraco is experimenting with and could become public.
/// </summary>
/// <remarks>
/// <para>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.</para>
/// <para>The issue tracker should contain more details, discussion, and planning.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
internal sealed class UmbracoExperimentalFeatureAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoExperimentalFeatureAttribute"/> class with a description.
/// </summary>
/// <param name="description">The text string that describes what is intended.</param>
public UmbracoExperimentalFeatureAttribute(string description)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoExperimentalFeatureAttribute"/> class with a tracker url and a description.
/// </summary>
/// <param name="trackerUrl">The url of a tracker issue containing more details, discussion, and planning.</param>
/// <param name="description">The text string that describes what is intended.</param>
public UmbracoExperimentalFeatureAttribute(string trackerUrl, string description)
{
}
}
}

View File

@@ -1,36 +0,0 @@
using System;
namespace Umbraco.Core.CodeAnnotations
{
/// <summary>
/// Marks the program elements that Umbraco is considering making public.
/// </summary>
/// <remarks>
/// <para>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.</para>
/// <para>Note that it is not a commitment to make the program element public. It may not ever become public.</para>
/// <para>The issue tracker should contain more details, discussion, and planning.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.All, AllowMultiple=false, Inherited=false)]
internal sealed class UmbracoProposedPublicAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoProposedPublicAttribute"/> class with a description.
/// </summary>
/// <param name="description">The text string that describes what is intended.</param>
public UmbracoProposedPublicAttribute(string description)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoProposedPublicAttribute"/> class with a tracker url and a description.
/// </summary>
/// <param name="trackerUrl">The url of a tracker issue containing more details, discussion, and planning.</param>
/// <param name="description">The text string that describes what is intended.</param>
public UmbracoProposedPublicAttribute(string trackerUrl, string description)
{
}
}
}

View File

@@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Umbraco.Core.CodeAnnotations
{
/// <summary>
/// Marks the program elements that Umbraco will obsolete.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
internal sealed class UmbracoWillObsoleteAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoWillObsoleteAttribute"/> class with a description.
/// </summary>
/// <param name="description">The text string that describes what is intended.</param>
public UmbracoWillObsoleteAttribute(string description)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UmbracoWillObsoleteAttribute"/> class with a tracker url and a description.
/// </summary>
/// <param name="trackerUrl">The url of a tracker issue containing more details, discussion, and planning.</param>
/// <param name="description">The text string that describes what is intended.</param>
public UmbracoWillObsoleteAttribute(string trackerUrl, string description)
{
}
}
}

View File

@@ -194,17 +194,19 @@ namespace Umbraco.Core.Models.PublishedContent
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>Used by property.HasValue and, for instance, in fallback scenarios.</remarks>
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);
}
/// <summary>

View File

@@ -18,9 +18,16 @@ namespace Umbraco.Core.PropertyEditors
bool IsConverter(PublishedPropertyType propertyType);
/// <summary>
/// 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.
/// </summary>
bool IsValue(object value);
/// <remarks>
/// <para>Called for Source, Inter and Object levels, until one does not return null.</para>
/// <para>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).</para>
/// </remarks>
bool? IsValue(object value, PropertyValueLevel level);
/// <summary>
/// Gets the type of values returned by the converter.

View File

@@ -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);

View File

@@ -0,0 +1,23 @@
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Indicates the level of a value.
/// </summary>
public enum PropertyValueLevel
{
/// <summary>
/// The source value, i.e. what is in the database.
/// </summary>
Source,
/// <summary>
/// The conversion intermediate value.
/// </summary>
Inter,
/// <summary>
/// The converted value.
/// </summary>
Object
}
}

View File

@@ -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
}
}

View File

@@ -1,27 +0,0 @@
namespace Umbraco.Core.Strings
{
/// <summary>
/// Provides extension methods to StringAliasCaseType to facilitate migration to CleanStringType.
/// </summary>
public static class StringAliasCaseTypeExtensions
{
/// <summary>
/// Gets the CleanStringType value corresponding to the StringAliasCaseType value.
/// </summary>
/// <param name="aliasCaseType">The value.</param>
/// <returns>A CleanStringType value corresponding to the StringAliasCaseType value.</returns>
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;
}
}
}
}

View File

@@ -128,11 +128,8 @@
<Compile Include="Cache\TypedCacheRefresherBase.cs" />
<Compile Include="CodeAnnotations\ActionMetadataAttribute.cs" />
<Compile Include="CodeAnnotations\FriendlyNameAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoObjectTypeAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoUdiTypeAttribute.cs" />
<Compile Include="CodeAnnotations\UmbracoWillObsoleteAttribute.cs" />
<Compile Include="Collections\CompositeNStringNStringKey.cs" />
<Compile Include="Collections\CompositeStringStringKey.cs" />
<Compile Include="Collections\CompositeIntStringKey.cs" />
@@ -406,6 +403,7 @@
<Compile Include="PropertyEditors\ParameterEditorCollection.cs" />
<Compile Include="PropertyEditors\PropertyEditorCollection.cs" />
<Compile Include="PropertyEditors\PropertyEditorTagsExtensions.cs" />
<Compile Include="PropertyEditors\PropertyValueLevel.cs" />
<Compile Include="PropertyEditors\SliderPropertyEditorConfiguration.cs" />
<Compile Include="PropertyEditors\TagConfiguration.cs" />
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValue.cs" />
@@ -1439,7 +1437,6 @@
<Compile Include="Strategies\ManifestWatcherComponent.cs" />
<Compile Include="Strategies\RelateOnCopyComponent.cs" />
<Compile Include="Strategies\RelateOnTrashComponent.cs" />
<Compile Include="StringAliasCaseType.cs" />
<Compile Include="StringExtensions.cs" />
<Compile Include="Strings\CleanStringType.cs" />
<Compile Include="Strings\ContentBaseExtensions.cs" />
@@ -1451,7 +1448,6 @@
<Compile Include="Strings\Diff.cs" />
<Compile Include="Strings\IShortStringHelper.cs" />
<Compile Include="Strings\IUrlSegmentProvider.cs" />
<Compile Include="Strings\StringAliasCaseTypeExtensions.cs" />
<Compile Include="Strings\UrlSegmentProviderCollection.cs" />
<Compile Include="Strings\UrlSegmentProviderCollectionBuilder.cs" />
<Compile Include="Strings\Utf8ToAsciiConverter.cs" />

View File

@@ -43,12 +43,30 @@ namespace Umbraco.Tests.Published
var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary<string, object> { { "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<string, object> { { "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<string, object> { { "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)

View File

@@ -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)

View File

@@ -761,9 +761,14 @@ function tinyMceService($log, imageHelper, $http, $timeout, macroResource, macro
* @param {string} input the string to parse
*/
getAnchorNames: function (input) {
var anchorPattern = /<a id=\\"(.*?)\\">/gi;
var anchors = [];
if (!input) {
return anchors;
}
var anchorPattern = /<a id=\\"(.*?)\\">/gi;
var matches = input.match(anchorPattern);
var anchors = [];
if (matches) {
anchors = matches.map(function (v) {

View File

@@ -34,7 +34,7 @@
<label class="checkbox no-indent">
<input type="checkbox"
ng-model="model.target.target"
ng-true-value="_blank"
ng-true-value="'_blank'"
ng-false-value="" /> <localize key="defaultdialogs_openInNewWindow">Opens the linked document in a new window or tab</localize>
</label>
</umb-control-group>

View File

@@ -48,10 +48,6 @@ namespace Umbraco.Web.Cache
runtimeCache.ClearCacheObjectTypes<PublicAccessEntry>();
// fixme - this is for entity service, not sure why we do it here and nowhere else?
runtimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
runtimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
var idsRemoved = new HashSet<int>();
foreach (var payload in payloads)
@@ -110,9 +106,6 @@ namespace Umbraco.Web.Cache
// when a public version changes
Current.ApplicationCache.ClearPartialViewCache();
MacroCacheRefresher.ClearMacroContentCache(CacheHelper); // just the content
Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
}
base.Refresh(payloads);

View File

@@ -61,7 +61,6 @@ namespace Umbraco.Web.Cache
foreach (var id in payloads.Select(x => x.Id))
{
_idkMap.ClearCache(id);
ClearLegacyCaches(id);
}
if (payloads.Any(x => x.ItemType == typeof(IContentType).Name))
@@ -103,30 +102,6 @@ namespace Umbraco.Web.Cache
throw new NotSupportedException();
}
private void ClearLegacyCaches(int contentTypeId /*, string contentTypeAlias, IEnumerable<int> propertyTypeIds*/)
{
// legacy umbraco.cms.businesslogic.ContentType
// TODO - get rid of all this mess
// clears the cache for each property type associated with the content type
// see src/umbraco.cms/businesslogic/propertytype/propertytype.cs
// that cache is disabled because we could not clear it properly
//foreach (var pid in propertyTypeIds)
// ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + pid);
// clears the cache associated with the content type itself
CacheHelper.RuntimeCache.ClearCacheItem(CacheKeys.ContentTypeCacheKey + contentTypeId);
// clears the cache associated with the content type properties collection
CacheHelper.RuntimeCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + contentTypeId);
// clears the dictionary object cache of the legacy ContentType
// see src/umbraco.cms/businesslogic/ContentType.cs
// that cache is disabled because we could not clear it properly
//global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(contentTypeAlias);
}
#endregion
#region Json

View File

@@ -101,7 +101,6 @@ namespace Umbraco.Web.Cache
{
return new[]
{
CacheKeys.MacroCacheKey, // umbraco.cms.businesslogic.macro.Macro objects cache
CacheKeys.MacroContentCacheKey, // macro render cache
};
}

View File

@@ -56,34 +56,6 @@ namespace Umbraco.Web.Cache
if (payload.ChangeTypes == TreeChangeTypes.Remove)
_idkMap.ClearCache(payload.Id);
// note: ClearCacheByKeySearch - does StartsWith(...)
// legacy alert!
//
// library cache library.GetMedia(int mediaId, bool deep) maintains a cache
// of media xml - and of *deep* media xml - using the key
// MediaCacheKey + "_" + mediaId + "_" + deep
//
// this clears the non-deep xml for the current media
//
Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(
$"{CacheKeys.MediaCacheKey}_{payload.Id}_False");
// and then, for the entire path, we have to clear whatever might contain the media
// bearing in mind there are probably nasty race conditions here - this is all legacy
var k = $"{CacheKeys.MediaCacheKey}_{payload.Id}_";
var x = Current.ApplicationCache.RuntimeCache.GetCacheItem(k)
as Tuple<XElement, string>;
if (x == null) continue;
var path = x.Item2;
foreach (var pathId in path.Split(',').Skip(1).Select(int.Parse))
{
// this clears the deep xml for the medias in the path (skipping -1)
Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(
$"{CacheKeys.MediaCacheKey}_{pathId}_True");
}
// repository cache
// it *was* done for each pathId but really that does not make sense
// only need to do it for the current media

View File

@@ -60,9 +60,6 @@ namespace Umbraco.Web.Cache
_idkMap.ClearCache(id);
CacheHelper.ClearPartialViewCache();
CacheHelper.RuntimeCache.ClearCacheByKeySearch($"{CacheKeys.MemberLibraryCacheKey}_{id}");
CacheHelper.RuntimeCache.ClearCacheByKeySearch($"{CacheKeys.MemberBusinessLogicCacheKey}{id}");
var memberCache = CacheHelper.IsolatedRuntimeCache.GetCache<IMember>();
if (memberCache)
memberCache.Result.ClearCacheItem(RepositoryCacheKeys.GetKey<IMember>(id));

View File

@@ -87,10 +87,34 @@ namespace Umbraco.Web.PublishedCache.NuCache
_variations = origin._variations;
}
// determines whether a property has value
public override bool HasValue(string culture = null, string segment = null)
{
ContextualizeVariation(ref culture, ref segment);
return PropertyType.IsValue(GetSourceValue(culture, segment));
var value = GetSourceValue(culture, segment);
var hasValue = PropertyType.IsValue(value, PropertyValueLevel.Source);
if (hasValue.HasValue) return hasValue.Value;
lock (_locko)
{
value = GetInterValue(culture, segment);
hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter);
if (hasValue.HasValue) return hasValue.Value;
var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment);
// initial reference cache level always is .Content
const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
if (!cacheValues.ObjectInitialized)
{
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, value, _isPreviewing);
cacheValues.ObjectInitialized = true;
}
value = cacheValues.ObjectValue;
return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false;
}
}
// used to cache the CacheValues of this property

View File

@@ -37,7 +37,28 @@ namespace Umbraco.Web.PublishedCache
}
public override bool HasValue(string culture = null, string segment = null)
=> _sourceValue != null && (!(_sourceValue is string s) || !string.IsNullOrWhiteSpace(s));
{
var hasValue = PropertyType.IsValue(_sourceValue, PropertyValueLevel.Source);
if (hasValue.HasValue) return hasValue.Value;
GetCacheLevels(out var cacheLevel, out var referenceCacheLevel);
lock (_locko)
{
var value = GetInterValue();
hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter);
if (hasValue.HasValue) return hasValue.Value;
var cacheValues = GetCacheValues(cacheLevel);
if (!cacheValues.ObjectInitialized)
{
cacheValues.ObjectValue = PropertyType.ConvertInterToObject(Element, referenceCacheLevel, value, IsPreviewing);
cacheValues.ObjectInitialized = true;
}
value = cacheValues.ObjectValue;
return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false;
}
}
// used to cache the CacheValues of this property
// ReSharper disable InconsistentlySynchronizedField