Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing list view usages to data type references (#14485) #14617

Merged
merged 2 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Umbraco.Core/Models/ContentEditing/DataTypeReferences.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.Serialization;
using System.Xml.Linq;

namespace Umbraco.Cms.Core.Models.ContentEditing;

Expand All @@ -20,6 +21,9 @@ public class ContentTypeReferences : EntityBasic
[DataMember(Name = "properties")]
public object? Properties { get; set; }

[DataMember(Name = "listViews")]
public object? ListViews { get; set; }

[DataContract(Name = "property", Namespace = "")]
public class PropertyTypeReferences
{
Expand All @@ -29,5 +33,12 @@ public class PropertyTypeReferences
[DataMember(Name = "alias")]
public string? Alias { get; set; }
}

[DataContract(Name = "listView", Namespace = "")]
public class ListViewReferences
{
[DataMember(Name = "name")]
public string? Name { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ public interface IDataTypeRepository : IReadWriteQueryRepository<int, IDataType>
/// <param name="id"></param>
/// <returns></returns>
IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id);

/// <summary>
/// Returns a dictionary of content type <see cref="Udi" />s and the data type (List view) aliases that use a
/// <see cref="IDataType" />
/// </summary>
/// <param name="id"></param>
/// <returns></returns>

IReadOnlyDictionary<Udi, IEnumerable<string>> FindListViewUsages(int id) => throw new NotImplementedException();
}
6 changes: 6 additions & 0 deletions src/Umbraco.Core/Services/DataTypeService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.Extensions.DependencyInjection;

Check notice on line 1 in src/Umbraco.Core/Services/DataTypeService.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

ℹ Getting worse: Primitive Obsession

The ratio of primitive types in function arguments increases from 40.00% to 40.85%, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Events;
Expand Down Expand Up @@ -612,6 +612,12 @@
return _dataTypeRepository.FindUsages(id);
}

public IReadOnlyDictionary<Udi, IEnumerable<string>> GetListViewReferences(int id)
{
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
return _dataTypeRepository.FindListViewUsages(id);
}

private void Audit(AuditType type, int userId, int objectId)
{
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
Expand Down
1 change: 1 addition & 0 deletions src/Umbraco.Core/Services/IDataTypeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public interface IDataTypeService : IService
/// <param name="id"></param>
/// <returns></returns>
IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id);
IReadOnlyDictionary<Udi, IEnumerable<string>> GetListViewReferences(int id) => throw new NotImplementedException();

Attempt<OperationResult<OperationResultType, EntityContainer>?> CreateContainer(int parentId, Guid key, string name, int userId = Constants.Security.SuperUserId);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Data;

Check warning on line 1 in src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

❌ New issue: Overall Code Complexity

This module has a mean cyclomatic complexity of 4.29 across 14 functions. The mean complexity threshold is 4. This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.
using System.Globalization;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
using NPoco;
using Umbraco.Cms.Core;
Expand All @@ -10,13 +11,15 @@
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.Persistence.Factories;
using Umbraco.Cms.Infrastructure.Persistence.Querying;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Extensions;
using static Umbraco.Cms.Core.Constants.SqlTemplates;
using static Umbraco.Cms.Core.Persistence.SqlExtensionsStatics;

namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
Expand Down Expand Up @@ -124,6 +127,84 @@
x => (IEnumerable<string>)x.PropertyTypes.Select(p => p.Alias).ToList());
}

