diff --git a/Lombiq.TrainingDemo/Controllers/ApiController.cs b/Lombiq.TrainingDemo/Controllers/ApiController.cs index da0f3a5e..c2b1de10 100644 --- a/Lombiq.TrainingDemo/Controllers/ApiController.cs +++ b/Lombiq.TrainingDemo/Controllers/ApiController.cs @@ -29,6 +29,11 @@ namespace Lombiq.TrainingDemo.Controllers; // endpoints should most of the time use the "Api" authentication scheme: This is not the same that standard users are // authenticated with (via cookies). [Authorize(AuthenticationSchemes = "Api"), IgnoreAntiforgeryToken, AllowAnonymous] +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Major Code Smell", + "S6961:API Controllers should derive from ControllerBase instead of Controller", + Justification = "Can't be changed due to line 62. Will be applicable after an Orchard upgrade due to " + + "https://github.com/OrchardCMS/OrchardCore/issues/16186 being fixed.")] public class ApiController : Controller { private readonly IAuthorizationService _authorizationService; @@ -46,6 +51,7 @@ public ApiController(IAuthorizationService authorizationService, IContentManager // authorize with a client ID and secret of an OpenID app set up from the Orchard admin. For more info see: // https://docs.orchardcore.net/en/latest/docs/reference/modules/OpenId/. If you just want to quickly test this API // then remove the Authorize attribute above. + [HttpGet] public async Task Get(string contentItemId) { // Authorization is important in API endpoints as well of course. We're re-using the previously created diff --git a/Lombiq.TrainingDemo/Controllers/CrossTenantServicesController.cs b/Lombiq.TrainingDemo/Controllers/CrossTenantServicesController.cs index 79fbd419..c637cb34 100644 --- a/Lombiq.TrainingDemo/Controllers/CrossTenantServicesController.cs +++ b/Lombiq.TrainingDemo/Controllers/CrossTenantServicesController.cs @@ -38,6 +38,9 @@ public class CrossTenantServicesController : Controller [Route("CrossTenantServices")] public async Task Index(string contentItemId) { + // If ModelState is not in a valid state we should abort the action and return null. + if (!ModelState.IsValid) return null; + // Even if you don't create tenants, there will still be a single tenant in an Orchard app, the Default tenant. // For all other tenants you create you can provide the technical name. diff --git a/Lombiq.TrainingDemo/Controllers/DatabaseStorageController.cs b/Lombiq.TrainingDemo/Controllers/DatabaseStorageController.cs index 3878818b..18d915ed 100644 --- a/Lombiq.TrainingDemo/Controllers/DatabaseStorageController.cs +++ b/Lombiq.TrainingDemo/Controllers/DatabaseStorageController.cs @@ -103,8 +103,7 @@ public async Task JKRosenzweigBooks() // NEXT STATION: Models/PersonPart.cs private static Book[] CreateDemoBooks() => - new[] - { + [ new Book { CoverPhotoUrl = "/Lombiq.TrainingDemo/images/HarryPotter.jpg", @@ -128,5 +127,5 @@ private static Book[] CreateDemoBooks() => Description = "The nation of Panay, formed from a post-apocalyptic North America, is a country " + "that consists of a wealthy Capitol region surrounded by 12 poorer districts.", }, - }; + ]; } diff --git a/Lombiq.TrainingDemo/Controllers/FileManagementController.cs b/Lombiq.TrainingDemo/Controllers/FileManagementController.cs index fe46b6a1..f40055f7 100644 --- a/Lombiq.TrainingDemo/Controllers/FileManagementController.cs +++ b/Lombiq.TrainingDemo/Controllers/FileManagementController.cs @@ -104,7 +104,7 @@ public async Task ReadFileFromMediaFolder() [HttpPost, ActionName(nameof(UploadFileToMedia)), ValidateAntiForgeryToken] public async Task UploadFileToMediaPost(IFormFile file) { - if (file == null) return BadRequest(); + if (!ModelState.IsValid || file == null) return BadRequest(); // You can use the Combine method to combine paths which is pretty much equivalent to the built-in method. var mediaFilePath = _mediaFileStore.Combine(UploadedFileFolderRelativePath, file.FileName); diff --git a/Lombiq.TrainingDemo/Permissions/DemoSettingsPermissions.cs b/Lombiq.TrainingDemo/Permissions/DemoSettingsPermissions.cs index 57dfa28c..59441d47 100644 --- a/Lombiq.TrainingDemo/Permissions/DemoSettingsPermissions.cs +++ b/Lombiq.TrainingDemo/Permissions/DemoSettingsPermissions.cs @@ -19,12 +19,11 @@ public Task> GetPermissionsAsync() => .AsEnumerable()); public IEnumerable GetDefaultStereotypes() => - new[] - { + [ new PermissionStereotype { Name = "Administrator", - Permissions = new[] { ManageDemoSettings }, + Permissions = [ManageDemoSettings], }, - }; + ]; } diff --git a/Lombiq.TrainingDemo/Permissions/PersonPermissions.cs b/Lombiq.TrainingDemo/Permissions/PersonPermissions.cs index 3663a942..2ede0ed6 100644 --- a/Lombiq.TrainingDemo/Permissions/PersonPermissions.cs +++ b/Lombiq.TrainingDemo/Permissions/PersonPermissions.cs @@ -22,7 +22,7 @@ public class PersonPermissions : IPermissionProvider public static readonly Permission AccessPersonListDashboard = new( nameof(AccessPersonListDashboard), "Access the Person List dashboard", - new[] { ManagePersons }); + [ManagePersons]); public Task> GetPermissionsAsync() => Task.FromResult(new[] @@ -34,21 +34,20 @@ public Task> GetPermissionsAsync() => public IEnumerable GetDefaultStereotypes() => // Giving some defaults: which roles should possess which permissions. - new[] - { + [ new PermissionStereotype { // Administrators will have all the permissions by default. Name = "Administrator", // Since AccessPersonListDashboard is implied by EditPersonList we don't have to list the former here. - Permissions = new[] { ManagePersons }, + Permissions = [ManagePersons], }, new PermissionStereotype { Name = "Editor", - Permissions = new[] { AccessPersonListDashboard }, + Permissions = [AccessPersonListDashboard], }, - }; + ]; } // NEXT STATION: Go back to AuthorizationController and find the CanManagePersons action. diff --git a/Lombiq.TrainingDemo/Services/DateTimeCachingService.cs b/Lombiq.TrainingDemo/Services/DateTimeCachingService.cs index b448b005..d07f0927 100644 --- a/Lombiq.TrainingDemo/Services/DateTimeCachingService.cs +++ b/Lombiq.TrainingDemo/Services/DateTimeCachingService.cs @@ -4,11 +4,16 @@ using OrchardCore.Environment.Cache; using OrchardCore.Modules; using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Threading.Tasks; namespace Lombiq.TrainingDemo.Services; +[SuppressMessage( + "Usage", + "VSTHRD003:Avoid awaiting foreign Tasks", + Justification = "We need LocalNowAsync to create cachedDate.")] public class DateTimeCachingService : IDateTimeCachingService { public const string MemoryCacheKey = "Lombiq.TrainingDemo.MemoryCache.DateTime"; diff --git a/Lombiq.TrainingDemo/ViewModels/PersonPartViewModel.cs b/Lombiq.TrainingDemo/ViewModels/PersonPartViewModel.cs index 204cc1e3..59d5af83 100644 --- a/Lombiq.TrainingDemo/ViewModels/PersonPartViewModel.cs +++ b/Lombiq.TrainingDemo/ViewModels/PersonPartViewModel.cs @@ -34,7 +34,7 @@ public IEnumerable Validate(ValidationContext validationContex if (BirthDateUtc.HasValue && clock.UtcNow < BirthDateUtc.Value.AddYears(18)) { - yield return new ValidationResult(localizer["The person must be 18 or older."], new[] { nameof(BirthDateUtc) }); + yield return new ValidationResult(localizer["The person must be 18 or older."], [nameof(BirthDateUtc)]); } // Now go back to the PersonPartDisplayDriver.