From 43ca3c5c9364fbf6ec133340a81991db9e6adbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:50:23 +0200 Subject: [PATCH 01/24] Creating, moving draft version of quote module --- .../Constants/FeatureNames.cs | 6 +++ .../Filters/DynamicMediaSizeActionFilter.cs | 34 ++++++++++++ .../Filters/UploadFileSizeFilter.cs | 33 ++++++++++++ .../License.md | 13 +++++ ...ting.Tenants.MediaStorageManagement.csproj | 46 ++++++++++++++++ .../Manifest.cs | 22 ++++++++ .../NuGetIcon.png | Bin 0 -> 4657 bytes .../Readme.md | 50 ++++++++++++++++++ ...amicMediaSizeApplicationModelConvention.cs | 33 ++++++++++++ .../Service/IMediaQuoteService.cs | 8 +++ .../Service/MediaQuoteService.cs | 27 ++++++++++ .../Settings/UploadFileLimitOptions.cs | 17 ++++++ .../Startup.cs | 29 ++++++++++ 13 files changed, 318 insertions(+) create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Constants/FeatureNames.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/License.md create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/NuGetIcon.png create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Constants/FeatureNames.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Constants/FeatureNames.cs new file mode 100644 index 00000000..059c4b9e --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Constants/FeatureNames.cs @@ -0,0 +1,6 @@ +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Constants; + +public static class FeatureNames +{ + public const string Module = "Lombiq.Hosting.Tenants.MediaStorageManagement"; +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs new file mode 100644 index 00000000..f9ab0644 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs @@ -0,0 +1,34 @@ +using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; + +public class DynamicMediaSizeActionFilter : IAsyncAuthorizationFilter, IOrderedFilter +{ + public int Order => 950; // Set the order above the InternalMediaSizeFilter (900) + + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + // Read the max file size from the configuration + var maxFileSize = await context + .HttpContext + .RequestServices + .GetRequiredService() + .GetRemainingMediaSpaceLeft(); + var formOptions = new FormOptions + { + MultipartBodyLengthLimit = maxFileSize, + }; + + context.HttpContext.Features.Set(new FormFeature(context.HttpContext.Request, formOptions)); + + var maxRequestBodySizeFeature = context.HttpContext.Features.Get(); + if (maxRequestBodySizeFeature != null && !maxRequestBodySizeFeature.IsReadOnly) + { + maxRequestBodySizeFeature.MaxRequestBodySize = maxFileSize; + } + } +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs new file mode 100644 index 00000000..5d504c9d --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using OrchardCore.DisplayManagement; +using OrchardCore.DisplayManagement.Layout; +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; + +public class UploadFileSizeFilter : IAsyncResultFilter +{ + private readonly IShapeFactory _shapeFactory; + private readonly ILayoutAccessor _layoutAccessor; + + public UploadFileSizeFilter(IShapeFactory shapeFactory, ILayoutAccessor layoutAccessor) + { + _shapeFactory = shapeFactory; + _layoutAccessor = layoutAccessor; + } + + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + if (!context.IsAdmin()) + { + await next(); + return; + } + + var layout = await _layoutAccessor.GetLayoutAsync(); + var contentZone = layout.Zones["Footer"]; + await contentZone.AddAsync(await _shapeFactory.CreateAsync("UploadFileSize"), "15"); + + await next(); + } +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/License.md b/Lombiq.Hosting.Tenants.MediaStorageManagement/License.md new file mode 100644 index 00000000..4add1069 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/License.md @@ -0,0 +1,13 @@ +Copyright © 2023, [Lombiq Technologies Ltd.](https://lombiq.com) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +- Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj b/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj new file mode 100644 index 00000000..9177c7a3 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj @@ -0,0 +1,46 @@ + + + + net6.0 + true + $(DefaultItemExcludes);.git*;node_modules\**;Tests\** + + + + Lombiq Hosting - Tenants Management for Orchard Core + Lombiq Technologies + Copyright © 2021, Lombiq Technologies Ltd. + Lombiq Hosting - Tenants Management for Orchard Core: With the help of this module, you can set restrictions on tenant creation. See the project website for detailed documentation. + NuGetIcon.png + OrchardCore;Lombiq;AspNetCore;Multitenancy;SaaS + https://github.com/Lombiq/Hosting-Tenants + https://github.com/Lombiq/Hosting-Tenants/blob/dev/Lombiq.Hosting.Tenants.Management/Readme.md + License.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs new file mode 100644 index 00000000..89f11165 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs @@ -0,0 +1,22 @@ +using OrchardCore.Modules.Manifest; +using static Lombiq.Hosting.Tenants.MediaStorageManagement.Constants.FeatureNames; + +[assembly: Module( + Name = "Lombiq Hosting - Tenants Media Storage Management", + Author = "Lombiq Technologies", + Website = "https://github.com/Lombiq/Hosting-Tenants", + Version = "0.0.1", + Description = "Ability to configure storage quota for tenants." +)] + +[assembly: Feature( + Id = Module, + Name = "Lombiq Hosting - Tenants Media Storage Management - Quota Management", + Description = "Ability to configure storage quota for tenants.", + Category = "Hosting", + Dependencies = new[] + { + "OrchardCore.Media", + "OrchardCore.DisplayManagement", + } +)] diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/NuGetIcon.png b/Lombiq.Hosting.Tenants.MediaStorageManagement/NuGetIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..162a00508d8041833604d427216238c1b23b2d47 GIT binary patch literal 4657 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 literal 0 HcmV?d00001 diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md b/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md new file mode 100644 index 00000000..73885046 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md @@ -0,0 +1,50 @@ +# Lombiq Hosting - Tenants Media Storage Management for Orchard Core + +[![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) + +## About + +With the help of this module, you can set restrictions on tenant creation. + +## Documentation + +This module contains two features: + +- `Lombiq.Hosting.Tenants.Management.ForbiddenTenantNames` +- `Lombiq.Hosting.Tenants.Management.HideRecipesFromSetup` + +### `Lombiq.Hosting.Tenants.Management.ForbiddenTenantNames` + +With this module, you can specify a list of host names that cannot be used to create a tenant. You can write the list of forbidden host names as a JSON array in the `appsettings.json` as follows: + +```json +"OrchardCore": { + "Lombiq_Hosting_Tenants_Management": { + "Forbidden_Tenants_Options": { + "RequestUrlHosts": [ + "forbidden.hostname1.net", + "forbidden.hostname2.net" + ] + } + } +} +``` + +### `Lombiq.Hosting.Tenants.Management.HideRecipesFromSetup` + +With this module, you can specify tags for recipes that won't be listed on the setup screen of tenants. Recipes with those tags will still be available from the Default tenant admin UI and they'll also be available to be used via the `AutoSetup` feature. + +By default you can use `"HiddenFromSetupScreen"` tag on the recipe to hide it or you can specify the recipe tags you want to hide using the `HideRecipesByTagsFromSetup()` method in the web project's `ConfigureServices()`. + +```csharp +public void ConfigureServices(IServiceCollection services) => + services.AddOrchardCms(builder => builder.HideRecipesByTagsFromSetup("hiddenTag1", "hiddenTag2")) +``` + +**NOTE:** This extension method not only sets the tags you want to hide but also registers the feature as a setup feature. If you just want to use the default `HideFromSetupScreen` tag then just call the extension method without any parameter. + +## Dependencies + +This module has the following dependencies: + +- [Lombiq Helpful Libraries for Orchard Core](https://github.com/Lombiq/Helpful-Libraries) diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs new file mode 100644 index 00000000..ee21d398 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs @@ -0,0 +1,33 @@ +using Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using System.Linq; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; + +public class DynamicMediaSizeApplicationModelConvention : IApplicationModelConvention +{ + private readonly DynamicMediaSizeActionFilter _mediaSizeFilter; + + public DynamicMediaSizeApplicationModelConvention() => + _mediaSizeFilter = new DynamicMediaSizeActionFilter(); + + public void Apply(ApplicationModel application) + { + // Find the desired controller by name + var targetController = application.Controllers.FirstOrDefault(controller => + controller.ControllerName == "Admin" && + controller.Actions.Any(action => action.ActionName == "Upload")); + + if (targetController != null) + { + // Find the desired action by name + var targetAction = targetController.Actions.FirstOrDefault(action => action.ActionName == "Upload"); + + // Add the DynamicMediaSizeActionFilter to the action + if (targetAction != null) + { + targetAction.Filters.Add(_mediaSizeFilter); + } + } + } +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs new file mode 100644 index 00000000..b74c018c --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs @@ -0,0 +1,8 @@ +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; + +public interface IMediaQuoteService +{ + Task GetRemainingMediaSpaceLeft(); +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs new file mode 100644 index 00000000..83f2354d --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.Options; +using OrchardCore.Media; +using System.Linq; +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; + +public class MediaQuoteService : IMediaQuoteService +{ + private readonly MediaOptions _mediaOptions; + private readonly IMediaFileStore _mediaFileStore; + + public MediaQuoteService(IOptions mediaOptions, IMediaFileStore mediaFileStore) + { + _mediaOptions = mediaOptions.Value; + _mediaFileStore = mediaFileStore; + } + + public async Task GetRemainingMediaSpaceLeft() + { + var directoryContent = _mediaFileStore.GetDirectoryContentAsync(includeSubDirectories: true); + + var listed = await directoryContent.ToListAsync(); + var sumSize = listed.Where(item => item.Length > 0).Sum(item => item.Length); + return _mediaOptions.MaxFileSize - sumSize; + } +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs new file mode 100644 index 00000000..b2352fe5 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs @@ -0,0 +1,17 @@ +using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; + +public class UploadFileLimitOptions : IConfigureOptions +{ + private readonly IServiceProvider _serviceProvider; + + public UploadFileLimitOptions(IServiceProvider serviceProvider) + => _serviceProvider = serviceProvider; + + public void Configure(MvcOptions options) + => options.Conventions.Add(new DynamicMediaSizeApplicationModelConvention(_serviceProvider)); +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs new file mode 100644 index 00000000..48744be0 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs @@ -0,0 +1,29 @@ +using Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; +using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; +using Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Modules; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement; + +public class Startup : StartupBase +{ + private readonly IShellConfiguration _shellConfiguration; + + public Startup(IShellConfiguration shellConfiguration) => _shellConfiguration = shellConfiguration; + + public override void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + + services.Configure(options => + { + options.Filters.Add(typeof(UploadFileSizeFilter)); + }); + + services.AddSingleton, UploadFileLimitOptions>(); + } +} From 403de33d42279bc3c53a9f83bfa36f3a3ccce34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:54:01 +0200 Subject: [PATCH 02/24] Moving view to module --- .../Views/UploadFileSize.cshtml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml new file mode 100644 index 00000000..ab8ae01d --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml @@ -0,0 +1,16 @@ +@using Lombiq.Hosting.Tenants.MediaStorageManagement.Service +@using Microsoft.AspNetCore.Mvc.Localization + +@inject IMediaQuoteService MediaQuoteService +@{ + var asd = await MediaQuoteService.GetRemainingMediaSpaceLeft(); + var maxSize = (asd / 1024f) / 1024f; +} + From 94aa67690297103f86b50a669d8e365fba671a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:54:30 +0200 Subject: [PATCH 03/24] Adding resource managemenet --- .../Lombiq.Hosting.Tenants.MediaStorageManagement.csproj | 2 ++ Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj b/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj index 9177c7a3..6e77ab30 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj @@ -36,6 +36,7 @@ + @@ -43,4 +44,5 @@ + diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs index 89f11165..1e5c6d54 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Manifest.cs @@ -18,5 +18,6 @@ { "OrchardCore.Media", "OrchardCore.DisplayManagement", + "OrchardCore.ResourceManagement", } )] From 5062aab771d97d7e78979f3bc3114bb40dce0281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:55:01 +0200 Subject: [PATCH 04/24] Cleanup of singleton as DI is not needed anymore --- .../Settings/UploadFileLimitOptions.cs | 17 ----------------- .../Startup.cs | 5 +---- 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs deleted file mode 100644 index b2352fe5..00000000 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/UploadFileLimitOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; -using System; - -namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; - -public class UploadFileLimitOptions : IConfigureOptions -{ - private readonly IServiceProvider _serviceProvider; - - public UploadFileLimitOptions(IServiceProvider serviceProvider) - => _serviceProvider = serviceProvider; - - public void Configure(MvcOptions options) - => options.Conventions.Add(new DynamicMediaSizeApplicationModelConvention(_serviceProvider)); -} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs index 48744be0..88de7377 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs @@ -1,9 +1,7 @@ using Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; -using Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; @@ -22,8 +20,7 @@ public override void ConfigureServices(IServiceCollection services) services.Configure(options => { options.Filters.Add(typeof(UploadFileSizeFilter)); + options.Conventions.Add(new DynamicMediaSizeApplicationModelConvention()); }); - - services.AddSingleton, UploadFileLimitOptions>(); } } From 4b6a890c4f53d8c975e8987311dd27ddc2c93aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:55:08 +0200 Subject: [PATCH 05/24] Adding missing viewimports --- .../Views/_ViewImports.cshtml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Views/_ViewImports.cshtml diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/_ViewImports.cshtml b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/_ViewImports.cshtml new file mode 100644 index 00000000..1bef89f1 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@inherits OrchardCore.DisplayManagement.Razor.RazorPage + +@addTagHelper *, OrchardCore.ResourceManagement From e035fd1347ed5c1af0a30fe07d2419edbd4aefdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:58:39 +0200 Subject: [PATCH 06/24] Code refactoring --- ...amicMediaSizeApplicationModelConvention.cs | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs index ee21d398..7a496783 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs @@ -1,33 +1,25 @@ using Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; using Microsoft.AspNetCore.Mvc.ApplicationModels; +using OrchardCore.Media.Controllers; +using OrchardCore.Mvc.Core.Utilities; using System.Linq; namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; public class DynamicMediaSizeApplicationModelConvention : IApplicationModelConvention { - private readonly DynamicMediaSizeActionFilter _mediaSizeFilter; - - public DynamicMediaSizeApplicationModelConvention() => - _mediaSizeFilter = new DynamicMediaSizeActionFilter(); - public void Apply(ApplicationModel application) { // Find the desired controller by name var targetController = application.Controllers.FirstOrDefault(controller => - controller.ControllerName == "Admin" && - controller.Actions.Any(action => action.ActionName == "Upload")); + controller.ControllerName == typeof(AdminController).ControllerName() && + controller.Actions.Any(action => action.ActionName == nameof(AdminController.Upload))); - if (targetController != null) - { - // Find the desired action by name - var targetAction = targetController.Actions.FirstOrDefault(action => action.ActionName == "Upload"); + // Find the desired action by name + var targetAction = targetController?.Actions.FirstOrDefault(action => + action.ActionName == nameof(AdminController.Upload)); - // Add the DynamicMediaSizeActionFilter to the action - if (targetAction != null) - { - targetAction.Filters.Add(_mediaSizeFilter); - } - } + // Add the DynamicMediaSizeActionFilter to the action + targetAction?.Filters.Add(new DynamicMediaSizeActionFilter()); } } From 65f967ad81ccee66fd8ea2d533dabab3814c695f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 18:58:59 +0200 Subject: [PATCH 07/24] Removing not needed comments --- .../Service/DynamicMediaSizeApplicationModelConvention.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs index 7a496783..b68a6c16 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs @@ -10,16 +10,13 @@ public class DynamicMediaSizeApplicationModelConvention : IApplicationModelConve { public void Apply(ApplicationModel application) { - // Find the desired controller by name var targetController = application.Controllers.FirstOrDefault(controller => controller.ControllerName == typeof(AdminController).ControllerName() && controller.Actions.Any(action => action.ActionName == nameof(AdminController.Upload))); - // Find the desired action by name var targetAction = targetController?.Actions.FirstOrDefault(action => action.ActionName == nameof(AdminController.Upload)); - // Add the DynamicMediaSizeActionFilter to the action targetAction?.Filters.Add(new DynamicMediaSizeActionFilter()); } } From e3e72e607dd381c23511c839f7c76eb5c5b27b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 20:20:32 +0200 Subject: [PATCH 08/24] Refactoring, adding options and styling --- .../Filters/DynamicMediaSizeActionFilter.cs | 4 +-- .../Filters/UploadFileSizeFilter.cs | 31 ++++++++++++++++--- .../Service/IMediaQuoteService.cs | 5 ++- .../Service/MediaQuoteService.cs | 18 +++++++---- .../Settings/MediaStorageManagementOptions.cs | 7 +++++ .../Startup.cs | 7 +++++ .../ViewModels/UploadFileSizeViewModel.cs | 6 ++++ .../Views/UploadFileSize.cshtml | 24 +++++++++----- 8 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/ViewModels/UploadFileSizeViewModel.cs diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs index f9ab0644..abd12f15 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs @@ -17,7 +17,7 @@ public async Task OnAuthorizationAsync(AuthorizationFilterContext context) .HttpContext .RequestServices .GetRequiredService() - .GetRemainingMediaSpaceLeft(); + .GetRemainingMediaSpaceLeftAsync(); var formOptions = new FormOptions { MultipartBodyLengthLimit = maxFileSize, @@ -26,7 +26,7 @@ public async Task OnAuthorizationAsync(AuthorizationFilterContext context) context.HttpContext.Features.Set(new FormFeature(context.HttpContext.Request, formOptions)); var maxRequestBodySizeFeature = context.HttpContext.Features.Get(); - if (maxRequestBodySizeFeature != null && !maxRequestBodySizeFeature.IsReadOnly) + if (maxRequestBodySizeFeature is { IsReadOnly: false }) { maxRequestBodySizeFeature.MaxRequestBodySize = maxFileSize; } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs index 5d504c9d..ccaa8ccb 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs @@ -1,6 +1,11 @@ +using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; +using Lombiq.Hosting.Tenants.MediaStorageManagement.ViewModels; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using OrchardCore.DisplayManagement; using OrchardCore.DisplayManagement.Layout; +using OrchardCore.Media.Controllers; +using OrchardCore.Mvc.Core.Utilities; using System.Threading.Tasks; namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; @@ -9,11 +14,16 @@ public class UploadFileSizeFilter : IAsyncResultFilter { private readonly IShapeFactory _shapeFactory; private readonly ILayoutAccessor _layoutAccessor; + private readonly IMediaQuoteService _mediaQuoteService; - public UploadFileSizeFilter(IShapeFactory shapeFactory, ILayoutAccessor layoutAccessor) + public UploadFileSizeFilter( + IShapeFactory shapeFactory, + ILayoutAccessor layoutAccessor, + IMediaQuoteService mediaQuoteService) { _shapeFactory = shapeFactory; _layoutAccessor = layoutAccessor; + _mediaQuoteService = mediaQuoteService; } public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) @@ -24,9 +34,22 @@ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultE return; } - var layout = await _layoutAccessor.GetLayoutAsync(); - var contentZone = layout.Zones["Footer"]; - await contentZone.AddAsync(await _shapeFactory.CreateAsync("UploadFileSize"), "15"); + var actionRouteController = context.ActionDescriptor.RouteValues["Controller"]; + var actionRouteArea = context.ActionDescriptor.RouteValues["Area"]; + var actionRouteValue = context.ActionDescriptor.RouteValues["Action"]; + + if (actionRouteController == typeof(AdminController).ControllerName() && + actionRouteArea == $"{nameof(OrchardCore)}.{nameof(OrchardCore.Media)}" && + actionRouteValue is nameof(AdminController.Index) && + context.Result is ViewResult) + { + var layout = await _layoutAccessor.GetLayoutAsync(); + var contentZone = layout.Zones["Footer"]; + var maximumSpace = _mediaQuoteService.MaxSpaceForTenantInMegabytes(); + await contentZone.AddAsync(await _shapeFactory.CreateAsync( + "UploadFileSize", + viewModel => viewModel.MaximumSpace = maximumSpace)); + } await next(); } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs index b74c018c..804b986a 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs @@ -4,5 +4,8 @@ namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; public interface IMediaQuoteService { - Task GetRemainingMediaSpaceLeft(); + Task GetRemainingMediaSpaceLeftAsync(); + long MaxSpaceForTenantInBytes(); + + float MaxSpaceForTenantInMegabytes(); } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs index 83f2354d..20e0d1c9 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Options; +using Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; +using Microsoft.Extensions.Options; using OrchardCore.Media; using System.Linq; using System.Threading.Tasks; @@ -7,21 +8,26 @@ namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; public class MediaQuoteService : IMediaQuoteService { - private readonly MediaOptions _mediaOptions; + private readonly MediaStorageManagementOptions _mediaStorageManagementOptions; private readonly IMediaFileStore _mediaFileStore; - public MediaQuoteService(IOptions mediaOptions, IMediaFileStore mediaFileStore) + public MediaQuoteService( + IOptions mediaStorageManagementOptions, + IMediaFileStore mediaFileStore) { - _mediaOptions = mediaOptions.Value; + _mediaStorageManagementOptions = mediaStorageManagementOptions.Value; _mediaFileStore = mediaFileStore; } - public async Task GetRemainingMediaSpaceLeft() + public async Task GetRemainingMediaSpaceLeftAsync() { var directoryContent = _mediaFileStore.GetDirectoryContentAsync(includeSubDirectories: true); var listed = await directoryContent.ToListAsync(); var sumSize = listed.Where(item => item.Length > 0).Sum(item => item.Length); - return _mediaOptions.MaxFileSize - sumSize; + return MaxSpaceForTenantInBytes() - sumSize; } + + public long MaxSpaceForTenantInBytes() => _mediaStorageManagementOptions.MaximumStorageQuote; + public float MaxSpaceForTenantInMegabytes() => MaxSpaceForTenantInBytes() / 1024f / 1024f; } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs new file mode 100644 index 00000000..e53aee55 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs @@ -0,0 +1,7 @@ +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; + +public class MediaStorageManagementOptions +{ + // Get or set the maximum storage qoute for a tenant in bytes. Default is 1GB. + public long MaximumStorageQuote { get; set; } = 1_073_741_824; +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs index 88de7377..9cf79337 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs @@ -1,6 +1,8 @@ using Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; using Lombiq.Hosting.Tenants.MediaStorageManagement.Service; +using Lombiq.Hosting.Tenants.MediaStorageManagement.Settings; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using OrchardCore.Environment.Shell.Configuration; using OrchardCore.Modules; @@ -15,6 +17,11 @@ public class Startup : StartupBase public override void ConfigureServices(IServiceCollection services) { + services.Configure(options => + _shellConfiguration + .GetSection("Lombiq_Hosting_Tenants_MediaStorageManagement:Media_Storage_Management_Options") + .Bind(options)); + services.AddScoped(); services.Configure(options => diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/ViewModels/UploadFileSizeViewModel.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/ViewModels/UploadFileSizeViewModel.cs new file mode 100644 index 00000000..faa0af3b --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/ViewModels/UploadFileSizeViewModel.cs @@ -0,0 +1,6 @@ +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.ViewModels; + +public class UploadFileSizeViewModel +{ + public float MaximumSpace { get; set; } +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml index ab8ae01d..8daca456 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml @@ -1,16 +1,26 @@ -@using Lombiq.Hosting.Tenants.MediaStorageManagement.Service @using Microsoft.AspNetCore.Mvc.Localization +@using Lombiq.Hosting.Tenants.MediaStorageManagement.ViewModels + +@model UploadFileSizeViewModel -@inject IMediaQuoteService MediaQuoteService -@{ - var asd = await MediaQuoteService.GetRemainingMediaSpaceLeft(); - var maxSize = (asd / 1024f) / 1024f; -} + From 4d1779ca5b59b1b3e2bb322619e5da3ac0f372bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 20:22:56 +0200 Subject: [PATCH 09/24] Movind and adding to head --- .../Views/UploadFileSize.cshtml | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml index 8daca456..23e5924f 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml @@ -2,6 +2,17 @@ @using Lombiq.Hosting.Tenants.MediaStorageManagement.ViewModels @model UploadFileSizeViewModel + - From 9f1f44f906f88c0eb1a39ec1001c5a18897b73da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kriszti=C3=A1n=20N=C3=A9meth?= Date: Sat, 15 Apr 2023 21:33:29 +0200 Subject: [PATCH 10/24] Adding documentation and renaming files --- ...er.cs => MediaStorageQuotaActionFilter.cs} | 7 +-- ...Filter.cs => UploadFileSizeShapeFilter.cs} | 12 +++--- ...ting.Tenants.MediaStorageManagement.csproj | 8 ++-- .../Readme.md | 43 +++++++++---------- .../Service/IMediaQuoteService.cs | 11 ----- .../Service/IMediaStorageQuotaService.cs | 24 +++++++++++ ...ediaStorageQuotaActionFilterConvention.cs} | 4 +- ...Service.cs => MediaStorageQuotaService.cs} | 9 ++-- .../Settings/MediaStorageManagementOptions.cs | 6 ++- .../Startup.cs | 6 +-- .../Views/UploadFileSize.cshtml | 2 +- 11 files changed, 73 insertions(+), 59 deletions(-) rename Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/{DynamicMediaSizeActionFilter.cs => MediaStorageQuotaActionFilter.cs} (84%) rename Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/{UploadFileSizeFilter.cs => UploadFileSizeShapeFilter.cs} (82%) delete mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs create mode 100644 Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaStorageQuotaService.cs rename Lombiq.Hosting.Tenants.MediaStorageManagement/Service/{DynamicMediaSizeApplicationModelConvention.cs => MediaStorageQuotaActionFilterConvention.cs} (82%) rename Lombiq.Hosting.Tenants.MediaStorageManagement/Service/{MediaQuoteService.cs => MediaStorageQuotaService.cs} (84%) diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/MediaStorageQuotaActionFilter.cs similarity index 84% rename from Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs rename to Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/MediaStorageQuotaActionFilter.cs index abd12f15..14619064 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/DynamicMediaSizeActionFilter.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/MediaStorageQuotaActionFilter.cs @@ -6,7 +6,7 @@ namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; -public class DynamicMediaSizeActionFilter : IAsyncAuthorizationFilter, IOrderedFilter +public class MediaStorageQuotaActionFilter : IAsyncAuthorizationFilter, IOrderedFilter { public int Order => 950; // Set the order above the InternalMediaSizeFilter (900) @@ -16,8 +16,9 @@ public async Task OnAuthorizationAsync(AuthorizationFilterContext context) var maxFileSize = await context .HttpContext .RequestServices - .GetRequiredService() - .GetRemainingMediaSpaceLeftAsync(); + .GetRequiredService() + .GetRemainingMediaSpaceQuotaLeftAsync(); + var formOptions = new FormOptions { MultipartBodyLengthLimit = maxFileSize, diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeShapeFilter.cs similarity index 82% rename from Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs rename to Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeShapeFilter.cs index ccaa8ccb..e4571ab9 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeFilter.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Filters/UploadFileSizeShapeFilter.cs @@ -10,20 +10,20 @@ namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Filters; -public class UploadFileSizeFilter : IAsyncResultFilter +public class UploadFileSizeShapeFilter : IAsyncResultFilter { private readonly IShapeFactory _shapeFactory; private readonly ILayoutAccessor _layoutAccessor; - private readonly IMediaQuoteService _mediaQuoteService; + private readonly IMediaStorageQuotaService _mediaStorageQuotaService; - public UploadFileSizeFilter( + public UploadFileSizeShapeFilter( IShapeFactory shapeFactory, ILayoutAccessor layoutAccessor, - IMediaQuoteService mediaQuoteService) + IMediaStorageQuotaService mediaStorageQuotaService) { _shapeFactory = shapeFactory; _layoutAccessor = layoutAccessor; - _mediaQuoteService = mediaQuoteService; + _mediaStorageQuotaService = mediaStorageQuotaService; } public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) @@ -45,7 +45,7 @@ actionRouteValue is nameof(AdminController.Index) && { var layout = await _layoutAccessor.GetLayoutAsync(); var contentZone = layout.Zones["Footer"]; - var maximumSpace = _mediaQuoteService.MaxSpaceForTenantInMegabytes(); + var maximumSpace = _mediaStorageQuotaService.MaxSpaceForTenantInMegabytes(); await contentZone.AddAsync(await _shapeFactory.CreateAsync( "UploadFileSize", viewModel => viewModel.MaximumSpace = maximumSpace)); diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj b/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj index 6e77ab30..50cba895 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Lombiq.Hosting.Tenants.MediaStorageManagement.csproj @@ -7,14 +7,14 @@ - Lombiq Hosting - Tenants Management for Orchard Core + Lombiq Hosting - Tenants Media Storage Management for Orchard Core Lombiq Technologies - Copyright © 2021, Lombiq Technologies Ltd. - Lombiq Hosting - Tenants Management for Orchard Core: With the help of this module, you can set restrictions on tenant creation. See the project website for detailed documentation. + Copyright © 2023, Lombiq Technologies Ltd. + Lombiq Hosting - Tenants Media Storage Management for Orchard Core: With the help of this module, you can set restrictions regarding maximum media space per tenant. See the project website for detailed documentation. NuGetIcon.png OrchardCore;Lombiq;AspNetCore;Multitenancy;SaaS https://github.com/Lombiq/Hosting-Tenants - https://github.com/Lombiq/Hosting-Tenants/blob/dev/Lombiq.Hosting.Tenants.Management/Readme.md + https://github.com/Lombiq/Hosting-Tenants/blob/dev/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md License.md diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md b/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md index 73885046..1aabc84a 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Readme.md @@ -1,48 +1,45 @@ # Lombiq Hosting - Tenants Media Storage Management for Orchard Core -[![Lombiq.Hosting.Tenants.Management NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.Management?label=Lombiq.Hosting.Tenants.Management)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.Management/) +[![Lombiq.Hosting.Tenants.MediaStorageManagement NuGet](https://img.shields.io/nuget/v/Lombiq.Hosting.Tenants.MediaStorageManagement?label=Lombiq.Hosting.Tenants.MediaStorageManagement)](https://www.nuget.org/packages/Lombiq.Hosting.Tenants.MediaStorageManagement/) ## About -With the help of this module, you can set restrictions on tenant creation. +With the help of this module, you can set restrictions regarding maximum media space per tenant. ## Documentation -This module contains two features: +This module currently contains one feature: -- `Lombiq.Hosting.Tenants.Management.ForbiddenTenantNames` -- `Lombiq.Hosting.Tenants.Management.HideRecipesFromSetup` +- `Lombiq.Hosting.Tenants.MediaStorageManagement` -### `Lombiq.Hosting.Tenants.Management.ForbiddenTenantNames` +### `Lombiq.Hosting.Tenants.MediaStorageManagement` -With this module, you can specify a list of host names that cannot be used to create a tenant. You can write the list of forbidden host names as a JSON array in the `appsettings.json` as follows: +With this module, you can specify how much space would you like to limit each tenant's storage space. The default is 1GB. If you want to set it to 2GB e.g. you can do it in bytes as an environment variable or in `appsettings.json` as follows: ```json "OrchardCore": { - "Lombiq_Hosting_Tenants_Management": { - "Forbidden_Tenants_Options": { - "RequestUrlHosts": [ - "forbidden.hostname1.net", - "forbidden.hostname2.net" - ] + "Lombiq_Hosting_Tenants_MediaStorageManagement": { + "Media_Storage_Management_Options": { + "MaximumSpace": 2147483648 } } } ``` -### `Lombiq.Hosting.Tenants.Management.HideRecipesFromSetup` +Tenant based configuration can be defined as the following, for more details read the [Orchard Core documentation](https://docs.orchardcore.net/en/main/docs/reference/core/Configuration/#tenant-postconfiguration). -With this module, you can specify tags for recipes that won't be listed on the setup screen of tenants. Recipes with those tags will still be available from the Default tenant admin UI and they'll also be available to be used via the `AutoSetup` feature. - -By default you can use `"HiddenFromSetupScreen"` tag on the recipe to hide it or you can specify the recipe tags you want to hide using the `HideRecipesByTagsFromSetup()` method in the web project's `ConfigureServices()`. - -```csharp -public void ConfigureServices(IServiceCollection services) => - services.AddOrchardCms(builder => builder.HideRecipesByTagsFromSetup("hiddenTag1", "hiddenTag2")) +```json +"OrchardCore": { + "TenantName": { + "Lombiq_Hosting_Tenants_MediaStorageManagement": { + "Media_Storage_Management_Options": { + "MaximumSpace": 2147483648 + } + } + } +} ``` -**NOTE:** This extension method not only sets the tags you want to hide but also registers the feature as a setup feature. If you just want to use the default `HideFromSetupScreen` tag then just call the extension method without any parameter. - ## Dependencies This module has the following dependencies: diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs deleted file mode 100644 index 804b986a..00000000 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaQuoteService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; - -namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; - -public interface IMediaQuoteService -{ - Task GetRemainingMediaSpaceLeftAsync(); - long MaxSpaceForTenantInBytes(); - - float MaxSpaceForTenantInMegabytes(); -} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaStorageQuotaService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaStorageQuotaService.cs new file mode 100644 index 00000000..3ce70fa7 --- /dev/null +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/IMediaStorageQuotaService.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; + +namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; + +/// +/// Storage quota related calculations. +/// +public interface IMediaStorageQuotaService +{ + /// + /// Returns remaining quota space left in bytes. + /// + Task GetRemainingMediaSpaceQuotaLeftAsync(); + + /// + /// Returns the maximum quota space in bytes. + /// + long MaxSpaceForTenantInBytes(); + + /// + /// Returns the maximum quota space in Megabytes. + /// + float MaxSpaceForTenantInMegabytes(); +} diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaStorageQuotaActionFilterConvention.cs similarity index 82% rename from Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs rename to Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaStorageQuotaActionFilterConvention.cs index b68a6c16..73a46555 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/DynamicMediaSizeApplicationModelConvention.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaStorageQuotaActionFilterConvention.cs @@ -6,7 +6,7 @@ namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; -public class DynamicMediaSizeApplicationModelConvention : IApplicationModelConvention +public class MediaStorageQuotaActionFilterConvention : IApplicationModelConvention { public void Apply(ApplicationModel application) { @@ -17,6 +17,6 @@ public void Apply(ApplicationModel application) var targetAction = targetController?.Actions.FirstOrDefault(action => action.ActionName == nameof(AdminController.Upload)); - targetAction?.Filters.Add(new DynamicMediaSizeActionFilter()); + targetAction?.Filters.Add(new MediaStorageQuotaActionFilter()); } } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaStorageQuotaService.cs similarity index 84% rename from Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs rename to Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaStorageQuotaService.cs index 20e0d1c9..b519044a 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaQuoteService.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Service/MediaStorageQuotaService.cs @@ -6,12 +6,12 @@ namespace Lombiq.Hosting.Tenants.MediaStorageManagement.Service; -public class MediaQuoteService : IMediaQuoteService +public class MediaStorageQuotaService : IMediaStorageQuotaService { private readonly MediaStorageManagementOptions _mediaStorageManagementOptions; private readonly IMediaFileStore _mediaFileStore; - public MediaQuoteService( + public MediaStorageQuotaService( IOptions mediaStorageManagementOptions, IMediaFileStore mediaFileStore) { @@ -19,7 +19,7 @@ public MediaQuoteService( _mediaFileStore = mediaFileStore; } - public async Task GetRemainingMediaSpaceLeftAsync() + public async Task GetRemainingMediaSpaceQuotaLeftAsync() { var directoryContent = _mediaFileStore.GetDirectoryContentAsync(includeSubDirectories: true); @@ -28,6 +28,7 @@ public async Task GetRemainingMediaSpaceLeftAsync() return MaxSpaceForTenantInBytes() - sumSize; } - public long MaxSpaceForTenantInBytes() => _mediaStorageManagementOptions.MaximumStorageQuote; + public long MaxSpaceForTenantInBytes() => _mediaStorageManagementOptions.MaximumStorageQuota; + public float MaxSpaceForTenantInMegabytes() => MaxSpaceForTenantInBytes() / 1024f / 1024f; } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs index e53aee55..fbe068fe 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Settings/MediaStorageManagementOptions.cs @@ -2,6 +2,8 @@ public class MediaStorageManagementOptions { - // Get or set the maximum storage qoute for a tenant in bytes. Default is 1GB. - public long MaximumStorageQuote { get; set; } = 1_073_741_824; + /// + /// Gets or sets the maximum storage quota for a tenant in bytes. Default is 1GB. + /// + public long MaximumStorageQuota { get; set; } } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs index 9cf79337..36f22b59 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Startup.cs @@ -22,12 +22,12 @@ public override void ConfigureServices(IServiceCollection services) .GetSection("Lombiq_Hosting_Tenants_MediaStorageManagement:Media_Storage_Management_Options") .Bind(options)); - services.AddScoped(); + services.AddScoped(); services.Configure(options => { - options.Filters.Add(typeof(UploadFileSizeFilter)); - options.Conventions.Add(new DynamicMediaSizeApplicationModelConvention()); + options.Filters.Add(typeof(UploadFileSizeShapeFilter)); + options.Conventions.Add(new MediaStorageQuotaActionFilterConvention()); }); } } diff --git a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml index 23e5924f..21a159ea 100644 --- a/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml +++ b/Lombiq.Hosting.Tenants.MediaStorageManagement/Views/UploadFileSize.cshtml @@ -3,7 +3,7 @@ @model UploadFileSizeViewModel