public IReadOnlyDictionary<Udi, IEnumerable<string>> FindListViewUsages(int id)
{
var usages = new Dictionary<Udi, IEnumerable<string>>();

if (id == default)
{
return usages;
}

IDataType? dataType = Get(id);

if (dataType != null && dataType.EditorAlias.Equals(Constants.PropertyEditors.Aliases.ListView))
{
// Get All contentTypes where isContainer (list view enabled) is set to true
Sql<ISqlContext> sql = Sql()
.Select<ContentTypeDto>(ct => ct.Select(node => node.NodeDto))
.From<ContentTypeDto>()
.InnerJoin<NodeDto>().On<NodeDto, ContentTypeDto>(n => n.NodeId, ct => ct.NodeId)
.Where<ContentTypeDto>(ct => ct.IsContainer == true);

List<ContentTypeDto> ctds = Database.Fetch<ContentTypeDto>(sql);

// If there are not any ContentTypes with a ListView return.
if (!ctds.Any())
{
return usages;
}

// First check if it is a custom list view
ContentTypeDto? customListView = ctds.Where(x => (Constants.Conventions.DataTypes.ListViewPrefix + x.Alias).Equals(dataType.Name)).FirstOrDefault();

if (customListView != null)
{
// Add usages as customListView
usages.Add(
new GuidUdi(ObjectTypes.GetUdiType(customListView.NodeDto.NodeObjectType!.Value), customListView.NodeDto.UniqueId),
new List<string> { dataType.Name! });
}
else
{
// It is not a custom ListView, so check the default ones.
foreach (ContentTypeDto contentWithListView in ctds)
{
var customListViewName = Constants.Conventions.DataTypes.ListViewPrefix + contentWithListView.Alias;
IDataType? clv = Get(Query<IDataType>().Where(x => x.Name == customListViewName))?.FirstOrDefault();

// Check if the content type has a custom listview (extra check to prevent duplicates)
if (clv == null)
{
// ContentType has no custom listview so it uses the default one
var udi = new GuidUdi(ObjectTypes.GetUdiType(contentWithListView.NodeDto.NodeObjectType!.Value), contentWithListView.NodeDto.UniqueId);
var listViewType = new List<string>();

if (dataType.Id.Equals(Constants.DataTypes.DefaultContentListView) && udi.EntityType == ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType))
listViewType.Add(Constants.Conventions.DataTypes.ListViewPrefix + "Content");
else if (dataType.Id.Equals(Constants.DataTypes.DefaultMediaListView) && udi.EntityType == ObjectTypes.GetUdiType(UmbracoObjectTypes.MediaType))
listViewType.Add(Constants.Conventions.DataTypes.ListViewPrefix + "Media");
else if (dataType.Id.Equals(Constants.DataTypes.DefaultMembersListView) && udi.EntityType == ObjectTypes.GetUdiType(UmbracoObjectTypes.MemberType))
listViewType.Add(Constants.Conventions.DataTypes.ListViewPrefix + "Members");

if (listViewType.Any())
{
var added = usages.TryAdd(udi, listViewType);
if (!added)
{
usages[udi] = usages[udi].Append(dataType.Name!);
}
}
}
}

}
}

return usages;
}

Check warning on line 205 in src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

❌ New issue: Complex Method

FindListViewUsages has a cyclomatic complexity of 19, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 205 in src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

❌ New issue: Bumpy Road Ahead

FindListViewUsages has 3 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

Check warning on line 205 in src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

❌ New issue: Deep, Nested Complexity

FindListViewUsages has a nested complexity depth of 6, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.


#region Overrides of RepositoryBase<int,DataTypeDefinition>

protected override IDataType? PerformGet(int id) => GetMany(id).FirstOrDefault();
Expand Down
34 changes: 33 additions & 1 deletion src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;

Check notice on line 1 in src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

ℹ Getting worse: Overall Code Complexity

The mean cyclomatic complexity increases from 4.13 to 4.16, threshold = 4. This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.

Check notice on line 1 in src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

✅ Getting better: Primitive Obsession

The ratio of primitive types in function arguments decreases from 35.56% to 34.04%, threshold = 30.0%. The functions in this file have too many primitive types (e.g. int, double, float) in their function argument lists. Using many primitive types lead to the code smell Primitive Obsession. Avoid adding more primitive arguments.
using System.Collections.Generic;
using System.Data;
using System.Linq;
Expand Down Expand Up @@ -430,19 +430,35 @@
var result = new DataTypeReferences();
var usages = _dataTypeService.GetReferences(id);

