diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs index be13776da8..d4cfcb9f37 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs @@ -9,6 +9,7 @@ using Umbraco.Cms.Web.Common.DependencyInjection; namespace Umbraco.Cms.Core.Configuration.Grid; +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridConfig : IGridConfig { public GridConfig( diff --git a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs index 5a82d860bb..290836d31e 100644 --- a/src/Umbraco.Core/Configuration/Models/ContentSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/ContentSettings.cs @@ -158,6 +158,7 @@ public class ContentSettings internal const bool StaticDisableDeleteWhenReferenced = false; internal const bool StaticDisableUnpublishWhenReferenced = false; internal const bool StaticAllowEditInvariantFromNonDefault = false; + internal const bool StaticShowDomainWarnings = true; /// /// Gets or sets a value for the content notification settings. @@ -262,4 +263,15 @@ public class ContentSettings /// [DefaultValue(StaticDisallowedUploadFiles)] public string[] DisallowedUploadedFileExtensions { get; set; } = StaticDisallowedUploadFiles.Split(','); + + /// + /// Gets or sets the allowed external host for media. If empty only relative paths are allowed. + /// + public string[] AllowedMediaHosts { get; set; } = Array.Empty(); + + /// + /// Gets or sets a value indicating whether to show domain warnings. + /// + [DefaultValue(StaticShowDomainWarnings)] + public bool ShowDomainWarnings { get; set; } = StaticShowDomainWarnings; } diff --git a/src/Umbraco.Core/PropertyEditors/GridEditor.cs b/src/Umbraco.Core/PropertyEditors/GridEditor.cs index d661fa9704..2ce26bdb79 100644 --- a/src/Umbraco.Core/PropertyEditors/GridEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/GridEditor.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Core.Configuration.Grid; namespace Umbraco.Cms.Core.PropertyEditors; [DataContract] +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridEditor : IGridEditorConfig { public GridEditor() diff --git a/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs index 055f4fea4d..92a206065b 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaPickerConfiguration.cs @@ -3,6 +3,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// Represents the configuration for the media picker value editor. /// +[Obsolete("Please use the MediaPicker3 instead, will be removed in V13")] public class MediaPickerConfiguration : IIgnoreUserStartNodesConfig { [ConfigurationField("multiPicker", "Pick multiple items", "boolean")] diff --git a/src/Umbraco.Core/PropertyEditors/MediaPickerConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/MediaPickerConfigurationEditor.cs index e4fa6f5c71..bd32f0067b 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaPickerConfigurationEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaPickerConfigurationEditor.cs @@ -11,6 +11,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// Represents the configuration editor for the media picker value editor. /// +[Obsolete("Please use the MediaPicker3 instead, will be removed in V13")] public class MediaPickerConfigurationEditor : ConfigurationEditor { // Scheduled for removal in v12 diff --git a/src/Umbraco.Core/PropertyEditors/NestedContentConfiguration.cs b/src/Umbraco.Core/PropertyEditors/NestedContentConfiguration.cs index fdef902b58..01656fe78f 100644 --- a/src/Umbraco.Core/PropertyEditors/NestedContentConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/NestedContentConfiguration.cs @@ -5,6 +5,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// Represents the configuration for the nested content value editor. /// +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentConfiguration { [ConfigurationField("contentTypes", "Element Types", "views/propertyeditors/nestedcontent/nestedcontent.doctypepicker.html", Description = "Select the Element Types to use as models for the items.")] diff --git a/src/Umbraco.Core/PropertyEditors/NestedContentConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/NestedContentConfigurationEditor.cs index cef30f3f4f..aad5529e1f 100644 --- a/src/Umbraco.Core/PropertyEditors/NestedContentConfigurationEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/NestedContentConfigurationEditor.cs @@ -11,6 +11,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// Represents the configuration editor for the nested content value editor. /// +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentConfigurationEditor : ConfigurationEditor { // Scheduled for removal in v12 diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs index 06269ef8e8..5eac35e67d 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MediaPickerValueConverter.cs @@ -9,6 +9,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters; /// The media picker property value converter. /// [DefaultPropertyValueConverter] +[Obsolete("Please use the MediaPicker3 instead, will be removed in V13")] public class MediaPickerValueConverter : PropertyValueConverterBase { // hard-coding "image" here but that's how it works at UI level too diff --git a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs index 71c0929e39..e843d7954b 100644 --- a/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs +++ b/src/Umbraco.Examine.Lucene/BackOfficeExamineSearcher.cs @@ -169,7 +169,10 @@ public class BackOfficeExamineSearcher : IBackOfficeExamineSearcher var allLangs = _languageService.GetAllLanguages().Select(x => x.IsoCode.ToLowerInvariant()).ToList(); // the chars [*-_] in the query will mess everything up so let's remove those - query = Regex.Replace(query, "[\\*\\-_]", string.Empty); + // However we cannot just remove - and _ since these signify a space, so we instead replace them with that. + query = Regex.Replace(query, "[\\*]", string.Empty); + query = Regex.Replace(query, "[\\-_]", " "); + //check if text is surrounded by single or double quotes, if so, then exact match var surroundedByQuotes = Regex.IsMatch(query, "^\".*?\"$") diff --git a/src/Umbraco.Infrastructure/Deploy/GridCellValueConnectorExtensions.cs b/src/Umbraco.Infrastructure/Deploy/GridCellValueConnectorExtensions.cs index fec3c07e3c..d421dcd771 100644 --- a/src/Umbraco.Infrastructure/Deploy/GridCellValueConnectorExtensions.cs +++ b/src/Umbraco.Infrastructure/Deploy/GridCellValueConnectorExtensions.cs @@ -8,6 +8,7 @@ namespace Umbraco.Cms.Core.Deploy; /// /// These extension methods will be removed in Umbraco 13. /// +[Obsolete("The grid is obsolete, will be removed in V13")] public static class GridCellValueConnectorExtensions { /// diff --git a/src/Umbraco.Infrastructure/Models/GridValue.cs b/src/Umbraco.Infrastructure/Models/GridValue.cs index 29e88ea564..e8eca571ce 100644 --- a/src/Umbraco.Infrastructure/Models/GridValue.cs +++ b/src/Umbraco.Infrastructure/Models/GridValue.cs @@ -8,6 +8,7 @@ namespace Umbraco.Cms.Core.Models; /// /// A model representing the value saved for the grid /// +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridValue { [JsonProperty("name")] @@ -43,6 +44,7 @@ public class GridValue public JToken? Config { get; set; } } + [Obsolete("The grid is obsolete, will be removed in V13")] public class GridArea { [JsonProperty("grid")] @@ -58,6 +60,7 @@ public class GridValue public JToken? Config { get; set; } } + [Obsolete("The grid is obsolete, will be removed in V13")] public class GridControl { [JsonProperty("value")] @@ -73,6 +76,7 @@ public class GridValue public JToken? Config { get; set; } } + [Obsolete("The grid is obsolete, will be removed in V13")] public class GridEditor { [JsonProperty("alias")] diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridConfiguration.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridConfiguration.cs index cd1ba4e412..ea72156a12 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridConfiguration.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridConfiguration.cs @@ -8,6 +8,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// Represents the configuration for the grid value editor. /// +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridConfiguration : IIgnoreUserStartNodesConfig { // TODO: Make these strongly typed, for now this works though diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs index aadd25273d..d2b8fba42d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridConfigurationEditor.cs @@ -13,6 +13,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// Represents the configuration editor for the grid value editor. /// +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridConfigurationEditor : ConfigurationEditor { // Scheduled for removal in v12 @@ -31,6 +32,7 @@ public class GridConfigurationEditor : ConfigurationEditor } } +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridValidator : IValueValidator { public IEnumerable Validate(object? rawValue, string? valueType, object? dataTypeConfiguration) @@ -51,6 +53,7 @@ public class GridValidator : IValueValidator } } +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridEditorModel { public GridEditorTemplateModel[]? Templates { get; set; } @@ -58,11 +61,13 @@ public class GridEditorModel public int Columns { get; set; } } +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridEditorTemplateModel { public GridEditorSectionModel[]? Sections { get; set; } } +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridEditorSectionModel { public int Grid { get; set; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs index ed8ef423cf..4fc868256b 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyEditor.cs @@ -31,6 +31,7 @@ namespace Umbraco.Cms.Core.PropertyEditors Group = Constants.PropertyEditors.Groups.RichContent, ValueEditorIsReusable = false, IsDeprecated = true)] + [Obsolete("The grid is obsolete, will be removed in V13")] public class GridPropertyEditor : DataEditor { private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs index 70eb9e3b7b..e0a13d2e9d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/GridPropertyIndexValueFactory.cs @@ -14,6 +14,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// Parses the grid value into indexable values /// + [Obsolete("The grid is obsolete, will be removed in V13")] public class GridPropertyIndexValueFactory : IPropertyIndexValueFactory { public IEnumerable>> GetIndexValues(IProperty property, string? culture, string? segment, bool published) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs index 6a037cda11..2eed35c8aa 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPickerPropertyEditor.cs @@ -28,6 +28,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; Icon = Constants.Icons.MediaImage, IsDeprecated = false, ValueEditorIsReusable = true)] +[Obsolete("Please use the MediaPicker3 instead, will be removed in V13")] public class MediaPickerPropertyEditor : DataEditor { private readonly IEditorConfigurationParser _editorConfigurationParser; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs index e30c400254..bf5781079a 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyEditor.cs @@ -28,6 +28,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; Icon = "icon-thumbnail-list", ValueEditorIsReusable = false, IsDeprecated = true)] +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentPropertyEditor : DataEditor { public const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs index b42dc2d155..c46d680273 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/NestedContentPropertyHandler.cs @@ -10,6 +10,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// /// A handler for NestedContent used to bind to notifications /// +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentPropertyHandler : ComplexPropertyEditorContentNotificationHandler { protected override string EditorAlias => Constants.PropertyEditors.Aliases.NestedContent; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/GridValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/GridValueConverter.cs index 48aa8ccf4f..41eb196ff1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/GridValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/GridValueConverter.cs @@ -14,6 +14,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters; /// This ensures that the grid config is merged in with the front-end value /// [DefaultPropertyValueConverter(typeof(JsonValueConverter))] // this shadows the JsonValueConverter +[Obsolete("The grid is obsolete, will be removed in V13")] public class GridValueConverter : JsonValueConverter { private readonly IGridConfig _config; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index aa449cb259..bbe1c92892 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -16,6 +16,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters; /// content. /// [DefaultPropertyValueConverter(typeof(JsonValueConverter))] +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentManyValueConverter : NestedContentValueConverterBase { private readonly IProfilingLogger _proflog; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs index 501aa5b024..1ee2759932 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentSingleValueConverter.cs @@ -15,6 +15,7 @@ namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters; /// content. /// [DefaultPropertyValueConverter(typeof(JsonValueConverter))] +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentSingleValueConverter : NestedContentValueConverterBase { private readonly IProfilingLogger _proflog; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs index 07abcb722a..ec4c62d297 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/NestedContentValueConverterBase.cs @@ -8,6 +8,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.PropertyEditors.ValueConverters; +[Obsolete("Nested content is obsolete, will be removed in V13")] public abstract class NestedContentValueConverterBase : PropertyValueConverterBase { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index 6c124c14a9..5aab77ed47 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -5,8 +5,10 @@ using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Actions; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.ContentApps; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Dictionary; @@ -65,6 +67,7 @@ public class ContentController : ContentControllerBase private readonly ISqlContext _sqlContext; private readonly IUmbracoMapper _umbracoMapper; private readonly IUserService _userService; + private readonly ContentSettings _contentSettings; [ActivatorUtilitiesConstructor] public ContentController( @@ -91,7 +94,8 @@ public class ContentController : ContentControllerBase ICoreScopeProvider scopeProvider, IAuthorizationService authorizationService, IContentVersionService contentVersionService, - ICultureImpactFactory cultureImpactFactory) + ICultureImpactFactory cultureImpactFactory, + IOptions contentSettings) : base(cultureDictionary, loggerFactory, shortStringHelper, eventMessages, localizedTextService, serializer) { _propertyEditors = propertyEditors; @@ -115,7 +119,63 @@ public class ContentController : ContentControllerBase _logger = loggerFactory.CreateLogger(); _scopeProvider = scopeProvider; _allLangs = new Lazy>(() => - _localizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase)); + _localizationService.GetAllLanguages().ToDictionary(x => x.IsoCode, x => x, StringComparer.InvariantCultureIgnoreCase)); + _contentSettings = contentSettings.Value; + } + + [Obsolete("Use constructor that accepts ContentSettings as a parameter, scheduled for removal in V13")] + public ContentController( + ICultureDictionary cultureDictionary, + ILoggerFactory loggerFactory, + IShortStringHelper shortStringHelper, + IEventMessagesFactory eventMessages, + ILocalizedTextService localizedTextService, + PropertyEditorCollection propertyEditors, + IContentService contentService, + IUserService userService, + IBackOfficeSecurityAccessor backofficeSecurityAccessor, + IContentTypeService contentTypeService, + IUmbracoMapper umbracoMapper, + IPublishedUrlProvider publishedUrlProvider, + IDomainService domainService, + IDataTypeService dataTypeService, + ILocalizationService localizationService, + IFileService fileService, + INotificationService notificationService, + ActionCollection actionCollection, + ISqlContext sqlContext, + IJsonSerializer serializer, + ICoreScopeProvider scopeProvider, + IAuthorizationService authorizationService, + IContentVersionService contentVersionService, + ICultureImpactFactory cultureImpactFactory) + : this( + cultureDictionary, + loggerFactory, + shortStringHelper, + eventMessages, + localizedTextService, + propertyEditors, + contentService, + userService, + backofficeSecurityAccessor, + contentTypeService, + umbracoMapper, + publishedUrlProvider, + domainService, + dataTypeService, + localizationService, + fileService, + notificationService, + actionCollection, + sqlContext, + serializer, + scopeProvider, + authorizationService, + contentVersionService, + cultureImpactFactory, + StaticServiceProvider.Instance.GetRequiredService>()) + { } [Obsolete("Use constructor that accepts ICultureImpactService as a parameter, scheduled for removal in V12")] @@ -1710,6 +1770,11 @@ public class ContentController : ContentControllerBase /// internal void AddDomainWarnings(IContent? persistedContent, string[]? culturesPublished, SimpleNotificationModel globalNotifications) { + if (_contentSettings.ShowDomainWarnings is false) + { + return; + } + // Don't try to verify if no cultures were published if (culturesPublished is null) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 8f7901b2b4..e718696ae3 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -1,10 +1,14 @@ using System.Web; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.Controllers; @@ -17,13 +21,31 @@ public class ImagesController : UmbracoAuthorizedApiController { private readonly IImageUrlGenerator _imageUrlGenerator; private readonly MediaFileManager _mediaFileManager; + private ContentSettings _contentSettings; + [Obsolete("Use non obsolete-constructor. Scheduled for removal in Umbraco 13.")] public ImagesController( MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) + : this(mediaFileManager, + imageUrlGenerator, + StaticServiceProvider.Instance.GetRequiredService>()) + { + + } + + [ActivatorUtilitiesConstructor] + public ImagesController( + MediaFileManager mediaFileManager, + IImageUrlGenerator imageUrlGenerator, + IOptionsMonitor contentSettingsMonitor) { _mediaFileManager = mediaFileManager; _imageUrlGenerator = imageUrlGenerator; + _contentSettings = contentSettingsMonitor.CurrentValue; + + contentSettingsMonitor.OnChange(x => _contentSettings = x); + } /// @@ -58,7 +80,7 @@ public class ImagesController : UmbracoAuthorizedApiController var ext = Path.GetExtension(encodedImagePath); // check if imagePath is local to prevent open redirect - if (!Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative)) + if (!IsAllowed(encodedImagePath)) { return Unauthorized(); } @@ -90,12 +112,33 @@ public class ImagesController : UmbracoAuthorizedApiController ImageCropMode = ImageCropMode.Max, CacheBusterValue = rnd }); - if (Url.IsLocalUrl(imageUrl)) + + if (imageUrl is not null) { - return new LocalRedirectResult(imageUrl, false); + return new RedirectResult(imageUrl, false); } - return Unauthorized(); + return NotFound(); + } + + private bool IsAllowed(string encodedImagePath) + { + if(Uri.IsWellFormedUriString(encodedImagePath, UriKind.Relative)) + { + return true; + } + + var builder = new UriBuilder(encodedImagePath); + + foreach (var allowedMediaHost in _contentSettings.AllowedMediaHosts) + { + if (string.Equals(builder.Host, allowedMediaHost, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + + return false; } /// diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs index 6cb8deb71b..3b2124d560 100644 --- a/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs +++ b/src/Umbraco.Web.BackOffice/PropertyEditors/NestedContentController.cs @@ -12,6 +12,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.PropertyEditors; [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] +[Obsolete("Nested content is obsolete, will be removed in V13")] public class NestedContentController : UmbracoAuthorizedJsonController { private readonly IContentTypeService _contentTypeService; diff --git a/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs b/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs index 10986d3882..9cd0a975a1 100644 --- a/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs +++ b/src/Umbraco.Web.Common/Controllers/PublishedRequestFilterAttribute.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Web.Common.ActionsResults; using Umbraco.Cms.Web.Common.Routing; namespace Umbraco.Cms.Web.Common.Controllers; @@ -15,9 +16,11 @@ internal class PublishedRequestFilterAttribute : ResultFilterAttribute /// public override void OnResultExecuting(ResultExecutingContext context) { - if (context.Result is not null) + if (context.Result is MaintenanceResult) { - // If the result is already set, we just skip the execution + // If the result is already set to a maintenance result we can't do anything + // Since the umbraco pipeline has not run. + // Fortunately we don't need to either. return; } diff --git a/src/Umbraco.Web.Common/Extensions/GridTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/GridTemplateExtensions.cs index 2c959ab441..8edabf8363 100644 --- a/src/Umbraco.Web.Common/Extensions/GridTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/GridTemplateExtensions.cs @@ -4,6 +4,7 @@ using Umbraco.Cms.Core.Models.PublishedContent; namespace Umbraco.Extensions; +[Obsolete("The grid is obsolete, will be removed in V13")] public static class GridTemplateExtensions { public static IHtmlContent GetGridHtml(this IHtmlHelper html, IPublishedProperty property, string framework = "bootstrap3") diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js b/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js index 28a781a8ec..8b42bdbe27 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/user.controller.js @@ -3,7 +3,7 @@ angular.module("umbraco.install").controller("Umbraco.Install.UserController", f $scope.majorVersion = Umbraco.Sys.ServerVariables.application.version; $scope.passwordPattern = /.*/; $scope.installer.current.model.subscribeToNewsLetter = $scope.installer.current.model.subscribeToNewsLetter || false; - setTelemetryLevelAndDescription($scope.installer.current.model.telemetryIndex ?? 1); + setTelemetryLevelAndDescription($scope.installer.current.model.telemetryIndex ?? 2); if ($scope.installer.current.model.minNonAlphaNumericLength > 0) { var exp = ""; diff --git a/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs index d57a9345af..d4fd13489b 100644 --- a/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs +++ b/src/Umbraco.Web.Website/Controllers/UmbLoginStatusController.cs @@ -38,6 +38,8 @@ public class UmbLoginStatusController : SurfaceController return CurrentUmbracoPage(); } + MergeRouteValuesToModel(model); + var isLoggedIn = HttpContext.User.Identity?.IsAuthenticated ?? false; if (isLoggedIn) @@ -56,4 +58,16 @@ public class UmbLoginStatusController : SurfaceController // Redirect to current page by default. return RedirectToCurrentUmbracoPage(); } + + /// + /// We pass in values via encrypted route values so they cannot be tampered with and merge them into the model for use + /// + /// + private void MergeRouteValuesToModel(PostRedirectModel model) + { + if (RouteData.Values.TryGetValue(nameof(PostRedirectModel.RedirectUrl), out var redirectUrl) && redirectUrl is not null) + { + model.RedirectUrl = redirectUrl.ToString(); + } + } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs index 7046972d83..0a1b2f3240 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/ContentControllerTests.cs @@ -4,9 +4,11 @@ using System.Globalization; using System.Linq; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Actions; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Mapping; @@ -266,7 +268,8 @@ public class ContentControllerTests Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of()); + Mock.Of(), + new OptionsWrapper(new ContentSettings())); return controller; }