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

Data type usage reporting and deletion warning #6625

Merged
merged 44 commits into from Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
69d97bc
first step for changing the datatype edit view
nielslyngsoe Oct 2, 2019
ab1f9f3
added fake test for this prototype
nielslyngsoe Oct 8, 2019
3ff2996
added a view for datatype relations
nielslyngsoe Oct 8, 2019
cca66be
Merge remote-tracking branch 'origin/v8/dev' into v8/feature/AB2918-u…
nielslyngsoe Oct 8, 2019
65c4989
added more translations
nielslyngsoe Oct 8, 2019
3365d6f
correctied area key for translation
nielslyngsoe Oct 8, 2019
79d20bf
whitespace
nielslyngsoe Oct 8, 2019
dbee0da
tabName translation
nielslyngsoe Oct 8, 2019
8e17245
changed look and translation keys
nielslyngsoe Oct 8, 2019
097ef82
corrected working
nielslyngsoe Oct 8, 2019
3da6465
changed to headlines to h5
nielslyngsoe Oct 8, 2019
bcc76b2
Implements DataTypeService.FindUsages
Shazwazza Oct 8, 2019
69fb12a
changed wording
nielslyngsoe Oct 8, 2019
eff284c
corrected open links
nielslyngsoe Oct 8, 2019
dfc642e
seperate row for icons
nielslyngsoe Oct 8, 2019
5deaf99
auto width for actions
nielslyngsoe Oct 8, 2019
982a7c6
Merge remote-tracking branch 'origin/v8/dev' into v8/feature/AB2913-D…
nielslyngsoe Oct 8, 2019
b53eae0
Merge remote-tracking branch 'origin/v8/feature/AB2918-update-datatyp…
nielslyngsoe Oct 8, 2019
ab6f4af
dont know why this was removed, brain fart...
nielslyngsoe Oct 8, 2019
a178a4d
removing console.logs
nielslyngsoe Oct 8, 2019
0e34c5e
Merge remote-tracking branch 'origin/v8/feature/AB2918-update-datatyp…
nielslyngsoe Oct 8, 2019
ac30017
Adds new endpoint to DataTypeController to return the correct data an…
Shazwazza Oct 9, 2019
4c131d1
ensure the data type refs are only loaded when the tab is activated
Shazwazza Oct 9, 2019
cf9add7
Fixes navigating to other items via url
Shazwazza Oct 9, 2019
4c344fc
fixes build
Shazwazza Oct 9, 2019
dd29fe5
added used by properties
nielslyngsoe Oct 9, 2019
fd7b85f
added no overflow for properties
nielslyngsoe Oct 9, 2019
7024bc4
set template to cache output since its not going to update unless you…
nielslyngsoe Oct 9, 2019
2d693dd
ups, misspelling of localization key
nielslyngsoe Oct 9, 2019
18e8cae
adding new localizations
nielslyngsoe Oct 9, 2019
dda9927
Delete dialog for datatype that has relations
nielslyngsoe Oct 9, 2019
0429711
rename to Reference
nielslyngsoe Oct 9, 2019
f658c15
rename relations to references, to avoid misunderstanding.
nielslyngsoe Oct 9, 2019
e7363ad
highlight properties begin deleted + corrected array
nielslyngsoe Oct 9, 2019
e7faa14
changed wording
nielslyngsoe Oct 9, 2019
5035438
simplified wording by removing "based on it"
nielslyngsoe Oct 9, 2019
188cc2a
listing out properties with exclamation mark icon in front of each
nielslyngsoe Oct 9, 2019
c091d21
align icons if the are in a umb-table
nielslyngsoe Oct 9, 2019
b400a72
Changes filter name
Shazwazza Oct 10, 2019
261dc2a
AB2913 - Added unit tests for the umbCmsJoinArray filter, and a small…
bergmania Oct 11, 2019
f2381da
no need for this comment anymore
nielslyngsoe Oct 21, 2019
c89ea71
removed comment
nielslyngsoe Oct 21, 2019
ba215f8
fix name to follow the class name
bergmania Oct 21, 2019
c072015
Merge remote-tracking branch 'origin/v8/dev' into v8/feature/AB2913-D…
bergmania Oct 21, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -7,5 +7,12 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IDataTypeRepository : IReadWriteQueryRepository<int, IDataType>
{
IEnumerable<MoveEventInfo<IDataType>> Move(IDataType toMove, EntityContainer container);

/// <summary>
/// Returns a dictionary of content type <see cref="Udi"/>s and the property type aliases that use a <see cref="IDataType"/>
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id);
}
}
Expand Up @@ -279,6 +279,28 @@ public IEnumerable<MoveEventInfo<IDataType>> Move(IDataType toMove, EntityContai
return moveInfo;
}

public IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id)
{
if (id == default)
return new Dictionary<Udi, IEnumerable<string>>();

var sql = Sql()
.Select<ContentTypeDto>(ct => ct.Select(node => node.NodeDto))
.AndSelect<PropertyTypeDto>(pt => Alias(pt.Alias, "ptAlias"), pt => Alias(pt.Name, "ptName"))
.From<PropertyTypeDto>()
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, PropertyTypeDto>(ct => ct.NodeId, pt => pt.ContentTypeId)
.InnerJoin<NodeDto>().On<NodeDto, ContentTypeDto>(n => n.NodeId, ct => ct.NodeId)
.Where<PropertyTypeDto>(pt => pt.DataTypeId == id)
.OrderBy<NodeDto>(node => node.NodeId)
.AndBy<PropertyTypeDto>(pt => pt.Alias);

var dtos = Database.FetchOneToMany<ContentTypeReferenceDto>(ct => ct.PropertyTypes, sql);

return dtos.ToDictionary(
x => (Udi)new GuidUdi(ObjectTypes.GetUdiType(x.NodeDto.NodeObjectType.Value), x.NodeDto.UniqueId).EnsureClosed(),
x => (IEnumerable<string>)x.PropertyTypes.Select(p => p.Alias).ToList());
}

private string EnsureUniqueNodeName(string nodeName, int id = 0)
{
var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql
Expand All @@ -291,5 +313,24 @@ private string EnsureUniqueNodeName(string nodeName, int id = 0)

return SimilarNodeName.GetUniqueName(names, id, nodeName);
}


[TableName(Constants.DatabaseSchema.Tables.ContentType)]
private class ContentTypeReferenceDto : ContentTypeDto
{
[ResultColumn]
[Reference(ReferenceType.Many)]
public List<PropertyTypeReferenceDto> PropertyTypes { get; set; }
}

[TableName(Constants.DatabaseSchema.Tables.PropertyType)]
private class PropertyTypeReferenceDto
{
[Column("ptAlias")]
public string Alias { get; set; }

[Column("ptName")]
public string Name { get; set; }
}
}
}
3 changes: 2 additions & 1 deletion src/Umbraco.Core/Services/IContentTypeServiceBase.cs
Expand Up @@ -25,7 +25,7 @@ public interface IContentTypeBaseService<TItem> : IContentTypeBaseService, IServ
/// <summary>
/// Gets a content type.
/// </summary>
TItem Get(int id);
new TItem Get(int id);

/// <summary>
/// Gets a content type.
Expand All @@ -40,6 +40,7 @@ public interface IContentTypeBaseService<TItem> : IContentTypeBaseService, IServ
int Count();

IEnumerable<TItem> GetAll(params int[] ids);
IEnumerable<TItem> GetAll(IEnumerable<Guid> ids);

IEnumerable<TItem> GetDescendants(int id, bool andSelf); // parent-child axis
IEnumerable<TItem> GetComposedOf(int id); // composition axis
Expand Down
7 changes: 7 additions & 0 deletions src/Umbraco.Core/Services/IDataTypeService.cs
Expand Up @@ -10,6 +10,13 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IDataTypeService : IService
{
/// <summary>
/// Returns a dictionary of content type <see cref="Udi"/>s and the property type aliases that use a <see cref="IDataType"/>
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id);

Attempt<OperationResult<OperationResultType, EntityContainer>> CreateContainer(int parentId, string name, int userId = Constants.Security.SuperUserId);
Attempt<OperationResult> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
EntityContainer GetContainer(int containerId);
Expand Down
Expand Up @@ -252,12 +252,12 @@ public IEnumerable<TItem> GetAll(params int[] ids)
}
}

