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

Expose and manage system media types #16343

Merged
merged 2 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;

namespace Umbraco.Cms.Api.Management.Controllers.MediaType.Tree;

Expand Down Expand Up @@ -38,6 +39,7 @@ protected override MediaTypeTreeItemResponseModel[] MapTreeItemViewModels(Guid?
if (mediaTypes.TryGetValue(entity.Id, out IMediaType? mediaType))
{
responseModel.Icon = mediaType.Icon ?? responseModel.Icon;
responseModel.IsDeletable = mediaType.IsSystemMediaType() is false;
}

return responseModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ private void Map(IMediaType source, MediaTypeResponseModel target, MapperContext
MediaType = referenceByIdModel,
CompositionType = compositionType,
});
target.IsDeletable = source.IsSystemMediaType() is false;
target.AliasCanBeChanged = source.IsSystemMediaType() is false;
}

// Umbraco.Code.MapAll
Expand Down
14 changes: 13 additions & 1 deletion src/Umbraco.Cms.Api.Management/OpenApi.json
Original file line number Diff line number Diff line change
Expand Up @@ -38587,12 +38587,14 @@
"MediaTypeResponseModel": {
"required": [
"alias",
"aliasCanBeChanged",
"allowedAsRoot",
"allowedMediaTypes",
"compositions",
"containers",
"icon",
"id",
"isDeletable",
"isElement",
"name",
"properties",
Expand Down Expand Up @@ -38680,6 +38682,12 @@
}
]
}
},
"isDeletable": {
"type": "boolean"
},
"aliasCanBeChanged": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -38710,6 +38718,7 @@
"hasChildren",
"icon",
"id",
"isDeletable",
"isFolder",
"name"
],
Expand Down Expand Up @@ -38738,6 +38747,9 @@
},
"icon": {
"type": "string"
},
"isDeletable": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -45027,4 +45039,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ public class MediaTypeResponseModel : ContentTypeResponseModelBase<MediaTypeProp
public IEnumerable<MediaTypeSort> AllowedMediaTypes { get; set; } = Enumerable.Empty<MediaTypeSort>();

public IEnumerable<MediaTypeComposition> Compositions { get; set; } = Enumerable.Empty<MediaTypeComposition>();

public bool IsDeletable { get; set; }

