From 9f357173c3d88dcd1e437dec0b2dcd2200b9a057 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Mon, 3 Feb 2025 12:50:23 +0100 Subject: [PATCH 1/4] Enforce user start nodes for media uploads through the RTE (#18204) --- .../RichTextEditorPastedImages.cs | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index 8dbe6ad5b3..64e349c245 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -8,12 +8,14 @@ using HtmlAgilityPack; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Exceptions; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -38,6 +40,9 @@ public sealed class RichTextEditorPastedImages private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly string _tempFolderAbsolutePath; private readonly IImageUrlGenerator _imageUrlGenerator; + private readonly IEntityService _entityService; + private readonly IUserService _userService; + private readonly AppCaches _appCaches; private readonly ContentSettings _contentSettings; private readonly Dictionary _uploadedImages = new(); @@ -67,6 +72,7 @@ public sealed class RichTextEditorPastedImages { } + [Obsolete("Use the non-obsolete constructor. Scheduled for removal in v14")] public RichTextEditorPastedImages( IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, @@ -79,6 +85,39 @@ public sealed class RichTextEditorPastedImages IPublishedUrlProvider publishedUrlProvider, IImageUrlGenerator imageUrlGenerator, IOptions contentSettings) + : this( + umbracoContextAccessor, + logger, + hostingEnvironment, + mediaService, + contentTypeBaseServiceProvider, + mediaFileManager, + mediaUrlGenerators, + shortStringHelper, + publishedUrlProvider, + imageUrlGenerator, + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService(), + StaticServiceProvider.Instance.GetRequiredService(), + contentSettings) + { + } + + public RichTextEditorPastedImages( + IUmbracoContextAccessor umbracoContextAccessor, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IMediaService mediaService, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, + IShortStringHelper shortStringHelper, + IPublishedUrlProvider publishedUrlProvider, + IImageUrlGenerator imageUrlGenerator, + IEntityService entityService, + IUserService userService, + AppCaches appCaches, + IOptions contentSettings) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); @@ -92,6 +131,9 @@ public sealed class RichTextEditorPastedImages _shortStringHelper = shortStringHelper; _publishedUrlProvider = publishedUrlProvider; _imageUrlGenerator = imageUrlGenerator; + _entityService = entityService; + _userService = userService; + _appCaches = appCaches; _contentSettings = contentSettings.Value; _tempFolderAbsolutePath = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempImageUploads); @@ -270,7 +312,7 @@ public sealed class RichTextEditorPastedImages : Constants.Conventions.MediaTypes.Image; IMedia mediaFile = mediaParentFolder == Guid.Empty - ? _mediaService.CreateMedia(mediaItemName, Constants.System.Root, mediaType, userId) + ? _mediaService.CreateMedia(mediaItemName, GetDefaultMediaRoot(userId), mediaType, userId) : _mediaService.CreateMedia(mediaItemName, mediaParentFolder, mediaType, userId); var fileInfo = new FileInfo(absoluteTempImagePath); @@ -354,4 +396,11 @@ public sealed class RichTextEditorPastedImages } private bool IsValidPath(string imagePath) => imagePath.StartsWith(_tempFolderAbsolutePath); + + private int GetDefaultMediaRoot(int userId) + { + IUser user = _userService.GetUserById(userId) ?? throw new ArgumentException("User could not be found"); + var userStartNodes = user.CalculateMediaStartNodeIds(_entityService, _appCaches); + return userStartNodes?.FirstOrDefault() ?? Constants.System.Root; + } } From e7411244fde73cbc5d85a536a48caaff8f2b49fd Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Mon, 3 Feb 2025 13:24:58 +0100 Subject: [PATCH 2/4] Show notifications menu only to users with permission for the feature. (#18184) --- src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs index 4cdd8cef7c..0ef895e207 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs @@ -317,13 +317,7 @@ public class ContentTreeController : ContentTreeControllerBase, ISearchableTreeW if (_emailSender.CanSendRequiredEmail()) { - menu.Items.Add(new MenuItem("notify", LocalizedTextService) - { - Icon = "icon-megaphone", - SeparatorBefore = true, - OpensDialog = true, - UseLegacyIcon = false - }); + AddActionNode(item, menu, hasSeparator: true, opensDialog: true, useLegacyIcon: false); } if ((item is DocumentEntitySlim documentEntity && documentEntity.IsContainer) == false) From b4a9dc0770a389b7e92a362e86e1e7b3f8490114 Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 3 Feb 2025 19:48:08 +0100 Subject: [PATCH 3/4] V13: Fix members while using basic auth. (#18206) * Flow additional identities to new principal * Add extension to more easily get member identity * Ensure the member is used instead of the backoffice user in `MemberManager` * Update snippet * Fix the comment that I broke * Update src/Umbraco.Web.Common/Extensions/MemberClaimsPrincipalExtensions.cs Co-authored-by: Andy Butland --------- Co-authored-by: Andy Butland --- .../Snippets/LoginStatus.cshtml | 4 +-- .../Extensions/HttpContextExtensions.cs | 11 ++++++-- .../MemberClaimsPrincipalExtensions.cs | 18 ++++++++++++ .../Security/MemberManager.cs | 28 ++++++++++++------- 4 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Web.Common/Extensions/MemberClaimsPrincipalExtensions.cs diff --git a/src/Umbraco.Core/EmbeddedResources/Snippets/LoginStatus.cshtml b/src/Umbraco.Core/EmbeddedResources/Snippets/LoginStatus.cshtml index 8f5477bca4..aa70da23c8 100644 --- a/src/Umbraco.Core/EmbeddedResources/Snippets/LoginStatus.cshtml +++ b/src/Umbraco.Core/EmbeddedResources/Snippets/LoginStatus.cshtml @@ -5,7 +5,7 @@ @using Umbraco.Extensions @{ - var isLoggedIn = Context.User?.Identity?.IsAuthenticated ?? false; + var isLoggedIn = Context.User.GetMemberIdentity()?.IsAuthenticated ?? false; var logoutModel = new PostRedirectModel(); // You can modify this to redirect to a different URL instead of the current one logoutModel.RedirectUrl = null; @@ -15,7 +15,7 @@ {