diff --git a/src/Umbraco.Core/Constants-Telemetry.cs b/src/Umbraco.Core/Constants-Telemetry.cs index f8a382b0bb..0e7c96d250 100644 --- a/src/Umbraco.Core/Constants-Telemetry.cs +++ b/src/Umbraco.Core/Constants-Telemetry.cs @@ -29,5 +29,6 @@ public static partial class Constants public static string DatabaseProvider = "DatabaseProvider"; public static string CurrentServerRole = "CurrentServerRole"; public static string RuntimeMode = "RuntimeMode"; + public static string BackofficeExternalLoginProviderCount = "BackofficeExternalLoginProviderCount"; } } diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml index d85e6d7eb4..035d0e42f1 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml @@ -3010,7 +3010,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont We will send: diff --git a/src/Umbraco.Infrastructure/Telemetry/Interfaces/IDetailedTelemetryProvider.cs b/src/Umbraco.Infrastructure/Telemetry/Interfaces/IDetailedTelemetryProvider.cs index b21b216e68..e6ce3b005f 100644 --- a/src/Umbraco.Infrastructure/Telemetry/Interfaces/IDetailedTelemetryProvider.cs +++ b/src/Umbraco.Infrastructure/Telemetry/Interfaces/IDetailedTelemetryProvider.cs @@ -2,7 +2,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Infrastructure.Telemetry.Interfaces; -internal interface IDetailedTelemetryProvider +public interface IDetailedTelemetryProvider { IEnumerable GetInformation(); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs index 3d93f9af6c..a9f81344a0 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/TinyMceController.cs @@ -26,6 +26,19 @@ public class TinyMceController : UmbracoAuthorizedApiController private readonly IIOHelper _ioHelper; private readonly IShortStringHelper _shortStringHelper; + private readonly Dictionary _fileContentTypeMappings = + new() + { + { "image/png", "png" }, + { "image/jpeg", "jpg" }, + { "image/gif", "gif" }, + { "image/bmp", "bmp" }, + { "image/x-icon", "ico" }, + { "image/svg+xml", "svg" }, + { "image/tiff", "tiff" }, + { "image/webp", "webp" }, + }; + public TinyMceController( IHostingEnvironment hostingEnvironment, IShortStringHelper shortStringHelper, @@ -43,16 +56,6 @@ public class TinyMceController : UmbracoAuthorizedApiController [HttpPost] public async Task UploadImage(List file) { - // Create an unique folder path to help with concurrent users to avoid filename clash - var imageTempPath = - _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempImageUploads + "/" + Guid.NewGuid()); - - // Ensure image temp path exists - if (Directory.Exists(imageTempPath) == false) - { - Directory.CreateDirectory(imageTempPath); - } - // Must have a file if (file.Count == 0) { @@ -65,13 +68,36 @@ public class TinyMceController : UmbracoAuthorizedApiController return new UmbracoProblemResult("Only one file can be uploaded at a time", HttpStatusCode.BadRequest); } + // Create an unique folder path to help with concurrent users to avoid filename clash + var imageTempPath = + _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempImageUploads + "/" + Guid.NewGuid()); + + // Ensure image temp path exists + if (Directory.Exists(imageTempPath) == false) + { + Directory.CreateDirectory(imageTempPath); + } + IFormFile formFile = file.First(); // Really we should only have one file per request to this endpoint // var file = result.FileData[0]; - var fileName = formFile.FileName.Trim(new[] { '\"' }).TrimEnd(); + var fileName = formFile.FileName.Trim(new[] {'\"'}).TrimEnd(); var safeFileName = fileName.ToSafeFileName(_shortStringHelper); - var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLowerInvariant(); + string ext; + var fileExtensionIndex = safeFileName.LastIndexOf('.'); + if (fileExtensionIndex is not -1) + { + ext = safeFileName.Substring(fileExtensionIndex + 1).ToLowerInvariant(); + } + else + { + _fileContentTypeMappings.TryGetValue(formFile.ContentType, out var fileExtension); + ext = fileExtension ?? string.Empty; + + // safeFileName will not have a file extension, so we need to add it back + safeFileName += $".{ext}"; + } if (_contentSettings.IsFileAllowedForUpload(ext) == false || _imageUrlGenerator.IsSupportedImageFormat(ext) == false) diff --git a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs index 6d3ff7edda..1844cf5885 100644 --- a/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs +++ b/src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilder.BackOfficeIdentity.cs @@ -11,7 +11,9 @@ using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Security; +using Umbraco.Cms.Infrastructure.Telemetry.Interfaces; using Umbraco.Cms.Web.BackOffice.Security; +using Umbraco.Cms.Web.BackOffice.Telemetry; using Umbraco.Cms.Web.Common.AspNetCore; using Umbraco.Cms.Web.Common.Security; @@ -65,6 +67,7 @@ public static partial class UmbracoBuilderExtensions services.TryAddScoped(); services.TryAddSingleton(); services.TryAddSingleton(); + services.AddTransient(); return new BackOfficeIdentityBuilder(services); } diff --git a/src/Umbraco.Web.BackOffice/Telemetry/ExternalLoginTelemetryProvider.cs b/src/Umbraco.Web.BackOffice/Telemetry/ExternalLoginTelemetryProvider.cs new file mode 100644 index 0000000000..21a59796b3 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Telemetry/ExternalLoginTelemetryProvider.cs @@ -0,0 +1,22 @@ +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Infrastructure.Telemetry.Interfaces; +using Umbraco.Cms.Web.BackOffice.Security; + +namespace Umbraco.Cms.Web.BackOffice.Telemetry; + +public class ExternalLoginTelemetryProvider : IDetailedTelemetryProvider +{ + private readonly IBackOfficeExternalLoginProviders _externalLoginProviders; + + public ExternalLoginTelemetryProvider(IBackOfficeExternalLoginProviders externalLoginProviders) + { + _externalLoginProviders = externalLoginProviders; + } + + public IEnumerable GetInformation() + { + IEnumerable providers = _externalLoginProviders.GetBackOfficeProvidersAsync().GetAwaiter().GetResult(); + yield return new UsageInformation(Constants.Telemetry.BackofficeExternalLoginProviderCount, providers.Count()); + } +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs index bc4b68b508..a310caa164 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs @@ -50,7 +50,8 @@ public class TelemetryServiceTests : UmbracoIntegrationTest Constants.Telemetry.IsDebug, Constants.Telemetry.DatabaseProvider, Constants.Telemetry.CurrentServerRole, - Constants.Telemetry.RuntimeMode, + Constants.Telemetry.BackofficeExternalLoginProviderCount, + Constants.Telemetry.RuntimeMode }; MetricsConsentService.SetConsentLevel(TelemetryLevel.Detailed); diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs index b287f6fbf3..01ca9284f7 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs @@ -19,6 +19,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.Persistence; +using Umbraco.Cms.Web.Common.DependencyInjection; using Directory = Lucene.Net.Store.Directory; using StaticServiceProvider = Umbraco.Cms.Core.DependencyInjection.StaticServiceProvider;