public bool AliasCanBeChanged { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public class MediaTypeTreeItemResponseModel : FolderTreeItemResponseModel
{
public string Icon { get; set; } = string.Empty;

public bool IsDeletable { get; set; }
}
16 changes: 16 additions & 0 deletions src/Umbraco.Core/Models/MediaType.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Strings;
using Umbraco.Extensions;

namespace Umbraco.Cms.Core.Models;

Expand Down Expand Up @@ -45,6 +46,21 @@ public MediaType(IShortStringHelper shortStringHelper, IMediaType parent, string
/// <inheritdoc />
public override ISimpleContentType ToSimple() => new SimpleContentType(this);

/// <inheritdoc />
public override string Alias
{
get => base.Alias;
set
{
if (this.IsSystemMediaType() && value != Alias)
{
throw new InvalidOperationException("Cannot change the alias of a system media type");
}

base.Alias = value;
}
}

/// <inheritdoc />
IMediaType IMediaType.DeepCloneWithResetIdentities(string newAlias) =>
(IMediaType)DeepCloneWithResetIdentities(newAlias);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Umbraco.Cms.Core.Media;

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

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v14/dev)

✅ No longer an issue: Code Duplication

The module no longer contains too many functions with similar structure
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentTypeEditing;
using Umbraco.Cms.Core.PropertyEditors;
Expand Down Expand Up @@ -42,6 +42,11 @@

public async Task<Attempt<IMediaType?, ContentTypeOperationStatus>> UpdateAsync(IMediaType mediaType, MediaTypeUpdateModel model, Guid userKey)
{
if (mediaType.IsSystemMediaType() && mediaType.Alias != model.Alias)
{
return Attempt.FailWithStatus<IMediaType?, ContentTypeOperationStatus>(ContentTypeOperationStatus.NotAllowed, null);
}

Attempt<IMediaType?, ContentTypeOperationStatus> result = await ValidateAndMapForUpdateAsync(mediaType, model);
if (result.Success)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Globalization;

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

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (v14/dev)

✅ Getting better: Primitive Obsession

The ratio of primitive types in function arguments decreases from 59.18% to 58.59%, 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.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.DependencyInjection;
Expand Down Expand Up @@ -620,6 +620,11 @@
return ContentTypeOperationStatus.NotFound;
}

if (CanDelete(item) is false)
{
return ContentTypeOperationStatus.NotAllowed;
}

Delete(item, performingUserId);

scope.Complete();
Expand All @@ -628,6 +633,11 @@

public void Delete(TItem item, int userId = Constants.Security.SuperUserId)
{
if (CanDelete(item) is false)
{
throw new InvalidOperationException("The item was not allowed to be deleted");
}

using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
EventMessages eventMessages = EventMessagesFactory.Get();
Expand Down Expand Up @@ -695,6 +705,10 @@
public void Delete(IEnumerable<TItem> items, int userId = Constants.Security.SuperUserId)
{
TItem[] itemsA = items.ToArray();
if (itemsA.All(CanDelete) is false)
{
throw new InvalidOperationException("One or more items were not allowed to be deleted");
}

using (ICoreScope scope = ScopeProvider.CreateCoreScope())
{
Expand Down Expand Up @@ -750,6 +764,8 @@

protected abstract void DeleteItemsOfTypes(IEnumerable<int> typeIds);

protected virtual bool CanDelete(TItem item) => true;

#endregion

#region Copy
Expand Down
4 changes: 4 additions & 0 deletions src/Umbraco.Core/Services/MediaTypeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services.Changes;
using Umbraco.Cms.Core.Services.Locking;
using Umbraco.Extensions;

namespace Umbraco.Cms.Core.Services;

Expand Down Expand Up @@ -77,6 +78,9 @@ protected override void DeleteItemsOfTypes(IEnumerable<int> typeIds)
}
}

protected override bool CanDelete(IMediaType item)
=> item.IsSystemMediaType() is false;

#region Notifications

protected override SavingNotification<IMediaType> GetSavingNotification(
Expand Down
1 change: 1 addition & 0 deletions src/Umbraco.Web.UI/umbraco/Licenses/umbracoForms.lic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woopsie?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Geez. That explains why Git was complaining so much ... thanks 🤦 it's been removed.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
76E6-AE74-5179-58AE-2328-901F-475B-9C02-BE96-49A6-65AA-C463-35EE-45E7-2A64-05BF-6824-E5C6-D977-6A35-F78F-6E81-A79B-A5C4-AB0E-B701-DE4C-C12E-F1B9-3A0E-8D5D-B6C8-878E-C6E3-CF12-082C-06BB-689F-8AB9-830B-E45A-0D79-2F2D-8D98-02DD-AE4D-FC34-D8E2-CCF5-9B69-C572-6F79-E082-6BDC-9954-F75B-D990-43F8-DE2E-D112-138A-ECAE-ABF5-E558-09C4-8E41-5272-5A03-36A8-DD5A-B0AA-15CA-4660-8ABB-10F9-9E0B-06EE-D419-752B-CE78-9DC3-4F09-2E5B-281C-67CF-A698-F542-A2B9-ACD8-1023-658B-5273-9209-0A0C-0652-2C4F-DEFE-BBD5-7498-9698-C73D-A205-C055-FC76-1119-61BC-C255-7034-A7D9-E609-CB59-3357-999B-A13C-D032-6E9C-29BC-1CFB-2BB8-CB47-805C-362D-D270-5748-BCC8-72D3-B36A-AB2D-BCEA-D401-276F-8E3D-19B4-6A68-CF00-706A-50EA-8942-CA0C-9B30-E771-E5B6-2E8B-C030-8677-D0FC-C6CE-26E9-A469-226F-58D1-92EF-259B-2A85-2518-B894-893E-B7E1-8ACB-7DC3-8518-E467-2D79-143B-1001-A2A4-F3EE-8484-E3BB-F2D8-E744-0B6E-6976-42F0-0F02-DE41-E533-F9A1-8B04-6E23-8BA5-E4E6-6D23-30F3-1177-24CF-E9E1-DA30-C602-7624-C83E-A6C6-C660-DEC2-9AA6-25BA-D18A-312A-5BA4-9BA7-7D8B-872D-C530-3BAE-156E-4E8E-F4B6-4DE2-9880-08ED-CE31-20AF-5327-416F-D46E-176E-4FFE-83E9-6116-255D-B8ED-BEF2-8A80-DBD0
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services;

Expand Down Expand Up @@ -113,4 +114,22 @@ public async Task Can_Edit_Properties()

Assert.AreEqual(2, mediaType.NoGroupPropertyTypes.Count());
}

[TestCase(Constants.Conventions.MediaTypes.File)]
[TestCase(Constants.Conventions.MediaTypes.Folder)]
[TestCase(Constants.Conventions.MediaTypes.Image)]
public async Task Cannot_Change_Alias_Of_System_Media_Type(string mediaTypeAlias)
{
var mediaType = MediaTypeService.Get(mediaTypeAlias);
Assert.IsNotNull(mediaType);

var updateModel = MediaTypeUpdateModel(mediaTypeAlias, $"{mediaTypeAlias}_updated");
var result = await MediaTypeEditingService.UpdateAsync(mediaType, updateModel, Constants.Security.SuperUserKey);

Assert.Multiple(() =>
{
Assert.IsFalse(result.Success);
Assert.AreEqual(ContentTypeOperationStatus.NotAllowed, result.Status);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.

using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Notifications;
Expand Down Expand Up @@ -222,6 +220,34 @@ public void Can_Copy_MediaType_To_New_Parent_By_Performing_Clone()
originalMediaType.PropertyGroups.First(x => x.Name.StartsWith("Media")).Id);
}

[TestCase(Constants.Conventions.MediaTypes.File)]
[TestCase(Constants.Conventions.MediaTypes.Folder)]
[TestCase(Constants.Conventions.MediaTypes.Image)]
public void Cannot_Delete_System_Media_Type(string mediaTypeAlias)
{
// Arrange
// Act
var mediaType = MediaTypeService.Get(mediaTypeAlias);
Assert.IsNotNull(mediaType);

// Assert
Assert.Throws<InvalidOperationException>(() => MediaTypeService.Delete(mediaType));
}

[TestCase(Constants.Conventions.MediaTypes.File)]
[TestCase(Constants.Conventions.MediaTypes.Folder)]
[TestCase(Constants.Conventions.MediaTypes.Image)]
public void Cannot_Change_Alias_Of_System_Media_Type(string mediaTypeAlias)
{
// Arrange
// Act
var mediaType = MediaTypeService.Get(mediaTypeAlias);
Assert.IsNotNull(mediaType);

// Assert
Assert.Throws<InvalidOperationException>(() => mediaType.Alias += "_updated");
}

public class ContentNotificationHandler :
INotificationHandler<MediaMovedToRecycleBinNotification>
{
Expand Down
Loading