Skip to content

Commit

Permalink
Adding Azure and AWS-specific Image Caches of ImageSharp (#15028)
Browse files Browse the repository at this point in the history
Co-authored-by: Mike Alhayek <mike@crestapps.com>
  • Loading branch information
Piedone and MikeAlhayek committed Apr 24, 2024
1 parent 08a6771 commit 18efbf2
Show file tree
Hide file tree
Showing 41 changed files with 994 additions and 375 deletions.
19 changes: 11 additions & 8 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,13 @@ nav:
- Navigate between pages: docs/topics/navigation/README.md
- Query and Search data: docs/topics/search/README.md
- Secure your application: docs/topics/security/README.md
# - Data: docs/topics/data/README.md
# - Configuration: docs/topics/configuration/README.md
- Data: docs/topics/data/README.md
- Configuration: docs/topics/configuration/README.md
- Workflows: docs/topics/workflows/README.md
- Publishing a new release: docs/topics/publishing-releases/README.md
- Using Docker: docs/topics/docker/README.md
- Using local NuGet packages: docs/topics/local-nuget-packages/README.md
- Managing the Orchard Core Red Hat Ecosystem Catalog certification: docs/topics/red-hat-ecosystem-catalog-certification/README.md
- Managing the Orchard Core Red Hat Ecosystem Catalog certification: docs/topics/red-hat-ecosystem-catalog-certification/README.md
- Reference:
- docs/reference/README.md
- CMS Modules:
Expand All @@ -164,11 +164,6 @@ nav:
- Placements: docs/reference/modules/Placements/README.md
- Themes: docs/reference/modules/Themes/README.md
- Liquid: docs/reference/modules/Liquid/README.md
- Indexing: docs/reference/modules/Indexing/README.md
- SQL Indexing: docs/reference/modules/SQLIndexing/README.md
- Lucene: docs/reference/modules/Lucene/README.md
- Elasticsearch: docs/reference/modules/Elasticsearch/README.md
- Queries: docs/reference/modules/Queries/README.md
- Media:
- Media: docs/reference/modules/Media/README.md
- Media Slugify: docs/reference/modules/Media.Slugify/README.md
Expand All @@ -177,6 +172,13 @@ nav:
- ReCaptcha: docs/reference/modules/ReCaptcha/README.md
- Resources: docs/reference/modules/Resources/README.md
- Rules: docs/reference/modules/Rules/README.md
- Search, Indexing, Querying:
- Azure AI Search: docs/reference/modules/AzureAISearch/README.md
- Elasticsearch: docs/reference/modules/Elasticsearch/README.md
- Indexing: docs/reference/modules/Indexing/README.md
- Lucene: docs/reference/modules/Lucene/README.md
- SQL Indexing: docs/reference/modules/SQLIndexing/README.md
- Queries: docs/reference/modules/Queries/README.md
- Shortcodes: docs/reference/modules/Shortcodes/README.md
- Sitemaps: docs/reference/modules/Sitemaps/README.md
- SMS: docs/reference/modules/Sms/README.md
Expand Down Expand Up @@ -214,6 +216,7 @@ nav:
- Placement: docs/reference/core/Placement/README.md
- Data: docs/reference/core/Data/README.md
- Data Migrations: docs/reference/modules/Migrations/README.md
- Diagnostics: docs/reference/modules/Diagnostics/README.md
- Dynamic Cache: docs/reference/modules/DynamicCache/README.md
- Email: docs/reference/modules/Email/README.md
- SMTP Provider: docs/reference/modules/Email.Smtp/README.md
Expand Down
2 changes: 2 additions & 0 deletions src/OrchardCore.Build/Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
<PackageManagement Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageManagement Include="Shortcodes" Version="1.3.3" />
<PackageManagement Include="SixLabors.ImageSharp.Web" Version="3.1.2" />
<PackageManagement Include="SixLabors.ImageSharp.Web.Providers.Azure" Version="3.1.1" />
<PackageManagement Include="SixLabors.ImageSharp.Web.Providers.AWS" Version="3.1.1" />
<PackageManagement Include="StackExchange.Redis" Version="2.7.33" />
<PackageManagement Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageManagement Include="System.Linq.Async" Version="6.0.1" />
Expand Down
37 changes: 29 additions & 8 deletions src/OrchardCore.Cms.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
// See https://docs.orchardcore.net/en/latest/docs/reference/modules/DataProtection.Azure/#configuration to configure data protection key storage in Azure Blob Storage.
//"OrchardCore_DataProtection_Azure": {
// "ConnectionString": "", // Set to your Azure Storage account connection string.
// "ContainerName": "dataprotection", // Default to dataprotection. Templatable, refer docs.
// "BlobName": "", // Optional, defaults to Sites/tenant_name/DataProtectionKeys.xml. Templatable, refer docs.
// "ContainerName": "dataprotection", // Default to dataprotection. Templatable, refer to docs.
// "BlobName": "", // Optional, defaults to Sites/tenant_name/DataProtectionKeys.xml. Templatable, refer to docs.
// "CreateContainer": true // Creates the container during app startup if it does not already exist.
//},
// See https://docs.orchardcore.net/en/latest/docs/reference/modules/Markdown/#markdown-configuration
Expand Down Expand Up @@ -62,17 +62,38 @@
// "SecretKey": "",
// "AccessKey": ""
// },
// "BasePath": "/media",
// "BasePath": "/media", // Optionally, set to a path to store media in a subdirectory inside your bucket. Templatable, refer to docs.
// "CreateBucket": true,
// "RemoveBucket": true, // Whether the 'Bucket' is deleted if the tenant is removed, false by default.
// "BucketName": ""
// "BucketName": "media" // Set the bucket's name (mandatory). Templatable, refer to docs.
//},
// See https://docs.orchardcore.net/en/latest/docs/reference/modules/Media.AmazonS3/#configuration_1 to configure media storage in Amazon S3 Storage.
//"OrchardCore_Media_AmazonS3_ImageSharp_Cache": {
// "Region": "eu-central-1",
// "Profile": "default",
// "ProfilesLocation": "",
// "Credentials": {
// "SecretKey": "",
// "AccessKey": ""
// },
// "BasePath": "/media", // Optionally, set to a path to store media in a subdirectory inside your bucket. Templatable, refer to docs.
// "CreateBucket": true,
// "RemoveBucket": true, // Whether the 'Bucket' is deleted if the tenant is removed, false by default.
// "BucketName": "imagesharp" // Set the bucket's name (mandatory). Templatable, refer to docs.
//},
// See https://docs.orchardcore.net/en/latest/docs/reference/modules/Media.Azure/#configuration to configure media storage in Azure Blob Storage.
//"OrchardCore_Media_Azure":
//{
//"OrchardCore_Media_Azure": {
// "ConnectionString": "", // Set to your Azure Storage account connection string.
// "ContainerName": "somecontainer", // Set to the Azure Blob container name. Templatable, refer to docs.
// "BasePath": "some/base/path", // Optionally, set to a path to store media in a subdirectory inside your container. Templatable, refer to docs.
// "CreateContainer": true, // Activates an event to create the container if it does not already exist.
// "RemoveContainer": true // Whether the 'Container' is deleted if the tenant is removed, false by default.
//},
// See http://127.0.0.1:8000/docs/reference/modules/Media.Azure/#configuration_1
//"OrchardCore_Media_Azure_ImageSharp_Cache": {
// "ConnectionString": "", // Set to your Azure Storage account connection string.
// "ContainerName": "somecontainer", // Set to the Azure Blob container name. Templatable, refer docs.
// "BasePath": "some/base/path", // Optionally, set to a path to store media in a subdirectory inside your container. Templatable, refer docs.
// "ContainerName": "somecontainer", // Set to the Azure Blob container name. Templatable, refer to docs.
// "BasePath": "some/base/path", // Optionally, set to a path to store media in a subdirectory inside your container. Templatable, refer to docs.
// "CreateContainer": true, // Activates an event to create the container if it does not already exist.
// "RemoveContainer": true // Whether the 'Container' is deleted if the tenant is removed, false by default.
//},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace OrchardCore.Media.AmazonS3;

internal static class AmazonS3Constants
{
internal static class ValidationMessages
{
public const string BucketNameIsEmpty = "BucketName is required attribute for S3 storage.";
public const string RegionAndServiceUrlAreEmpty = "Region or ServiceURL is a required attribute for S3 storage.";
}

internal static class AwsCredentialParamNames
{
public const string SecretKey = "SecretKey";
public const string AccessKey = "AccessKey";
}

internal static class ConfigSections
{
public const string AmazonS3 = "OrchardCore_Media_AmazonS3";
public const string AmazonS3ImageSharpCache = "OrchardCore_Media_AmazonS3_ImageSharp_Cache";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,25 @@ namespace OrchardCore.Media.AmazonS3;

public static class AwsStorageOptionsExtension
{
public static IEnumerable<ValidationResult> Validate(this AwsStorageOptions options)
public static IEnumerable<ValidationResult> Validate(this AwsStorageOptionsBase options)
{
if (string.IsNullOrWhiteSpace(options.BucketName))
{
yield return new ValidationResult(Constants.ValidationMessages.BucketNameIsEmpty);
yield return new ValidationResult(AmazonS3Constants.ValidationMessages.BucketNameIsEmpty);
}

if (options.AwsOptions is not null)
{
if (options.AwsOptions.Region is null && options.AwsOptions.DefaultClientConfig.ServiceURL is null)
{
yield return new ValidationResult(Constants.ValidationMessages.RegionEndpointIsEmpty);
yield return new ValidationResult(AmazonS3Constants.ValidationMessages.RegionAndServiceUrlAreEmpty);
}
}
}

public static AwsStorageOptions BindConfiguration(this AwsStorageOptions options, IShellConfiguration shellConfiguration, ILogger logger)
public static AwsStorageOptionsBase BindConfiguration(this AwsStorageOptionsBase options, string configSection, IShellConfiguration shellConfiguration, ILogger logger)
{
var section = shellConfiguration.GetSection("OrchardCore_Media_AmazonS3");
var section = shellConfiguration.GetSection(configSection);

if (!section.Exists())
{
Expand All @@ -53,8 +53,8 @@ public static AwsStorageOptions BindConfiguration(this AwsStorageOptions options
var credentials = section.GetSection("Credentials");
if (credentials.Exists())
{
var secretKey = credentials.GetValue(Constants.AwsCredentialParamNames.SecretKey, string.Empty);
var accessKey = credentials.GetValue(Constants.AwsCredentialParamNames.AccessKey, string.Empty);
var secretKey = credentials.GetValue(AmazonS3Constants.AwsCredentialParamNames.SecretKey, string.Empty);
var accessKey = credentials.GetValue(AmazonS3Constants.AwsCredentialParamNames.AccessKey, string.Empty);

if (!string.IsNullOrWhiteSpace(accessKey) ||
!string.IsNullOrWhiteSpace(secretKey))
Expand Down
17 changes: 0 additions & 17 deletions src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/Constants.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Fluid;
using OrchardCore.Environment.Shell;

namespace OrchardCore.Media.AmazonS3.Helpers;

// This is almost the same as in OrchardCore.Media.Azure but there isn't really a good common place for it.
internal sealed class OptionsFluidParserHelper<TOptions> where TOptions : class
{
// Local instance since it can be discarded once the startup is over.
private readonly FluidParser _fluidParser = new();
private readonly ShellSettings _shellSettings;

private TemplateContext _templateContext;

public OptionsFluidParserHelper(ShellSettings shellSettings)
{
_shellSettings = shellSettings;
}

public string ParseAndFormat(string template)
{
var templateContext = GetTemplateContext();

// Use Fluid directly as this is transient and cannot invoke _liquidTemplateManager.
var parsedTemplate = _fluidParser.Parse(template);
return parsedTemplate.Render(templateContext, NullEncoder.Default)
.Replace("\r", string.Empty)
.Replace("\n", string.Empty)
.Trim();
}

private TemplateContext GetTemplateContext()
{
if (_templateContext == null)
{
var templateOptions = new TemplateOptions();
_templateContext = new TemplateContext(templateOptions);
templateOptions.MemberAccessStrategy.Register<ShellSettings>();
templateOptions.MemberAccessStrategy.Register<TOptions>();
_templateContext.SetValue("ShellSettings", _shellSettings);
}

return _templateContext;
}
}
14 changes: 13 additions & 1 deletion src/OrchardCore.Modules/OrchardCore.Media.AmazonS3/Manifest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,22 @@
[assembly: Feature(
Id = "OrchardCore.Media.AmazonS3",
Name = "Amazon Media Storage",
Description = "Enables support for storing media files in Amazon S3 Bucket.",
Description = "Enables support for storing media files in Amazon S3.",
Dependencies =
[
"OrchardCore.Media.Cache"
],
Category = "Hosting"
)]

[assembly: Feature(
Id = "OrchardCore.Media.AmazonS3.ImageSharpImageCache",
Name = "Amazon Media ImageSharp Image Cache",
Description = "Provides storage of ImageSharp-generated images within the Amazon S3 storage service.",
Dependencies =
[
"OrchardCore.Media",
"OrchardCore.Media.AmazonS3"
],
Category = "Hosting"
)]
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Media.Core\OrchardCore.Media.Core.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Module.Targets\OrchardCore.Module.Targets.csproj" />
<ProjectReference Include="..\..\OrchardCore\OrchardCore.Navigation.Core\OrchardCore.Navigation.Core.csproj" />
<ProjectReference Include="..\OrchardCore.Media\OrchardCore.Media.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp.Web.Providers.AWS" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.Extensions.Options;
using OrchardCore.FileStorage.AmazonS3;
using SixLabors.ImageSharp.Web.Caching.AWS;

namespace OrchardCore.Media.AmazonS3.Services;

// Configuration for ImageSharp's own configuration object. We just pass the settings from the Orchard Core
// configuration.
internal sealed class AWSS3StorageCacheOptionsConfiguration : IConfigureOptions<AWSS3StorageCacheOptions>
{
private readonly AwsImageSharpImageCacheOptions _options;

public AWSS3StorageCacheOptionsConfiguration(IOptions<AwsImageSharpImageCacheOptions> options)
{
_options = options.Value;
}

public void Configure(AWSS3StorageCacheOptions options)
{
var credentials = _options.AwsOptions.Credentials.GetCredentials();

// Only Endpoint or Region is necessary.
options.Endpoint = _options.AwsOptions.DefaultClientConfig.ServiceURL;
options.Region = _options.AwsOptions.Region?.SystemName;
options.BucketName = _options.BucketName;
options.AccessKey = credentials.AccessKey;
options.AccessSecret = credentials.SecretKey;
options.CacheFolder = _options.BasePath;
}
}

0 comments on commit 18efbf2

Please sign in to comment.