diff --git a/src/KInspector.Core/Constants/ModuleTags.cs b/src/KInspector.Core/Constants/ModuleTags.cs index d9c4aec3..6e7439dc 100644 --- a/src/KInspector.Core/Constants/ModuleTags.cs +++ b/src/KInspector.Core/Constants/ModuleTags.cs @@ -20,5 +20,7 @@ public static class ModuleTags public const string WebFarms = "Web farms"; public const string Staging = "Content staging"; public const string Emails = "Emails"; + public const string Azure = "Azure"; + public const string AmazonS3 = "Amazon S3"; } } \ No newline at end of file diff --git a/src/KInspector.Reports/ApplicationRestartAnalysis/Metadata/en-US.yaml b/src/KInspector.Reports/ApplicationRestartAnalysis/Metadata/en-US.yaml index d0c41e83..d7cb1bd4 100644 --- a/src/KInspector.Reports/ApplicationRestartAnalysis/Metadata/en-US.yaml +++ b/src/KInspector.Reports/ApplicationRestartAnalysis/Metadata/en-US.yaml @@ -7,6 +7,6 @@ terms: summaries: good: No restart events found. - information: ' total ( STARTAPP, ENDAPP) spanning - .' + information: total ( STARTAPP, ENDAPP) spanning - . tableTitles: applicationRestartEvents: Application restart events diff --git a/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Metadata/en-US.yaml b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Metadata/en-US.yaml new file mode 100644 index 00000000..c52f8066 --- /dev/null +++ b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Metadata/en-US.yaml @@ -0,0 +1,13 @@ +details: + name: External storage limitation analysis + shortDescription: Checks whether media libraries are following recommendations for external storage. + longDescription: | + Web sites utilizing external storage (Azure Blob or Amazon S3) should limit media library folder size to a maximum 100 items per folder. If you exceed the limit, alter the structure that allows you to divide the files into multiple folders. + + See [Media library limitations when storing files in an external storage](https://docs.kentico.com/13/configuring-xperience/configuring-the-environment-for-content-editors/configuring-media-libraries#media-library-limitations-when-storing-files-in-an-external-storage). +terms: + summaries: + good: No issues found. + error: with excessive media files. + tableTitles: + tablesOverLimit: Folders with more than 100 files \ No newline at end of file diff --git a/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Models/CmsMediaFolderResult.cs b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Models/CmsMediaFolderResult.cs new file mode 100644 index 00000000..6217ddce --- /dev/null +++ b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Models/CmsMediaFolderResult.cs @@ -0,0 +1,13 @@ +namespace KInspector.Reports.ExternalStorageLimitationAnalysis.Models +{ + public class CmsMediaFolderResult + { + public string? LibraryDisplayName { get; set; } + + public string? FolderName { get; set; } + + public string? SiteName { get; set; } + + public int NumberOfFiles { get; set; } + } +} diff --git a/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Models/Terms.cs b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Models/Terms.cs new file mode 100644 index 00000000..2c2036c1 --- /dev/null +++ b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Models/Terms.cs @@ -0,0 +1,23 @@ +using KInspector.Core.Models; + +namespace KInspector.Reports.ExternalStorageLimitationAnalysis.Models +{ + public class Terms + { + public Summaries? Summaries { get; set; } + + public TableTitles? TableTitles { get; set; } + } + + public class Summaries + { + public Term? Good { get; set; } + + public Term? Error { get; set; } + } + + public class TableTitles + { + public Term? TablesOverLimit { get; set; } + } +} \ No newline at end of file diff --git a/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Report.cs b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Report.cs new file mode 100644 index 00000000..9104d14d --- /dev/null +++ b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Report.cs @@ -0,0 +1,55 @@ +using KInspector.Core; +using KInspector.Core.Constants; +using KInspector.Core.Helpers; +using KInspector.Core.Models; +using KInspector.Core.Services.Interfaces; +using KInspector.Reports.ExternalStorageLimitationAnalysis.Models; + +namespace KInspector.Reports.ExternalStorageLimitationAnalysis +{ + public class Report : AbstractReport + { + private readonly IDatabaseService databaseService; + + public Report(IDatabaseService databaseService, IModuleMetadataService moduleMetadataService) : base(moduleMetadataService) + { + this.databaseService = databaseService; + } + + public override IList CompatibleVersions => VersionHelper.GetVersionList("12", "13"); + + public override IList Tags => new List { + ModuleTags.Performance, + ModuleTags.Azure, + ModuleTags.AmazonS3 + }; + + public async override Task GetResults() + { + var foldersOverLimit = await databaseService.ExecuteSqlFromFile(Scripts.GetMediaFoldersWithMoreThan100Files); + if (foldersOverLimit.Any()) + { + var result = new ModuleResults + { + Status = ResultsStatus.Error, + Type = ResultsType.TableList, + Summary = Metadata.Terms.Summaries?.Error?.With(new { totalFolders = foldersOverLimit.Count() }) + }; + result.TableResults.Add(new TableResult + { + Name = Metadata.Terms.TableTitles?.TablesOverLimit, + Rows = foldersOverLimit + }); + + return result; + } + + return new ModuleResults + { + Status = ResultsStatus.Good, + Type = ResultsType.NoResults, + Summary = Metadata.Terms.Summaries?.Good + }; + } + } +} diff --git a/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Scripts.cs b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Scripts.cs new file mode 100644 index 00000000..78c9445c --- /dev/null +++ b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Scripts.cs @@ -0,0 +1,9 @@ +namespace KInspector.Reports.ExternalStorageLimitationAnalysis +{ + public static class Scripts + { + public static string BaseDirectory => $"{nameof(ExternalStorageLimitationAnalysis)}/Scripts"; + + public static string GetMediaFoldersWithMoreThan100Files => $"{BaseDirectory}/{nameof(GetMediaFoldersWithMoreThan100Files)}.sql"; + } +} diff --git a/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Scripts/GetMediaFoldersWithMoreThan100Files.sql b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Scripts/GetMediaFoldersWithMoreThan100Files.sql new file mode 100644 index 00000000..9567b312 --- /dev/null +++ b/src/KInspector.Reports/ExternalStorageLimitationAnalysis/Scripts/GetMediaFoldersWithMoreThan100Files.sql @@ -0,0 +1,16 @@ +-- File parent folder path is used to group items +-- This path is calculated from FilePath field by removing the file part +-- Example: +-- /somefolder/subfolder/filename.ext -> /somefolder/subfolder +-- /filename.ext -> '' (empty string) + +SELECT LibraryDisplayName, + COALESCE(NULLIF(SUBSTRING('/' + f.FilePath, 0, LEN('/' + f.FilePath) - CHARINDEX('/', REVERSE('/' + f.FilePath)) + 1), ''), '/') AS 'FolderName', + SiteName, + COUNT(*) AS 'NumberOfFiles' +FROM Media_File AS F +INNER JOIN Media_Library AS L ON F.FileLibraryID = L.LibraryID +INNER JOIN CMS_Site AS S ON S.SiteID = L.LibrarySiteID +GROUP BY SUBSTRING('/' + F.FilePath, 0, LEN('/' + F.FilePath) - CHARINDEX('/', REVERSE('/' + F.FilePath)) + 1), LibraryDisplayName, SiteName +HAVING COUNT(*) > 100 +ORDER BY LibraryDisplayName \ No newline at end of file diff --git a/src/KInspector.Reports/KInspector.Reports.csproj b/src/KInspector.Reports/KInspector.Reports.csproj index b52516dd..d15e98b0 100644 --- a/src/KInspector.Reports/KInspector.Reports.csproj +++ b/src/KInspector.Reports/KInspector.Reports.csproj @@ -26,6 +26,12 @@ Always + + Always + + + Always + Always