foreach(var groupOfEntityType in usages.GroupBy(x => x.Key.EntityType))
// properties
foreach (var groupOfEntityType in usages.GroupBy(x => x.Key.EntityType))
{
//get all the GUIDs for the content types to find
var guidsAndPropertyAliases = groupOfEntityType.ToDictionary(i => ((GuidUdi)i.Key).Guid, i => i.Value);

if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType))
result.DocumentTypes = GetContentTypeUsages(_contentTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MediaType))
result.MediaTypes = GetContentTypeUsages(_mediaTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MemberType))
result.MemberTypes = GetContentTypeUsages(_memberTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
}

// ListView
var listViewUsages = _dataTypeService.GetListViewReferences(id);
foreach (var groupOfEntityType in listViewUsages.GroupBy(x => x.Key.EntityType))
{
//get all the GUIDs for the content types to find
var guidsAndPropertyAliases = groupOfEntityType.ToDictionary(i => ((GuidUdi)i.Key).Guid, i => i.Value);

if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType))
result.DocumentTypes = result.DocumentTypes.Concat(GetListViewContentTypeUsages(_contentTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases));
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MediaType))
result.MediaTypes = result.MediaTypes.Concat(GetListViewContentTypeUsages(_mediaTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases));
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MemberType))
result.MemberTypes = result.MemberTypes.Concat(GetListViewContentTypeUsages(_memberTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases));
}

Check warning on line 461 in src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

❌ New issue: Complex Method

GetReferences has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 461 in src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (contrib)

❌ New issue: Bumpy Road Ahead

GetReferences has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.
return result;
}

Expand Down Expand Up @@ -481,6 +497,22 @@
});
}

private IEnumerable<DataTypeReferences.ContentTypeReferences> GetListViewContentTypeUsages(
IEnumerable<IContentTypeBase> cts,
IReadOnlyDictionary<Guid, IEnumerable<string>> usages) => cts.Select(x => new DataTypeReferences.ContentTypeReferences
{
Id = x.Id,
Key = x.Key,
Alias = x.Alias,
Icon = x.Icon,
Name = x.Name,
Udi = new GuidUdi(ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType), x.Key),
ListViews = usages.GetValueOrDefault(x.Key)?.Select(lv => new DataTypeReferences.ContentTypeReferences.ListViewReferences
{
Name = lv
})
});

#region ReadOnly actions to return basic data - allow access for: content ,media, members, settings, developer
/// <summary>
/// Gets the content json for all data types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ <h5 class="mt0" style="margin-bottom: 20px;">
<div class="umb-table-cell"><umb-icon icon="{{reference.icon}}" class="umb-table-body__icon"></umb-icon></div>
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}} {{::reference.listViews | umbCmsJoinArray:', ':'name'}}</span></div>
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/documentTypes/edit/{{::reference.id}}" ng-click="vm.openDocumentType(reference.id, $event)"><localize key="general_open">Open</localize></a></div>
</div>
</div>
Expand All @@ -52,7 +52,7 @@ <h5 class="mt0" style="margin-bottom: 20px;">

<!-- Media Types -->
<div ng-if="vm.references.mediaTypes.length > 0" class="mb4">

<h5 class="mt0" style="margin-bottom: 20px;">
<localize key="references_labelUsedByMediaTypes">Used in Media Types</localize>
</h5>
Expand All @@ -72,7 +72,7 @@ <h5 class="mt0" style="margin-bottom: 20px;">
<div class="umb-table-cell"><umb-icon icon="{{reference.icon}}" class="umb-table-body__icon"></umb-icon></div>
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}} {{::reference.listViews | umbCmsJoinArray:', ':'name'}} </span></div>
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/mediaTypes/edit/{{::reference.id}}" ng-click="vm.openMediaType(reference.id, $event)"><localize key="general_open">Open</localize></a></div>
</div>
</div>
Expand Down Expand Up @@ -103,7 +103,7 @@ <h5 class="mt0" style="margin-bottom: 20px;">
<div class="umb-table-cell"><umb-icon icon="{{reference.icon}}" class="umb-table-body__icon"></umb-icon></div>
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}} {{::reference.listViews | umbCmsJoinArray:', ':'name'}}</span></div>
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/memberTypes/edit/{{::reference.id}}" ng-click="vm.openMemberType(reference.id, $event)"><localize key="general_open">Open</localize></a></div>
</div>
</div>
Expand Down