diff --git a/src/Umbraco.Core/Mapping/IUmbracoMapper.cs b/src/Umbraco.Core/Mapping/IUmbracoMapper.cs index 5ca86eaab3..1851cec29a 100644 --- a/src/Umbraco.Core/Mapping/IUmbracoMapper.cs +++ b/src/Umbraco.Core/Mapping/IUmbracoMapper.cs @@ -70,7 +70,7 @@ namespace Umbraco.Cms.Core.Mapping /// The target type. /// The source object. /// The target object. - TTarget? Map(TSource source); + TTarget? Map(TSource? source); /// /// Maps a source object to a new target object. diff --git a/src/Umbraco.Core/Models/ContentEditing/DataTypeConfigurationFieldSave.cs b/src/Umbraco.Core/Models/ContentEditing/DataTypeConfigurationFieldSave.cs index d75e5863f3..a82a6eb257 100644 --- a/src/Umbraco.Core/Models/ContentEditing/DataTypeConfigurationFieldSave.cs +++ b/src/Umbraco.Core/Models/ContentEditing/DataTypeConfigurationFieldSave.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing /// Gets or sets the configuration field key. /// [DataMember(Name = "key", IsRequired = true)] - public string? Key { get; set; } + public string Key { get; set; } = null!; /// /// Gets or sets the configuration field value. diff --git a/src/Umbraco.Core/Models/ContentEditing/MacroDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/MacroDisplay.cs index 5cb65c00cc..f794143aab 100644 --- a/src/Umbraco.Core/Models/ContentEditing/MacroDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/MacroDisplay.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing /// Gets or sets the view. /// [DataMember(Name = "view")] - public string? View { get; set; } + public string View { get; set; } = null!; /// /// Gets or sets the parameters. diff --git a/src/Umbraco.Core/Models/ContentEditing/MacroParameterDisplay.cs b/src/Umbraco.Core/Models/ContentEditing/MacroParameterDisplay.cs index 134740aa4a..8cd630d66f 100644 --- a/src/Umbraco.Core/Models/ContentEditing/MacroParameterDisplay.cs +++ b/src/Umbraco.Core/Models/ContentEditing/MacroParameterDisplay.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing /// Gets or sets the key. /// [DataMember(Name = "key")] - public string? Key { get; set; } + public string Key { get; set; } = null!; /// /// Gets or sets the label. @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing /// Gets or sets the editor. /// [DataMember(Name = "editor")] - public string? Editor { get; set; } + public string Editor { get; set; } = null!; /// /// Gets or sets the id. diff --git a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs index 0255cfd40e..532bda8e13 100644 --- a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs +++ b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs @@ -30,12 +30,12 @@ namespace Umbraco.Cms.Core.Models.Editors /// /// Gets or sets the unique identifier of the content owning the property. /// - public Guid ContentKey { get; set; } + public Guid? ContentKey { get; set; } /// /// Gets or sets the unique identifier of the property type. /// - public Guid PropertyTypeKey { get; set; } + public Guid? PropertyTypeKey { get; set; } /// /// Gets or sets the uploaded files. diff --git a/src/Umbraco.Core/Models/PropertyTagsExtensions.cs b/src/Umbraco.Core/Models/PropertyTagsExtensions.cs index ebcf073a63..75b0ea3cb3 100644 --- a/src/Umbraco.Core/Models/PropertyTagsExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyTagsExtensions.cs @@ -190,7 +190,7 @@ namespace Umbraco.Extensions /// This is used both by the content repositories to initialize a property with some tag values, and by the /// content controllers to update a property with values received from the property editor. /// - public static void SetTagsValue(this IProperty property, IJsonSerializer serializer, object value, TagConfiguration tagConfiguration, string culture) + public static void SetTagsValue(this IProperty property, IJsonSerializer serializer, object? value, TagConfiguration? tagConfiguration, string? culture) { if (property == null) throw new ArgumentNullException(nameof(property)); if (tagConfiguration == null) throw new ArgumentNullException(nameof(tagConfiguration)); diff --git a/src/Umbraco.Core/Models/RelationType.cs b/src/Umbraco.Core/Models/RelationType.cs index fcc5ab699e..4c4c69c5f1 100644 --- a/src/Umbraco.Core/Models/RelationType.cs +++ b/src/Umbraco.Core/Models/RelationType.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Core.Models } - public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType, bool isDependency) + public RelationType(string? name, string? alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType, bool isDependency) { if (name == null) throw new ArgumentNullException(nameof(name)); if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name)); diff --git a/src/Umbraco.Core/Models/UserTourStatus.cs b/src/Umbraco.Core/Models/UserTourStatus.cs index 1b83721f02..72e0a81cba 100644 --- a/src/Umbraco.Core/Models/UserTourStatus.cs +++ b/src/Umbraco.Core/Models/UserTourStatus.cs @@ -47,12 +47,12 @@ namespace Umbraco.Cms.Core.Models return Alias.GetHashCode(); } - public static bool operator ==(UserTourStatus left, UserTourStatus right) + public static bool operator ==(UserTourStatus? left, UserTourStatus? right) { return Equals(left, right); } - public static bool operator !=(UserTourStatus left, UserTourStatus right) + public static bool operator !=(UserTourStatus? left, UserTourStatus? right) { return !Equals(left, right); } diff --git a/src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs index 2307ff2b8b..a7187ec1ca 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Core.Persistence.Repositories /// /// /// - IMemberGroup? GetByName(string name); + IMemberGroup? GetByName(string? name); /// /// Creates the new member group if it doesn't already exist diff --git a/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs b/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs index d243bb79bf..b4d41f8e33 100644 --- a/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IConfigurationEditor.cs @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// /// The values posted by the configuration editor. /// The current configuration object. - object? FromConfigurationEditor(IDictionary editorValues, object configuration); + object? FromConfigurationEditor(IDictionary? editorValues, object? configuration); /// /// Converts the configuration object to values for the configuration editor. diff --git a/src/Umbraco.Core/Services/DashboardService.cs b/src/Umbraco.Core/Services/DashboardService.cs index c3e01a974f..b5e4755122 100644 --- a/src/Umbraco.Core/Services/DashboardService.cs +++ b/src/Umbraco.Core/Services/DashboardService.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Core.Services /// - public IEnumerable> GetDashboards(string section, IUser currentUser) + public IEnumerable> GetDashboards(string section, IUser? currentUser) { var tabs = new List>(); var tabId = 0; @@ -56,14 +56,14 @@ namespace Umbraco.Cms.Core.Services } /// - public IDictionary>> GetDashboards(IUser currentUser) + public IDictionary>> GetDashboards(IUser? currentUser) { return _sectionService.GetSections().ToDictionary(x => x.Alias, x => GetDashboards(x.Alias, currentUser)); } - private bool CheckUserAccessByRules(IUser user, ISectionService sectionService, IEnumerable rules) + private bool CheckUserAccessByRules(IUser? user, ISectionService sectionService, IEnumerable rules) { - if (user.Id == Constants.Security.SuperUserId) + if (user?.Id == Constants.Security.SuperUserId) return true; var (denyRules, grantRules, grantBySectionRules) = GroupRules(rules); diff --git a/src/Umbraco.Core/Services/IDashboardService.cs b/src/Umbraco.Core/Services/IDashboardService.cs index 7aba03e106..70e3410627 100644 --- a/src/Umbraco.Core/Services/IDashboardService.cs +++ b/src/Umbraco.Core/Services/IDashboardService.cs @@ -14,14 +14,14 @@ namespace Umbraco.Cms.Core.Services /// /// /// - IEnumerable> GetDashboards(string section, IUser currentUser); + IEnumerable> GetDashboards(string section, IUser? currentUser); /// /// Gets all dashboards, organized by section, for a user. /// /// /// - IDictionary>> GetDashboards(IUser currentUser); + IDictionary>> GetDashboards(IUser? currentUser); } } diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 9df9a671c3..03798c8597 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -251,7 +251,7 @@ namespace Umbraco.Cms.Core.Services /// /// The template created /// - Attempt?> CreateTemplateForContentType(string contentTypeAlias, string contentTypeName, int userId = Constants.Security.SuperUserId); + Attempt?> CreateTemplateForContentType(string contentTypeAlias, string? contentTypeName, int userId = Constants.Security.SuperUserId); ITemplate CreateTemplateWithIdentity(string? name, string? alias, string? content, ITemplate? masterTemplate = null, int userId = Constants.Security.SuperUserId); diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index 587a5b3052..b478aed5bd 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -22,7 +22,7 @@ namespace Umbraco.Cms.Core.Services /// /// - void AddOrUpdateDictionaryValue(IDictionaryItem item, ILanguage language, string value); + void AddOrUpdateDictionaryValue(IDictionaryItem item, ILanguage? language, string? value); /// /// Creates and saves a new dictionary item and assigns a value to all languages if defaultValue is specified. diff --git a/src/Umbraco.Core/Services/ILocalizedTextService.cs b/src/Umbraco.Core/Services/ILocalizedTextService.cs index 53028ec272..c49a4e6b2f 100644 --- a/src/Umbraco.Core/Services/ILocalizedTextService.cs +++ b/src/Umbraco.Core/Services/ILocalizedTextService.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Core.Services /// /// This can be null /// - string Localize(string? area, string? alias, CultureInfo culture, IDictionary? tokens = null); + string Localize(string? area, string? alias, CultureInfo? culture, IDictionary? tokens = null); /// diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 9b24159b71..1ec534029b 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Core.Services /// Alias of the /// Optional id of the user creating the media item /// - IMedia CreateMedia(string name, int parentId, string mediaTypeAlias, int userId = Constants.Security.SuperUserId); + IMedia CreateMedia(string? name, int parentId, string mediaTypeAlias, int userId = Constants.Security.SuperUserId); /// /// Creates an object using the alias of the diff --git a/src/Umbraco.Core/Services/IMemberGroupService.cs b/src/Umbraco.Core/Services/IMemberGroupService.cs index 91ef6f0d3c..9b8c4a8d53 100644 --- a/src/Umbraco.Core/Services/IMemberGroupService.cs +++ b/src/Umbraco.Core/Services/IMemberGroupService.cs @@ -10,7 +10,7 @@ namespace Umbraco.Cms.Core.Services IMemberGroup? GetById(int id); IMemberGroup? GetById(Guid id); IEnumerable GetByIds(IEnumerable ids); - IMemberGroup? GetByName(string name); + IMemberGroup? GetByName(string? name); void Save(IMemberGroup memberGroup); void Delete(IMemberGroup memberGroup); } diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index de87be05a5..6701090915 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Core.Services /// Search text filter /// IEnumerable GetAll(long pageIndex, int pageSize, out long totalRecords, - string orderBy, Direction orderDirection, bool orderBySystemField, string memberTypeAlias, string filter); + string orderBy, Direction orderDirection, bool orderBySystemField, string? memberTypeAlias, string filter); /// /// Creates an object without persisting it diff --git a/src/Umbraco.Core/Services/LocalizationService.cs b/src/Umbraco.Core/Services/LocalizationService.cs index c8b406e3b5..efccd5ddf4 100644 --- a/src/Umbraco.Core/Services/LocalizationService.cs +++ b/src/Umbraco.Core/Services/LocalizationService.cs @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Core.Services /// /// This does not save the item, that needs to be done explicitly /// - public void AddOrUpdateDictionaryValue(IDictionaryItem item, ILanguage language, string value) + public void AddOrUpdateDictionaryValue(IDictionaryItem item, ILanguage? language, string? value) { if (item == null) throw new ArgumentNullException(nameof(item)); if (language == null) throw new ArgumentNullException(nameof(language)); diff --git a/src/Umbraco.Core/Services/LocalizedTextService.cs b/src/Umbraco.Core/Services/LocalizedTextService.cs index 1f81995a63..6aa7e8fb2b 100644 --- a/src/Umbraco.Core/Services/LocalizedTextService.cs +++ b/src/Umbraco.Core/Services/LocalizedTextService.cs @@ -174,7 +174,7 @@ namespace Umbraco.Cms.Core.Services return Localize(area, alias, culture, tokens); } - public string Localize(string? area, string? alias, CultureInfo culture, + public string Localize(string? area, string? alias, CultureInfo? culture, IDictionary? tokens = null) { if (culture == null) throw new ArgumentNullException(nameof(culture)); diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs index 1f56ab8c22..4d926338f3 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs @@ -31,7 +31,7 @@ namespace Umbraco.Extensions /// /// /// - public static string Localize(this ILocalizedTextService manager, string area, string alias, string?[] tokens) + public static string Localize(this ILocalizedTextService manager, string area, string alias, string?[]? tokens) => manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, ConvertToDictionaryVars(tokens)); /// diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index a08a1bd405..dc5d171109 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -134,7 +134,7 @@ namespace Umbraco.Cms.Core.Services /// The alias of the media type. /// The optional id of the user creating the media. /// The media object. - public IMedia CreateMedia(string name, int parentId, string mediaTypeAlias, int userId = Cms.Core.Constants.Security.SuperUserId) + public IMedia CreateMedia(string? name, int parentId, string mediaTypeAlias, int userId = Cms.Core.Constants.Security.SuperUserId) { var mediaType = GetMediaType(mediaTypeAlias); if (mediaType == null) diff --git a/src/Umbraco.Core/Services/MemberGroupService.cs b/src/Umbraco.Core/Services/MemberGroupService.cs index 6fb6d3a06e..305223979b 100644 --- a/src/Umbraco.Core/Services/MemberGroupService.cs +++ b/src/Umbraco.Core/Services/MemberGroupService.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Core.Services } } - public IMemberGroup? GetByName(string name) + public IMemberGroup? GetByName(string? name) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { diff --git a/src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs b/src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs index bf521d7c6b..1239f22877 100644 --- a/src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs +++ b/src/Umbraco.Core/Templates/IUmbracoComponentRenderer.cs @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Core.Templates /// Currently only used when the node is unpublished and unable to get the contentId item from the /// content cache as its unpublished. This deals with taking in a preview/draft version of the content node /// - Task RenderMacroForContent(IPublishedContent content, string alias, IDictionary parameters); + Task RenderMacroForContent(IPublishedContent content, string alias, IDictionary? parameters); } } diff --git a/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs b/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs index 45a44d4edb..407f85ad60 100644 --- a/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Core/Templates/UmbracoComponentRenderer.cs @@ -78,7 +78,7 @@ namespace Umbraco.Cms.Core.Templates } /// - public async Task RenderMacroForContent(IPublishedContent content, string alias, IDictionary parameters) + public async Task RenderMacroForContent(IPublishedContent content, string alias, IDictionary? parameters) { if(content == null) { diff --git a/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs b/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs index cdff934da0..bb5a4f5a46 100644 --- a/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs +++ b/src/Umbraco.Infrastructure/Examine/ExamineIndexModel.cs @@ -16,7 +16,7 @@ namespace Umbraco.Cms.Infrastructure.Examine public bool IsHealthy => HealthStatus == "Healthy"; [DataMember(Name = "providerProperties")] - public IReadOnlyDictionary? ProviderProperties { get; set; } + public IReadOnlyDictionary? ProviderProperties { get; set; } [DataMember(Name = "canRebuild")] public bool CanRebuild { get; set; } diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs index 9d77f0ac00..2bda63c96b 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs @@ -16,12 +16,12 @@ namespace Umbraco.Cms.Core.Logging.Viewer /// /// Adds a new saved search to chosen data source and returns the updated searches /// - IReadOnlyList? AddSavedSearch(string name, string query); + IReadOnlyList? AddSavedSearch(string? name, string? query); /// /// Deletes a saved search to chosen data source and returns the remaining searches /// - IReadOnlyList? DeleteSavedSearch(string name, string query); + IReadOnlyList? DeleteSavedSearch(string? name, string? query); /// /// A count of number of errors diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs index 2123e0e655..5be26a1099 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Core.Logging.Viewer public interface ILogViewerConfig { IReadOnlyList? GetSavedSearches(); - IReadOnlyList? AddSavedSearch(string name, string query); - IReadOnlyList? DeleteSavedSearch(string name, string query); + IReadOnlyList? AddSavedSearch(string? name, string? query); + IReadOnlyList? DeleteSavedSearch(string? name, string? query); } } diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs index a3dfb96d1e..8b11639696 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Core.Logging.Viewer return result; } - public IReadOnlyList? AddSavedSearch(string name, string query) + public IReadOnlyList? AddSavedSearch(string? name, string? query) { using var scope = _scopeProvider.CreateScope(autoComplete: true); _logViewerQueryRepository.Save(new LogViewerQuery(name, query)); @@ -33,10 +33,10 @@ namespace Umbraco.Cms.Core.Logging.Viewer return GetSavedSearches(); } - public IReadOnlyList? DeleteSavedSearch(string name, string query) + public IReadOnlyList? DeleteSavedSearch(string? name, string? query) { using var scope = _scopeProvider.CreateScope(autoComplete: true); - var item = _logViewerQueryRepository.GetByName(name); + var item = name is null ? null : _logViewerQueryRepository.GetByName(name); if (item is not null) { _logViewerQueryRepository.Delete(item); diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs index 699a1e7ec2..ca5c5c3a46 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs @@ -43,10 +43,10 @@ namespace Umbraco.Cms.Core.Logging.Viewer public virtual IReadOnlyList? GetSavedSearches() => _logViewerConfig.GetSavedSearches(); - public virtual IReadOnlyList? AddSavedSearch(string name, string query) + public virtual IReadOnlyList? AddSavedSearch(string? name, string? query) => _logViewerConfig.AddSavedSearch(name, query); - public virtual IReadOnlyList? DeleteSavedSearch(string name, string query) + public virtual IReadOnlyList? DeleteSavedSearch(string? name, string? query) => _logViewerConfig.DeleteSavedSearch(name, query); public int GetNumberOfErrors(LogTimePeriod logTimePeriod) diff --git a/src/Umbraco.Infrastructure/Mapping/UmbracoMapper.cs b/src/Umbraco.Infrastructure/Mapping/UmbracoMapper.cs index 9d24c8a6e5..7810e8468a 100644 --- a/src/Umbraco.Infrastructure/Mapping/UmbracoMapper.cs +++ b/src/Umbraco.Infrastructure/Mapping/UmbracoMapper.cs @@ -167,7 +167,7 @@ namespace Umbraco.Cms.Core.Mapping /// The target type. /// The source object. /// The target object. - public TTarget? Map(TSource source) + public TTarget? Map(TSource? source) => Map(source, new MapperContext(this)); /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs index 66eb4b5da5..0e2e9914a8 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberGroupRepository.cs @@ -131,7 +131,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return dto == null ? null : MemberGroupFactory.BuildEntity(dto); } - public IMemberGroup? GetByName(string name) + public IMemberGroup? GetByName(string? name) { return IsolatedCache.GetCacheItem( typeof(IMemberGroup).FullName + "." + name, diff --git a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs index 851560b0ba..5067a44b2c 100644 --- a/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/IUmbracoUserManager.cs @@ -251,7 +251,7 @@ namespace Umbraco.Cms.Core.Security /// The that represents the asynchronous operation, containing the /// of the operation. /// - Task CreateAsync(TUser user, string password); + Task CreateAsync(TUser user, string? password); /// /// Generate a password for a user based on the current password validator @@ -267,7 +267,7 @@ namespace Umbraco.Cms.Core.Security /// The password. /// A representing whether validation was successful. - Task ValidatePasswordAsync(string password); + Task ValidatePasswordAsync(string? password); /// /// Generates an email confirmation token for the specified user. diff --git a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs index 74538f14c0..7df608e521 100644 --- a/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs +++ b/src/Umbraco.Infrastructure/Security/UmbracoUserManager.cs @@ -104,7 +104,7 @@ namespace Umbraco.Cms.Core.Security /// /// The password. /// A representing whether validation was successful. - public async Task ValidatePasswordAsync(string password) + public async Task ValidatePasswordAsync(string? password) { var errors = new List(); var isValid = true; diff --git a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs index 10ac4d6d75..a1c79ef589 100644 --- a/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs +++ b/src/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResource.cs @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Web.BackOffice.Authorization { public class MediaPermissionsResource { - public MediaPermissionsResource(IMedia media) + public MediaPermissionsResource(IMedia? media) { Media = media; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs index f206168237..37c35d1f1f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeServerVariables.cs @@ -28,6 +28,7 @@ using Umbraco.Cms.Web.BackOffice.Trees; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Models; using Umbraco.Extensions; +using Language = Umbraco.Cms.Core.Models.ContentEditing.Language; namespace Umbraco.Cms.Web.BackOffice.Controllers { @@ -209,7 +210,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "iconApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.GetIcon("")) + controller => controller.GetIcon("") ?? new IconModel()) }, { "imagesApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( @@ -265,7 +266,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "memberApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.GetByKey(Guid.Empty)) + controller => controller.GetByKey(Guid.Empty) ?? new MemberDisplay()) }, { "packageApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( @@ -273,7 +274,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "relationApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.GetById(0)) + controller => controller.GetById(0) ?? new RelationDisplay()) }, { "rteApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( @@ -357,7 +358,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "languageApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllLanguages()) + controller => controller.GetAllLanguages() ?? Enumerable.Empty()) }, { "relationTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( @@ -377,7 +378,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }, { "imageUrlGeneratorApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( - controller => controller.GetCropUrl(string.Empty, null, null, null)) + controller => controller.GetCropUrl(string.Empty, null, null, null) ?? string.Empty) }, { "elementTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs index b22a7d715c..6e4784eb09 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs @@ -90,13 +90,18 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// internal void MapPropertyValuesForPersistence( TSaved contentItem, - ContentPropertyCollectionDto dto, - Func getPropertyValue, - Action savePropertyValue, - string culture) + ContentPropertyCollectionDto? dto, + Func getPropertyValue, + Action savePropertyValue, + string? culture) where TPersisted : IContentBase where TSaved : IContentSave { + if (dto is null) + { + return; + } + // map the property values foreach (ContentPropertyDto propertyDto in dto.Properties) { @@ -116,7 +121,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } // get the property - IProperty property = contentItem.PersistedContent.Properties[propertyDto.Alias]; + IProperty? property = contentItem.PersistedContent?.Properties[propertyDto.Alias]; // prepare files, if any matching property and culture ContentPropertyFile[] files = contentItem.UploadedFiles @@ -125,32 +130,32 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers foreach (ContentPropertyFile file in files) { - file.FileName = file.FileName.ToSafeFileName(ShortStringHelper); + file.FileName = file.FileName?.ToSafeFileName(ShortStringHelper); } // create the property data for the property editor - var data = new ContentPropertyData(propertyDto.Value, propertyDto.DataType.Configuration) + var data = new ContentPropertyData(propertyDto.Value, propertyDto.DataType?.Configuration) { - ContentKey = contentItem.PersistedContent.Key, - PropertyTypeKey = property.PropertyType.Key, + ContentKey = contentItem.PersistedContent?.Key, + PropertyTypeKey = property?.PropertyType.Key, Files = files }; // let the editor convert the value that was received, deal with files, etc - object value = valueEditor.FromEditor(data, getPropertyValue(contentItem, property)); + object? value = valueEditor.FromEditor(data, getPropertyValue(contentItem, property)); // set the value - tags are special - TagsPropertyEditorAttribute tagAttribute = propertyDto.PropertyEditor.GetTagAttribute(); + TagsPropertyEditorAttribute? tagAttribute = propertyDto.PropertyEditor.GetTagAttribute(); if (tagAttribute != null) { - TagConfiguration tagConfiguration = ConfigurationEditor.ConfigurationAs(propertyDto.DataType.Configuration); - if (tagConfiguration.Delimiter == default) + TagConfiguration? tagConfiguration = ConfigurationEditor.ConfigurationAs(propertyDto.DataType?.Configuration); + if (tagConfiguration is not null && tagConfiguration.Delimiter == default) { tagConfiguration.Delimiter = tagAttribute.Delimiter; } - var tagCulture = property.PropertyType.VariesByCulture() ? culture : null; - property.SetTagsValue(_serializer, value, tagConfiguration, tagCulture); + var tagCulture = property?.PropertyType.VariesByCulture() ?? false ? culture : null; + property?.SetTagsValue(_serializer, value, tagConfiguration, tagCulture); } else { @@ -170,12 +175,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// This is useful for when filters have already looked up a persisted entity and we don't want to have /// to look it up again. /// - protected TPersisted GetObjectFromRequest(Func getFromService) + protected TPersisted? GetObjectFromRequest(Func getFromService) { // checks if the request contains the key and the item is not null, if that is the case, return it from the request, otherwise return // it from the callback return HttpContext.Items.ContainsKey(typeof(TPersisted).ToString()) && HttpContext.Items[typeof(TPersisted).ToString()] != null - ? (TPersisted)HttpContext.Items[typeof(TPersisted).ToString()] + ? (TPersisted?)HttpContext.Items[typeof(TPersisted).ToString()] : getFromService(); } @@ -194,19 +199,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// protected void AddCancelMessage( - INotificationModel display, + INotificationModel? display, string messageArea = "speechBubbles", string messageAlias ="operationCancelledText", - string[] messageParams = null) + string[]? messageParams = null) { // if there's already a default event message, don't add our default one IEventMessagesFactory messages = EventMessages; - if (messages != null && messages.GetOrDefault().GetAll().Any(x => x.IsDefaultEventMessage)) + if (messages != null && (messages.GetOrDefault()?.GetAll().Any(x => x.IsDefaultEventMessage) ?? false)) { return; } - display.AddWarningNotification( + display?.AddWarningNotification( LocalizedTextService.Localize("speechBubbles", "operationCancelledHeader"), LocalizedTextService.Localize(messageArea, messageAlias, messageParams)); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs index 37ddd5e7cb..fa7ed04ab6 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeController.cs @@ -116,7 +116,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Gets the document type a given id /// [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var ct = _contentTypeService.Get(id); if (ct == null) @@ -132,7 +132,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Gets the document type a given guid /// [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var contentType = _contentTypeService.Get(id); if (contentType == null) @@ -148,7 +148,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Gets the document type a given udi /// [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -178,7 +178,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - _contentTypeService.Delete(foundType, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _contentTypeService.Delete(foundType, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return Ok(); } @@ -218,7 +218,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return actionResult.Result; } - var result = actionResult.Value + var result = actionResult.Value? .Select(x => new { contentType = x.Item1, @@ -246,7 +246,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) { - var result = PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType).Value + var result = PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType).Value? .Select(x => new { contentType = x @@ -264,15 +264,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - var configuration = _dataTypeService.GetDataType(id).Configuration; + var configuration = _dataTypeService.GetDataType(id)?.Configuration; var editor = _propertyEditors[dataTypeDiff.EditorAlias]; return new ContentPropertyDisplay() { Editor = dataTypeDiff.EditorAlias, Validation = new PropertyTypeValidation(), - View = editor.GetValueEditor().View, - Config = editor.GetConfigurationEditor().ToConfigurationEditor(configuration) + View = editor?.GetValueEditor().View, + Config = editor?.GetConfigurationEditor().ToConfigurationEditor(configuration) }; } @@ -284,7 +284,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult DeleteContainer(int id) { - _contentTypeService.DeleteContainer(id, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _contentTypeService.DeleteContainer(id, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return Ok(); } @@ -292,27 +292,27 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostCreateContainer(int parentId, string name) { - var result = _contentTypeService.CreateContainer(parentId, Guid.NewGuid(), name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var result = _contentTypeService.CreateContainer(parentId, Guid.NewGuid(), name, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); if (result.Success) return Ok(result.Result); //return the id else - return ValidationProblem(result.Exception.Message); + return ValidationProblem(result.Exception?.Message); } [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] public IActionResult PostRenameContainer(int id, string name) { - var result = _contentTypeService.RenameContainer(id, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var result = _contentTypeService.RenameContainer(id, name, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); if (result.Success) return Ok(result.Result); //return the id else - return ValidationProblem(result.Exception.Message); + return ValidationProblem(result.Exception?.Message); } [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public ActionResult PostSave(DocumentTypeSave contentTypeSave) + public ActionResult PostSave(DocumentTypeSave contentTypeSave) { //Before we send this model into this saving/mapping pipeline, we need to do some cleanup on variations. //If the doc type does not allow content variations, we need to update all of it's property types to not allow this either @@ -336,22 +336,24 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var template = CreateTemplateForContentType(ctSave.Alias, ctSave.Name); - // If the alias has been manually updated before the first save, - // make sure to also update the first allowed template, as the - // name will come back as a SafeAlias of the document type name, - // not as the actual document type alias. - // For more info: http://issues.umbraco.org/issue/U4-11059 - if (ctSave.DefaultTemplate != template.Alias) + if (template is not null) { - var allowedTemplates = ctSave.AllowedTemplates.ToArray(); - if (allowedTemplates.Any()) - allowedTemplates[0] = template.Alias; - ctSave.AllowedTemplates = allowedTemplates; + // If the alias has been manually updated before the first save, + // make sure to also update the first allowed template, as the + // name will come back as a SafeAlias of the document type name, + // not as the actual document type alias. + // For more info: http://issues.umbraco.org/issue/U4-11059 + if (ctSave.DefaultTemplate != template.Alias) + { + var allowedTemplates = ctSave.AllowedTemplates?.ToArray(); + if (allowedTemplates?.Any() ?? false) + allowedTemplates[0] = template.Alias; + ctSave.AllowedTemplates = allowedTemplates; + } + + //make sure the template alias is set on the default and allowed template so we can map it back + ctSave.DefaultTemplate = template.Alias; } - - //make sure the template alias is set on the default and allowed template so we can map it back - ctSave.DefaultTemplate = template.Alias; - } }); @@ -363,7 +365,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var display = _umbracoMapper.Map(savedCt.Value); - display.AddSuccessNotification( + display?.AddSuccessNotification( _localizedTextService.Localize("speechBubbles","contentTypeSavedHeader"), string.Empty); @@ -371,7 +373,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public ActionResult PostCreateDefaultTemplate(int id) + public ActionResult PostCreateDefaultTemplate(int id) { var contentType = _contentTypeService.Get(id); if (contentType == null) @@ -388,7 +390,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return _umbracoMapper.Map(template); } - private ITemplate CreateTemplateForContentType(string contentTypeAlias, string contentTypeName) + private ITemplate? CreateTemplateForContentType(string contentTypeAlias, string? contentTypeName) { var template = _fileService.GetTemplate(contentTypeAlias); if (template == null) @@ -397,10 +399,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (tryCreateTemplate == false) { _logger.LogWarning("Could not create a template for Content Type: \"{ContentTypeAlias}\", status: {Status}", - contentTypeAlias, tryCreateTemplate.Result.Result); + contentTypeAlias, tryCreateTemplate.Result?.Result); } - template = tryCreateTemplate.Result.Entity; + template = tryCreateTemplate.Result?.Entity; } return template; @@ -412,7 +414,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)] - public DocumentTypeDisplay GetEmpty(int parentId) + public DocumentTypeDisplay? GetEmpty(int parentId) { IContentType ct; if (parentId != Constants.System.Root) @@ -437,7 +439,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetAll() { var types = _contentTypeService.GetAll(); - var basics = types.Select(_umbracoMapper.Map); + var basics = types.Select(_umbracoMapper.Map).WhereNotNull(); return basics.Select(basic => { @@ -471,14 +473,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } var contentType = _contentTypeBaseServiceProvider.GetContentTypeOf(contentItem); - var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); + var ids = contentType?.AllowedContentTypes?.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); - if (ids.Any() == false) return Enumerable.Empty(); + if (ids is null || ids.Any() == false) return Enumerable.Empty(); types = _contentTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); } - var basics = types.Where(type => type.IsElement == false).Select(_umbracoMapper.Map).ToList(); + var basics = types.Where(type => type.IsElement == false).Select(_umbracoMapper.Map).WhereNotNull().ToList(); var localizedTextService = _localizedTextService; foreach (var basic in basics) @@ -488,13 +490,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } //map the blueprints - var blueprints = _contentService.GetBlueprintsForContentTypes(types.Select(x => x.Id).ToArray()).ToArray(); + var blueprints = _contentService.GetBlueprintsForContentTypes(types.Select(x => x.Id).ToArray())?.ToArray(); foreach (var basic in basics) { - var docTypeBluePrints = blueprints.Where(x => x.ContentTypeId == (int) basic.Id).ToArray(); - foreach (var blueprint in docTypeBluePrints) + var docTypeBluePrints = blueprints?.Where(x => x.ContentTypeId == (int?) basic.Id).ToArray(); + if (docTypeBluePrints is not null) { - basic.Blueprints[blueprint.Id] = blueprint.Name; + foreach (var blueprint in docTypeBluePrints) + { + basic.Blueprints[blueprint.Id] = blueprint.Name ?? string.Empty; + } } } @@ -560,7 +565,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); - var userId = _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0); + var userId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0); var element = XElement.Parse(xd.InnerXml); if (userId is not null) { @@ -610,8 +615,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }; xd.Load(model.TempFileName); - model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; - model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; + model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild?.Value; + model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild?.Value; } else { diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs index 0190d5d400..38f3c82008 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentTypeControllerBase.cs @@ -79,11 +79,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers protected ActionResult>> PerformGetAvailableCompositeContentTypes( int contentTypeId, UmbracoObjectTypes type, - string[] filterContentTypes, - string[] filterPropertyTypes, + string[]? filterContentTypes, + string[]? filterPropertyTypes, bool isElement) { - IContentTypeComposition source = null; + IContentTypeComposition? source = null; //below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic @@ -266,7 +266,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers .ToList(); } - protected string TranslateItem(string text) + protected string? TranslateItem(string? text) { if (text == null) { @@ -284,9 +284,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers protected ActionResult PerformPostSave( TContentTypeSave contentTypeSave, - Func getContentType, + Func getContentType, Action saveContentType, - Action beforeCreateNew = null) + Action? beforeCreateNew = null) where TContentTypeDisplay : ContentTypeCompositionDisplay where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic @@ -454,16 +454,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// protected IActionResult PerformMove( MoveOrCopy move, - Func getContentType, - Func>> doMove) + Func getContentType, + Func?>> doMove) { - TContentType toMove = getContentType(move.Id); + TContentType? toMove = getContentType(move.Id); if (toMove == null) { return NotFound(); } - Attempt> result = doMove(toMove, move.ParentId); + Attempt?> result = doMove(toMove, move.ParentId); if (result.Success) { return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); @@ -491,8 +491,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// protected IActionResult PerformCopy( MoveOrCopy move, - Func getContentType, - Func>> doCopy) + Func getContentType, + Func?>> doCopy) { TContentType toMove = getContentType(move.Id); if (toMove == null) diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index d2ba59cc65..0084c0680d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public Dictionary GetPermissions(int[] nodeIds) { var permissions = _userService - .GetPermissions(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, nodeIds); + .GetPermissions(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, nodeIds); var permissionsDictionary = new Dictionary(); foreach (var nodeId in nodeIds) @@ -147,7 +147,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpGet] public bool HasPermission(string permissionToCheck, int nodeId) { - var p = _userService.GetPermissions(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser, nodeId).GetAllPermissions(); + var p = _userService.GetPermissions(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser, nodeId).GetAllPermissions(); if (p.Contains(permissionToCheck.ToString(CultureInfo.InvariantCulture))) { return true; @@ -161,43 +161,51 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public IEnumerable PostSetUserTour(UserTourStatus status) + public IEnumerable PostSetUserTour(UserTourStatus? status) { if (status == null) throw new ArgumentNullException(nameof(status)); - List userTours; - if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData.IsNullOrWhiteSpace()) + List? userTours = null; + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.TourData.IsNullOrWhiteSpace() ?? true) { userTours = new List { status }; - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData = JsonConvert.SerializeObject(userTours); - _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser is not null) + { + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData = JsonConvert.SerializeObject(userTours); + _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + } + return userTours; } - userTours = JsonConvert.DeserializeObject>(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData).ToList(); - var found = userTours.FirstOrDefault(x => x.Alias == status.Alias); - if (found != null) + if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData is not null) { - //remove it and we'll replace it next - userTours.Remove(found); + userTours = JsonConvert.DeserializeObject>(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData)?.ToList(); + var found = userTours?.FirstOrDefault(x => x.Alias == status.Alias); + if (found != null) + { + //remove it and we'll replace it next + userTours?.Remove(found); + } + userTours?.Add(status); } - userTours.Add(status); + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData = JsonConvert.SerializeObject(userTours); _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); - return userTours; + return userTours ?? Enumerable.Empty(); } /// /// Returns the user's tours /// /// - public IEnumerable GetUserTours() + public IEnumerable? GetUserTours() { - if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData.IsNullOrWhiteSpace()) + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.TourData.IsNullOrWhiteSpace() ?? true) return Enumerable.Empty(); - var userTours = JsonConvert.DeserializeObject>(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData); - return userTours; + var userTours = JsonConvert.DeserializeObject>(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.TourData!); + return userTours ?? Enumerable.Empty(); } public IEnumerable GetUserData() => _userDataService.GetUserData(); @@ -212,9 +220,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// This only works when the user is logged in (partially) /// [AllowAnonymous] - public async Task> PostSetInvitedUserPassword([FromBody]string newPassword) + public async Task> PostSetInvitedUserPassword([FromBody]string newPassword) { - var user = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0).ToString()); + var user = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0).ToString()); if (user == null) throw new InvalidOperationException("Could not find user"); var result = await _backOfficeUserManager.AddPasswordAsync(user, newPassword); @@ -228,26 +236,34 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return ValidationProblem(ModelState); } - //They've successfully set their password, we can now update their user account to be approved - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsApproved = true; - //They've successfully set their password, and will now get fully logged into the back office, so the lastlogindate is set so the backoffice shows they have logged in - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.LastLoginDate = DateTime.UtcNow; - _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser is not null) + { + //They've successfully set their password, we can now update their user account to be approved + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsApproved = true; + //They've successfully set their password, and will now get fully logged into the back office, so the lastlogindate is set so the backoffice shows they have logged in + _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.LastLoginDate = DateTime.UtcNow; + _userService.Save(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + } + //now we can return their full object since they are now really logged into the back office - var userDisplay = _umbracoMapper.Map(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + var userDisplay = _umbracoMapper.Map(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser); + + if (userDisplay is not null) + { + userDisplay.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds(); + } - userDisplay.SecondsUntilTimeout = HttpContext.User.GetRemainingAuthSeconds(); return userDisplay; } [AppendUserModifiedHeader] public IActionResult PostSetAvatar(IList file) { - var userId = _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId(); - var result = userId.ResultOr(0); + var userId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId(); + var result = userId?.ResultOr(0); //borrow the logic from the user controller - return UsersController.PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileManager, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? 0); + return UsersController.PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileManager, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? 0); } /// @@ -257,26 +273,34 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// If the password is being reset it will return the newly reset password, otherwise will return an empty value /// - public async Task>> PostChangePassword(ChangingPasswordModel changingPasswordModel) + public async Task>?> PostChangePassword(ChangingPasswordModel changingPasswordModel) { - IUser currentUser = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + IUser? currentUser = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + if (currentUser is null) + { + return null; + } + changingPasswordModel.Id = currentUser.Id; // all current users have access to reset/manually change their password - Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _backOfficeUserManager); + Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _backOfficeUserManager); if (passwordChangeResult.Success) { // even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword - var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); + var result = new ModelWithNotifications(passwordChangeResult.Result?.ResetPassword); result.AddSuccessNotification(_localizedTextService.Localize("user","password"), _localizedTextService.Localize("user","passwordChanged")); return result; } - foreach (string memberName in passwordChangeResult.Result.ChangeError.MemberNames) + if (passwordChangeResult.Result?.ChangeError?.MemberNames is not null) { - ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage); + foreach (string memberName in passwordChangeResult.Result.ChangeError.MemberNames) + { + ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage ?? string.Empty); + } } return ValidationProblem(ModelState); @@ -287,7 +311,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [ValidateAngularAntiForgeryToken] public async Task> GetCurrentUserLinkedLogins() { - var identityUser = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)?.ToString(CultureInfo.InvariantCulture)); + var identityUser = await _backOfficeUserManager.FindByIdAsync(_backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().ResultOr(0)?.ToString(CultureInfo.InvariantCulture)); // deduplicate in case there are duplicates (there shouldn't be now since we have a unique constraint on the external logins // but there didn't used to be) diff --git a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs index 70fb864482..8599adaedc 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DashboardController.cs @@ -82,11 +82,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [ValidateAngularAntiForgeryToken] public async Task GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.com/") { - var user = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - var allowedSections = string.Join(",", user.AllowedSections); - var language = user.Language; + var user = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + var allowedSections = string.Join(",", user?.AllowedSections ?? Array.Empty()); + var language = user?.Language; var version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild(); - var isAdmin = user.IsAdmin(); + var isAdmin = user?.IsAdmin() ?? false; _siteIdentifierService.TryGetOrCreateSiteIdentifier(out Guid siteIdentifier); if (!IsAllowedUrl(baseUrl)) @@ -260,7 +260,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [OutgoingEditorModelEvent] public IEnumerable> GetDashboard(string section) { - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; return _dashboardService.GetDashboards(section, currentUser).Select(x => new Tab { Id = x.Id, @@ -270,7 +270,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers Type = x.Type, Expanded = x.Expanded, IsActive = x.IsActive, - Properties = x.Properties.Select(y => new DashboardSlim + Properties = x.Properties?.Select(y => new DashboardSlim { Alias = y.Alias, View = y.View diff --git a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs index 3b6f4533ef..7c0567b1bb 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DataTypeController.cs @@ -79,7 +79,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public DataTypeDisplay GetByName(string name) + public DataTypeDisplay? GetByName(string name) { var dataType = _dataTypeService.GetDataType(name); return dataType == null ? null : _umbracoMapper.Map(dataType); @@ -90,7 +90,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var dataType = _dataTypeService.GetDataType(id); if (dataType == null) @@ -106,7 +106,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var dataType = _dataTypeService.GetDataType(id); if (dataType == null) @@ -122,7 +122,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -151,13 +151,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { return NotFound(); } - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - _dataTypeService.Delete(foundType, currentUser.Id); + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + _dataTypeService.Delete(foundType, currentUser?.Id ?? -1); return Ok(); } - public DataTypeDisplay GetEmpty(int parentId) + public DataTypeDisplay? GetEmpty(int parentId) { // cannot create an "empty" data type, so use something by default. var editor = _propertyEditors[Constants.PropertyEditors.Aliases.Label]; @@ -170,7 +170,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// a DataTypeDisplay - public ActionResult GetCustomListView(string contentTypeAlias) + public ActionResult GetCustomListView(string contentTypeAlias) { var dt = _dataTypeService.GetDataType(Constants.Conventions.DataTypes.ListViewPrefix + contentTypeAlias); if (dt == null) @@ -186,7 +186,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public DataTypeDisplay PostCreateCustomListView(string contentTypeAlias) + public DataTypeDisplay? PostCreateCustomListView(string contentTypeAlias) { var dt = _dataTypeService.GetDataType(Constants.Conventions.DataTypes.ListViewPrefix + contentTypeAlias); @@ -218,7 +218,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (dataTypeId == -1) { //this is a new data type, so just return the field editors with default values - return new ActionResult>(_umbracoMapper.Map>(propEd)); + return new ActionResult>(_umbracoMapper.Map>(propEd) ?? Enumerable.Empty()); } //we have a data type associated @@ -234,11 +234,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (dataType.EditorAlias == editorAlias) { //this is the currently assigned pre-value editor, return with values. - return new ActionResult>(_umbracoMapper.Map>(dataType)); + return new ActionResult>(_umbracoMapper.Map>(dataType) ?? Enumerable.Empty()); } //these are new pre-values, so just return the field editors with default values - return new ActionResult>(_umbracoMapper.Map>(propEd)); + return new ActionResult>(_umbracoMapper.Map>(propEd) ?? Enumerable.Empty()); } /// @@ -251,21 +251,21 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult DeleteContainer(int id) { - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - _dataTypeService.DeleteContainer(id, currentUser.Id); + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + _dataTypeService.DeleteContainer(id, currentUser?.Id ?? -1); return Ok(); } public IActionResult PostCreateContainer(int parentId, string name) { - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - var result = _dataTypeService.CreateContainer(parentId, Guid.NewGuid(), name, currentUser.Id); + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + var result = _dataTypeService.CreateContainer(parentId, Guid.NewGuid(), name, currentUser?.Id ?? -1); if (result.Success) return Ok(result.Result); //return the id else - return ValidationProblem(result.Exception.Message); + return ValidationProblem(result.Exception?.Message); } /// @@ -274,7 +274,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [DataTypeValidate] - public ActionResult PostSave(DataTypeSave dataType) + public ActionResult PostSave(DataTypeSave dataType) { //If we've made it here, then everything has been wired up and validated by the attribute @@ -284,18 +284,23 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // get the current configuration, // get the new configuration as a dictionary (this is how we get it from model) // and map to an actual configuration object - var currentConfiguration = dataType.PersistedDataType.Configuration; - var configurationDictionary = dataType.ConfigurationFields.ToDictionary(x => x.Key, x => x.Value); - var configuration = dataType.PropertyEditor.GetConfigurationEditor().FromConfigurationEditor(configurationDictionary, currentConfiguration); + var currentConfiguration = dataType.PersistedDataType?.Configuration; + var configurationDictionary = dataType.ConfigurationFields?.ToDictionary(x => x.Key, x => x.Value); + var configuration = dataType.PropertyEditor?.GetConfigurationEditor().FromConfigurationEditor(configurationDictionary, currentConfiguration); - dataType.PersistedDataType.Configuration = configuration; + if (dataType.PersistedDataType is not null) + { + dataType.PersistedDataType.Configuration = configuration; + } - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; // save the data type try { - - _dataTypeService.Save(dataType.PersistedDataType, currentUser.Id); + if (dataType.PersistedDataType is not null) + { + _dataTypeService.Save(dataType.PersistedDataType, currentUser?.Id ?? -1); + } } catch (DuplicateNameException ex) { @@ -305,7 +310,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // map back to display model, and return var display = _umbracoMapper.Map(dataType.PersistedDataType); - display.AddSuccessNotification(_localizedTextService.Localize("speechBubbles", "dataTypeSaved"), ""); + display?.AddSuccessNotification(_localizedTextService.Localize("speechBubbles", "dataTypeSaved"), ""); return display; } @@ -328,7 +333,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return Content(toMove.Path,MediaTypeNames.Text.Plain, Encoding.UTF8); } - switch (result.Result.Result) + switch (result.Result?.Result) { case MoveOperationStatusType.FailedParentNotFound: return NotFound(); @@ -345,13 +350,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult PostRenameContainer(int id, string name) { - var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; - var result = _dataTypeService.RenameContainer(id, name, currentUser.Id); + var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; + var result = _dataTypeService.RenameContainer(id, name, currentUser?.Id ?? -1); if (result.Success) return Ok(result.Result); else - return ValidationProblem(result.Exception.Message); + return ValidationProblem(result.Exception?.Message); } /// @@ -417,11 +422,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members /// [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)] - public IEnumerable GetAll() + public IEnumerable? GetAll() { - return _dataTypeService - .GetAll() - .Select(_umbracoMapper.Map).Where(x => x.IsSystemDataType == false); + return _dataTypeService? + .GetAll()? + .Select(_umbracoMapper.Map).WhereNotNull().Where(x => x.IsSystemDataType == false); } /// @@ -432,24 +437,29 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Permission is granted to this method if the user has access to any of these sections: Content, media, settings, developer, members /// [Authorize(Policy = AuthorizationPolicies.SectionAccessForDataTypeReading)] - public IDictionary> GetGroupedDataTypes() + public IDictionary>? GetGroupedDataTypes() { - var dataTypes = _dataTypeService - .GetAll() + var dataTypes = _dataTypeService? + .GetAll()? .Select(_umbracoMapper.Map) .ToArray(); var propertyEditors =_propertyEditorCollection.ToArray(); - foreach (var dataType in dataTypes) + if (dataTypes is not null) { - var propertyEditor = propertyEditors.SingleOrDefault(x => x.Alias == dataType.Alias); - if (propertyEditor != null) - dataType.HasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); + foreach (var dataType in dataTypes) + { + var propertyEditor = propertyEditors.SingleOrDefault(x => x.Alias == dataType?.Alias); + if (propertyEditor != null && dataType is not null) + dataType.HasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); + } + + } - var grouped = dataTypes - .GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group.ToLower()) + var grouped = dataTypes?.WhereNotNull() + .GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group!.ToLower()) .ToDictionary(group => group.Key, group => group.OrderBy(d => d.Name).AsEnumerable()); return grouped; @@ -474,12 +484,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var hasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); var basic = _umbracoMapper.Map(propertyEditor); - basic.HasPrevalues = hasPrevalues; - datatypes.Add(basic); + if (basic is not null) + { + basic.HasPrevalues = hasPrevalues; + datatypes.Add(basic); + } } var grouped = Enumerable.ToDictionary(datatypes - .GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group.ToLower()), group => group.Key, group => group.OrderBy(d => d.Name).AsEnumerable()); + .GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group!.ToLower()), group => group.Key, group => group.OrderBy(d => d.Name).AsEnumerable()); return grouped; } @@ -497,8 +510,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { return _propertyEditorCollection .OrderBy(x => x.Name) - .Select(_umbracoMapper.Map); + .Select(_umbracoMapper.Map).WhereNotNull(); } #endregion } } + diff --git a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs index 504b3db25f..1d31ffc78e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs @@ -76,10 +76,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers foreach (var dictionaryItem in foundDictionaryDescendants) { - _localizationService.Delete(dictionaryItem, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _localizationService.Delete(dictionaryItem, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); } - _localizationService.Delete(foundDictionary, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _localizationService.Delete(foundDictionary, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return Ok(); } @@ -106,8 +106,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var message = _localizedTextService.Localize( "dictionaryItem","changeKeyError", - _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings), - new Dictionary { { "0", key } }); + _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.GetUserCulture(_localizedTextService, _globalSettings), + new Dictionary { { "0", key } }); return ValidationProblem(message); } @@ -116,7 +116,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers Guid? parentGuid = null; if (parentId > 0) - parentGuid = _localizationService.GetDictionaryItemById(parentId).Key; + parentGuid = _localizationService.GetDictionaryItemById(parentId)?.Key; var item = _localizationService.CreateDictionaryItemWithIdentity( key, @@ -142,7 +142,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The . Returns a not found response when dictionary item does not exist /// - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var dictionary = _localizationService.GetDictionaryItemById(id); if (dictionary == null) @@ -160,7 +160,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The . Returns a not found response when dictionary item does not exist /// - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var dictionary = _localizationService.GetDictionaryItemById(id); if (dictionary == null) @@ -178,7 +178,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The . Returns a not found response when dictionary item does not exist /// - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -200,20 +200,20 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The . /// - public ActionResult PostSave(DictionarySave dictionary) + public ActionResult PostSave(DictionarySave dictionary) { - var dictionaryItem = - _localizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString(), CultureInfo.InvariantCulture)); + var dictionaryItem = dictionary.Id is null ? null : + _localizationService.GetDictionaryItemById(int.Parse(dictionary.Id.ToString()!, CultureInfo.InvariantCulture)); if (dictionaryItem == null) return ValidationProblem("Dictionary item does not exist"); - var userCulture = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserCulture(_localizedTextService, _globalSettings); + var userCulture = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.GetUserCulture(_localizedTextService, _globalSettings); if (dictionary.NameIsDirty) { // if the name (key) has changed, we need to check if the new key does not exist - var dictionaryByKey = _localizationService.GetDictionaryItemByKey(dictionary.Name); + var dictionaryByKey = _localizationService.GetDictionaryItemByKey(dictionary.Name!); if (dictionaryByKey != null && dictionaryItem.Id != dictionaryByKey.Id) { @@ -221,12 +221,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var message = _localizedTextService.Localize( "dictionaryItem","changeKeyError", userCulture, - new Dictionary { { "0", dictionary.Name } }); + new Dictionary { { "0", dictionary.Name } }); ModelState.AddModelError("Name", message); return ValidationProblem(ModelState); } - dictionaryItem.ItemKey = dictionary.Name; + dictionaryItem.ItemKey = dictionary.Name!; } foreach (var translation in dictionary.Translations) @@ -241,7 +241,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var model = _umbracoMapper.Map(dictionaryItem); - model.Notifications.Add(new BackOfficeNotification( + model?.Notifications.Add(new BackOfficeNotification( _localizedTextService.Localize("speechBubbles","dictionaryItemSaved", userCulture), string.Empty, NotificationStyle.Success)); @@ -277,8 +277,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers foreach(var child in children.OrderBy(ItemSort())) { var display = _umbracoMapper.Map(child); - display.Level = level; - list.Add(display); + if (display is not null) + { + display.Level = level; + list.Add(display); + } BuildTree(level + 1, child.Key); } @@ -303,11 +306,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// private void GetChildItemsForList(IDictionaryItem dictionaryItem, int level, ICollection list) { - foreach (var childItem in _localizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort())) + foreach (var childItem in _localizationService.GetDictionaryItemChildren(dictionaryItem.Key)?.OrderBy(ItemSort()) ?? Enumerable.Empty()) { var item = _umbracoMapper.Map(childItem); - item.Level = level; - list.Add(item); + if (item is not null) + { + item.Level = level; + list.Add(item); + } GetChildItemsForList(childItem, level + 1, list); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs index 911e50d0a0..0789900228 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ExamineManagementController.cs @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetIndexerDetails() => _examineManager.Indexes .Select(index => CreateModel(index)) - .OrderBy(examineIndexModel => examineIndexModel.Name.TrimEnd("Indexer")); + .OrderBy(examineIndexModel => examineIndexModel.Name?.TrimEnd("Indexer")); /// /// Get the details for searchers @@ -58,11 +58,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var model = new List( _examineManager.RegisteredSearchers.Select(searcher => new ExamineSearcherModel { Name = searcher.Name }) - .OrderBy(x => x.Name.TrimEnd("Searcher"))); //order by name , but strip the "Searcher" from the end if it exists + .OrderBy(x => x.Name?.TrimEnd("Searcher"))); //order by name , but strip the "Searcher" from the end if it exists return model; } - public ActionResult GetSearchResults(string searcherName, string query, int pageIndex = 0, int pageSize = 20) + public ActionResult GetSearchResults(string searcherName, string? query, int pageIndex = 0, int pageSize = 20) { query = query?.Trim(); @@ -112,7 +112,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// This is kind of rudimentary since there's no way we can know that the index has rebuilt, we /// have a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache /// - public ActionResult PostCheckRebuildIndex(string indexName) + public ActionResult PostCheckRebuildIndex(string indexName) { var validate = ValidateIndex(indexName, out var index); @@ -121,7 +121,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return validate; } - validate = ValidatePopulator(index); + validate = ValidatePopulator(index!); if (!validate.IsSuccessStatusCode()) { return validate; @@ -133,7 +133,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //if its still there then it's not done return found != null ? null - : CreateModel(index); + : CreateModel(index!); } /// @@ -147,14 +147,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (!validate.IsSuccessStatusCode()) return validate; - validate = ValidatePopulator(index); + validate = ValidatePopulator(index!); if (!validate.IsSuccessStatusCode()) return validate; _logger.LogInformation("Rebuilding index '{IndexName}'", indexName); //remove it in case there's a handler there already - index.IndexOperationComplete -= Indexer_IndexOperationComplete; + index!.IndexOperationComplete -= Indexer_IndexOperationComplete; //now add a single handler index.IndexOperationComplete += Indexer_IndexOperationComplete; @@ -189,13 +189,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var isHealth = indexDiag.IsHealthy(); - var properties = new Dictionary + var properties = new Dictionary { ["DocumentCount"] = indexDiag.GetDocumentCount(), ["FieldCount"] = indexDiag.GetFieldNames().Count(), }; - foreach (KeyValuePair p in indexDiag.Metadata) + foreach (KeyValuePair p in indexDiag.Metadata) { properties[p.Key] = p.Value; } @@ -243,7 +243,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return response; } - private ActionResult ValidateIndex(string indexName, out IIndex index) + private ActionResult ValidateIndex(string indexName, out IIndex? index) { index = null; @@ -258,18 +258,21 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return response; } - private void Indexer_IndexOperationComplete(object sender, EventArgs e) + private void Indexer_IndexOperationComplete(object? sender, EventArgs e) { - var indexer = (IIndex)sender; + var indexer = (IIndex?)sender; - _logger.LogDebug("Logging operation completed for index {IndexName}", indexer.Name); + _logger.LogDebug("Logging operation completed for index {IndexName}", indexer?.Name); - //ensure it's not listening anymore - indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; + if (indexer is not null) + { + //ensure it's not listening anymore + indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; + } - _logger.LogInformation($"Rebuilding index '{indexer.Name}' done."); + _logger.LogInformation($"Rebuilding index '{indexer?.Name}' done."); - var cacheKey = "temp_indexing_op_" + indexer.Name; + var cacheKey = "temp_indexing_op_" + indexer?.Name; _runtimeCache.Clear(cacheKey); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/HelpController.cs b/src/Umbraco.Web.BackOffice/Controllers/HelpController.cs index f431b1a827..f7bdd519e1 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/HelpController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/HelpController.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class HelpController : UmbracoAuthorizedJsonController { private readonly ILogger _logger; - private HelpPageSettings _helpPageSettings; + private HelpPageSettings? _helpPageSettings; [Obsolete("Use constructor that takes IOptions")] public HelpController(ILogger logger) @@ -44,7 +44,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _helpPageSettings = settings; } - private static HttpClient _httpClient; + private static HttpClient? _httpClient; public async Task> GetContextHelpForPage(string section, string tree, string baseUrl = "https://our.umbraco.com") { @@ -83,7 +83,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private bool IsAllowedUrl(string url) { - if (_helpPageSettings.HelpPageUrlAllowList is null || + if (_helpPageSettings?.HelpPageUrlAllowList is null || _helpPageSettings.HelpPageUrlAllowList.Contains(url)) { return true; @@ -97,16 +97,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class HelpPage { [DataMember(Name = "name")] - public string Name { get; set; } + public string? Name { get; set; } [DataMember(Name = "description")] - public string Description { get; set; } + public string? Description { get; set; } [DataMember(Name = "url")] - public string Url { get; set; } + public string? Url { get; set; } [DataMember(Name = "type")] - public string Type { get; set; } + public string? Type { get; set; } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/IconController.cs b/src/Umbraco.Web.BackOffice/Controllers/IconController.cs index 4e00abf1f3..46a97ffd9b 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/IconController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/IconController.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public IconModel GetIcon(string iconName) + public IconModel? GetIcon(string iconName) { return _iconService.GetIcon(iconName); } @@ -38,7 +38,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Gets a list of all svg icons found at at the global icons path. /// /// - public IReadOnlyDictionary GetIcons() => _iconService.GetIcons(); + public IReadOnlyDictionary? GetIcons() => _iconService.GetIcons(); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs index 7546fdf38d..f234c7bfd9 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImageUrlGeneratorController.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator) => _imageUrlGenerator = imageUrlGenerator; - public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null) => _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath) + public string? GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null) => _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(mediaPath) { Width = width, Height = height, diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index a10d524c03..d02d7e74ee 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -54,19 +54,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult GetResized(string imagePath, int width) { var ext = Path.GetExtension(imagePath); - + // check if imagePath is local to prevent open redirect if (!Uri.IsWellFormedUriString(imagePath, UriKind.Relative)) { return Unauthorized(); } - + // we need to check if it is an image by extension if (_imageUrlGenerator.IsSupportedImageFormat(ext) == false) { return NotFound(); } - + // redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file DateTimeOffset? imageLastModified = null; try @@ -111,7 +111,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// If there is no media, image property or image file is found then this will return not found. /// - public string GetProcessedImageUrl(string imagePath, + public string? GetProcessedImageUrl(string imagePath, int? width = null, int? height = null, decimal? focalPointLeft = null, diff --git a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs index 8dcdcb060b..cef800fcb6 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LanguageController.cs @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [HttpGet] - public IEnumerable GetAllLanguages() + public IEnumerable? GetAllLanguages() { var allLanguages = _localizationService.GetAllLanguages(); @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [HttpGet] - public ActionResult GetLanguage(int id) + public ActionResult GetLanguage(int id) { var lang = _localizationService.GetLanguageById(id); if (lang == null) @@ -112,7 +112,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// [Authorize(Policy = AuthorizationPolicies.TreeAccessLanguages)] [HttpPost] - public ActionResult SaveLanguage(Language language) + public ActionResult SaveLanguage(Language language) { if (!ModelState.IsValid) return ValidationProblem(ModelState); @@ -143,7 +143,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers CultureInfo culture; try { - culture = CultureInfo.GetCultureInfo(language.IsoCode); + culture = CultureInfo.GetCultureInfo(language.IsoCode!); } catch (CultureNotFoundException) { @@ -179,7 +179,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers try { // language has the CultureName of the previous lang so we get information about new culture. - cultureAfterChange = CultureInfo.GetCultureInfo(language.IsoCode); + cultureAfterChange = CultureInfo.GetCultureInfo(language.IsoCode!); } catch (CultureNotFoundException) { @@ -189,7 +189,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers existingById.CultureName = cultureAfterChange.DisplayName; existingById.IsDefault = language.IsDefault; existingById.FallbackLanguageId = language.FallbackLanguageId; - existingById.IsoCode = language.IsoCode; + existingById.IsoCode = language.IsoCode!; // modifying an existing language can create a fallback, verify // note that the service will check again, dealing with race conditions diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs index 5134c99792..c90012b0de 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs @@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Controllers @@ -68,7 +69,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers long totalRecords; var dateQuery = sinceDate.HasValue ? _sqlContext.Query().Where(x => x.CreateDate >= sinceDate) : null; var result = _auditService.GetPagedItemsByEntity(id, pageNumber - 1, pageSize, out totalRecords, orderDirection, customFilter: dateQuery); - var mapped = result.Select(item => _umbracoMapper.Map(item)); + var mapped = result.Select(item => _umbracoMapper.Map(item)).WhereNotNull(); var page = new PagedResult(totalRecords, pageNumber, pageSize) { @@ -91,9 +92,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers long totalRecords; var dateQuery = sinceDate.HasValue ? _sqlContext.Query().Where(x => x.CreateDate >= sinceDate) : null; - var userId = _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? 0; + var userId = _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1; var result = _auditService.GetPagedItemsByUser(userId, pageNumber - 1, pageSize, out totalRecords, orderDirection, customFilter:dateQuery); - var mapped = _umbracoMapper.MapEnumerable(result); + var mapped = _umbracoMapper.MapEnumerable(result).WhereNotNull(); return new PagedResult(totalRecords, pageNumber, pageSize) { Items = MapAvatarsAndNames(mapped) @@ -103,7 +104,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetLog(AuditType logType, DateTime? sinceDate = null) { var result = _auditService.GetLogs(Enum.Parse(logType.ToString()), sinceDate); - var mapped = _umbracoMapper.MapEnumerable(result); + var mapped = _umbracoMapper.MapEnumerable(result).WhereNotNull(); return mapped; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs index b7303e53d5..4e562fca60 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs @@ -96,7 +96,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [HttpGet] - public ActionResult> GetLogs(string orderDirection = "Descending", int pageNumber = 1, string filterExpression = null, [FromQuery(Name = "logLevels[]")]string[] logLevels = null, [FromQuery]DateTime? startDate = null, [FromQuery]DateTime? endDate = null) + public ActionResult> GetLogs(string orderDirection = "Descending", int pageNumber = 1, string? filterExpression = null, [FromQuery(Name = "logLevels[]")]string[]? logLevels = null, [FromQuery]DateTime? startDate = null, [FromQuery]DateTime? endDate = null) { var logTimePeriod = GetTimePeriod(startDate, endDate); @@ -131,19 +131,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [HttpGet] - public IEnumerable GetSavedSearches() + public IEnumerable? GetSavedSearches() { return _logViewer.GetSavedSearches(); } [HttpPost] - public IEnumerable PostSavedSearch(SavedLogSearch item) + public IEnumerable? PostSavedSearch(SavedLogSearch item) { return _logViewer.AddSavedSearch(item.Name, item.Query); } [HttpPost] - public IEnumerable DeleteSavedSearch(SavedLogSearch item) + public IEnumerable? DeleteSavedSearch(SavedLogSearch item) { return _logViewer.DeleteSavedSearch(item.Name, item.Query); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs index d869d0ca34..34f3e1f632 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MacroRenderingController.cs @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - return new ActionResult>(_umbracoMapper.Map>(macro).OrderBy(x => x.SortOrder)); + return new ActionResult>(_umbracoMapper.Map>(macro)!.OrderBy(x => x.SortOrder)); } /// @@ -107,19 +107,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class MacroParameterModel { - public string MacroAlias { get; set; } + public string? MacroAlias { get; set; } public int PageId { get; set; } - public IDictionary MacroParams { get; set; } + public IDictionary? MacroParams { get; set; } } - private async Task GetMacroResultAsHtml(string macroAlias, int pageId, IDictionary macroParams) + private async Task GetMacroResultAsHtml(string? macroAlias, int pageId, IDictionary? macroParams) { - var m = _macroService.GetByAlias(macroAlias); + var m = macroAlias is null ? null : _macroService.GetByAlias(macroAlias); if (m == null) return NotFound(); var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var publishedContent = umbracoContext.Content.GetById(true, pageId); + var publishedContent = umbracoContext.Content?.GetById(true, pageId); //if it isn't supposed to be rendered in the editor then return an empty string //currently we cannot render a macro if the page doesn't yet exist @@ -151,7 +151,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. - return Content((await _componentRenderer.RenderMacroForContent(publishedContent, m.Alias, macroParams)).ToString(), "text/html", + return Content((await _componentRenderer.RenderMacroForContent(publishedContent, m.Alias, macroParams)).ToString() ?? string.Empty, "text/html", Encoding.UTF8); } } @@ -178,8 +178,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class CreatePartialViewMacroWithFileModel { - public string Filename { get; set; } - public string VirtualPath { get; set; } + public string? Filename { get; set; } + public string? VirtualPath { get; set; } } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs index 834d8cc565..af99d68548 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs @@ -97,7 +97,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers MacroSource = string.Empty }; - _macroService.Save(macro, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _macroService.Save(macro, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return macro.Id; } @@ -110,7 +110,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [HttpGet] - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var macro = _macroService.GetById(id); @@ -125,7 +125,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [HttpGet] - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var macro = _macroService.GetById(id); @@ -140,7 +140,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [HttpGet] - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -185,7 +185,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return ValidationProblem("Name cannnot be more than 255 characters in length."); } - var macro = _macroService.GetById(int.Parse(macroDisplay.Id.ToString(), CultureInfo.InvariantCulture)); + var macro = macroDisplay.Id is null ? null : _macroService.GetById(int.Parse(macroDisplay.Id.ToString()!, CultureInfo.InvariantCulture)); if (macro == null) { @@ -214,7 +214,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers try { - _macroService.Save(macro, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _macroService.Save(macro, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); macroDisplay.Notifications.Clear(); @@ -280,7 +280,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The . /// - public IDataEditor GetParameterEditorByAlias(string alias) + public IDataEditor? GetParameterEditorByAlias(string alias) { var parameterEditors = _parameterEditorCollection.ToArray(); @@ -404,7 +404,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - private MacroDisplay MapToDisplay(IMacro macro) + private MacroDisplay? MapToDisplay(IMacro macro) { var display = _umbracoMapper.Map(macro); @@ -418,7 +418,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers Id = x.Id }); - display.Parameters = parameters; + if (display is not null) + { + display.Parameters = parameters; + } return display; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 0dc6a45946..a548e14c1f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -124,7 +124,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [OutgoingEditorModelEvent] - public ActionResult GetEmpty(string contentTypeAlias, int parentId) + public ActionResult GetEmpty(string contentTypeAlias, int parentId) { var contentType = _mediaTypeService.Get(contentTypeAlias); if (contentType == null) @@ -132,11 +132,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - var emptyContent = _mediaService.CreateMedia("", parentId, contentType.Alias, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + var emptyContent = _mediaService.CreateMedia("", parentId, contentType.Alias, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); var mapped = _umbracoMapper.Map(emptyContent); - //remove the listview app if it exists - mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList(); + if (mapped is not null) + { + //remove the listview app if it exists + mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList(); + } return mapped; } @@ -173,7 +176,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] - public MediaItemDisplay GetById(int id) + public MediaItemDisplay? GetById(int id) { var foundMedia = GetObjectFromRequest(() => _mediaService.GetById(id)); @@ -193,7 +196,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] - public MediaItemDisplay GetById(Guid id) + public MediaItemDisplay? GetById(Guid id) { var foundMedia = GetObjectFromRequest(() => _mediaService.GetById(id)); @@ -213,7 +216,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// [OutgoingEditorModelEvent] [Authorize(Policy = AuthorizationPolicies.MediaPermissionPathById)] - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi != null) @@ -230,7 +233,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [FilterAllowedOutgoingMedia(typeof(IEnumerable))] - public IEnumerable GetByIds([FromQuery] int[] ids) + public IEnumerable GetByIds([FromQuery] int[] ids) { var foundMedia = _mediaService.GetByIds(ids); return foundMedia.Select(media => _umbracoMapper.Map(media)); @@ -266,7 +269,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return new PagedResult>(total, pageNumber, pageSize) { - Items = children.Select(_umbracoMapper.Map>) + Items = children.Select(_umbracoMapper.Map>).WhereNotNull() }; } @@ -278,13 +281,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { // TODO: Add permissions check! - return _mediaService.GetRootMedia() - .Select(_umbracoMapper.Map>); + return _mediaService.GetRootMedia()? + .Select(_umbracoMapper.Map>).WhereNotNull() ?? Enumerable.Empty>(); } #region GetChildren - private int[] _userStartNodes; + private int[]? _userStartNodes; private readonly PropertyEditorCollection _propertyEditors; private readonly MediaFileManager _mediaFileManager; private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; @@ -293,7 +296,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers protected int[] UserStartNodes { - get { return _userStartNodes ?? (_userStartNodes = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.CalculateMediaStartNodeIds(_entityService, _appCaches)); } + get { return _userStartNodes ??= _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.CalculateMediaStartNodeIds(_entityService, _appCaches) ?? Array.Empty(); } } /// @@ -321,7 +324,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers pageSize = nodes.Length; // bah var pr = new PagedResult>(nodes.Length, pageNumber, pageSize) { - Items = nodes.Select(_umbracoMapper.Map>) + Items = nodes.Select(_umbracoMapper.Map>).WhereNotNull() }; return pr; } @@ -332,12 +335,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers List children; if (pageNumber > 0 && pageSize > 0) { - IQuery queryFilter = null; + IQuery? queryFilter = null; if (filter.IsNullOrWhiteSpace() == false) { //add the default text filter queryFilter = _sqlContext.Query() - .Where(x => x.Name.Contains(filter)); + .Where(x => x.Name != null) + .Where(x => x.Name!.Contains(filter)); } children = _mediaService @@ -361,7 +365,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var pagedResult = new PagedResult>(totalChildren, pageNumber, pageSize); pagedResult.Items = children - .Select(_umbracoMapper.Map>); + .Select(_umbracoMapper.Map>).WhereNotNull(); return pagedResult; } @@ -449,7 +453,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //if the current item is in the recycle bin if (foundMedia.Trashed == false) { - var moveResult = _mediaService.MoveToRecycleBin(foundMedia, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + var moveResult = _mediaService.MoveToRecycleBin(foundMedia, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); if (moveResult == false) { return ValidationProblem(); @@ -457,7 +461,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } else { - var deleteResult = _mediaService.Delete(foundMedia, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + var deleteResult = _mediaService.Delete(foundMedia, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); if (deleteResult == false) { return ValidationProblem(); @@ -490,9 +494,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } var destinationParentID = move.ParentId; - var sourceParentID = toMove.ParentId; + var sourceParentID = toMove?.ParentId; - var moveResult = _mediaService.Move(toMove, move.ParentId, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + var moveResult = toMove is null ? false : _mediaService.Move(toMove, move.ParentId, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); if (sourceParentID == destinationParentID) { @@ -504,7 +508,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } else { - return Content(toMove.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); + return Content(toMove!.Path, MediaTypeNames.Text.Plain, Encoding.UTF8); } } @@ -515,7 +519,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [FileUploadCleanupFilter] [MediaItemSaveValidation] [OutgoingEditorModelEvent] - public ActionResult PostSave( + public ActionResult? PostSave( [ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem) { @@ -538,7 +542,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // * Permissions are valid //Don't update the name if it is empty - if (contentItem.Name.IsNullOrWhiteSpace() == false) + if (contentItem.Name.IsNullOrWhiteSpace() == false && contentItem.PersistedContent is not null) { contentItem.PersistedContent.Name = contentItem.Name; } @@ -546,8 +550,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers MapPropertyValuesForPersistence( contentItem, contentItem.PropertyCollectionDto, - (save, property) => property.GetValue(), //get prop val - (save, property, v) => property.SetValue(v), //set prop val + (save, property) => property?.GetValue(), //get prop val + (save, property, v) => property?.SetValue(v), //set prop val null); // media are all invariant //we will continue to save if model state is invalid, however we cannot save if critical data is missing. @@ -559,13 +563,18 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { //ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue! // add the model state to the outgoing object and throw validation response - MediaItemDisplay forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); + MediaItemDisplay? forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); return ValidationProblem(forDisplay, ModelState); } } + if (contentItem.PersistedContent is null) + { + return null; + } + //save the item - var saveStatus = _mediaService.Save(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + var saveStatus = _mediaService.Save(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); //return the updated model var display = _umbracoMapper.Map(contentItem.PersistedContent); @@ -583,7 +592,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers case ContentSaveAction.SaveNew: if (saveStatus.Success) { - display.AddSuccessNotification( + display?.AddSuccessNotification( _localizedTextService.Localize("speechBubbles", "editMediaSaved"), _localizedTextService.Localize("speechBubbles", "editMediaSavedText")); } @@ -594,7 +603,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //If the item is new and the operation was cancelled, we need to return a different // status code so the UI can handle it since it won't be able to redirect since there // is no Id to redirect to! - if (saveStatus.Result.Result == OperationResultType.FailedCancelledByEvent && IsCreatingAction(contentItem.Action)) + if (saveStatus.Result?.Result == OperationResultType.FailedCancelledByEvent && IsCreatingAction(contentItem.Action)) { return ValidationProblem(display); } @@ -614,7 +623,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpPost] public IActionResult EmptyRecycleBin() { - _mediaService.EmptyRecycleBin(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + _mediaService.EmptyRecycleBin(_backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); return Ok(_localizedTextService.Localize("defaultdialogs", "recycleBinIsEmpty")); } @@ -632,7 +641,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } //if there's nothing to sort just return ok - if (sorted.IdSortOrder.Length == 0) + if (sorted.IdSortOrder?.Length == 0) { return Ok(); } @@ -649,7 +658,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var sortedMedia = new List(); try { - sortedMedia.AddRange(sorted.IdSortOrder.Select(_mediaService.GetById)); + sortedMedia.AddRange(sorted.IdSortOrder?.Select(_mediaService.GetById).WhereNotNull() ?? Enumerable.Empty()); // Save Media with new sort order and update content xml in db accordingly if (_mediaService.Sort(sortedMedia) == false) @@ -666,15 +675,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } } - public async Task> PostAddFolder(PostedFolder folder) + public async Task> PostAddFolder(PostedFolder folder) { var parentIdResult = await GetParentIdAsIntAsync(folder.ParentId, validatePermissions: true); - if (!(parentIdResult.Result is null)) + if (!(parentIdResult?.Result is null)) { - return new ActionResult(parentIdResult.Result); + return new ActionResult(parentIdResult.Result); } - var parentId = parentIdResult.Value; + var parentId = parentIdResult?.Value; if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); @@ -687,7 +696,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } var f = _mediaService.CreateMedia(folder.Name, parentId.Value, Constants.Conventions.MediaTypes.Folder); - _mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _mediaService.Save(f, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return _umbracoMapper.Map(f); } @@ -713,12 +722,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //get the string json from the request var parentIdResult = await GetParentIdAsIntAsync(currentFolder, validatePermissions: true); - if (!(parentIdResult.Result is null)) + if (!(parentIdResult?.Result is null)) { return parentIdResult.Result; } - var parentId = parentIdResult.Value; + var parentId = parentIdResult?.Value; if (!parentId.HasValue) { return NotFound("The passed id doesn't exist"); @@ -740,14 +749,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers for (int i = 0; i < folders.Length - 1; i++) { var folderName = folders[i]; - IMedia folderMediaItem; + IMedia? folderMediaItem; //if uploading directly to media root and not a subfolder if (parentId == Constants.System.Root) { //look for matching folder folderMediaItem = - _mediaService.GetRootMedia().FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder); + _mediaService.GetRootMedia()?.FirstOrDefault(x => x.Name == folderName && x.ContentType.Alias == Constants.Conventions.MediaTypes.Folder); if (folderMediaItem == null) { //if null, create a folder @@ -789,21 +798,27 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (parentId != Constants.System.Root) { var mediaFolderItem = _mediaService.GetById(parentId.Value); - var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == mediaFolderItem.ContentType.Alias); + var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == mediaFolderItem?.ContentType.Alias); if (mediaFolderType != null) { - IMediaType mediaTypeItem = null; + IMediaType? mediaTypeItem = null; - foreach (ContentTypeSort allowedContentType in mediaFolderType.AllowedContentTypes) + if (mediaFolderType.AllowedContentTypes is not null) { - IMediaType checkMediaTypeItem = allMediaTypes.FirstOrDefault(x => x.Id == allowedContentType.Id.Value); - allowedContentTypes.Add(checkMediaTypeItem); - - var fileProperty = checkMediaTypeItem?.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == Constants.Conventions.Media.File); - if (fileProperty != null) + foreach (ContentTypeSort allowedContentType in mediaFolderType.AllowedContentTypes) { - mediaTypeItem = checkMediaTypeItem; + IMediaType? checkMediaTypeItem = allMediaTypes.FirstOrDefault(x => x.Id == allowedContentType.Id.Value); + if (checkMediaTypeItem is not null) + { + allowedContentTypes.Add(checkMediaTypeItem); + } + + var fileProperty = checkMediaTypeItem?.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == Constants.Conventions.Media.File); + if (fileProperty != null) + { + mediaTypeItem = checkMediaTypeItem; + } } } @@ -892,14 +907,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var mediaItemName = fileName.ToFriendlyName(); - var createdMediaItem = _mediaService.CreateMedia(mediaItemName, parentId.Value, mediaTypeAlias, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var createdMediaItem = _mediaService.CreateMedia(mediaItemName, parentId.Value, mediaTypeAlias, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); await using (var stream = formFile.OpenReadStream()) { createdMediaItem.SetValue(_mediaFileManager, _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File, fileName, stream); } - var saveResult = _mediaService.Save(createdMediaItem, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var saveResult = _mediaService.Save(createdMediaItem, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); if (saveResult == false) { AddCancelMessage(tempFiles, _localizedTextService.Localize("speechBubbles", "operationCancelledText") + " -- " + mediaItemName); @@ -931,18 +946,18 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers else { var parentMediaType = _mediaService.GetById(parentId); - var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == parentMediaType.ContentType.Alias); + var mediaFolderType = allMediaTypes.FirstOrDefault(x => x.Alias == parentMediaType?.ContentType.Alias); if (mediaFolderType != null) { isFolderAllowed = - mediaFolderType.AllowedContentTypes.Any(x => x.Alias == Constants.Conventions.MediaTypes.Folder); + mediaFolderType.AllowedContentTypes?.Any(x => x.Alias == Constants.Conventions.MediaTypes.Folder) ?? false; } } return isFolderAllowed; } - private IMedia FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) + private IMedia? FindInChildren(int mediaId, string nameToFind, string contentTypeAlias) { const int pageSize = 500; var page = 0; @@ -969,14 +984,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// and if that check fails an unauthorized exception will occur /// /// - private async Task> GetParentIdAsIntAsync(string parentId, bool validatePermissions) + private async Task?> GetParentIdAsIntAsync(string? parentId, bool validatePermissions) { int intParentId; // test for udi - if (UdiParser.TryParse(parentId, out GuidUdi parentUdi)) + if (UdiParser.TryParse(parentId, out GuidUdi? parentUdi)) { - parentId = parentUdi.Guid.ToString(); + parentId = parentUdi?.Guid.ToString(); } //if it's not an INT then we'll check for GUID @@ -1062,7 +1077,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //check if the item is allowed under this one var parentContentType = _mediaTypeService.Get(parent.ContentTypeId); - if (parentContentType.AllowedContentTypes.Select(x => x.Id).ToArray() + if (parentContentType?.AllowedContentTypes?.Select(x => x.Id).ToArray() .Any(x => x.Value == toMove.ContentType.Id) == false) { var notificationModel = new SimpleNotificationModel(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs index cd1e9037ec..a02a7ed3c4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaTypeController.cs @@ -79,7 +79,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var ct = _mediaTypeService.Get(id); if (ct == null) @@ -97,7 +97,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var mediaType = _mediaTypeService.Get(id); if (mediaType == null) @@ -115,7 +115,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaOrMediaTypes)] - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -147,7 +147,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - _mediaTypeService.Delete(foundType, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _mediaTypeService.Delete(foundType, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return Ok(); } @@ -186,7 +186,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return actionResult.Result; } - var result = actionResult.Value.Select(x => new + var result = actionResult.Value?.Select(x => new { contentType = x.Item1, allowed = x.Item2 @@ -206,7 +206,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult GetWhereCompositionIsUsedInContentTypes(GetAvailableCompositionsFilter filter) { var result = - PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType).Value + PerformGetWhereCompositionIsUsedInContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType).Value? .Select(x => new { contentType = x @@ -215,7 +215,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] - public MediaTypeDisplay GetEmpty(int parentId) + public MediaTypeDisplay? GetEmpty(int parentId) { IMediaType mt; if (parentId != Constants.System.Root) @@ -241,7 +241,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IEnumerable GetAll() => _mediaTypeService.GetAll() - .Select(_umbracoMapper.Map); + .Select(_umbracoMapper.Map).WhereNotNull(); /// /// Deletes a media type container with a given ID @@ -253,7 +253,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult DeleteContainer(int id) { - _mediaTypeService.DeleteContainer(id, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _mediaTypeService.DeleteContainer(id, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return Ok(); } @@ -261,27 +261,27 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostCreateContainer(int parentId, string name) { - var result = _mediaTypeService.CreateContainer(parentId, Guid.NewGuid(), name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var result = _mediaTypeService.CreateContainer(parentId, Guid.NewGuid(), name, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); if (result.Success) return Ok(result.Result); //return the id else - return ValidationProblem(result.Exception.Message); + return ValidationProblem(result.Exception?.Message); } [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] public IActionResult PostRenameContainer(int id, string name) { - var result = _mediaTypeService.RenameContainer(id, name, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + var result = _mediaTypeService.RenameContainer(id, name, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); if (result.Success) return Ok(result.Result); //return the id else - return ValidationProblem(result.Exception.Message); + return ValidationProblem(result.Exception?.Message); } [Authorize(Policy = AuthorizationPolicies.TreeAccessMediaTypes)] - public ActionResult PostSave(MediaTypeSave contentTypeSave) + public ActionResult PostSave(MediaTypeSave contentTypeSave) { var savedCt = PerformPostSave( contentTypeSave, @@ -296,7 +296,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var display = _umbracoMapper.Map(savedCt.Value); - display.AddSuccessNotification( + display?.AddSuccessNotification( _localizedTextService.Localize("speechBubbles","mediaTypeSavedHeader"), string.Empty); @@ -362,14 +362,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } var contentType = _mediaTypeService.Get(contentItem.ContentTypeId); - var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); + var ids = contentType?.AllowedContentTypes?.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray(); - if (ids.Any() == false) return Enumerable.Empty(); + if (ids is null || ids.Any() == false) return Enumerable.Empty(); types = _mediaTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList(); } - var basics = types.Select(_umbracoMapper.Map).ToList(); + var basics = types.Select(_umbracoMapper.Map).WhereNotNull().ToList(); foreach (var basic in basics) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs index ec7ebe30a1..48088db073 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberController.cs @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers Direction orderDirection = Direction.Ascending, bool orderBySystemField = true, string filter = "", - string memberTypeAlias = null) + string? memberTypeAlias = null) { if (pageNumber <= 0 || pageSize <= 0) @@ -150,7 +150,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var pagedResult = new PagedResult(totalRecords, pageNumber, pageSize) { - Items = members.Select(x => _umbracoMapper.Map(x)) + Items = members.Select(x => _umbracoMapper.Map(x)).WhereNotNull() }; return pagedResult; } @@ -162,8 +162,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// The member list for display public MemberListDisplay GetListNodeDisplay(string listName) { - IMemberType foundType = _memberTypeService.Get(listName); - string name = foundType != null ? foundType.Name : listName; + IMemberType? foundType = _memberTypeService.Get(listName); + string? name = foundType != null ? foundType.Name : listName; var apps = new List(); apps.Add(ListViewContentAppFactory.CreateContentApp(_dataTypeService, _propertyEditors, listName, "member", Core.Constants.DataTypes.DefaultMembersListView)); @@ -190,9 +190,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// The Guid key of the member /// The member for display [OutgoingEditorModelEvent] - public MemberDisplay GetByKey(Guid key) + public MemberDisplay? GetByKey(Guid key) { - IMember foundMember = _memberService.GetByKey(key); + IMember? foundMember = _memberService.GetByKey(key); if (foundMember == null) { HandleContentNotFound(key); @@ -207,14 +207,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// The content type /// The empty member for display [OutgoingEditorModelEvent] - public ActionResult GetEmpty(string contentTypeAlias = null) + public ActionResult GetEmpty(string? contentTypeAlias = null) { if (contentTypeAlias == null) { return NotFound(); } - IMemberType contentType = _memberTypeService.Get(contentTypeAlias); + IMemberType? contentType = _memberTypeService.Get(contentTypeAlias); if (contentType == null) { return NotFound(); @@ -223,7 +223,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers string newPassword = _memberManager.GeneratePassword(); IMember emptyContent = new Member(contentType); - emptyContent.AdditionalData["NewPassword"] = newPassword; + if (emptyContent.AdditionalData is not null) + { + emptyContent.AdditionalData["NewPassword"] = newPassword; + } + return _umbracoMapper.Map(emptyContent); } @@ -235,7 +239,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [FileUploadCleanupFilter] [OutgoingEditorModelEvent] [MemberSaveValidation] - public async Task> PostSave([ModelBinder(typeof(MemberBinder))] MemberSave contentItem) + public async Task> PostSave([ModelBinder(typeof(MemberBinder))] MemberSave contentItem) { if (contentItem == null) { @@ -257,7 +261,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // Unlike content/media - if there are errors for a member, we do NOT proceed to save them, we cannot so return the errors if (ModelState.IsValid == false) { - MemberDisplay forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); + MemberDisplay? forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); return ValidationProblem(forDisplay, ModelState); } @@ -292,7 +296,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } // return the updated model - MemberDisplay display = _umbracoMapper.Map(contentItem.PersistedContent); + MemberDisplay? display = _umbracoMapper.Map(contentItem.PersistedContent); // lastly, if it is not valid, add the model state to the outgoing object and throw a 403 if (!ModelState.IsValid) @@ -305,7 +309,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { case ContentSaveAction.Save: case ContentSaveAction.SaveNew: - display.AddSuccessNotification( + display?.AddSuccessNotification( _localizedTextService.Localize("speechBubbles","editMemberSaved"), _localizedTextService.Localize("speechBubbles","editMemberSaved")); break; @@ -323,23 +327,26 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// The member content item to map properties from private void MapPropertyValues(MemberSave contentItem) { - // Don't update the name if it is empty - if (contentItem.Name.IsNullOrWhiteSpace() == false) + if (contentItem.PersistedContent is not null) { - contentItem.PersistedContent.Name = contentItem.Name; - } + // Don't update the name if it is empty + if (contentItem.Name.IsNullOrWhiteSpace() == false) + { + contentItem.PersistedContent.Name = contentItem.Name; + } - // map the custom properties - this will already be set for new entities in our member binder - contentItem.PersistedContent.IsApproved = contentItem.IsApproved; - contentItem.PersistedContent.Email = contentItem.Email.Trim(); - contentItem.PersistedContent.Username = contentItem.Username; + // map the custom properties - this will already be set for new entities in our member binder + contentItem.PersistedContent.IsApproved = contentItem.IsApproved; + contentItem.PersistedContent.Email = contentItem.Email.Trim(); + contentItem.PersistedContent.Username = contentItem.Username; + } // use the base method to map the rest of the properties MapPropertyValuesForPersistence( contentItem, contentItem.PropertyCollectionDto, - (save, property) => property.GetValue(), // get prop val - (save, property, v) => property.SetValue(v), // set prop val + (save, property) => property?.GetValue(), // get prop val + (save, property, v) => property?.SetValue(v), // set prop val null); // member are all invariant } @@ -352,7 +359,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// The identity result of the created member private async Task> CreateMemberAsync(MemberSave contentItem) { - IMemberType memberType = _memberTypeService.Get(contentItem.ContentTypeAlias); + IMemberType? memberType = _memberTypeService.Get(contentItem.ContentTypeAlias); if (memberType == null) { throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}"); @@ -365,11 +372,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers contentItem.IsApproved, contentItem.Name); - IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password.NewPassword); + IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password?.NewPassword); if (created.Succeeded == false) { - MemberDisplay forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); + MemberDisplay? forDisplay = _umbracoMapper.Map(contentItem.PersistedContent); foreach (IdentityError error in created.Errors) { switch (error.Code) @@ -411,12 +418,17 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } // now re-look up the member, which will now exist - IMember member = _memberService.GetByEmail(contentItem.Email); + IMember? member = _memberService.GetByEmail(contentItem.Email); + + if (member is null) + { + return false; + } // map the save info over onto the user member = _umbracoMapper.Map(contentItem, member); - int creatorId = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id; + int creatorId = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1; member.CreatorId = creatorId; // assign the mapped property values that are not part of the identity properties @@ -425,7 +437,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { if (builtInAliases.Contains(property.Alias) == false) { - member.Properties[property.Alias].SetValue(property.Value); + member.Properties[property.Alias]?.SetValue(property.Value); } } @@ -458,40 +470,49 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// private async Task> UpdateMemberAsync(MemberSave contentItem) { - contentItem.PersistedContent.WriterId = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id; + if (contentItem.PersistedContent is not null) + { + contentItem.PersistedContent.WriterId = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1; + } // If the user doesn't have access to sensitive values, then we need to check if any of the built in member property types // have been marked as sensitive. If that is the case we cannot change these persisted values no matter what value has been posted. // There's only 3 special ones we need to deal with that are part of the MemberSave instance: Comments, IsApproved, IsLockedOut // but we will take care of this in a generic way below so that it works for all props. - if (!_backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser.HasAccessToSensitiveData()) + if (!_backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.HasAccessToSensitiveData() ?? true) { - IMemberType memberType = _memberTypeService.Get(contentItem.PersistedContent.ContentTypeId); - var sensitiveProperties = memberType + IMemberType? memberType = contentItem.PersistedContent is null ? null : _memberTypeService.Get(contentItem.PersistedContent.ContentTypeId); + var sensitiveProperties = memberType? .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias)) .ToList(); - foreach (IPropertyType sensitiveProperty in sensitiveProperties) + if (sensitiveProperties is not null) { - // TODO: This logic seems to deviate from the logic that is in v8 where we are explitly checking - // against 3 properties: Comments, IsApproved, IsLockedOut, is the v8 version incorrect? - - ContentPropertyBasic destProp = contentItem.Properties.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias); - if (destProp != null) + foreach (IPropertyType sensitiveProperty in sensitiveProperties) { - // if found, change the value of the contentItem model to the persisted value so it remains unchanged - object origValue = contentItem.PersistedContent.GetValue(sensitiveProperty.Alias); - destProp.Value = origValue; + // TODO: This logic seems to deviate from the logic that is in v8 where we are explitly checking + // against 3 properties: Comments, IsApproved, IsLockedOut, is the v8 version incorrect? + + ContentPropertyBasic? destProp = contentItem.Properties.FirstOrDefault(x => x.Alias == sensitiveProperty.Alias); + if (destProp != null) + { + // if found, change the value of the contentItem model to the persisted value so it remains unchanged + object? origValue = contentItem.PersistedContent?.GetValue(sensitiveProperty.Alias); + destProp.Value = origValue; + } } } } - // First save the IMember with mapped values before we start updating data with aspnet identity - _memberService.Save(contentItem.PersistedContent); + if (contentItem.PersistedContent is not null) + { + // First save the IMember with mapped values before we start updating data with aspnet identity + _memberService.Save(contentItem.PersistedContent); + } bool needsResync = false; - MemberIdentityUser identityMember = await _memberManager.FindByIdAsync(contentItem.Id.ToString()); + MemberIdentityUser identityMember = await _memberManager.FindByIdAsync(contentItem.Id?.ToString()); if (identityMember == null) { return ValidationProblem("Member was not found"); @@ -538,13 +559,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers }; // Change and persist the password - Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _memberManager); + Attempt passwordChangeResult = await _passwordChanger.ChangePasswordWithIdentityAsync(changingPasswordModel, _memberManager); if (!passwordChangeResult.Success) { foreach (string memberName in passwordChangeResult.Result?.ChangeError?.MemberNames ?? Enumerable.Empty()) { - ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError?.ErrorMessage ?? string.Empty); + ModelState.AddModelError(memberName, passwordChangeResult.Result?.ChangeError?.ErrorMessage ?? string.Empty); } return ValidationProblem(ModelState); } @@ -565,7 +586,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // If there have been underlying changes made by ASP.NET Identity, then we need to resync the // IMember on the PersistedContent with what is stored since it will be mapped to display. - if (needsResync) + if (needsResync && contentItem.PersistedContent is not null) { contentItem.PersistedContent = _memberService.GetById(contentItem.PersistedContent.Id); } @@ -595,7 +616,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } } - IMember byUsername = _memberService.GetByUsername(contentItem.Username); + IMember? byUsername = _memberService.GetByUsername(contentItem.Username); if (byUsername != null && byUsername.Key != contentItem.Key) { ModelState.AddPropertyError( @@ -604,7 +625,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return false; } - IMember byEmail = _memberService.GetByEmail(contentItem.Email); + IMember? byEmail = _memberService.GetByEmail(contentItem.Email); if (byEmail != null && byEmail.Key != contentItem.Key) { ModelState.AddPropertyError( @@ -634,7 +655,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The groups to updates /// The member as an identity user - private async Task> AddOrUpdateRoles(IEnumerable groups, MemberIdentityUser identityMember) + private async Task> AddOrUpdateRoles(IEnumerable? groups, MemberIdentityUser identityMember) { var hasChanges = false; @@ -646,7 +667,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // find the ones to remove and remove them IEnumerable roles = currentRoles.ToList(); - string[] rolesToRemove = roles.Except(groups).ToArray(); + string[] rolesToRemove = roles.Except(groups ?? Enumerable.Empty()).ToArray(); // Now let's do the role provider stuff - now that we've saved the content item (that is important since // if we are changing the username, it must be persisted before looking up the member roles). @@ -661,8 +682,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } // find the ones to add and add them - string[] toAdd = groups.Except(roles).ToArray(); - if (toAdd.Any()) + string[]? toAdd = groups?.Except(roles).ToArray(); + if (toAdd?.Any() ?? false) { // add the ones submitted IdentityResult identityResult = await _memberManager.AddToRolesAsync(identityMember, toAdd); @@ -685,7 +706,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpPost] public IActionResult DeleteByKey(Guid key) { - IMember foundMember = _memberService.GetByKey(key); + IMember? foundMember = _memberService.GetByKey(key); if (foundMember == null) { return HandleContentNotFound(key); @@ -704,14 +725,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpGet] public IActionResult ExportMemberData(Guid key) { - IUser currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; - if (currentUser.HasAccessToSensitiveData() == false) + if (currentUser?.HasAccessToSensitiveData() == false) { return Forbid(); } - MemberExportModel member = ((MemberService)_memberService).ExportMember(key); + MemberExportModel? member = ((MemberService)_memberService).ExportMember(key); if (member is null) { throw new NullReferenceException("No member found with key " + key); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs index 24f128e410..c5c8771146 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberGroupController.cs @@ -43,15 +43,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(int id) + public ActionResult GetById(int id) { - IMemberGroup memberGroup = _memberGroupService.GetById(id); + IMemberGroup? memberGroup = _memberGroupService.GetById(id); if (memberGroup == null) { return NotFound(); } - MemberGroupDisplay dto = _umbracoMapper.Map(memberGroup); + MemberGroupDisplay? dto = _umbracoMapper.Map(memberGroup); return dto; } @@ -60,9 +60,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { - IMemberGroup memberGroup = _memberGroupService.GetById(id); + IMemberGroup? memberGroup = _memberGroupService.GetById(id); if (memberGroup == null) { return NotFound(); @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -84,7 +84,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - IMemberGroup memberGroup = _memberGroupService.GetById(guidUdi.Guid); + IMemberGroup? memberGroup = _memberGroupService.GetById(guidUdi.Guid); if (memberGroup == null) { return NotFound(); @@ -94,13 +94,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } public IEnumerable GetByIds([FromQuery] int[] ids) - => _memberGroupService.GetByIds(ids).Select(_umbracoMapper.Map); + => _memberGroupService.GetByIds(ids).Select(_umbracoMapper.Map).WhereNotNull(); [HttpDelete] [HttpPost] public IActionResult DeleteById(int id) { - IMemberGroup memberGroup = _memberGroupService.GetById(id); + IMemberGroup? memberGroup = _memberGroupService.GetById(id); if (memberGroup == null) { return NotFound(); @@ -112,22 +112,22 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetAllGroups() => _memberGroupService.GetAll() - .Select(_umbracoMapper.Map); + .Select(_umbracoMapper.Map).WhereNotNull(); - public MemberGroupDisplay GetEmpty() + public MemberGroupDisplay? GetEmpty() { var item = new MemberGroup(); return _umbracoMapper.Map(item); } - public bool IsMemberGroupNameUnique(int id, string oldName, string newName) + public bool IsMemberGroupNameUnique(int id, string? oldName, string? newName) { if (newName == oldName) { return true; // name hasn't changed } - IMemberGroup memberGroup = _memberGroupService.GetByName(newName); + IMemberGroup? memberGroup = _memberGroupService.GetByName(newName); if (memberGroup == null) { return true; // no member group found @@ -136,10 +136,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return memberGroup.Id == id; } - public ActionResult PostSave(MemberGroupSave saveModel) + public ActionResult PostSave(MemberGroupSave saveModel) { - var id = int.Parse(saveModel.Id.ToString(), CultureInfo.InvariantCulture); - IMemberGroup memberGroup = id > 0 ? _memberGroupService.GetById(id) : new MemberGroup(); + var id = saveModel.Id is not null ? int.Parse(saveModel.Id.ToString()!, CultureInfo.InvariantCulture) : default; + IMemberGroup? memberGroup = id > 0 ? _memberGroupService.GetById(id) : new MemberGroup(); if (memberGroup == null) { return NotFound(); @@ -150,9 +150,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers memberGroup.Name = saveModel.Name; _memberGroupService.Save(memberGroup); - MemberGroupDisplay display = _umbracoMapper.Map(memberGroup); + MemberGroupDisplay? display = _umbracoMapper.Map(memberGroup); - display.AddSuccessNotification( + display?.AddSuccessNotification( _localizedTextService.Localize("speechBubbles", "memberGroupSavedHeader"), string.Empty); @@ -160,8 +160,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } else { - MemberGroupDisplay display = _umbracoMapper.Map(memberGroup); - display.AddErrorNotification( + MemberGroupDisplay? display = _umbracoMapper.Map(memberGroup); + display?.AddErrorNotification( _localizedTextService.Localize("speechBubbles", "memberGroupNameDuplicate"), string.Empty); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs index 7c1f6f4187..6ac985e2d5 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeController.cs @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var mt = _memberTypeService.Get(id); if (mt == null) @@ -81,7 +81,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var memberType = _memberTypeService.Get(id); if (memberType == null) @@ -98,7 +98,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -129,7 +129,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return NotFound(); } - _memberTypeService.Delete(foundType, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); + _memberTypeService.Delete(foundType, _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1); return Ok(); } @@ -160,7 +160,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return actionResult.Result; } - var result = actionResult.Value + var result = actionResult.Value? .Select(x => new { contentType = x.Item1, @@ -169,7 +169,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return Ok(result); } - public MemberTypeDisplay GetEmpty() + public MemberTypeDisplay? GetEmpty() { var ct = new MemberType(_shortStringHelper, -1); ct.Icon = Constants.Icons.Member; @@ -187,16 +187,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetAllTypes() { return _memberTypeService.GetAll() - .Select(_umbracoMapper.Map); + .Select(_umbracoMapper.Map).WhereNotNull(); } - public ActionResult PostSave(MemberTypeSave contentTypeSave) + public ActionResult PostSave(MemberTypeSave contentTypeSave) { //get the persisted member type var ctId = Convert.ToInt32(contentTypeSave.Id); var ct = ctId > 0 ? _memberTypeService.Get(ctId) : null; - if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.HasAccessToSensitiveData() == false) + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.HasAccessToSensitiveData() == false) { //We need to validate if any properties on the contentTypeSave have had their IsSensitiveValue changed, //and if so, we need to check if the current user has access to sensitive values. If not, we have to return an error @@ -246,7 +246,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var display =_umbracoMapper.Map(savedCt.Value); - display.AddSuccessNotification( + display?.AddSuccessNotification( _localizedTextService.Localize("speechBubbles","memberTypeSavedHeader"), string.Empty); diff --git a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs index 1d15a6448a..e2dd4b8a4c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MemberTypeQueryController.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Web.Common.Attributes; using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Web.BackOffice.Controllers @@ -36,7 +37,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// public IEnumerable GetAllTypes() => _memberTypeService.GetAll() - .Select(_umbracoMapper.Map); + .Select(_umbracoMapper.Map).WhereNotNull(); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs index 09cb7f97c9..5faf83d430 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs @@ -16,6 +16,7 @@ using Umbraco.Cms.Web.Common.Authorization; using Constants = Umbraco.Cms.Core.Constants; using Microsoft.Extensions.Logging; using Umbraco.Cms.Infrastructure.Install; +using Umbraco.Extensions; namespace Umbraco.Cms.Web.BackOffice.Controllers { @@ -45,7 +46,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetCreatedPackages() { - return _packagingService.GetAllCreatedPackages(); + return _packagingService.GetAllCreatedPackages().WhereNotNull(); } public ActionResult GetCreatedPackageById(int id) @@ -91,7 +92,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpDelete] public IActionResult DeleteCreatedPackage(int packageId) { - _packagingService.DeleteCreatedPackage(packageId, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? -1); + _packagingService.DeleteCreatedPackage(packageId, _backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? -1); return Ok(); } @@ -144,7 +145,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public ActionResult GetInstalledPackageByName([FromQuery] string packageName) { - InstalledPackage pack = _packagingService.GetInstalledPackageByName(packageName); + InstalledPackage? pack = _packagingService.GetInstalledPackageByName(packageName); if (pack == null) { return NotFound(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/ParameterSwapControllerActionSelectorAttribute.cs b/src/Umbraco.Web.BackOffice/Controllers/ParameterSwapControllerActionSelectorAttribute.cs index c1ef5b153e..c6840b8db7 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ParameterSwapControllerActionSelectorAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ParameterSwapControllerActionSelectorAttribute.cs @@ -82,7 +82,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // if it's a post we can try to read from the body and bind from the json value if (context.RouteContext.HttpContext.Request.Method.Equals(HttpMethod.Post.Method)) { - JObject postBodyJson; + JObject? postBodyJson; if (httpContext.Items.TryGetValue(Constants.HttpContext.Items.RequestBodyAsJObject, out var value) && value is JObject cached) { @@ -149,7 +149,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private bool TryBindFromUri(ActionConstraintContext context, out ActionSelectorCandidate? foundCandidate) { - string requestParam = null; + string? requestParam = null; if (context.RouteContext.HttpContext.Request.Query.TryGetValue(_parameterName, out var stringValues)) { requestParam = stringValues.ToString(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs index a7ba84f405..303ad44423 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs @@ -72,7 +72,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (id.HasValue) { var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var content = umbracoContext.Content.GetById(true, id.Value); + var content = umbracoContext.Content?.GetById(true, id.Value); if (content is null) return NotFound(); @@ -82,7 +82,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { - var viewEngineResult = _viewEngines.FindView(ControllerContext, model.PreviewExtendedHeaderView, false); + var viewEngineResult = _viewEngines.FindView(ControllerContext, model.PreviewExtendedHeaderView!, false); if (viewEngineResult.View == null) throw new InvalidOperationException("Could not find the view " + model.PreviewExtendedHeaderView + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations)); } @@ -126,15 +126,15 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return RedirectPermanent($"../../{id}{query}"); } - public ActionResult EnterPreview(int id) + public ActionResult? EnterPreview(int id) { - var user = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser; + var user = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser; _cookieManager.SetCookieValue(Constants.Web.PreviewCookieName, "preview"); return null; } - public ActionResult End(string redir = null) + public ActionResult End(string? redir = null) { _cookieManager.ExpireCookie(Constants.Web.PreviewCookieName); diff --git a/src/Umbraco.Web.BackOffice/Controllers/PublicAccessController.cs b/src/Umbraco.Web.BackOffice/Controllers/PublicAccessController.cs index d26736bfdb..4877f1c805 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PublicAccessController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PublicAccessController.cs @@ -50,13 +50,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpGet] public ActionResult GetPublicAccess(int contentId) { - IContent content = _contentService.GetById(contentId); + IContent? content = _contentService.GetById(contentId); if (content == null) { return NotFound(); } - PublicAccessEntry entry = _publicAccessService.GetEntryForContent(content); + PublicAccessEntry? entry = _publicAccessService.GetEntryForContent(content); if (entry == null || entry.ProtectedNodeId != content.Id) { return Ok(); @@ -66,12 +66,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers .GetAll(UmbracoObjectTypes.Document, entry.LoginNodeId, entry.NoAccessNodeId) .ToDictionary(x => x.Id); - if (!nodes.TryGetValue(entry.LoginNodeId, out IEntitySlim loginPageEntity)) + if (!nodes.TryGetValue(entry.LoginNodeId, out IEntitySlim? loginPageEntity)) { throw new InvalidOperationException($"Login node with id ${entry.LoginNodeId} was not found"); } - if (!nodes.TryGetValue(entry.NoAccessNodeId, out IEntitySlim errorPageEntity)) + if (!nodes.TryGetValue(entry.NoAccessNodeId, out IEntitySlim? errorPageEntity)) { throw new InvalidOperationException($"Error node with id ${entry.LoginNodeId} was not found"); } @@ -85,16 +85,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers MemberDisplay[] members = usernames .Select(username => _memberService.GetByUsername(username)) - .Where(member => member != null) .Select(_umbracoMapper.Map) + .WhereNotNull() .ToArray(); - var allGroups = _memberRoleManager.Roles.ToDictionary(x => x.Name); + var allGroups = _memberRoleManager.Roles.Where(x => x.Name != null).ToDictionary(x => x.Name!); MemberGroupDisplay[] groups = entry.Rules .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType) - .Select(rule => allGroups.TryGetValue(rule.RuleValue, out UmbracoIdentityRole memberRole) ? memberRole : null) - .Where(x => x != null) + .Select(rule => rule.RuleValue is not null && allGroups.TryGetValue(rule.RuleValue, out UmbracoIdentityRole? memberRole) ? memberRole : null) .Select(_umbracoMapper.Map) + .WhereNotNull() .ToArray(); return new PublicAccess @@ -138,9 +138,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { entry = new PublicAccessEntry(content, loginPage, errorPage, new List()); - foreach (var ruleValue in candidateRuleValues) + if (candidateRuleValues is not null) { - entry.AddRule(ruleValue, newRuleType); + foreach (var ruleValue in candidateRuleValues) + { + entry.AddRule(ruleValue, newRuleType); + } } } else @@ -151,9 +154,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var currentRules = entry.Rules.ToArray(); var obsoleteRules = currentRules.Where(rule => rule.RuleType != newRuleType - || candidateRuleValues.Contains(rule.RuleValue) == false + || candidateRuleValues?.Contains(rule.RuleValue) == false ); - var newRuleValues = candidateRuleValues.Where(group => + var newRuleValues = candidateRuleValues?.Where(group => currentRules.Any(rule => rule.RuleType == newRuleType && rule.RuleValue == group @@ -163,9 +166,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { entry.RemoveRule(rule); } - foreach (var ruleValue in newRuleValues) + + if (newRuleValues is not null) { - entry.AddRule(ruleValue, newRuleType); + foreach (var ruleValue in newRuleValues) + { + entry.AddRule(ruleValue, newRuleType); + } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs b/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs index 99f5e03174..a5f5999a15 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RedirectUrlManagementController.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System; +using System.Linq; using System.Security; using System.Threading; using Microsoft.AspNetCore.Mvc; @@ -55,7 +56,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult GetEnableState() { var enabled = _webRoutingSettings.CurrentValue.DisableRedirectUrlTracking == false; - var userIsAdmin = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsAdmin(); + var userIsAdmin = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.IsAdmin() ?? false; return Ok(new { enabled, userIsAdmin }); } @@ -70,7 +71,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers ? _redirectUrlService.GetAllRedirectUrls(page, pageSize, out resultCount) : _redirectUrlService.SearchRedirectUrls(searchTerm, page, pageSize, out resultCount); - searchResult.SearchResults = _umbracoMapper.MapEnumerable(redirects); + searchResult.SearchResults = _umbracoMapper.MapEnumerable(redirects).WhereNotNull(); searchResult.TotalCount = resultCount; searchResult.CurrentPage = page; searchResult.PageCount = ((int)resultCount + pageSize - 1) / pageSize; @@ -88,11 +89,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public RedirectUrlSearchResult RedirectUrlsForContentItem(string contentUdi) { var redirectsResult = new RedirectUrlSearchResult(); - if (UdiParser.TryParse(contentUdi, out GuidUdi guidIdi)) + if (UdiParser.TryParse(contentUdi, out GuidUdi? guidIdi)) { - var redirects = _redirectUrlService.GetContentRedirectUrls(guidIdi.Guid); - var mapped = _umbracoMapper.MapEnumerable(redirects); + var redirects = _redirectUrlService.GetContentRedirectUrls(guidIdi!.Guid); + var mapped = _umbracoMapper.MapEnumerable(redirects).WhereNotNull().ToList(); redirectsResult.SearchResults = mapped; //not doing paging 'yet' redirectsResult.TotalCount = mapped.Count; @@ -111,7 +112,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [HttpPost] public IActionResult ToggleUrlTracker(bool disable) { - var userIsAdmin = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsAdmin(); + var userIsAdmin = _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.IsAdmin(); if (userIsAdmin == false) { var errorMessage = "User is not a member of the administrators group and so is not allowed to toggle the URL tracker"; diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs index e9f6d6cb63..9d1f2bf66d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RelationController.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _relationService = relationService ?? throw new ArgumentNullException(nameof(relationService)); } - public RelationDisplay GetById(int id) + public RelationDisplay? GetById(int id) { return _umbracoMapper.Map(_relationService.GetById(id)); } @@ -46,10 +46,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { return _umbracoMapper.MapEnumerable( - relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))); + relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))).WhereNotNull(); } - return _umbracoMapper.MapEnumerable(relations); + return _umbracoMapper.MapEnumerable(relations).WhereNotNull(); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs index 197148a2a3..5966cd5e17 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/RelationTypeController.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The relation type ID. /// Returns the . - public ActionResult GetById(int id) + public ActionResult GetById(int id) { var relationType = _relationService.GetRelationTypeById(id); @@ -67,7 +67,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The relation type ID. /// Returns the . - public ActionResult GetById(Guid id) + public ActionResult GetById(Guid id) { var relationType = _relationService.GetRelationTypeById(id); if (relationType == null) @@ -82,7 +82,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The relation type ID. /// Returns the . - public ActionResult GetById(Udi id) + public ActionResult GetById(Udi id) { var guidUdi = id as GuidUdi; if (guidUdi == null) @@ -96,7 +96,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return _umbracoMapper.Map(relationType); } - public PagedResult GetPagedResults(int id, int pageNumber = 1, int pageSize = 100) + public PagedResult GetPagedResults(int id, int pageNumber = 1, int pageSize = 100) { if (pageNumber <= 0 || pageSize <= 0) @@ -107,7 +107,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers // Ordering do we need to pass through? var relations = _relationService.GetPagedByRelationTypeId(id, pageNumber -1, pageSize, out long totalRecords); - return new PagedResult(totalRecords, pageNumber, pageSize) + return new PagedResult(totalRecords, pageNumber, pageSize) { Items = relations.Select(x => _umbracoMapper.Map(x)) }; @@ -145,7 +145,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var relationTypePersisted = new RelationType( relationType.Name, - relationType.Name.ToSafeAlias(_shortStringHelper, true), + relationType.Name?.ToSafeAlias(_shortStringHelper, true), relationType.IsBidirectional, relationType.ParentObjectType, relationType.ChildObjectType, @@ -169,7 +169,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// The relation type to update. /// A display object containing the updated relation type. - public ActionResult PostSave(RelationTypeSave relationType) + public ActionResult PostSave(RelationTypeSave relationType) { var relationTypePersisted = _relationService.GetRelationTypeById(relationType.Key); @@ -184,7 +184,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { _relationService.Save(relationTypePersisted); var display = _umbracoMapper.Map(relationTypePersisted); - display.AddSuccessNotification("Relation type saved", ""); + display?.AddSuccessNotification("Relation type saved", ""); return display; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs b/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs index 3432d4ae12..ae0fff7da5 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/SectionController.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; +using Umbraco.Cms.Core.Collections; using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models.ContentEditing; using Umbraco.Cms.Core.Models.Trees; @@ -53,9 +54,9 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public async Task>> GetSections() { - var sections = _sectionService.GetAllowedSections(_backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().Result ?? 0); + var sections = _sectionService.GetAllowedSections(_backofficeSecurityAccessor.BackOfficeSecurity?.GetUserId().Result ?? 0); - var sectionModels = sections.Select(_umbracoMapper.Map
).ToArray(); + var sectionModels = sections.Select(_umbracoMapper.Map
).WhereNotNull().ToArray(); // this is a bit nasty since we'll be proxying via the app tree controller but we sort of have to do that // since tree's by nature are controllers and require request contextual data @@ -65,24 +66,28 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers ControllerContext = ControllerContext }; - var dashboards = _dashboardService.GetDashboards(_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser); + var dashboards = _dashboardService.GetDashboards(_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser); //now we can add metadata for each section so that the UI knows if there's actually anything at all to render for //a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree) foreach (var section in sectionModels) { - var hasDashboards = dashboards.TryGetValue(section.Alias, out var dashboardsForSection) && + var hasDashboards = section?.Alias is not null && dashboards.TryGetValue(section.Alias, out var dashboardsForSection) && dashboardsForSection.Any(); if (hasDashboards) continue; // get the first tree in the section and get its root node route path - var sectionRoot = await appTreeController.GetApplicationTrees(section.Alias, null, null); + var sectionRoot = await appTreeController.GetApplicationTrees(section?.Alias, null, null); if (!(sectionRoot.Result is null)) { return sectionRoot.Result; } - section.RoutePath = GetRoutePathForFirstTree(sectionRoot.Value); + + if (section is not null) + { + section.RoutePath = GetRoutePathForFirstTree(sectionRoot.Value!); + } } return sectionModels; @@ -93,16 +98,19 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - private string GetRoutePathForFirstTree(TreeRootNode rootNode) + private string? GetRoutePathForFirstTree(TreeRootNode rootNode) { if (!rootNode.IsContainer || !rootNode.ContainsTrees) return rootNode.RoutePath; - foreach (var node in rootNode.Children) + if (rootNode.Children is not null) { - if (node is TreeRootNode groupRoot) - return GetRoutePathForFirstTree(groupRoot); //recurse to get the first tree in the group - return node.RoutePath; + foreach (var node in rootNode.Children) + { + if (node is TreeRootNode groupRoot) + return GetRoutePathForFirstTree(groupRoot); //recurse to get the first tree in the group + return node.RoutePath; + } } return string.Empty; @@ -112,14 +120,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// Returns all the sections that the user has access to /// /// - public IEnumerable
GetAllSections() + public IEnumerable GetAllSections() { var sections = _sectionService.GetSections(); var mapped = sections.Select(_umbracoMapper.Map
); - if (_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.IsAdmin()) + if (_backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.IsAdmin() ?? false) return mapped; - return mapped.Where(x => _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.AllowedSections.Contains(x.Alias)).ToArray(); + return mapped.Where(x => _backofficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.AllowedSections.Contains(x?.Alias) ?? false).ToArray(); } } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs b/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs index 4f64a34632..750ff90137 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs @@ -34,8 +34,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetRulesByName(string name) { var css = _fileService.GetStylesheet(name.EnsureEndsWith(".css")); - if (css == null) + if (css is null || css.Properties is null) + { return Enumerable.Empty(); + } return css.Properties.Select(x => new StylesheetRule() { Name = x.Name, Selector = x.Alias }); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs index 9e81cb196e..472b67224c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs @@ -41,9 +41,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - protected virtual ActionResult ValidationProblem(IErrorModel model, ModelStateDictionary modelStateDictionary, int statusCode = StatusCodes.Status400BadRequest) + protected virtual ActionResult ValidationProblem(IErrorModel? model, ModelStateDictionary modelStateDictionary, int statusCode = StatusCodes.Status400BadRequest) { - model.Errors = modelStateDictionary.ToErrorDictionary(); + if (model is not null) + { + model.Errors = modelStateDictionary.ToErrorDictionary(); + } + return ValidationProblem(model, statusCode); } @@ -127,7 +131,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - protected virtual ActionResult ValidationProblem(INotificationModel model, int statusCode = StatusCodes.Status400BadRequest) + protected virtual ActionResult ValidationProblem(INotificationModel? model, int statusCode = StatusCodes.Status400BadRequest) => new ValidationErrorResult(model, statusCode); } } diff --git a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs index 6ff690ad1f..67b97892ea 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ApplicationTreeController.cs @@ -59,9 +59,9 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// An optional single tree alias, if specified will only load the single tree for the request app /// The query strings /// Tree use. - public async Task> GetApplicationTrees(string application, string tree, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection queryStrings, TreeUse use = TreeUse.Main) + public async Task> GetApplicationTrees(string? application, string? tree, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormCollection? queryStrings, TreeUse use = TreeUse.Main) { - application = application.CleanForXss(); + application = application?.CleanForXss(); if (string.IsNullOrEmpty(application)) { @@ -189,7 +189,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// loading multiple trees we will just return null so that it's not added /// to the list /// - private async Task?> TryGetRootNode(Tree tree, FormCollection querystring) + private async Task?> TryGetRootNode(Tree tree, FormCollection? querystring) { if (tree == null) { @@ -202,7 +202,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// Get the tree root node of a tree. /// - private async Task> GetTreeRootNode(Tree tree, int id, FormCollection querystring) + private async Task> GetTreeRootNode(Tree tree, int id, FormCollection? querystring) { if (tree == null) { @@ -249,7 +249,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// Gets the root node of a tree. /// - private async Task?> GetRootNode(Tree tree, FormCollection querystring) + private async Task?> GetRootNode(Tree tree, FormCollection? querystring) { if (tree == null) { @@ -288,7 +288,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// Get the child nodes of a tree node. /// - private async Task?> GetChildren(Tree tree, int id, FormCollection querystring) + private async Task?> GetChildren(Tree tree, int id, FormCollection? querystring) { if (tree == null) { @@ -298,7 +298,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees // the method we proxy has an 'id' parameter which is *not* in the querystring, // we need to add it for the proxy to work (else, it does not find the method, // when trying to run auth filters etc). - var d = querystring.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(); + var d = querystring?.ToDictionary(x => x.Key, x => x.Value) ?? new Dictionary(); d["id"] = StringValues.Empty; var proxyQuerystring = new FormCollection(d); @@ -324,7 +324,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// and context etc. so it can execute the specified . Runs the authorization /// filters for that action, to ensure that the user has permission to execute it. /// - private async Task> GetApiControllerProxy(Type controllerType, string action, FormCollection querystring) + private async Task> GetApiControllerProxy(Type controllerType, string action, FormCollection? querystring) { // note: this is all required in order to execute the auth-filters for the sub request, we // need to "trick" mvc into thinking that it is actually executing the proxied controller. diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs index a09d3a2cc5..a1b9da0a56 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeControllerBase.cs @@ -121,7 +121,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// /// /// - public async Task> GetRootNode([ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) + public async Task> GetRootNode([ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection? queryStrings) { if (queryStrings == null) queryStrings = FormCollection.Empty; var nodeResult = CreateRootNode(queryStrings); @@ -164,7 +164,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees /// We are allowing an arbitrary number of query strings to be passed in so that developers are able to persist custom data from the front-end /// to the back end to be used in the query for model data. /// - public async Task> GetNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection queryStrings) + public async Task> GetNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormCollection? queryStrings) { if (queryStrings == null) queryStrings = FormCollection.Empty; var nodesResult = await GetTreeNodesAsync(id, queryStrings);