public IEnumerable<TItem> GetAll(params Guid[] ids)
public IEnumerable<TItem> GetAll(IEnumerable<Guid> ids)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(ReadLockIds);
return Repository.GetMany(ids);
return Repository.GetMany(ids.ToArray());
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/Umbraco.Core/Services/Implement/DataTypeService.cs
Expand Up @@ -466,6 +466,14 @@ public void Delete(IDataType dataType, int userId = Constants.Security.SuperUser
}
}

public IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id)
{
using (var scope = ScopeProvider.CreateScope(autoComplete:true))
{
return _dataTypeRepository.FindUsages(id);
}
}

private void Audit(AuditType type, int userId, int objectId)
{
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
Expand Down
Expand Up @@ -28,6 +28,68 @@ private EntityContainerRepository CreateContainerRepository(IScopeAccessor scope
return new EntityContainerRepository(scopeAccessor, AppCaches.Disabled, Logger, Constants.ObjectTypes.DataTypeContainer);
}

[Test]
public void Can_Find_Usages()
{
var provider = TestObjects.GetScopeProvider(Logger);

using (provider.CreateScope())
{
var dtRepo = CreateRepository();
IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService)) { Name = "dt1" };
dtRepo.Save(dataType1);
IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService)) { Name = "dt2" };
dtRepo.Save(dataType2);

var ctRepo = Factory.GetInstance<IContentTypeRepository>();
IContentType ct = new ContentType(-1)
{
Alias = "ct1",
Name = "CT1",
AllowedAsRoot = true,
Icon = "icon-home",
PropertyGroups = new PropertyGroupCollection
{
new PropertyGroup(true)
{
Name = "PG1",
PropertyTypes = new PropertyTypeCollection(true)
{
new PropertyType(dataType1, "pt1")
{
Name = "PT1"
},
new PropertyType(dataType1, "pt2")
{
Name = "PT2"
},
new PropertyType(dataType2, "pt3")
{
Name = "PT3"
}
}
}
}
};
ctRepo.Save(ct);

var usages = dtRepo.FindUsages(dataType1.Id);

var key = usages.First().Key;
Assert.AreEqual(ct.Key, ((GuidUdi)key).Guid);
Assert.AreEqual(2, usages[key].Count());
Assert.AreEqual("pt1", usages[key].ElementAt(0));
Assert.AreEqual("pt2", usages[key].ElementAt(1));

usages = dtRepo.FindUsages(dataType2.Id);

key = usages.First().Key;
Assert.AreEqual(ct.Key, ((GuidUdi)key).Guid);
Assert.AreEqual(1, usages[key].Count());
Assert.AreEqual("pt3", usages[key].ElementAt(0));
}
}

[Test]
public void Can_Move()
{
Expand Down
5 changes: 5 additions & 0 deletions src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs
Expand Up @@ -329,6 +329,11 @@ public Attempt<OperationResult<MoveOperationStatusType>> Move(IDataType toMove,
{
throw new NotImplementedException();
}

public IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id)
{
throw new NotImplementedException();
}
}

#endregion
Expand Down
Expand Up @@ -43,6 +43,30 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
"Failed to retrieve pre values for editor alias " + editorAlias);
},

/**
* @ngdoc method
* @name umbraco.resources.dataTypeResource#getReferences
* @methodOf umbraco.resources.dataTypeResource
*
* @description
* Retrieves relations of a given data type.
*
* @param {Int} id id of datatype to retrieve relations for
* @returns {Promise} resourcePromise object.
*
*/
getReferences: function (id) {

return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"GetReferences",
{ id: id })),
"Failed to retrieve usages for data type of id " + id);

},

/**
* @ngdoc method
* @name umbraco.resources.dataTypeResource#getById
Expand Down
4 changes: 2 additions & 2 deletions src/Umbraco.Web.UI.Client/src/init.js
Expand Up @@ -134,14 +134,14 @@ app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService',

var toRetain = navigationService.retainQueryStrings(currentRouteParams, next.params);

//if toRetain is not null it means that there are missing query strings and we need to update the current params
//if toRetain is not null it means that there are missing query strings and we need to update the current params.
if (toRetain) {
$route.updateParams(toRetain);
}

//check if the location being changed is only due to global/state query strings which means the location change
//isn't actually going to cause a route change.
if (!toRetain && navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) {
if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) {

//The location change will cause a route change, continue the route if the query strings haven't been updated.
$route.reload();
Expand Down