From 5f6a71e065cf61b32ca5d3caea5f0d07e854a70c Mon Sep 17 00:00:00 2001 From: Michael Green <84688932+michael-j-green@users.noreply.github.com> Date: Wed, 31 Jan 2024 07:28:07 +1100 Subject: [PATCH] Collections needs to honour the logged in users age rating permissionFixes #273 Known issue: #275 --- gaseous-server/Classes/Collections.cs | 335 +++++++++++------- gaseous-server/Classes/Metadata/AgeGroups.cs | 60 ++-- gaseous-server/Classes/Metadata/Games.cs | 2 + .../Controllers/V1.0/CollectionsController.cs | 212 +++++++---- .../Controllers/V1.1/GamesController.cs | 8 +- gaseous-server/ProcessQueue.cs | 3 +- .../Support/Database/MySQL/gaseous-1016.sql | 2 +- .../Support/Database/MySQL/gaseous-1017.sql | 4 + gaseous-server/gaseous-server.csproj | 2 + .../wwwroot/pages/dialogs/collectionedit.html | 17 +- 10 files changed, 421 insertions(+), 224 deletions(-) create mode 100644 gaseous-server/Support/Database/MySQL/gaseous-1017.sql diff --git a/gaseous-server/Classes/Collections.cs b/gaseous-server/Classes/Collections.cs index 5b9a69b..cc34163 100644 --- a/gaseous-server/Classes/Collections.cs +++ b/gaseous-server/Classes/Collections.cs @@ -7,33 +7,26 @@ using System.Security.Cryptography; using Authentication; using gaseous_server.Classes.Metadata; using gaseous_server.Controllers; +using gaseous_server.Controllers.v1_1; using gaseous_server.Models; using IGDB.Models; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc.Filters; using Newtonsoft.Json; using SharpCompress.Common; +using static gaseous_server.Classes.Metadata.Games; namespace gaseous_server.Classes { public class Collections { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public Collections( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - public static List GetCollections() { + public static List GetCollections(string userid) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT * FROM RomCollections ORDER BY `Name`"; - - DataTable data = db.ExecuteCMD(sql); + string sql = "SELECT * FROM RomCollections WHERE OwnedBy=@ownedby ORDER BY `Name`"; + Dictionary dbDict = new Dictionary{ + { "ownedby", userid } + }; + DataTable data = db.ExecuteCMD(sql, dbDict); List collectionItems = new List(); @@ -44,11 +37,24 @@ namespace gaseous_server.Classes return collectionItems; } - public static CollectionItem GetCollection(long Id) { + public static CollectionItem GetCollection(long Id, string userid) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`"; - Dictionary dbDict = new Dictionary(); - dbDict.Add("id", Id); + string sql; + if (userid == "") + { + // reserved for internal operations + sql = "SELECT * FROM RomCollections WHERE Id = @id ORDER BY `Name`"; + } + else + { + // instigated by a user + sql = "SELECT * FROM RomCollections WHERE Id = @id AND OwnedBy = @ownedby ORDER BY `Name`"; + } + Dictionary dbDict = new Dictionary + { + { "id", Id }, + { "ownedby", userid } + }; DataTable romDT = db.ExecuteCMD(sql, dbDict); if (romDT.Rows.Count > 0) @@ -64,60 +70,66 @@ namespace gaseous_server.Classes } } - public static CollectionItem NewCollection(CollectionItem item) + public static CollectionItem NewCollection(CollectionItem item, string userid) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, ArchiveType, AlwaysInclude, BuiltStatus) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @archivetype, @alwaysinclude, @builtstatus); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; - Dictionary dbDict = new Dictionary(); - dbDict.Add("name", item.Name); - dbDict.Add("description", item.Description); - dbDict.Add("platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List()))); - dbDict.Add("genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List()))); - dbDict.Add("players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List()))); - dbDict.Add("playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List()))); - dbDict.Add("themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List()))); - dbDict.Add("minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1)); - dbDict.Add("maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1)); - dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1)); - dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1)); - dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1)); - dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous)); - dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0)); - dbDict.Add("archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip)); - dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List()))); - dbDict.Add("builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild); + string sql = "INSERT INTO RomCollections (`Name`, Description, Platforms, Genres, Players, PlayerPerspectives, Themes, MinimumRating, MaximumRating, MaximumRomsPerPlatform, MaximumBytesPerPlatform, MaximumCollectionSizeInBytes, FolderStructure, IncludeBIOSFiles, ArchiveType, AlwaysInclude, BuiltStatus, OwnedBy) VALUES (@name, @description, @platforms, @genres, @players, @playerperspectives, @themes, @minimumrating, @maximumrating, @maximumromsperplatform, @maximumbytesperplatform, @maximumcollectionsizeinbytes, @folderstructure, @includebiosfiles, @archivetype, @alwaysinclude, @builtstatus, @ownedby); SELECT CAST(LAST_INSERT_ID() AS SIGNED);"; + Dictionary dbDict = new Dictionary + { + { "name", item.Name }, + { "description", item.Description }, + { "platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List())) }, + { "genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List())) }, + { "players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List())) }, + { "playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List())) }, + { "themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List())) }, + { "minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1) }, + { "maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1) }, + { "maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1) }, + { "maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1) }, + { "maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1) }, + { "folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous) }, + { "includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0) }, + { "archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip) }, + { "alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List())) }, + { "builtstatus", CollectionItem.CollectionBuildStatus.WaitingForBuild }, + { "ownedby", userid } + }; DataTable romDT = db.ExecuteCMD(sql, dbDict); long CollectionId = (long)romDT.Rows[0][0]; - CollectionItem collectionItem = GetCollection(CollectionId); + CollectionItem collectionItem = GetCollection(CollectionId, userid); - StartCollectionItemBuild(CollectionId); + StartCollectionItemBuild(CollectionId, userid); return collectionItem; } - public static CollectionItem EditCollection(long Id, CollectionItem item, bool ForceRebuild = true) + public static CollectionItem EditCollection(long Id, CollectionItem item, string userid, bool ForceRebuild = true) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, ArchiveType=@archivetype, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id"; - Dictionary dbDict = new Dictionary(); - dbDict.Add("id", Id); - dbDict.Add("name", item.Name); - dbDict.Add("description", item.Description); - dbDict.Add("platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List()))); - dbDict.Add("genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List()))); - dbDict.Add("players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List()))); - dbDict.Add("playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List()))); - dbDict.Add("themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List()))); - dbDict.Add("minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1)); - dbDict.Add("maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1)); - dbDict.Add("maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1)); - dbDict.Add("maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1)); - dbDict.Add("maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1)); - dbDict.Add("folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous)); - dbDict.Add("includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0)); - dbDict.Add("alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List()))); - dbDict.Add("archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip)); + string sql = "UPDATE RomCollections SET `Name`=@name, Description=@description, Platforms=@platforms, Genres=@genres, Players=@players, PlayerPerspectives=@playerperspectives, Themes=@themes, MinimumRating=@minimumrating, MaximumRating=@maximumrating, MaximumRomsPerPlatform=@maximumromsperplatform, MaximumBytesPerPlatform=@maximumbytesperplatform, MaximumCollectionSizeInBytes=@maximumcollectionsizeinbytes, FolderStructure=@folderstructure, IncludeBIOSFiles=@includebiosfiles, ArchiveType=@archivetype, AlwaysInclude=@alwaysinclude, BuiltStatus=@builtstatus WHERE Id=@id AND OwnedBy=@ownedby"; + Dictionary dbDict = new Dictionary + { + { "id", Id }, + { "name", item.Name }, + { "description", item.Description }, + { "platforms", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Platforms, new List())) }, + { "genres", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Genres, new List())) }, + { "players", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Players, new List())) }, + { "playerperspectives", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.PlayerPerspectives, new List())) }, + { "themes", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.Themes, new List())) }, + { "minimumrating", Common.ReturnValueIfNull(item.MinimumRating, -1) }, + { "maximumrating", Common.ReturnValueIfNull(item.MaximumRating, -1) }, + { "maximumromsperplatform", Common.ReturnValueIfNull(item.MaximumRomsPerPlatform, -1) }, + { "maximumbytesperplatform", Common.ReturnValueIfNull(item.MaximumBytesPerPlatform, -1) }, + { "maximumcollectionsizeinbytes", Common.ReturnValueIfNull(item.MaximumCollectionSizeInBytes, -1) }, + { "folderstructure", Common.ReturnValueIfNull(item.FolderStructure, CollectionItem.FolderStructures.Gaseous) }, + { "includebiosfiles", Common.ReturnValueIfNull(item.IncludeBIOSFiles, 0) }, + { "alwaysinclude", Newtonsoft.Json.JsonConvert.SerializeObject(Common.ReturnValueIfNull(item.AlwaysInclude, new List())) }, + { "archivetype", Common.ReturnValueIfNull(item.ArchiveType, CollectionItem.ArchiveTypes.Zip) }, + { "ownedby", userid } + }; string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + item.ArchiveExtension); if (ForceRebuild == true) @@ -142,22 +154,25 @@ namespace gaseous_server.Classes } db.ExecuteCMD(sql, dbDict); - CollectionItem collectionItem = GetCollection(Id); + CollectionItem collectionItem = GetCollection(Id, userid); if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild) { - StartCollectionItemBuild(Id); + StartCollectionItemBuild(Id, userid); } return collectionItem; } - public static void DeleteCollection(long Id) + public static void DeleteCollection(long Id, string userid) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - string sql = "DELETE FROM RomCollections WHERE Id=@id"; - Dictionary dbDict = new Dictionary(); - dbDict.Add("id", Id); + string sql = "DELETE FROM RomCollections WHERE Id=@id AND OwnedBy=@ownedby"; + Dictionary dbDict = new Dictionary + { + { "id", Id }, + { "ownedby", userid } + }; db.ExecuteCMD(sql, dbDict); string CollectionZipFile = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, Id + ".zip"); @@ -167,9 +182,10 @@ namespace gaseous_server.Classes } } - public static void StartCollectionItemBuild(long Id) + public static void StartCollectionItemBuild(long Id, string userid) { - CollectionItem collectionItem = GetCollection(Id); + // send blank user id to getcollection as this is not a user initiated process + CollectionItem collectionItem = GetCollection(Id, userid); if (collectionItem.BuildStatus != CollectionItem.CollectionBuildStatus.Building) { @@ -183,13 +199,40 @@ namespace gaseous_server.Classes // start background task ProcessQueue.QueueItem queueItem = new ProcessQueue.QueueItem(ProcessQueue.QueueItemType.CollectionCompiler, 1, false, true); - queueItem.Options = Id; + queueItem.Options = new Dictionary{ + { "Id", Id }, + { "UserId", userid } + }; queueItem.ForceExecute(); ProcessQueue.QueueItems.Add(queueItem); } } - public static CollectionContents GetCollectionContent(CollectionItem collectionItem) { + public static CollectionContents GetCollectionContent(CollectionItem collectionItem, string userid) { + Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); + + // get age ratings for specified user + List UserAgeGroupings = new List(); + bool UserAgeGroupIncludeUnrated = true; + if (userid != "") + { + Authentication.UserTable userTable = new UserTable(db); + var user = userTable.GetUserById(userid); + + if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == false) + { + UserAgeGroupIncludeUnrated = false; + } + + foreach (AgeGroups.AgeRestrictionGroupings ageGrouping in Enum.GetValues(typeof(AgeGroups.AgeRestrictionGroupings))) + { + if (ageGrouping <= user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction && ageGrouping != AgeGroups.AgeRestrictionGroupings.Unclassified) + { + UserAgeGroupings.Add(ageGrouping); + } + } + } + List collectionPlatformItems = new List(); // get platforms @@ -230,6 +273,10 @@ namespace gaseous_server.Classes } } + // age ratings + AgeGroups.AgeRestrictionGroupings AgeGrouping = AgeGroups.AgeRestrictionGroupings.Unclassified; + bool ContainsUnclassifiedAgeGroup = false; + // build collection List platformItems = new List(); @@ -247,18 +294,29 @@ namespace gaseous_server.Classes isDynamic = true; } - List games = new List(); + Controllers.v1_1.GamesController.GameReturnPackage games = new Controllers.v1_1.GamesController.GameReturnPackage(); if (isDynamic == true) { - games = GamesController.GetGames("", - platform.Id.ToString(), - string.Join(",", collectionItem.Genres), - string.Join(",", collectionItem.Players), - string.Join(",", collectionItem.PlayerPerspectives), - string.Join(",", collectionItem.Themes), - collectionItem.MinimumRating, - collectionItem.MaximumRating - ); + Controllers.v1_1.GamesController.GameSearchModel searchModel = new Controllers.v1_1.GamesController.GameSearchModel{ + Name = "", + Platform = new List{ + platform.Id.ToString() + }, + Genre = collectionItem.Genres.ConvertAll(s => s.ToString()), + GameMode = collectionItem.Players.ConvertAll(s => s.ToString()), + PlayerPerspective = collectionItem.PlayerPerspectives.ConvertAll(s => s.ToString()), + Theme = collectionItem.Themes.ConvertAll(s => s.ToString()), + GameRating = new Controllers.v1_1.GamesController.GameSearchModel.GameRatingItem{ + MinimumRating = collectionItem.MinimumRating, + MaximumRating = collectionItem.MaximumRating + }, + GameAgeRating = new Controllers.v1_1.GamesController.GameSearchModel.GameAgeRatingItem{ + AgeGroupings = UserAgeGroupings, + IncludeUnrated = UserAgeGroupIncludeUnrated + } + }; + games = Controllers.v1_1.GamesController.GetGames(searchModel, userid); + } CollectionContents.CollectionPlatformItem collectionPlatformItem = new CollectionContents.CollectionPlatformItem(platform); @@ -274,7 +332,7 @@ namespace gaseous_server.Classes ) && alwaysIncludeItem.PlatformId == platform.Id ) { - Game AlwaysIncludeGame = Games.GetGame(alwaysIncludeItem.GameId, false, false, false); + MinimalGameItem AlwaysIncludeGame = new MinimalGameItem(Games.GetGame(alwaysIncludeItem.GameId, false, false, false)); CollectionContents.CollectionPlatformItem.CollectionGameItem gameItem = new CollectionContents.CollectionPlatformItem.CollectionGameItem(AlwaysIncludeGame); gameItem.InclusionStatus = new CollectionItem.AlwaysIncludeItem(); gameItem.InclusionStatus.PlatformId = alwaysIncludeItem.PlatformId; @@ -286,7 +344,7 @@ namespace gaseous_server.Classes } } - foreach (Game game in games) { + foreach (MinimalGameItem game in games.Games) { bool gameAlreadyInList = false; foreach (CollectionContents.CollectionPlatformItem.CollectionGameItem existingGame in collectionPlatformItem.Games) { @@ -341,6 +399,17 @@ namespace gaseous_server.Classes } } } + + // handle age grouping + AgeGroups.AgeRestrictionGroupings CurrentAgeGroup = AgeGroups.GetAgeGroupFromAgeRatings(game.AgeRatings); + if (CurrentAgeGroup > AgeGrouping) + { + AgeGrouping = CurrentAgeGroup; + } + if (CurrentAgeGroup == AgeGroups.AgeRestrictionGroupings.Unclassified) + { + ContainsUnclassifiedAgeGroup = true; + } } collectionPlatformItem.Games.Sort((x, y) => x.Name.CompareTo(y.Name)); @@ -369,29 +438,39 @@ namespace gaseous_server.Classes collectionPlatformItems.Sort((x, y) => x.Name.CompareTo(y.Name)); - CollectionContents collectionContents = new CollectionContents(); - collectionContents.Collection = collectionPlatformItems; + CollectionContents collectionContents = new CollectionContents + { + Collection = collectionPlatformItems, + AgeGroup = AgeGrouping, + ContainsUnclassifiedAgeGroup = ContainsUnclassifiedAgeGroup + }; return collectionContents; } - public static void CompileCollections(long CollectionId) + public static void CompileCollections(long CollectionId, string userid) { Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); - CollectionItem collectionItem = GetCollection(CollectionId); + CollectionItem collectionItem = GetCollection(CollectionId, userid); if (collectionItem.BuildStatus == CollectionItem.CollectionBuildStatus.WaitingForBuild) { Logging.Log(Logging.LogType.Information, "Collections", "Beginning build of collection: " + collectionItem.Name); - // set starting - string sql = "UPDATE RomCollections SET BuiltStatus=@bs WHERE Id=@id"; - Dictionary dbDict = new Dictionary(); - dbDict.Add("id", collectionItem.Id); - dbDict.Add("bs", CollectionItem.CollectionBuildStatus.Building); - db.ExecuteCMD(sql, dbDict); + CollectionContents collectionContents = GetCollectionContent(collectionItem, userid); - List collectionPlatformItems = GetCollectionContent(collectionItem).Collection; + // set starting + string sql = "UPDATE RomCollections SET BuiltStatus=@bs, AgeGroup=@ag, AgeGroupUnclassified=@agu WHERE Id=@id"; + Dictionary dbDict = new Dictionary + { + { "id", collectionItem.Id }, + { "bs", CollectionItem.CollectionBuildStatus.Building }, + { "ag", collectionContents.AgeGroup }, + { "agu", collectionContents.ContainsUnclassifiedAgeGroup } + }; + db.ExecuteCMD(sql, dbDict); + + List collectionPlatformItems = collectionContents.Collection; string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, collectionItem.Id + collectionItem.ArchiveExtension); string ZipFileTempPath = Path.Combine(Config.LibraryConfiguration.LibraryTempDirectory, collectionItem.Id.ToString()); @@ -758,6 +837,9 @@ namespace gaseous_server.Classes } } + public AgeGroups.AgeRestrictionGroupings AgeGroup { get; set; } + public bool ContainsUnclassifiedAgeGroup { get; set; } + public class CollectionPlatformItem { public CollectionPlatformItem(IGDB.Models.Platform platform) { string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug" }; @@ -808,48 +890,43 @@ namespace gaseous_server.Classes } } - public class CollectionGameItem { - public CollectionGameItem(IGDB.Models.Game game) { - string[] PropertyWhitelist = new string[] { "Id", "Name", "Slug", "Cover" }; - PropertyInfo[] srcProperties = typeof(IGDB.Models.Game).GetProperties(); - PropertyInfo[] dstProperties = typeof(CollectionPlatformItem.CollectionGameItem).GetProperties(); - foreach (PropertyInfo srcProperty in srcProperties) { - if (PropertyWhitelist.Contains(srcProperty.Name)) + public class CollectionGameItem : MinimalGameItem + { + public CollectionGameItem(MinimalGameItem gameObject) + { + this.Id = gameObject.Id; + this.Name = gameObject.Name; + this.Slug = gameObject.Slug; + this.TotalRating = gameObject.TotalRating; + this.TotalRatingCount = gameObject.TotalRatingCount; + this.Cover = gameObject.Cover; + this.Artworks = gameObject.Artworks; + this.FirstReleaseDate = gameObject.FirstReleaseDate; + this.AgeRatings = gameObject.AgeRatings; + } + + public IGDB.Models.Cover? CoverItem + { + get + { + if (Cover != null) { - foreach (PropertyInfo dstProperty in dstProperties) - { - if (srcProperty.Name == dstProperty.Name) - { - if (srcProperty.GetValue(game) != null) { - string compareName = srcProperty.PropertyType.Name.ToLower().Split("`")[0]; - switch(compareName) { - case "identityorvalue": - string newObjectValue = Newtonsoft.Json.JsonConvert.SerializeObject(srcProperty.GetValue(game)); - Dictionary newDict = Newtonsoft.Json.JsonConvert.DeserializeObject>(newObjectValue); - dstProperty.SetValue(this, newDict["Id"]); - break; - default: - dstProperty.SetValue(this, srcProperty.GetValue(game)); - break; - } - } - } - } + IGDB.Models.Cover cover = Covers.GetCover(Cover.Id, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory, "Games", Slug), false); + + return cover; + } + else + { + return null; } } } - public long Id { get; set; } - public string Name { get; set; } - public string Slug { get; set; } - public long Cover { get; set;} - public IGDB.Models.Cover CoverItem - { + public AgeGroups.AgeRestrictionGroupings AgeGrouping + { get { - IGDB.Models.Cover cover = Covers.GetCover(Cover, Path.Combine(Config.LibraryConfiguration.LibraryMetadataDirectory, "Games", Slug), false); - - return cover; + return AgeGroups.GetAgeGroupFromAgeRatings(this.AgeRatings); } } diff --git a/gaseous-server/Classes/Metadata/AgeGroups.cs b/gaseous-server/Classes/Metadata/AgeGroups.cs index 2d5eda2..8dae05e 100644 --- a/gaseous-server/Classes/Metadata/AgeGroups.cs +++ b/gaseous-server/Classes/Metadata/AgeGroups.cs @@ -72,32 +72,7 @@ namespace gaseous_server.Classes.Metadata } // compile the ratings values into the ratings groups - AgeRestrictionGroupings highestAgeGroup = AgeRestrictionGroupings.Unclassified; - foreach (AgeRating ageRating in ageRatings) - { - foreach (KeyValuePair ageGroupItem in AgeGroupingsFlat) - { - - PropertyInfo[] groupProps = typeof(AgeGroupItem).GetProperties(); - foreach (PropertyInfo property in groupProps) - { - if (RatingsBoards.Contains(property.Name)) - { - List ratingBoard = (List)property.GetValue(ageGroupItem.Value); - foreach (AgeRatingTitle ratingTitle in ratingBoard) - { - if (ageRating.Rating == ratingTitle) - { - if (highestAgeGroup < ageGroupItem.Key) - { - highestAgeGroup = ageGroupItem.Key; - } - } - } - } - } - } - } + AgeRestrictionGroupings highestAgeGroup = GetAgeGroupFromAgeRatings(ageRatings); // return the compiled ratings group AgeGroup ageGroup = new AgeGroup(); @@ -138,6 +113,39 @@ namespace gaseous_server.Classes.Metadata return null; } + public static AgeRestrictionGroupings GetAgeGroupFromAgeRatings(List ageRatings) + { + // compile the ratings values into the ratings groups + AgeRestrictionGroupings highestAgeGroup = AgeRestrictionGroupings.Unclassified; + foreach (AgeRating ageRating in ageRatings) + { + foreach (KeyValuePair ageGroupItem in AgeGroupingsFlat) + { + + PropertyInfo[] groupProps = typeof(AgeGroupItem).GetProperties(); + foreach (PropertyInfo property in groupProps) + { + if (RatingsBoards.Contains(property.Name)) + { + List ratingBoard = (List)property.GetValue(ageGroupItem.Value); + foreach (AgeRatingTitle ratingTitle in ratingBoard) + { + if (ageRating.Rating == ratingTitle) + { + if (highestAgeGroup < ageGroupItem.Key) + { + highestAgeGroup = ageGroupItem.Key; + } + } + } + } + } + } + } + + return highestAgeGroup; + } + public class AgeGroup { public long? Id { get; set; } diff --git a/gaseous-server/Classes/Metadata/Games.cs b/gaseous-server/Classes/Metadata/Games.cs index 77c0be5..fc0aba9 100644 --- a/gaseous-server/Classes/Metadata/Games.cs +++ b/gaseous-server/Classes/Metadata/Games.cs @@ -538,6 +538,7 @@ namespace gaseous_server.Classes.Metadata { this.Id = gameObject.Id; this.Name = gameObject.Name; + this.Slug = gameObject.Slug; this.TotalRating = gameObject.TotalRating; this.TotalRatingCount = gameObject.TotalRatingCount; this.Cover = gameObject.Cover; @@ -561,6 +562,7 @@ namespace gaseous_server.Classes.Metadata public long? Id { get; set; } public string Name { get; set; } + public string Slug { get; set; } public double? TotalRating { get; set; } public int? TotalRatingCount { get; set; } public bool HasSavedGame { get; set; } = false; diff --git a/gaseous-server/Controllers/V1.0/CollectionsController.cs b/gaseous-server/Controllers/V1.0/CollectionsController.cs index c05a227..6a67307 100644 --- a/gaseous-server/Controllers/V1.0/CollectionsController.cs +++ b/gaseous-server/Controllers/V1.0/CollectionsController.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.IO.Compression; using System.Linq; using System.Threading.Tasks; +using Authentication; using gaseous_server.Classes; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; namespace gaseous_server.Controllers @@ -16,6 +18,17 @@ namespace gaseous_server.Controllers [Authorize] public class CollectionsController : Controller { + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + + public CollectionsController( + UserManager userManager, + SignInManager signInManager) + { + _userManager = userManager; + _signInManager = signInManager; + } + /// /// Gets all ROM collections /// @@ -24,9 +37,16 @@ namespace gaseous_server.Controllers [MapToApiVersion("1.1")] [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] - public List GetCollections() + public async Task GetCollectionsAsync() { - return Classes.Collections.GetCollections(); + var user = await _userManager.GetUserAsync(User); + + if (user != null) + { + return Ok(Classes.Collections.GetCollections(user.Id)); + } + + return NotFound(); } /// @@ -41,18 +61,27 @@ namespace gaseous_server.Controllers [Route("{CollectionId}")] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetCollection(long CollectionId, bool Build = false) + public async Task GetCollection(long CollectionId, bool Build = false) { - try - { - if (Build == true) - { - Classes.Collections.StartCollectionItemBuild(CollectionId); - } + var user = await _userManager.GetUserAsync(User); - return Ok(Classes.Collections.GetCollection(CollectionId)); + if (user != null) + { + try + { + if (Build == true) + { + Classes.Collections.StartCollectionItemBuild(CollectionId, user.Id); + } + + return Ok(Classes.Collections.GetCollection(CollectionId, user.Id)); + } + catch + { + return NotFound(); + } } - catch + else { return NotFound(); } @@ -69,14 +98,23 @@ namespace gaseous_server.Controllers [Route("{CollectionId}/Roms")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetCollectionRoms(long CollectionId) + public async Task GetCollectionRoms(long CollectionId) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId); - return Ok(Classes.Collections.GetCollectionContent(collectionItem)); + try + { + Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id); + return Ok(Classes.Collections.GetCollectionContent(collectionItem, user.Id)); + } + catch + { + return NotFound(); + } } - catch + else { return NotFound(); } @@ -94,15 +132,24 @@ namespace gaseous_server.Controllers [Authorize(Roles = "Admin,Gamer")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetCollectionRomsPreview(Classes.Collections.CollectionItem Item) + public async Task GetCollectionRomsPreview(Classes.Collections.CollectionItem Item) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - return Ok(Classes.Collections.GetCollectionContent(Item)); + try + { + return Ok(Classes.Collections.GetCollectionContent(Item, user.Id)); + } + catch (Exception ex) + { + return NotFound(ex); + } } - catch (Exception ex) + else { - return NotFound(ex); + return NotFound(); } } @@ -117,25 +164,34 @@ namespace gaseous_server.Controllers [Route("{CollectionId}/Roms/Zip")] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetCollectionRomsZip(long CollectionId) + public async Task GetCollectionRomsZip(long CollectionId) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId); - - string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, CollectionId + ".zip"); - - if (System.IO.File.Exists(ZipFilePath)) + try { - var stream = new FileStream(ZipFilePath, FileMode.Open); - return File(stream, "application/zip", collectionItem.Name + ".zip"); + Classes.Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id); + + string ZipFilePath = Path.Combine(Config.LibraryConfiguration.LibraryCollectionsDirectory, CollectionId + ".zip"); + + if (System.IO.File.Exists(ZipFilePath)) + { + var stream = new FileStream(ZipFilePath, FileMode.Open); + return File(stream, "application/zip", collectionItem.Name + ".zip"); + } + else + { + return NotFound(); + } } - else + catch { return NotFound(); } } - catch + else { return NotFound(); } @@ -152,15 +208,24 @@ namespace gaseous_server.Controllers [Authorize(Roles = "Admin,Gamer")] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public ActionResult NewCollection(Classes.Collections.CollectionItem Item) + public async Task NewCollectionAsync(Classes.Collections.CollectionItem Item) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - return Ok(Classes.Collections.NewCollection(Item)); + try + { + return Ok(Classes.Collections.NewCollection(Item, user.Id)); + } + catch (Exception ex) + { + return BadRequest(ex); + } } - catch (Exception ex) + else { - return BadRequest(ex); + return NotFound(); } } @@ -177,13 +242,22 @@ namespace gaseous_server.Controllers [Authorize(Roles = "Admin,Gamer")] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult EditCollection(long CollectionId, Classes.Collections.CollectionItem Item) + public async Task EditCollection(long CollectionId, Classes.Collections.CollectionItem Item) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - return Ok(Classes.Collections.EditCollection(CollectionId, Item, true)); + try + { + return Ok(Classes.Collections.EditCollection(CollectionId, Item, user.Id, true)); + } + catch + { + return NotFound(); + } } - catch + else { return NotFound(); } @@ -202,27 +276,36 @@ namespace gaseous_server.Controllers [Route("{CollectionId}/AlwaysInclude")] [ProducesResponseType(typeof(Classes.Collections.CollectionItem), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult EditCollectionAlwaysInclude(long CollectionId, [FromQuery]bool Rebuild, [FromBody]Collections.CollectionItem.AlwaysIncludeItem Inclusion) + public async Task EditCollectionAlwaysInclude(long CollectionId, [FromQuery]bool Rebuild, [FromBody]Collections.CollectionItem.AlwaysIncludeItem Inclusion) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId); - bool ItemFound = false; - foreach (Collections.CollectionItem.AlwaysIncludeItem includeItem in collectionItem.AlwaysInclude) + try { - if (includeItem.PlatformId == Inclusion.PlatformId && includeItem.GameId == Inclusion.GameId) + Collections.CollectionItem collectionItem = Classes.Collections.GetCollection(CollectionId, user.Id); + bool ItemFound = false; + foreach (Collections.CollectionItem.AlwaysIncludeItem includeItem in collectionItem.AlwaysInclude) { - ItemFound = true; + if (includeItem.PlatformId == Inclusion.PlatformId && includeItem.GameId == Inclusion.GameId) + { + ItemFound = true; + } + } + if (ItemFound == false) + { + collectionItem.AlwaysInclude.Add(Inclusion); } - } - if (ItemFound == false) - { - collectionItem.AlwaysInclude.Add(Inclusion); - } - return Ok(Classes.Collections.EditCollection(CollectionId, collectionItem, Rebuild)); + return Ok(Classes.Collections.EditCollection(CollectionId, collectionItem, user.Id, Rebuild)); + } + catch + { + return NotFound(); + } } - catch + else { return NotFound(); } @@ -239,14 +322,23 @@ namespace gaseous_server.Controllers [Route("{CollectionId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteCollection(long CollectionId) + public async Task DeleteCollection(long CollectionId) { - try + var user = await _userManager.GetUserAsync(User); + + if (user != null) { - Classes.Collections.DeleteCollection(CollectionId); - return Ok(); + try + { + Classes.Collections.DeleteCollection(CollectionId, user.Id); + return Ok(); + } + catch + { + return NotFound(); + } } - catch + else { return NotFound(); } diff --git a/gaseous-server/Controllers/V1.1/GamesController.cs b/gaseous-server/Controllers/V1.1/GamesController.cs index 916c218..979da2b 100644 --- a/gaseous-server/Controllers/V1.1/GamesController.cs +++ b/gaseous-server/Controllers/V1.1/GamesController.cs @@ -474,6 +474,7 @@ SELECT DISTINCT Game.Id, Game.`Name`, Game.NameThe, + Game.Slug, Game.PlatformId, Game.TotalRating, Game.TotalRatingCount, @@ -543,9 +544,12 @@ FROM int pageOffset = pageSize * (pageNumber - 1); for (int i = pageOffset; i < dbResponse.Rows.Count; i++) { - if (i >= (pageOffset + pageSize)) + if (pageNumber != 0 && pageSize != 0) { - break; + if (i >= (pageOffset + pageSize)) + { + break; + } } Game retGame = Storage.BuildCacheObject(new Game() , dbResponse.Rows[i]); diff --git a/gaseous-server/ProcessQueue.cs b/gaseous-server/ProcessQueue.cs index 82807e0..8b0f0d9 100644 --- a/gaseous-server/ProcessQueue.cs +++ b/gaseous-server/ProcessQueue.cs @@ -334,7 +334,8 @@ namespace gaseous_server case QueueItemType.CollectionCompiler: Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Collection Compiler"); - Classes.Collections.CompileCollections((long)Options); + Dictionary collectionOptions = (Dictionary)Options; + Classes.Collections.CompileCollections((long)collectionOptions["Id"], (string)collectionOptions["UserId"]); break; case QueueItemType.MediaGroupCompiler: diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1016.sql b/gaseous-server/Support/Database/MySQL/gaseous-1016.sql index 3a94880..cfa3f97 100644 --- a/gaseous-server/Support/Database/MySQL/gaseous-1016.sql +++ b/gaseous-server/Support/Database/MySQL/gaseous-1016.sql @@ -3,4 +3,4 @@ ADD COLUMN `ValueType` INT NULL DEFAULT 0 AFTER `Setting`, ADD COLUMN `ValueDate` DATETIME NULL DEFAULT NULL AFTER `Value`; ALTER TABLE `GameState` -ADD COLUMN `Zipped` BOOLEAN NOT NULL DEFAULT 0; \ No newline at end of file +ADD COLUMN `Zipped` BOOLEAN NOT NULL DEFAULT 0; diff --git a/gaseous-server/Support/Database/MySQL/gaseous-1017.sql b/gaseous-server/Support/Database/MySQL/gaseous-1017.sql new file mode 100644 index 0000000..6c7edb9 --- /dev/null +++ b/gaseous-server/Support/Database/MySQL/gaseous-1017.sql @@ -0,0 +1,4 @@ +ALTER TABLE `RomCollections` +ADD COLUMN `OwnedBy` VARCHAR(45) NULL, +ADD COLUMN `AgeGroup` INT NULL, +ADD COLUMN `AgeGroupUnclassified` BOOLEAN NULL; \ No newline at end of file diff --git a/gaseous-server/gaseous-server.csproj b/gaseous-server/gaseous-server.csproj index f07ff59..029f548 100644 --- a/gaseous-server/gaseous-server.csproj +++ b/gaseous-server/gaseous-server.csproj @@ -61,6 +61,7 @@ + @@ -99,5 +100,6 @@ + diff --git a/gaseous-server/wwwroot/pages/dialogs/collectionedit.html b/gaseous-server/wwwroot/pages/dialogs/collectionedit.html index 7cd27f8..1dcd9cf 100644 --- a/gaseous-server/wwwroot/pages/dialogs/collectionedit.html +++ b/gaseous-server/wwwroot/pages/dialogs/collectionedit.html @@ -59,7 +59,7 @@ Directory Layout - @@ -85,7 +85,7 @@ Include BIOS files (if available) - @@ -589,11 +589,11 @@ gameCoverCell.className = 'collections_preview_gamecovercell'; var gameImage = document.createElement('img'); - gameImage.className = 'game_tile_image game_tile_image_small'; + gameImage.className = 'game_tile_image game_tile_image_small lazy'; + gameImage.src = '/images/unknowngame.png'; if (gameItem.cover) { - gameImage.src = '/api/v1.1/Games/' + gameItem.id + '/cover/image/cover_small/' + gameItem.coverItem.imageId + '.jpg'; + gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameItem.id + '/cover/image/cover_small/' + gameItem.coverItem.imageId + '.jpg'); } else { - gameImage.src = '/images/unknowngame.png'; gameImage.className = 'game_tile_image game_tile_image_small unknown'; } gameCoverCell.appendChild(gameImage); @@ -626,6 +626,13 @@ var collectionSize = document.getElementById('collectionedit_previewbox_size'); collectionSize.innerHTML = "Estimated uncompressed collection size: " + formatBytes(data.collectionProjectedSizeBytes); } + + $('#collectionedit_previewbox .lazy').Lazy({ + scrollDirection: 'vertical', + effect: 'fadeIn', + visibleOnly: true, + appendScroll: $('#collectionedit_previewbox') + }); } function DisplayFormattedBytes(inputElement, labelElement) {