1
+ using Microsoft . Extensions . DependencyInjection ;
2
+ using Microsoft . Extensions . Logging ;
1
3
using Umbraco . Cms . Core . Composing ;
4
+ using Umbraco . Cms . Core . DependencyInjection ;
2
5
using Umbraco . Cms . Core . Models ;
3
6
using Umbraco . Cms . Core . Models . Editors ;
4
7
@@ -12,14 +15,27 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase<IDataVa
12
15
// TODO: We could further reduce circular dependencies with PropertyEditorCollection by not having IDataValueReference implemented
13
16
// by property editors and instead just use the already built in IDataValueReferenceFactory and/or refactor that into a more normal collection
14
17
18
+ private readonly ILogger < DataValueReferenceFactoryCollection > _logger ;
19
+
15
20
/// <summary>
16
21
/// Initializes a new instance of the <see cref="DataValueReferenceFactoryCollection" /> class.
17
22
/// </summary>
18
23
/// <param name="items">The items.</param>
24
+ [ Obsolete ( "Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17." ) ]
19
25
public DataValueReferenceFactoryCollection ( Func < IEnumerable < IDataValueReferenceFactory > > items )
20
- : base ( items )
26
+ : this (
27
+ items ,
28
+ StaticServiceProvider . Instance . GetRequiredService < ILogger < DataValueReferenceFactoryCollection > > ( ) )
21
29
{ }
22
30
31
+ /// <summary>
32
+ /// Initializes a new instance of the <see cref="DataValueReferenceFactoryCollection" /> class.
33
+ /// </summary>
34
+ /// <param name="items">The items.</param>
35
+ /// <param name="logger">The logger.</param>
36
+ public DataValueReferenceFactoryCollection ( Func < IEnumerable < IDataValueReferenceFactory > > items , ILogger < DataValueReferenceFactoryCollection > logger )
37
+ : base ( items ) => _logger = logger ;
38
+
23
39
/// <summary>
24
40
/// Gets all unique references from the specified properties.
25
41
/// </summary>
@@ -33,7 +49,7 @@ public ISet<UmbracoEntityReference> GetAllReferences(IPropertyCollection propert
33
49
var references = new HashSet < UmbracoEntityReference > ( ) ;
34
50
35
51
// Group by property editor alias to avoid duplicate lookups and optimize value parsing
36
- foreach ( var propertyValuesByPropertyEditorAlias in properties . GroupBy ( x => x . PropertyType . PropertyEditorAlias , x => x . Values ) )
52
+ foreach ( IGrouping < string , IReadOnlyCollection < IPropertyValue > > propertyValuesByPropertyEditorAlias in properties . GroupBy ( x => x . PropertyType . PropertyEditorAlias , x => x . Values ) )
37
53
{
38
54
if ( ! propertyEditors . TryGet ( propertyValuesByPropertyEditorAlias . Key , out IDataEditor ? dataEditor ) )
39
55
{
@@ -48,7 +64,7 @@ public ISet<UmbracoEntityReference> GetAllReferences(IPropertyCollection propert
48
64
values . Add ( propertyValue . PublishedValue ) ;
49
65
}
50
66
51
- references . UnionWith ( GetReferences ( dataEditor , values ) ) ;
67
+ references . UnionWith ( GetReferences ( dataEditor , values , propertyValuesByPropertyEditorAlias . Key ) ) ;
52
68
}
53
69
54
70
return references ;
@@ -74,14 +90,18 @@ public IEnumerable<UmbracoEntityReference> GetReferences(IDataEditor dataEditor,
74
90
/// The references.
75
91
/// </returns>
76
92
public ISet < UmbracoEntityReference > GetReferences ( IDataEditor dataEditor , IEnumerable < object ? > values ) =>
77
- GetReferencesEnumerable ( dataEditor , values ) . ToHashSet ( ) ;
78
- private IEnumerable < UmbracoEntityReference > GetReferencesEnumerable ( IDataEditor dataEditor , IEnumerable < object ? > values )
93
+ GetReferencesEnumerable ( dataEditor , values , null ) . ToHashSet ( ) ;
94
+
95
+ private ISet < UmbracoEntityReference > GetReferences ( IDataEditor dataEditor , IEnumerable < object ? > values , string propertyEditorAlias ) =>
96
+ GetReferencesEnumerable ( dataEditor , values , propertyEditorAlias ) . ToHashSet ( ) ;
97
+
98
+ private IEnumerable < UmbracoEntityReference > GetReferencesEnumerable ( IDataEditor dataEditor , IEnumerable < object ? > values , string ? propertyEditorAlias )
79
99
{
80
100
// TODO: We will need to change this once we support tracking via variants/segments
81
101
// for now, we are tracking values from ALL variants
82
102
if ( dataEditor . GetValueEditor ( ) is IDataValueReference dataValueReference )
83
103
{
84
- foreach ( UmbracoEntityReference reference in values . SelectMany ( dataValueReference . GetReferences ) )
104
+ foreach ( UmbracoEntityReference reference in GetReferencesFromPropertyValues ( values , dataValueReference , propertyEditorAlias ) )
85
105
{
86
106
yield return reference ;
87
107
}
@@ -107,6 +127,38 @@ private IEnumerable<UmbracoEntityReference> GetReferencesEnumerable(IDataEditor
107
127
}
108
128
}
109
129
130
+ private IEnumerable < UmbracoEntityReference > GetReferencesFromPropertyValues ( IEnumerable < object ? > values , IDataValueReference dataValueReference , string ? propertyEditorAlias )
131
+ {
132
+ var result = new List < UmbracoEntityReference > ( ) ;
133
+ foreach ( var value in values )
134
+ {
135
+ // When property editors on data types are changed, we could have values that are incompatible with the new editor.
136
+ // Leading to issues such as:
137
+ // - https://github.com/umbraco/Umbraco-CMS/issues/17628
138
+ // - https://github.com/umbraco/Umbraco-CMS/issues/17725
139
+ // Although some changes like this are not intended to be compatible, we should handle them gracefully and not
140
+ // error in retrieving references, which would prevent manipulating or deleting the content that uses the data type.
141
+ try
142
+ {
143
+ IEnumerable < UmbracoEntityReference > references = dataValueReference . GetReferences ( value ) ;
144
+ result . AddRange ( references ) ;
145
+ }
146
+ catch ( Exception ex )
147
+ {
148
+ // Log the exception but don't throw, continue with the next value.
149
+ _logger . LogError (
150
+ ex ,
151
+ "Error getting references from value {Value} with data editor {DataEditor} and property editor alias {PropertyEditorAlias}." ,
152
+ value ,
153
+ dataValueReference . GetType ( ) . FullName ,
154
+ propertyEditorAlias ?? "n/a" ) ;
155
+ throw ;
156
+ }
157
+ }
158
+
159
+ return result ;
160
+ }
161
+
110
162
/// <summary>
111
163
/// Gets all relation type aliases that are automatically tracked.
112
164
/// </summary>
@@ -117,6 +169,7 @@ private IEnumerable<UmbracoEntityReference> GetReferencesEnumerable(IDataEditor
117
169
[ Obsolete ( "Use GetAllAutomaticRelationTypesAliases. This will be removed in Umbraco 15." ) ]
118
170
public ISet < string > GetAutomaticRelationTypesAliases ( PropertyEditorCollection propertyEditors ) =>
119
171
GetAllAutomaticRelationTypesAliases ( propertyEditors ) ;
172
+
120
173
public ISet < string > GetAllAutomaticRelationTypesAliases ( PropertyEditorCollection propertyEditors )
121
174
{
122
175
// Always add default automatic relation types
0 commit comments