Compare commits

...

12 Commits

Author SHA1 Message Date
Michael Green
7b241ee13e Miscellaneous bug fixes (#220) 2023-12-09 15:50:39 +11:00
Michael Green
84017639eb Logs now have filtering options (#219)
* Added logging configuration options

* Add support for filtering logs
2023-12-09 14:07:08 +11:00
Michael Green
9e346910f4 Set paging default to "paged" (#218) 2023-12-07 13:57:40 +11:00
Michael Green
e7239c428b Library filtering and display enhancements (#214)
* Implement infinite scrolling and paging (selected via preference) (closes #202)
* Display game counts on more filter types (closes #194)
* Make game counts larger (closes #194)
* Include age groups in filtering (closes #200)
* Add sorting options (closes #145)
2023-12-07 13:20:53 +11:00
Michael Green
9288eb8f12 Enhanced firmware availability page (#213) 2023-12-01 17:01:16 +11:00
Michael Green
e32e7ad36f Improved handling of password user feedback (#212) 2023-12-01 15:51:00 +11:00
Michael Green
3de551be95 Fix spelling error (#211) 2023-12-01 14:40:32 +11:00
Michael Green
8e3fa4f8d5 Added release notes configuration (#210) 2023-12-01 14:27:20 +11:00
Michael Green
b564edb158 Background task execution intervals are now user configurable (#209)
* Store background task intervals in database

* Background task intervals are now user customisable
2023-12-01 13:28:41 +11:00
Michael Green
0bf2ba5d96 Store background task intervals in database (#207) 2023-11-30 09:35:05 +11:00
Michael Green
688af162f5 Make library scan run once a day rather than every hour (#206) 2023-11-30 09:19:00 +11:00
Michael Green
7e4fccb0c1 Apply indexes to improve signature search (#205) 2023-11-30 07:50:32 +11:00
48 changed files with 2450 additions and 500 deletions

15
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
changelog:
categories:
- title: What's New
labels:
- '*'
exclude:
labels:
- bug
- dependencies
- title: Bug Fixes
labels:
- bug
- title: Dependencies
labels:
- dependencies

View File

@@ -11,5 +11,6 @@ namespace Authentication
public class ApplicationUser : IdentityUser public class ApplicationUser : IdentityUser
{ {
public SecurityProfileViewModel SecurityProfile { get; set; } public SecurityProfileViewModel SecurityProfile { get; set; }
public List<UserPreferenceViewModel> UserPreferences { get; set; }
} }
} }

View File

@@ -99,6 +99,7 @@ namespace Authentication
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user); user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
} }
return user; return user;
@@ -135,6 +136,7 @@ namespace Authentication
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user); user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
users.Add(user); users.Add(user);
} }
@@ -166,6 +168,7 @@ namespace Authentication
user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]); user.AccessFailedCount = string.IsNullOrEmpty((string?)row["AccessFailedCount"]) ? 0 : int.Parse((string?)row["AccessFailedCount"]);
user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false; user.TwoFactorEnabled = row["TwoFactorEnabled"] == "1" ? true:false;
user.SecurityProfile = GetSecurityProfile(user); user.SecurityProfile = GetSecurityProfile(user);
user.UserPreferences = GetPreferences(user);
users.Add(user); users.Add(user);
} }
@@ -273,6 +276,9 @@ namespace Authentication
// set default security profile // set default security profile
SetSecurityProfile(user, new SecurityProfileViewModel()); SetSecurityProfile(user, new SecurityProfileViewModel());
// set default preferences
SetPreferences(user, new List<UserPreferenceViewModel>());
return _database.ExecuteCMD(commandText, parameters).Rows.Count; return _database.ExecuteCMD(commandText, parameters).Rows.Count;
} }
@@ -283,7 +289,7 @@ namespace Authentication
/// <returns></returns> /// <returns></returns>
private int Delete(string userId) private int Delete(string userId)
{ {
string commandText = "Delete from Users where Id = @userId"; string commandText = "Delete from Users where Id = @userId; Delete from User_Settings where Id = @userId;";
Dictionary<string, object> parameters = new Dictionary<string, object>(); Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@userId", userId); parameters.Add("@userId", userId);
@@ -328,6 +334,9 @@ namespace Authentication
// set the security profile // set the security profile
SetSecurityProfile(user, user.SecurityProfile); SetSecurityProfile(user, user.SecurityProfile);
// set preferences
SetPreferences(user, user.UserPreferences);
return _database.ExecuteCMD(commandText, parameters).Rows.Count; return _database.ExecuteCMD(commandText, parameters).Rows.Count;
} }
@@ -367,5 +376,66 @@ namespace Authentication
return _database.ExecuteCMD(commandText, parameters).Rows.Count; return _database.ExecuteCMD(commandText, parameters).Rows.Count;
} }
public List<UserPreferenceViewModel> GetPreferences(TUser user)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT `Setting`, `Value` FROM User_Settings WHERE Id=@id;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", user.Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
List<UserPreferenceViewModel> userPrefs = new List<UserPreferenceViewModel>();
foreach (DataRow row in data.Rows)
{
UserPreferenceViewModel userPref = new UserPreferenceViewModel();
userPref.Setting = (string)row["Setting"];
userPref.Value = (string)row["Value"];
userPrefs.Add(userPref);
}
return userPrefs;
}
public int SetPreferences(TUser user, List<UserPreferenceViewModel> model)
{
if (model != null)
{
List<UserPreferenceViewModel> userPreferences = GetPreferences(user);
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
foreach (UserPreferenceViewModel modelItem in model)
{
bool prefItemFound = false;
foreach (UserPreferenceViewModel existing in userPreferences)
{
if (existing.Setting.ToLower() == modelItem.Setting.ToLower())
{
prefItemFound = true;
break;
}
}
string sql = "INSERT INTO User_Settings (`Id`, `Setting`, `Value`) VALUES (@id, @setting, @value);";
if (prefItemFound == true)
{
sql = "UPDATE User_Settings SET `Value`=@value WHERE `Id`=@id AND `Setting`=@setting";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", user.Id);
dbDict.Add("setting", modelItem.Setting);
dbDict.Add("value", modelItem.Value);
db.ExecuteNonQuery(sql, dbDict);
}
return model.Count;
}
else
{
return 0;
}
}
} }
} }

View File

@@ -7,6 +7,7 @@ namespace Authentication
public string EmailAddress { get; set; } public string EmailAddress { get; set; }
public List<String> Roles { get; set; } public List<String> Roles { get; set; }
public SecurityProfileViewModel SecurityProfile { get; set; } public SecurityProfileViewModel SecurityProfile { get; set; }
public List<UserPreferenceViewModel> UserPreferences { get; set; }
public string HighestRole { public string HighestRole {
get get
{ {

View File

@@ -5,13 +5,13 @@ namespace Authentication
public class SecurityProfileViewModel public class SecurityProfileViewModel
{ {
public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{ public AgeRestrictionItem AgeRestrictionPolicy { get; set; } = new AgeRestrictionItem{
MaximumAgeRestriction = "Adult", MaximumAgeRestriction = gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult,
IncludeUnrated = true IncludeUnrated = true
}; };
public class AgeRestrictionItem public class AgeRestrictionItem
{ {
public string MaximumAgeRestriction { get; set; } public gaseous_server.Classes.Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction { get; set; }
public bool IncludeUnrated { get; set; } public bool IncludeUnrated { get; set; }
} }
} }

View File

@@ -0,0 +1,8 @@
namespace Authentication;
public class UserPreferenceViewModel
{
public string Setting { get; set; }
public string Value { get; set; }
}

View File

@@ -4,19 +4,27 @@ using System.IO.Compression;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using Authentication;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using gaseous_server.Controllers; using gaseous_server.Controllers;
using gaseous_server.Models; using gaseous_server.Models;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Identity;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace gaseous_server.Classes namespace gaseous_server.Classes
{ {
public class Collections public class Collections
{ {
public Collections() private readonly UserManager<ApplicationUser> _userManager;
{ private readonly SignInManager<ApplicationUser> _signInManager;
public Collections(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
} }
public static List<CollectionItem> GetCollections() { public static List<CollectionItem> GetCollections() {
@@ -211,8 +219,8 @@ namespace gaseous_server.Classes
} }
} else { } else {
// get all platforms to pull from // get all platforms to pull from
FilterController filterController = new FilterController(); Dictionary<string, object> FilterDict = Filters.Filter(AgeRatings.AgeGroups.AgeRestrictionGroupings.Adult, true);
platforms.AddRange((List<FilterController.FilterPlatform>)filterController.Filter()["platforms"]); platforms.AddRange((List<Filters.FilterPlatform>)FilterDict["platforms"]);
} }
// build collection // build collection

View File

@@ -503,11 +503,7 @@ namespace gaseous_server.Classes
// log retention in days // log retention in days
public int LogRetention = 7; public int LogRetention = 7;
public enum LoggingFormat public bool AlwaysLogToDisk = false;
{
Json,
Text
}
} }
} }
} }

View File

@@ -0,0 +1,225 @@
using System.Data;
using System.Reflection.Metadata.Ecma335;
using gaseous_server.Classes.Metadata;
using IGDB.Models;
namespace gaseous_server.Classes
{
public class Filters
{
public static Dictionary<string, object> Filter(Metadata.AgeRatings.AgeGroups.AgeRestrictionGroupings MaximumAgeRestriction, bool IncludeUnrated)
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> FilterSet = new Dictionary<string, object>();
// platforms
List<FilterPlatform> platforms = new List<FilterPlatform>();
string ageRestriction_Platform = "Game.AgeGroupId <= " + (int)MaximumAgeRestriction;
string ageRestriction_Generic = "view_Games.AgeGroupId <= " + (int)MaximumAgeRestriction;
if (IncludeUnrated == true)
{
ageRestriction_Platform += " OR Game.AgeGroupId IS NULL";
ageRestriction_Generic += " OR view_Games.AgeGroupId IS NULL";
}
string sql = "SELECT DISTINCT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId, view_Games.AgeGroupId FROM Games_Roms LEFT JOIN view_Games ON view_Games.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id AND (" + ageRestriction_Platform + ")) AS GameCount FROM Platform LEFT JOIN Relation_Game_Platforms ON Relation_Game_Platforms.PlatformsId = Platform.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_Platforms.GameId HAVING GameCount > 0 ORDER BY Platform.`Name`;";
DataTable dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"]));
platformItem.GameCount = (int)(long)dr["GameCount"];
platforms.Add(platformItem);
}
FilterSet.Add("platforms", platforms);
// genres
List<FilterGenre> genres = new List<FilterGenre>();
dbResponse = GetGenericFilterItem(db, "Genre", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterGenre genreItem = new FilterGenre(Classes.Metadata.Genres.GetGenres((long)dr["id"]));
genreItem.GameCount = (int)(long)dr["GameCount"];
genres.Add(genreItem);
}
FilterSet.Add("genres", genres);
// game modes
List<FilterGameMode> gameModes = new List<FilterGameMode>();
dbResponse = GetGenericFilterItem(db, "GameMode", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterGameMode gameModeItem = new FilterGameMode(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"]));
gameModeItem.GameCount = (int)(long)dr["GameCount"];
gameModes.Add(gameModeItem);
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<FilterPlayerPerspective> playerPerspectives = new List<FilterPlayerPerspective>();
dbResponse = GetGenericFilterItem(db, "PlayerPerspective", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlayerPerspective playerPerspectiveItem = new FilterPlayerPerspective(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"]));
playerPerspectiveItem.GameCount = (int)(long)dr["GameCount"];
playerPerspectives.Add(playerPerspectiveItem);
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<FilterTheme> themes = new List<FilterTheme>();
dbResponse = GetGenericFilterItem(db, "Theme", ageRestriction_Generic);
foreach (DataRow dr in dbResponse.Rows)
{
FilterTheme themeItem = new FilterTheme(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"]));
themeItem.GameCount = (int)(long)dr["GameCount"];
themes.Add(themeItem);
}
FilterSet.Add("themes", themes);
// age groups
List<FilterAgeGrouping> agegroupings = new List<FilterAgeGrouping>();
sql = "SELECT view_Games.Id, view_Games.AgeGroupId, COUNT(view_Games.Id) AS GameCount FROM view_Games WHERE (" + ageRestriction_Generic + ") GROUP BY view_Games.AgeGroupId ORDER BY view_Games.AgeGroupId DESC;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterAgeGrouping filterAgeGrouping = new FilterAgeGrouping();
if (dr["AgeGroupId"] == DBNull.Value)
{
filterAgeGrouping.Id = (long)AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
filterAgeGrouping.AgeGroup = AgeRatings.AgeGroups.AgeRestrictionGroupings.Unclassified;
}
else
{
filterAgeGrouping.Id = (long)(AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
filterAgeGrouping.AgeGroup = (AgeRatings.AgeGroups.AgeRestrictionGroupings)dr["AgeGroupId"];
}
filterAgeGrouping.GameCount = (int)(long)dr["GameCount"];
agegroupings.Add(filterAgeGrouping);
}
FilterSet.Add("agegroupings", agegroupings);
return FilterSet;
}
private static DataTable GetGenericFilterItem(Database db, string Name, string AgeRestriction_Generic)
{
string sql = "SELECT DISTINCT <ITEMNAME>.Id, <ITEMNAME>.`Name`, COUNT(view_Games.Id) AS GameCount FROM <ITEMNAME> LEFT JOIN Relation_Game_<ITEMNAME>s ON Relation_Game_<ITEMNAME>s.<ITEMNAME>sId = <ITEMNAME>.Id LEFT JOIN view_Games ON view_Games.Id = Relation_Game_<ITEMNAME>s.GameId WHERE (" + AgeRestriction_Generic + ") GROUP BY <ITEMNAME>.Id ORDER BY <ITEMNAME>.`Name`;";
sql = sql.Replace("<ITEMNAME>", Name);
DataTable dbResponse = db.ExecuteCMD(sql);
return dbResponse;
}
public class FilterPlatform : IGDB.Models.Platform
{
public FilterPlatform(Platform obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterGenre : IGDB.Models.Genre
{
public FilterGenre(Genre obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterGameMode : IGDB.Models.GameMode
{
public FilterGameMode(GameMode obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterPlayerPerspective : IGDB.Models.PlayerPerspective
{
public FilterPlayerPerspective(PlayerPerspective obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterTheme : IGDB.Models.Theme
{
public FilterTheme(Theme obj)
{
var properties = obj.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(obj));
}
}
}
public int GameCount { get; set; }
}
public class FilterAgeGrouping
{
public long Id { get; set; }
public AgeRatings.AgeGroups.AgeRestrictionGroupings AgeGroup { get ; set; }
public string Name
{
get
{
return this.AgeGroup.ToString();
}
}
public int GameCount { get; set; }
}
}
}

View File

@@ -45,6 +45,11 @@ namespace gaseous_server
DataRow row = data.Rows[0]; DataRow row = data.Rows[0];
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"])); LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
if (!Directory.Exists(library.Path))
{
Directory.CreateDirectory(library.Path);
}
return library; return library;
} }
} }
@@ -61,6 +66,15 @@ namespace gaseous_server
{ {
LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"])); LibraryItem library = new LibraryItem((int)row["Id"], (string)row["Name"], (string)row["Path"], (long)row["DefaultPlatform"], Convert.ToBoolean((int)row["DefaultLibrary"]));
libraryItems.Add(library); libraryItems.Add(library);
if (library.IsDefaultLibrary == true)
{
// check directory exists
if (!Directory.Exists(library.Path))
{
Directory.CreateDirectory(library.Path);
}
}
} }
return libraryItems; return libraryItems;
@@ -143,6 +157,11 @@ namespace gaseous_server
_Path = Path; _Path = Path;
_DefaultPlatformId = DefaultPlatformId; _DefaultPlatformId = DefaultPlatformId;
_IsDefaultLibrary = IsDefaultLibrary; _IsDefaultLibrary = IsDefaultLibrary;
if (!Directory.Exists(Path))
{
Directory.CreateDirectory(Path);
}
} }
int _Id = 0; int _Id = 0;

View File

@@ -755,11 +755,11 @@ namespace gaseous_server.Classes
string sql = ""; string sql = "";
if (ForceExecute == false) if (ForceExecute == false)
{ {
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 OR GameId = 0 OR MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;"; sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0) AND (LastMatchAttemptDate IS NULL OR LastMatchAttemptDate < @lastmatchattemptdate) LIMIT 100;";
} }
else else
{ {
sql = "SELECT * FROM Games_Roms WHERE (PlatformId = 0 OR GameId = 0 OR MetadataSource = 0);"; sql = "SELECT * FROM Games_Roms WHERE ((PlatformId = 0 OR GameId = 0) AND MetadataSource = 0);";
} }
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7)); dbDict.Add("lastmatchattemptdate", DateTime.UtcNow.AddDays(-7));

View File

@@ -7,6 +7,7 @@ namespace gaseous_server.Classes
{ {
public class Logging public class Logging
{ {
private static DateTime lastDiskRetentionSweep = DateTime.UtcNow;
public static bool WriteToDiskOnly { get; set; } = false; public static bool WriteToDiskOnly { get; set; } = false;
static public void Log(LogType EventType, string ServerProcess, string Message, Exception? ExceptionValue = null, bool LogToDiskOnly = false) static public void Log(LogType EventType, string ServerProcess, string Message, Exception? ExceptionValue = null, bool LogToDiskOnly = false)
@@ -69,6 +70,11 @@ namespace gaseous_server.Classes
if (LogToDiskOnly == false) if (LogToDiskOnly == false)
{ {
if (Config.LoggingConfiguration.AlwaysLogToDisk == true)
{
LogToDisk(logItem, TraceOutput, null);
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception) VALUES (@EventTime, @EventType, @Process, @Message, @Exception);"; string sql = "DELETE FROM ServerLogs WHERE EventTime < @EventRententionDate; INSERT INTO ServerLogs (EventTime, EventType, Process, Message, Exception) VALUES (@EventTime, @EventType, @Process, @Message, @Exception);";
Dictionary<string, object> dbDict = new Dictionary<string, object>(); Dictionary<string, object> dbDict = new Dictionary<string, object>();
@@ -93,6 +99,22 @@ namespace gaseous_server.Classes
LogToDisk(logItem, TraceOutput, null); LogToDisk(logItem, TraceOutput, null);
} }
} }
if (lastDiskRetentionSweep.AddMinutes(60) < DateTime.UtcNow)
{
// time to delete any old logs
lastDiskRetentionSweep = DateTime.UtcNow;
string[] files = Directory.GetFiles(Config.LogPath);
foreach (string file in files)
{
FileInfo fi = new FileInfo(file);
if (fi.LastAccessTime < DateTime.Now.AddDays(Config.LoggingConfiguration.LogRetention * -1))
{
fi.Delete();
}
}
}
} }
static void LogToDisk(LogItem logItem, string TraceOutput, Exception? exception) static void LogToDisk(LogItem logItem, string TraceOutput, Exception? exception)
@@ -110,22 +132,82 @@ namespace gaseous_server.Classes
File.AppendAllText(Config.LogFilePath, TraceOutput); File.AppendAllText(Config.LogFilePath, TraceOutput);
} }
static public List<LogItem> GetLogs(long? StartIndex, int PageNumber = 1, int PageSize = 100) static public List<LogItem> GetLogs(LogsViewModel model)
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", model.StartIndex);
dbDict.Add("PageNumber", (model.PageNumber - 1) * model.PageSize);
dbDict.Add("PageSize", model.PageSize);
string sql = ""; string sql = "";
if (StartIndex == null)
List<string> whereClauses = new List<string>();
// handle status criteria
if (model.Status != null)
{ {
sql = "SELECT * FROM ServerLogs ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;"; if (model.Status.Count > 0)
{
List<string> statusWhere = new List<string>();
for (int i = 0; i < model.Status.Count; i++)
{
string valueName = "@eventtype" + i;
statusWhere.Add(valueName);
dbDict.Add(valueName, (int)model.Status[i]);
}
whereClauses.Add("EventType IN (" + string.Join(",", statusWhere) + ")");
}
}
// handle start date criteria
if (model.StartDateTime != null)
{
dbDict.Add("startdate", model.StartDateTime);
whereClauses.Add("EventTime >= @startdate");
}
// handle end date criteria
if (model.EndDateTime != null)
{
dbDict.Add("enddate", model.EndDateTime);
whereClauses.Add("EventTime <= @enddate");
}
// handle search text criteria
if (model.SearchText != null)
{
if (model.SearchText.Length > 0)
{
dbDict.Add("messageSearch", model.SearchText);
whereClauses.Add("MATCH(Message) AGAINST (@messageSearch)");
}
}
// compile WHERE clause
string whereClause = "";
if (whereClauses.Count > 0)
{
whereClause = "(" + String.Join(" AND ", whereClauses) + ")";
}
// execute query
if (model.StartIndex == null)
{
if (whereClause.Length > 0)
{
whereClause = "WHERE " + whereClause;
}
sql = "SELECT * FROM ServerLogs " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
} }
else else
{ {
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;"; if (whereClause.Length > 0)
{
whereClause = "AND " + whereClause;
}
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex " + whereClause + " ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
} }
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", StartIndex);
dbDict.Add("PageNumber", (PageNumber - 1) * PageSize);
dbDict.Add("PageSize", PageSize);
DataTable dataTable = db.ExecuteCMD(sql, dbDict); DataTable dataTable = db.ExecuteCMD(sql, dbDict);
List<LogItem> logs = new List<LogItem>(); List<LogItem> logs = new List<LogItem>();
@@ -175,6 +257,17 @@ namespace gaseous_server.Classes
} }
public string? ExceptionValue { get; set; } public string? ExceptionValue { get; set; }
} }
public class LogsViewModel
{
public long? StartIndex { get; set; }
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 100;
public List<LogType> Status { get; set; } = new List<LogType>();
public DateTime? StartDateTime { get; set; }
public DateTime? EndDateTime { get; set; }
public string? SearchText { get; set; }
}
} }
} }

View File

@@ -60,7 +60,7 @@ namespace gaseous_server.Classes
{ {
retVal += responseRow.ItemArray[i] + "; "; retVal += responseRow.ItemArray[i] + "; ";
} }
Logging.Log(Logging.LogType.Information, "Maintenance", "Optimizse table " + row[0].ToString() + ": " + retVal); Logging.Log(Logging.LogType.Information, "Maintenance", "Optimise table " + row[0].ToString() + ": " + retVal);
} }
} }
} }

View File

@@ -3,6 +3,7 @@ using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.CodeAnalysis.Classification;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -153,6 +154,44 @@ namespace gaseous_server.Classes.Metadata
public string[] Descriptions { get; set; } public string[] Descriptions { get; set; }
} }
public static void PopulateAgeMap()
{
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "DELETE FROM ClassificationMap;";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
db.ExecuteNonQuery(sql);
// loop all age groups
foreach(KeyValuePair<AgeGroups.AgeRestrictionGroupings, AgeGroups.AgeGroupItem> ageGrouping in AgeGroups.AgeGroupingsFlat)
{
AgeGroups.AgeGroupItem ageGroupItem = ageGrouping.Value;
var properties = ageGroupItem.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
List<string> AgeRatingCategories = new List<string>(Enum.GetNames(typeof(AgeRatingCategory)));
if (AgeRatingCategories.Contains(prop.Name))
{
AgeRatingCategory ageRatingCategory = (AgeRatingCategory)Enum.Parse(typeof(AgeRatingCategory), prop.Name);
List<AgeRatingTitle> ageRatingTitles = (List<AgeRatingTitle>)prop.GetValue(ageGroupItem);
foreach (AgeRatingTitle ageRatingTitle in ageRatingTitles)
{
dbDict.Clear();
dbDict.Add("AgeGroupId", ageGrouping.Key);
dbDict.Add("ClassificationBoardId", ageRatingCategory);
dbDict.Add("RatingId", ageRatingTitle);
sql = "INSERT INTO ClassificationMap (AgeGroupId, ClassificationBoardId, RatingId) VALUES (@AgeGroupId, @ClassificationBoardId, @RatingId);";
db.ExecuteCMD(sql, dbDict);
}
}
}
}
}
}
public class AgeGroups public class AgeGroups
{ {
public AgeGroups() public AgeGroups()
@@ -160,93 +199,55 @@ namespace gaseous_server.Classes.Metadata
} }
public static Dictionary<string, List<AgeGroupItem>> AgeGroupings public static Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>> AgeGroupings
{ {
get get
{ {
return new Dictionary<string, List<AgeGroupItem>>{ return new Dictionary<AgeRestrictionGroupings, List<AgeGroupItem>>{
{ {
"Adult", new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item } AgeRestrictionGroupings.Adult, new List<AgeGroupItem>{ Adult_Item, Mature_Item, Teen_Item, Child_Item }
}, },
{ {
"Mature", new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item } AgeRestrictionGroupings.Mature, new List<AgeGroupItem>{ Mature_Item, Teen_Item, Child_Item }
}, },
{ {
"Teen", new List<AgeGroupItem>{ Teen_Item, Child_Item } AgeRestrictionGroupings.Teen, new List<AgeGroupItem>{ Teen_Item, Child_Item }
}, },
{ {
"Child", new List<AgeGroupItem>{ Child_Item } AgeRestrictionGroupings.Child, new List<AgeGroupItem>{ Child_Item }
} }
}; };
} }
} }
public static Dictionary<string, AgeGroupItem> AgeGroupingsFlat public static Dictionary<AgeRestrictionGroupings, AgeGroupItem> AgeGroupingsFlat
{ {
get get
{ {
return new Dictionary<string, AgeGroupItem>{ return new Dictionary<AgeRestrictionGroupings, AgeGroupItem>{
{ {
"Adult", Adult_Item AgeRestrictionGroupings.Adult, Adult_Item
}, },
{ {
"Mature", Mature_Item AgeRestrictionGroupings.Mature, Mature_Item
}, },
{ {
"Teen", Teen_Item AgeRestrictionGroupings.Teen, Teen_Item
}, },
{ {
"Child", Child_Item AgeRestrictionGroupings.Child, Child_Item
} }
}; };
} }
} }
public static List<ClassificationBoardItem> ClassificationBoards public enum AgeRestrictionGroupings
{ {
get Adult = 4,
{ Mature = 3,
ClassificationBoardItem boardItem = new ClassificationBoardItem{ Teen = 2,
Board = AgeRatingCategory.ACB, Child = 1,
Classifications = new List<AgeRatingTitle>{ Unclassified = 0
AgeRatingTitle.ACB_G, AgeRatingTitle.ACB_M, AgeRatingTitle.ACB_MA15, AgeRatingTitle.ACB_R18, AgeRatingTitle.ACB_RC
}
};
return new List<ClassificationBoardItem>{
new ClassificationBoardItem{
Board = AgeRatingCategory.ACB,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.ACB_G,
AgeRatingTitle.ACB_M,
AgeRatingTitle.ACB_MA15,
AgeRatingTitle.ACB_R18,
AgeRatingTitle.ACB_RC
}
},
new ClassificationBoardItem{
Board = AgeRatingCategory.CERO,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.CERO_A,
AgeRatingTitle.CERO_B,
AgeRatingTitle.CERO_C,
AgeRatingTitle.CERO_D,
AgeRatingTitle.CERO_Z
}
},
new ClassificationBoardItem{
Board = AgeRatingCategory.CLASS_IND,
Classifications = new List<AgeRatingTitle>{
AgeRatingTitle.CLASS_IND_L,
AgeRatingTitle.CLASS_IND_Ten,
AgeRatingTitle.CLASS_IND_Twelve,
AgeRatingTitle.CLASS_IND_Fourteen,
AgeRatingTitle.CLASS_IND_Sixteen,
AgeRatingTitle.CLASS_IND_Eighteen
}
}
};
}
} }
readonly static AgeGroupItem Adult_Item = new AgeGroupItem{ readonly static AgeGroupItem Adult_Item = new AgeGroupItem{
@@ -341,12 +342,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
} }
public class ClassificationBoardItem
{
public IGDB.Models.AgeRatingCategory Board { get; set; }
public List<IGDB.Models.AgeRatingTitle> Classifications { get; set; }
}
} }
} }
} }

View File

@@ -174,9 +174,6 @@ namespace gaseous_server.Classes.Metadata
} }
} }
// optional metadata - usually downloaded as needed
if (getAllMetadata == true)
{
if (Game.AgeRatings != null) if (Game.AgeRatings != null)
{ {
foreach (long AgeRatingId in Game.AgeRatings.Ids) foreach (long AgeRatingId in Game.AgeRatings.Ids)
@@ -185,6 +182,17 @@ namespace gaseous_server.Classes.Metadata
} }
} }
if (Game.ReleaseDates != null)
{
foreach (long ReleaseDateId in Game.ReleaseDates.Ids)
{
ReleaseDate GameReleaseDate = ReleaseDates.GetReleaseDates(ReleaseDateId);
}
}
// optional metadata - usually downloaded as needed
if (getAllMetadata == true)
{
if (Game.AlternativeNames != null) if (Game.AlternativeNames != null)
{ {
foreach (long AlternativeNameId in Game.AlternativeNames.Ids) foreach (long AlternativeNameId in Game.AlternativeNames.Ids)
@@ -334,5 +342,40 @@ namespace gaseous_server.Classes.Metadata
search = 2, search = 2,
searchNoPlatform = 3 searchNoPlatform = 3
} }
public class MinimalGameItem
{
public MinimalGameItem(Game gameObject)
{
this.Id = gameObject.Id;
this.Name = gameObject.Name;
this.TotalRating = gameObject.TotalRating;
this.TotalRatingCount = gameObject.TotalRatingCount;
this.Cover = gameObject.Cover;
this.Artworks = gameObject.Artworks;
// compile age ratings
this.AgeRatings = new List<AgeRating>();
if (gameObject.AgeRatings != null)
{
foreach (long ageRatingId in gameObject.AgeRatings.Ids)
{
AgeRating? rating = Classes.Metadata.AgeRatings.GetAgeRatings(ageRatingId);
if (rating != null)
{
this.AgeRatings.Add(rating);
}
}
}
}
public long? Id { get; set; }
public string Name { get; set; }
public double? TotalRating { get; set; }
public int? TotalRatingCount { get; set; }
public IGDB.IdentityOrValue<IGDB.Models.Cover> Cover { get; set; }
public IGDB.IdentitiesOrValues<IGDB.Models.Artwork> Artworks { get; set; }
public List<IGDB.Models.AgeRating> AgeRatings { get; set; }
}
} }
} }

View File

@@ -0,0 +1,113 @@
using System;
using IGDB;
using IGDB.Models;
namespace gaseous_server.Classes.Metadata
{
public class ReleaseDates
{
const string fieldList = "fields category,checksum,created_at,date,game,human,m,platform,region,status,updated_at,y;";
public ReleaseDates()
{
}
private static IGDBClient igdb = new IGDBClient(
// Found in Twitch Developer portal for your app
Config.IGDB.ClientId,
Config.IGDB.Secret
);
public static ReleaseDate? GetReleaseDates(long? Id)
{
if ((Id == 0) || (Id == null))
{
return null;
}
else
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.id, Id);
return RetVal.Result;
}
}
public static ReleaseDate GetReleaseDates(string Slug)
{
Task<ReleaseDate> RetVal = _GetReleaseDates(SearchUsing.slug, Slug);
return RetVal.Result;
}
private static async Task<ReleaseDate> _GetReleaseDates(SearchUsing searchUsing, object searchValue)
{
// check database first
Storage.CacheStatus? cacheStatus = new Storage.CacheStatus();
if (searchUsing == SearchUsing.id)
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (long)searchValue);
}
else
{
cacheStatus = Storage.GetCacheStatus("ReleaseDate", (string)searchValue);
}
// set up where clause
string WhereClause = "";
switch (searchUsing)
{
case SearchUsing.id:
WhereClause = "where id = " + searchValue;
break;
case SearchUsing.slug:
WhereClause = "where slug = " + searchValue;
break;
default:
throw new Exception("Invalid search type");
}
ReleaseDate returnValue = new ReleaseDate();
switch (cacheStatus)
{
case Storage.CacheStatus.NotPresent:
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue);
break;
case Storage.CacheStatus.Expired:
try
{
returnValue = await GetObjectFromServer(WhereClause);
Storage.NewCacheValue(returnValue, true);
}
catch (Exception ex)
{
Logging.Log(Logging.LogType.Warning, "Metadata: " + returnValue.GetType().Name, "An error occurred while connecting to IGDB. WhereClause: " + WhereClause, ex);
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
}
break;
case Storage.CacheStatus.Current:
returnValue = Storage.GetCacheValue<ReleaseDate>(returnValue, "id", (long)searchValue);
break;
default:
throw new Exception("How did you get here?");
}
return returnValue;
}
private enum SearchUsing
{
id,
slug
}
private static async Task<ReleaseDate> GetObjectFromServer(string WhereClause)
{
// get ReleaseDates metadata
var results = await igdb.QueryAsync<ReleaseDate>(IGDBClient.Endpoints.ReleaseDates, query: fieldList + " " + WhereClause + ";");
var result = results.First();
return result;
}
}
}

View File

@@ -218,9 +218,19 @@ namespace gaseous_server.Classes.Metadata
{ {
DataRow dataRow = dt.Rows[0]; DataRow dataRow = dt.Rows[0];
object returnObject = BuildCacheObject<T>(EndpointType, dataRow); object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
try {
if (!ObjectCache.ContainsKey(Endpoint + SearchValue))
{
ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{ ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{
Object = returnObject Object = returnObject
}); });
}
}
catch
{
// unable add item to cache
ObjectCache.Clear();
}
return (T)returnObject; return (T)returnObject;
} }
} }

View File

@@ -30,7 +30,16 @@ namespace gaseous_server.Classes
} }
// update games // update games
if (forceRefresh == true)
{
// when forced, only update games with ROMs for
sql = "SELECT Id, `Name` FROM view_GamesWithRoms;";
}
else
{
// when run normally, update all games (since this will honour cache timeouts)
sql = "SELECT Id, `Name` FROM Game;"; sql = "SELECT Id, `Name` FROM Game;";
}
dt = db.ExecuteCMD(sql); dt = db.ExecuteCMD(sql);
foreach (DataRow dr in dt.Rows) foreach (DataRow dr in dt.Rows)
@@ -38,7 +47,7 @@ namespace gaseous_server.Classes
try try
{ {
Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")"); Logging.Log(Logging.LogType.Information, "Metadata Refresh", "Refreshing metadata for game " + dr["name"] + " (" + dr["id"] + ")");
Metadata.Games.GetGame((long)dr["id"], true, true, forceRefresh); Metadata.Games.GetGame((long)dr["id"], true, false, forceRefresh);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,3 +1,4 @@
using System.Data;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using Authentication; using Authentication;
@@ -95,6 +96,7 @@ namespace gaseous_server.Controllers
profile.EmailAddress = await _userManager.GetEmailAsync(user); profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user)); profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile; profile.SecurityProfile = user.SecurityProfile;
profile.UserPreferences = user.UserPreferences;
profile.Roles.Sort(); profile.Roles.Sort();
return Ok(profile); return Ok(profile);
@@ -115,6 +117,7 @@ namespace gaseous_server.Controllers
profile.EmailAddress = await _userManager.GetEmailAsync(user); profile.EmailAddress = await _userManager.GetEmailAsync(user);
profile.Roles = new List<string>(await _userManager.GetRolesAsync(user)); profile.Roles = new List<string>(await _userManager.GetRolesAsync(user));
profile.SecurityProfile = user.SecurityProfile; profile.SecurityProfile = user.SecurityProfile;
profile.UserPreferences = user.UserPreferences;
profile.Roles.Sort(); profile.Roles.Sort();
string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";"; string profileString = "var userProfile = " + Newtonsoft.Json.JsonConvert.SerializeObject(profile, Newtonsoft.Json.Formatting.Indented) + ";";
@@ -375,7 +378,7 @@ namespace gaseous_server.Controllers
IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, resetToken, model.NewPassword); IdentityResult passwordChangeResult = await _userManager.ResetPasswordAsync(user, resetToken, model.NewPassword);
if (passwordChangeResult.Succeeded == true) if (passwordChangeResult.Succeeded == true)
{ {
return Ok(); return Ok(passwordChangeResult);
} }
else else
{ {
@@ -392,5 +395,23 @@ namespace gaseous_server.Controllers
return NotFound(); return NotFound();
} }
} }
[HttpPost]
[Route("Preferences")]
public async Task<IActionResult> SetPreferences(List<UserPreferenceViewModel> model)
{
ApplicationUser? user = await _userManager.GetUserAsync(User);
if (user == null)
{
return Unauthorized();
}
else
{
user.UserPreferences = model;
await _userManager.UpdateAsync(user);
return Ok();
}
}
} }
} }

View File

@@ -3,10 +3,12 @@ using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Authentication;
using gaseous_server.Classes; using gaseous_server.Classes;
using IGDB.Models; using IGDB.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
@@ -18,96 +20,27 @@ namespace gaseous_server.Controllers
[ApiController] [ApiController]
public class FilterController : ControllerBase public class FilterController : ControllerBase
{ {
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
public FilterController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager)
{
_userManager = userManager;
_signInManager = signInManager;
}
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
//[ResponseCache(CacheProfileName = "5Minute")] //[ResponseCache(CacheProfileName = "5Minute")]
public Dictionary<string, object> Filter() public async Task<IActionResult> FilterAsync()
{ {
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); var user = await _userManager.GetUserAsync(User);
Dictionary<string, object> FilterSet = new Dictionary<string, object>(); return Ok(Filters.Filter(user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction, user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated));
// platforms
List<FilterPlatform> platforms = new List<FilterPlatform>();
//string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`";
string sql = "SELECT Platform.Id, Platform.Abbreviation, Platform.AlternativeName, Platform.`Name`, Platform.PlatformLogo, (SELECT COUNT(Games_Roms.Id) AS RomCount FROM Games_Roms WHERE Games_Roms.PlatformId = Platform.Id) AS RomCount, (SELECT COUNT(*) AS GameCount FROM (SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Games_Roms.PlatformId FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId) Game WHERE Game.PlatformId = Platform.Id) AS GameCount FROM Platform HAVING RomCount > 0 ORDER BY `Name`";
DataTable dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
FilterPlatform platformItem = new FilterPlatform(Classes.Metadata.Platforms.GetPlatform((long)dr["id"]));
platformItem.RomCount = (int)(long)dr["RomCount"];
platformItem.GameCount = (int)(long)dr["GameCount"];
platforms.Add(platformItem);
}
FilterSet.Add("platforms", platforms);
// genres
List<Genre> genres = new List<Genre>();
sql = "SELECT DISTINCT Relation_Game_Genres.GenresId AS id, Genre.`Name` FROM Relation_Game_Genres JOIN Genre ON Relation_Game_Genres.GenresId = Genre.Id WHERE Relation_Game_Genres.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
genres.Add(Classes.Metadata.Genres.GetGenres((long)dr["id"]));
}
FilterSet.Add("genres", genres);
// game modes
List<GameMode> gameModes = new List<GameMode>();
sql = "SELECT DISTINCT Relation_Game_GameModes.GameModesId AS id, GameMode.`Name` FROM Relation_Game_GameModes JOIN GameMode ON Relation_Game_GameModes.GameModesId = GameMode.Id WHERE Relation_Game_GameModes.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
gameModes.Add(Classes.Metadata.GameModes.GetGame_Modes((long)dr["id"]));
}
FilterSet.Add("gamemodes", gameModes);
// player perspectives
List<PlayerPerspective> playerPerspectives = new List<PlayerPerspective>();
sql = "SELECT DISTINCT Relation_Game_PlayerPerspectives.PlayerPerspectivesId AS id, PlayerPerspective.`Name` FROM Relation_Game_PlayerPerspectives JOIN PlayerPerspective ON Relation_Game_PlayerPerspectives.PlayerPerspectivesId = PlayerPerspective.Id WHERE Relation_Game_PlayerPerspectives.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
playerPerspectives.Add(Classes.Metadata.PlayerPerspectives.GetGame_PlayerPerspectives((long)dr["id"]));
}
FilterSet.Add("playerperspectives", playerPerspectives);
// themes
List<Theme> themes = new List<Theme>();
sql = "SELECT DISTINCT Relation_Game_Themes.ThemesId AS id, Theme.`Name` FROM Relation_Game_Themes JOIN Theme ON Relation_Game_Themes.ThemesId = Theme.Id WHERE Relation_Game_Themes.GameId IN (SELECT DISTINCT GameId FROM Games_Roms) ORDER BY `Name`;";
dbResponse = db.ExecuteCMD(sql);
foreach (DataRow dr in dbResponse.Rows)
{
themes.Add(Classes.Metadata.Themes.GetGame_Themes((long)dr["id"]));
}
FilterSet.Add("themes", themes);
return FilterSet;
}
public class FilterPlatform : IGDB.Models.Platform
{
public FilterPlatform(Platform platform)
{
var properties = platform.GetType().GetProperties();
foreach (var prop in properties)
{
if (prop.GetGetMethod() != null)
{
this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(platform));
}
}
}
public int RomCount { get; set; }
public int GameCount { get; set; }
} }
} }
} }

View File

@@ -181,9 +181,10 @@ namespace gaseous_server.Controllers
List<long> AgeClassificationsList = new List<long>(); List<long> AgeClassificationsList = new List<long>();
foreach (string ratingGroup in ratinggroup.Split(',')) foreach (string ratingGroup in ratinggroup.Split(','))
{ {
if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup)) AgeGroups.AgeRestrictionGroupings ageRestriction = (AgeGroups.AgeRestrictionGroupings)Enum.Parse(typeof(AgeGroups.AgeRestrictionGroupings), ratingGroup);
if (AgeGroups.AgeGroupings.ContainsKey(ageRestriction))
{ {
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ratingGroup]; List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ageRestriction];
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups) foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{ {
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues); AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
@@ -824,6 +825,44 @@ namespace gaseous_server.Controllers
} }
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/releasedates")]
[ProducesResponseType(typeof(List<ReleaseDate>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ResponseCache(CacheProfileName = "7Days")]
public ActionResult GameReleaseDates(long GameId)
{
try
{
IGDB.Models.Game gameObject = Classes.Metadata.Games.GetGame(GameId, false, false, false);
if (gameObject != null)
{
List<ReleaseDate> rdObjects = new List<ReleaseDate>();
if (gameObject.ReleaseDates != null)
{
foreach (long icId in gameObject.ReleaseDates.Ids)
{
ReleaseDate releaseDate = Classes.Metadata.ReleaseDates.GetReleaseDates(icId);
rdObjects.Add(releaseDate);
}
}
return Ok(rdObjects);
}
else
{
return NotFound();
}
}
catch
{
return NotFound();
}
}
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpGet]

View File

@@ -17,11 +17,11 @@ namespace gaseous_server.Controllers
{ {
[MapToApiVersion("1.0")] [MapToApiVersion("1.0")]
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpGet] [HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100) public List<Logging.LogItem> Logs(Logging.LogsViewModel model)
{ {
return Logging.GetLogs(StartIndex, PageNumber, PageSize); return Logging.GetLogs(model);
} }
} }
} }

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -10,6 +11,7 @@ using gaseous_server.Classes;
using gaseous_server.Classes.Metadata; using gaseous_server.Classes.Metadata;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Razor.Hosting;
namespace gaseous_server.Controllers namespace gaseous_server.Controllers
{ {
@@ -80,6 +82,12 @@ namespace gaseous_server.Controllers
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
// get age ratings dictionary // get age ratings dictionary
Dictionary<int, string> ClassificationBoardsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingCategory ageRatingCategory in Enum.GetValues(typeof(IGDB.Models.AgeRatingCategory)) )
{
ClassificationBoardsStrings.Add((int)ageRatingCategory, ageRatingCategory.ToString());
}
Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>(); Dictionary<int, string> AgeRatingsStrings = new Dictionary<int, string>();
foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) ) foreach(IGDB.Models.AgeRatingTitle ageRatingTitle in Enum.GetValues(typeof(IGDB.Models.AgeRatingTitle)) )
{ {
@@ -89,6 +97,9 @@ namespace gaseous_server.Controllers
string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine + string ver = "var AppVersion = \"" + Assembly.GetExecutingAssembly().GetName().Version.ToString() + "\";" + Environment.NewLine +
"var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine + "var DBSchemaVersion = \"" + db.GetDatabaseSchemaVersion() + "\";" + Environment.NewLine +
"var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine + "var FirstRunStatus = " + Config.ReadSetting("FirstRunStatus", "0") + ";" + Environment.NewLine +
"var AgeRatingBoardsStrings = " + JsonSerializer.Serialize(ClassificationBoardsStrings, new JsonSerializerOptions{
WriteIndented = true
}) + ";" + Environment.NewLine +
"var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{ "var AgeRatingStrings = " + JsonSerializer.Serialize(AgeRatingsStrings, new JsonSerializerOptions{
WriteIndented = true WriteIndented = true
}) + ";" + Environment.NewLine + }) + ";" + Environment.NewLine +
@@ -99,6 +110,109 @@ namespace gaseous_server.Controllers
return File(bytes, "text/javascript"); return File(bytes, "text/javascript");
} }
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Settings/BackgroundTasks/Intervals")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetBackgroundTasks()
{
Dictionary<string, BackgroundTaskItem> Intervals = new Dictionary<string, BackgroundTaskItem>();
Intervals.Add(ProcessQueue.QueueItemType.SignatureIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.SignatureIngestor));
Intervals.Add(ProcessQueue.QueueItemType.TitleIngestor.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.TitleIngestor));
Intervals.Add(ProcessQueue.QueueItemType.MetadataRefresh.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.MetadataRefresh));
Intervals.Add(ProcessQueue.QueueItemType.OrganiseLibrary.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.OrganiseLibrary));
Intervals.Add(ProcessQueue.QueueItemType.LibraryScan.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.LibraryScan));
Intervals.Add(ProcessQueue.QueueItemType.Rematcher.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Rematcher));
Intervals.Add(ProcessQueue.QueueItemType.Maintainer.ToString(), new BackgroundTaskItem(ProcessQueue.QueueItemType.Maintainer));
return Ok(Intervals);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Settings/BackgroundTasks/Intervals")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult SetBackgroundTasks(Dictionary<string, int> Intervals)
{
foreach (KeyValuePair<string, int> Interval in Intervals)
{
if (Enum.IsDefined(typeof(ProcessQueue.QueueItemType), Interval.Key))
{
try
{
BackgroundTaskItem taskItem = new BackgroundTaskItem(
(ProcessQueue.QueueItemType)Enum.Parse(typeof(ProcessQueue.QueueItemType), Interval.Key)
);
if (Interval.Value >= taskItem.MinimumAllowedValue)
{
Logging.Log(Logging.LogType.Information, "Update Background Task", "Updating task " + Interval.Key + " with new interval " + Interval.Value);
Config.SetSetting("Interval_" + Interval.Key, Interval.Value.ToString());
// update existing process
foreach (ProcessQueue.QueueItem item in ProcessQueue.QueueItems)
{
if (item.ItemType.ToString().ToLower() == Interval.Key.ToLower())
{
item.Interval = Interval.Value;
}
}
}
else
{
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Interval " + Interval.Value + " for task " + Interval.Key + " is below the minimum allowed value of " + taskItem.MinimumAllowedValue + ". Skipping.");
}
}
catch
{
// task name not defined
Logging.Log(Logging.LogType.Warning, "Update Background Task", "Task " + Interval.Key + " is not user definable. Skipping.");
}
}
}
return Ok();
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpGet]
[Route("Settings/System")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult GetSystemSettings()
{
SystemSettingsModel systemSettingsModel = new SystemSettingsModel{
AlwaysLogToDisk = Config.LoggingConfiguration.AlwaysLogToDisk,
MinimumLogRetentionPeriod = Config.LoggingConfiguration.LogRetention
};
return Ok(systemSettingsModel);
}
[MapToApiVersion("1.0")]
[MapToApiVersion("1.1")]
[HttpPost]
[Route("Settings/System")]
[Authorize(Roles = "Admin")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult SetSystemSettings(SystemSettingsModel model)
{
if (ModelState.IsValid)
{
Config.LoggingConfiguration.AlwaysLogToDisk = model.AlwaysLogToDisk;
Config.LoggingConfiguration.LogRetention = model.MinimumLogRetentionPeriod;
Config.UpdateConfig();
}
return Ok(model);
}
private SystemInfo.PathItem GetDisk(string Path) private SystemInfo.PathItem GetDisk(string Path)
{ {
SystemInfo.PathItem pathItem = new SystemInfo.PathItem { SystemInfo.PathItem pathItem = new SystemInfo.PathItem {
@@ -139,4 +253,69 @@ namespace gaseous_server.Controllers
} }
} }
} }
public class BackgroundTaskItem
{
public BackgroundTaskItem(ProcessQueue.QueueItemType TaskName)
{
this.Task = TaskName.ToString();
switch (TaskName)
{
case ProcessQueue.QueueItemType.SignatureIngestor:
this.DefaultInterval = 60;
this.MinimumAllowedValue = 20;
break;
case ProcessQueue.QueueItemType.TitleIngestor:
this.DefaultInterval = 1;
this.MinimumAllowedValue = 1;
break;
case ProcessQueue.QueueItemType.MetadataRefresh:
this.DefaultInterval = 360;
this.MinimumAllowedValue = 360;
break;
case ProcessQueue.QueueItemType.OrganiseLibrary:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 120;
break;
case ProcessQueue.QueueItemType.LibraryScan:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 120;
break;
case ProcessQueue.QueueItemType.Rematcher:
this.DefaultInterval = 1440;
this.MinimumAllowedValue = 360;
break;
case ProcessQueue.QueueItemType.Maintainer:
this.DefaultInterval = 10080;
this.MinimumAllowedValue = 10080;
break;
default:
throw new Exception("Invalid task");
}
}
public string Task { get; set; }
public int Interval {
get
{
return int.Parse(Config.ReadSetting("Interval_" + Task, DefaultInterval.ToString()));
}
}
public int DefaultInterval { get; set; }
public int MinimumAllowedValue { get; set; }
}
public class SystemSettingsModel
{
public bool AlwaysLogToDisk { get; set; }
public int MinimumLogRetentionPeriod { get; set; }
}
} }

View File

@@ -63,7 +63,11 @@ namespace gaseous_server.Controllers
Config.SetSetting("FirstRunStatus", "1"); Config.SetSetting("FirstRunStatus", "1");
return Ok(); return Ok(result);
}
else
{
return Ok(result);
} }
} }

View File

@@ -39,33 +39,41 @@ namespace gaseous_server.Controllers.v1_1
[MapToApiVersion("1.1")] [MapToApiVersion("1.1")]
[HttpPost] [HttpPost]
[ProducesResponseType(typeof(List<Game>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> Game_v1_1(GameSearchModel model) public async Task<IActionResult> Game_v1_1(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
{ {
var user = await _userManager.GetUserAsync(User); var user = await _userManager.GetUserAsync(User);
if (user != null) if (user != null)
{ {
// apply security profile filtering // apply security profile filtering
List<string> RemoveAgeGroups = new List<string>(); if (model.GameAgeRating.AgeGroupings.Count == 0)
switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction.ToLower())
{ {
case "adult": model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Adult);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Mature);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Teen);
model.GameAgeRating.AgeGroupings.Add(AgeGroups.AgeRestrictionGroupings.Child);
model.GameAgeRating.IncludeUnrated = true;
}
List<AgeGroups.AgeRestrictionGroupings> RemoveAgeGroups = new List<AgeGroups.AgeRestrictionGroupings>();
switch (user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction)
{
case AgeGroups.AgeRestrictionGroupings.Adult:
break; break;
case "mature": case AgeGroups.AgeRestrictionGroupings.Mature:
RemoveAgeGroups.Add("Adult"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
break; break;
case "teen": case AgeGroups.AgeRestrictionGroupings.Teen:
RemoveAgeGroups.Add("Adult"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
RemoveAgeGroups.Add("Mature"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature);
break; break;
case "child": case AgeGroups.AgeRestrictionGroupings.Child:
RemoveAgeGroups.Add("Adult"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Adult);
RemoveAgeGroups.Add("Mature"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Mature);
RemoveAgeGroups.Add("Teen"); RemoveAgeGroups.Add(AgeGroups.AgeRestrictionGroupings.Teen);
break; break;
} }
foreach (string RemoveAgeGroup in RemoveAgeGroups) foreach (AgeGroups.AgeRestrictionGroupings RemoveAgeGroup in RemoveAgeGroups)
{ {
if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup)) if (model.GameAgeRating.AgeGroupings.Contains(RemoveAgeGroup))
{ {
@@ -77,7 +85,47 @@ namespace gaseous_server.Controllers.v1_1
model.GameAgeRating.IncludeUnrated = false; model.GameAgeRating.IncludeUnrated = false;
} }
return Ok(GetGames(model)); return Ok(GetGames(model, pageNumber, pageSize));
}
else
{
return Unauthorized();
}
}
[MapToApiVersion("1.1")]
[HttpGet]
[Route("{GameId}/Related")]
[ProducesResponseType(typeof(GameReturnPackage), StatusCodes.Status200OK)]
public async Task<IActionResult> GameRelated(long GameId)
{
var user = await _userManager.GetUserAsync(User);
if (user != null)
{
string IncludeUnrated = "";
if (user.SecurityProfile.AgeRestrictionPolicy.IncludeUnrated == true) {
IncludeUnrated = " OR view_Games.AgeGroupId IS NULL";
}
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT view_Games.Id, view_Games.AgeGroupId, Relation_Game_SimilarGames.SimilarGamesId FROM view_Games JOIN Relation_Game_SimilarGames ON view_Games.Id = Relation_Game_SimilarGames.GameId AND Relation_Game_SimilarGames.SimilarGamesId IN (SELECT Id FROM view_Games) WHERE view_Games.Id = @id AND (view_Games.AgeGroupId <= @agegroupid" + IncludeUnrated + ")";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("id", GameId);
dbDict.Add("agegroupid", (int)user.SecurityProfile.AgeRestrictionPolicy.MaximumAgeRestriction);
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, dbDict);
foreach (DataRow dr in dbResponse.Rows)
{
RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["SimilarGamesId"], false, false, false));
}
GameReturnPackage gameReturn = new GameReturnPackage(RetVal.Count, RetVal);
return Ok(gameReturn);
} }
else else
{ {
@@ -109,7 +157,12 @@ namespace gaseous_server.Controllers.v1_1
public class GameAgeRatingItem public class GameAgeRatingItem
{ {
public List<string> AgeGroupings { get; set; } = new List<string>{ "Child", "Teen", "Mature", "Adult" }; public List<AgeGroups.AgeRestrictionGroupings> AgeGroupings { get; set; } = new List<AgeGroups.AgeRestrictionGroupings>{
AgeGroups.AgeRestrictionGroupings.Child,
AgeGroups.AgeRestrictionGroupings.Teen,
AgeGroups.AgeRestrictionGroupings.Mature,
AgeGroups.AgeRestrictionGroupings.Adult
};
public bool IncludeUnrated { get; set; } = true; public bool IncludeUnrated { get; set; } = true;
} }
@@ -128,7 +181,7 @@ namespace gaseous_server.Controllers.v1_1
} }
} }
public static List<Game> GetGames(GameSearchModel model) public static GameReturnPackage GetGames(GameSearchModel model, int pageNumber = 0, int pageSize = 0)
{ {
string whereClause = ""; string whereClause = "";
string havingClause = ""; string havingClause = "";
@@ -191,16 +244,23 @@ namespace gaseous_server.Controllers.v1_1
} }
} }
string unratedClause = "totalRating IS NOT NULL"; string unratedClause = "";
if (model.GameRating.IncludeUnrated == true) if (model.GameRating.IncludeUnrated == true)
{ {
unratedClause = "totalRating IS NULL"; unratedClause = "totalRating IS NULL";
} }
if (ratingClauseValue.Length > 0) if (ratingClauseValue.Length > 0)
{
if (unratedClause.Length > 0)
{ {
havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")"); havingClauses.Add("((" + ratingClauseValue + ") OR " + unratedClause + ")");
} }
else
{
havingClauses.Add("(" + ratingClauseValue + ")");
}
}
} }
if (model.Platform.Count > 0) if (model.Platform.Count > 0)
@@ -292,24 +352,8 @@ namespace gaseous_server.Controllers.v1_1
{ {
if (model.GameAgeRating.AgeGroupings.Count > 0) if (model.GameAgeRating.AgeGroupings.Count > 0)
{ {
List<long> AgeClassificationsList = new List<long>(); tempVal = "(AgeGroupId IN (";
foreach (string ratingGroup in model.GameAgeRating.AgeGroupings) for (int i = 0; i < model.GameAgeRating.AgeGroupings.Count; i++)
{
if (AgeGroups.AgeGroupings.ContainsKey(ratingGroup))
{
List<AgeGroups.AgeGroupItem> ageGroups = AgeGroups.AgeGroupings[ratingGroup];
foreach (AgeGroups.AgeGroupItem ageGroup in ageGroups)
{
AgeClassificationsList.AddRange(ageGroup.AgeGroupItemValues);
}
}
}
if (AgeClassificationsList.Count > 0)
{
AgeClassificationsList = new HashSet<long>(AgeClassificationsList).ToList();
tempVal = "(view_AgeRatings.Rating IN (";
for (int i = 0; i < AgeClassificationsList.Count; i++)
{ {
if (i > 0) if (i > 0)
{ {
@@ -317,20 +361,19 @@ namespace gaseous_server.Controllers.v1_1
} }
string themeLabel = "@Rating" + i; string themeLabel = "@Rating" + i;
tempVal += themeLabel; tempVal += themeLabel;
whereParams.Add(themeLabel, AgeClassificationsList[i]); whereParams.Add(themeLabel, model.GameAgeRating.AgeGroupings[i]);
} }
tempVal += ")"; tempVal += ")";
if (model.GameAgeRating.IncludeUnrated == true) if (model.GameAgeRating.IncludeUnrated == true)
{ {
tempVal += " OR view_AgeRatings.Rating IS NULL"; tempVal += " OR AgeGroupId IS NULL";
} }
tempVal += ")"; tempVal += ")";
whereClauses.Add(tempVal); whereClauses.Add(tempVal);
} }
} }
}
// build where clause // build where clause
if (whereClauses.Count > 0) if (whereClauses.Count > 0)
@@ -396,18 +439,66 @@ namespace gaseous_server.Controllers.v1_1
string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder; string orderByClause = "ORDER BY `" + orderByField + "` " + orderByOrder;
Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT DISTINCT Games_Roms.GameId AS ROMGameId, Game.*, case when Game.`Name` like 'The %' then CONCAT(trim(substr(Game.`Name` from 4)), ', The') else Game.`Name` end as NameThe FROM Games_Roms LEFT JOIN Game ON Game.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON Game.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON Game.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON Game.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON Game.Id = Relation_Game_Themes.GameId LEFT JOIN (SELECT Relation_Game_AgeRatings.GameId, AgeRating.* FROM Relation_Game_AgeRatings JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id) view_AgeRatings ON Game.Id = view_AgeRatings.GameId " + whereClause + " " + havingClause + " " + orderByClause; string sql = "SELECT DISTINCT view_Games.* FROM view_Games LEFT JOIN Games_Roms ON view_Games.Id = Games_Roms.GameId LEFT JOIN Relation_Game_Genres ON view_Games.Id = Relation_Game_Genres.GameId LEFT JOIN Relation_Game_GameModes ON view_Games.Id = Relation_Game_GameModes.GameId LEFT JOIN Relation_Game_PlayerPerspectives ON view_Games.Id = Relation_Game_PlayerPerspectives.GameId LEFT JOIN Relation_Game_Themes ON view_Games.Id = Relation_Game_Themes.GameId " + whereClause + " " + havingClause + " " + orderByClause;
List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>(); List<IGDB.Models.Game> RetVal = new List<IGDB.Models.Game>();
DataTable dbResponse = db.ExecuteCMD(sql, whereParams); DataTable dbResponse = db.ExecuteCMD(sql, whereParams);
foreach (DataRow dr in dbResponse.Rows)
// get count
int RecordCount = dbResponse.Rows.Count;
// compile data for return
int pageOffset = pageSize * (pageNumber - 1);
for (int i = 0; i < dbResponse.Rows.Count; i++)
{ {
//RetVal.Add(Classes.Metadata.Games.GetGame((long)dr["ROMGameId"], false, false)); DataRow dr = dbResponse.Rows[i];
RetVal.Add(Classes.Metadata.Games.GetGame(dr));
bool includeGame = false;
if (pageSize == 0)
{
// page size is full size include all
includeGame = true;
}
else if (i >= pageOffset && i < (pageOffset + pageSize))
{
includeGame = true;
} }
return RetVal; if (includeGame == true)
{
RetVal.Add(Classes.Metadata.Games.GetGame(dr));
}
}
GameReturnPackage gameReturn = new GameReturnPackage(RecordCount, RetVal);
return gameReturn;
}
public class GameReturnPackage
{
public GameReturnPackage()
{
}
public GameReturnPackage(int Count, List<Game> Games)
{
this.Count = Count;
List<Games.MinimalGameItem> minimalGames = new List<Games.MinimalGameItem>();
foreach (Game game in Games)
{
minimalGames.Add(new Classes.Metadata.Games.MinimalGameItem(game));
}
this.Games = minimalGames;
}
public int Count { get; set; }
public List<Games.MinimalGameItem> Games { get; set; } = new List<Games.MinimalGameItem>();
} }
} }
} }

View File

@@ -67,7 +67,17 @@ namespace gaseous_server
return LastRunTime.AddMinutes(Interval); return LastRunTime.AddMinutes(Interval);
} }
} }
public int Interval => _Interval; public int Interval
{
get
{
return _Interval;
}
set
{
_Interval = value;
}
}
public string LastResult => _LastResult; public string LastResult => _LastResult;
public string? LastError => _LastError; public string? LastError => _LastError;
public bool Force => _ForceExecute; public bool Force => _ForceExecute;
@@ -124,7 +134,7 @@ namespace gaseous_server
case QueueItemType.MetadataRefresh: case QueueItemType.MetadataRefresh:
Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher"); Logging.Log(Logging.LogType.Debug, "Timered Event", "Starting Metadata Refresher");
Classes.MetadataManagement.RefreshMetadata(); Classes.MetadataManagement.RefreshMetadata(_ForceExecute);
_SaveLastRunTime = true; _SaveLastRunTime = true;

View File

@@ -13,6 +13,8 @@ using Microsoft.OpenApi.Models;
using Authentication; using Authentication;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Identity.UI.Services;
using IGDB.Models;
using gaseous_server.Classes.Metadata;
Logging.WriteToDiskOnly = true; Logging.WriteToDiskOnly = true;
Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version); Logging.Log(Logging.LogType.Information, "Startup", "Starting Gaseous Server " + Assembly.GetExecutingAssembly().GetName().Version);
@@ -39,6 +41,9 @@ db = new Database(Database.databaseType.MySql, Config.DatabaseConfiguration.Conn
// set up db // set up db
db.InitDB(); db.InitDB();
// populate db with static data for lookups
AgeRatings.PopulateAgeMap();
// load app settings // load app settings
Config.InitSettings(); Config.InitSettings();
// write updated settings back to the config file // write updated settings back to the config file
@@ -397,15 +402,18 @@ gaseous_server.Classes.Metadata.Platforms.GetPlatform(0);
// extract platform map if not present // extract platform map if not present
PlatformMapping.ExtractPlatformMap(); PlatformMapping.ExtractPlatformMap();
// force load platform map into cache
var platformMap = PlatformMapping.PlatformMap;
// add background tasks // add background tasks
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.SignatureIngestor, ProcessQueue.QueueItemType.SignatureIngestor,
60 int.Parse(Config.ReadSetting("Interval_SignatureIngestor", "60"))
) )
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.TitleIngestor, ProcessQueue.QueueItemType.TitleIngestor,
1, int.Parse(Config.ReadSetting("Interval_TitleIngestor", "1")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
@@ -414,12 +422,12 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.MetadataRefresh, ProcessQueue.QueueItemType.MetadataRefresh,
360 int.Parse(Config.ReadSetting("Interval_MetadataRefresh", "360"))
) )
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
1440, int.Parse(Config.ReadSetting("Interval_OrganiseLibrary", "1440")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.LibraryScan, ProcessQueue.QueueItemType.LibraryScan,
@@ -429,7 +437,7 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScan, ProcessQueue.QueueItemType.LibraryScan,
60, int.Parse(Config.ReadSetting("Interval_LibraryScan", "1440")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
@@ -438,7 +446,7 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Rematcher, ProcessQueue.QueueItemType.Rematcher,
1440, int.Parse(Config.ReadSetting("Interval_Rematcher", "1440")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.OrganiseLibrary, ProcessQueue.QueueItemType.OrganiseLibrary,
@@ -447,7 +455,7 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.Maintainer, ProcessQueue.QueueItemType.Maintainer,
10080, int.Parse(Config.ReadSetting("Interval_Maintainer", "10080")),
new List<ProcessQueue.QueueItemType> new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.All ProcessQueue.QueueItemType.All

View File

@@ -0,0 +1,2 @@
ALTER TABLE `Signatures_Roms`
ADD INDEX `name_Idx` USING BTREE (`Name`) VISIBLE;

View File

@@ -0,0 +1,76 @@
CREATE TABLE `ClassificationMap` (
`AgeGroupId` INT NOT NULL,
`ClassificationBoardId` INT NOT NULL,
`RatingId` INT NOT NULL,
PRIMARY KEY (`AgeGroupId`, `ClassificationBoardId`, `RatingId`));
CREATE OR REPLACE VIEW `view_GamesWithRoms` AS
SELECT DISTINCT
Games_Roms.GameId AS ROMGameId,
Game.*,
CASE
WHEN
Game.`Name` LIKE 'The %'
THEN
CONCAT(TRIM(SUBSTR(Game.`Name` FROM 4)),
', The')
ELSE Game.`Name`
END AS NameThe
FROM
Games_Roms
LEFT JOIN
Game ON Game.Id = Games_Roms.GameId;
CREATE OR REPLACE VIEW `view_Games` AS
SELECT
*
FROM
(SELECT DISTINCT
row_number() over (partition by Id order by AgeGroupId desc) as seqnum, view_GamesWithRoms.*,
(SELECT
AgeGroupId
FROM
ClassificationMap
WHERE
RatingId = AgeRating.Rating
ORDER BY AgeGroupId DESC) AgeGroupId
FROM
view_GamesWithRoms
LEFT JOIN Relation_Game_AgeRatings ON view_GamesWithRoms.Id = Relation_Game_AgeRatings.GameId
LEFT JOIN AgeRating ON Relation_Game_AgeRatings.AgeRatingsId = AgeRating.Id
) g
WHERE g.seqnum = 1;
CREATE TABLE `ReleaseDate` (
`Id` BIGINT NOT NULL,
`Category` INT(11) NULL DEFAULT NULL,
`Checksum` VARCHAR(45) NULL DEFAULT NULL,
`CreatedAt` DATETIME NULL DEFAULT NULL,
`Date` DATETIME NULL,
`Game` BIGINT NULL,
`Human` VARCHAR(100) NULL,
`m` INT NULL,
`Platform` BIGINT NULL,
`Region` INT NULL,
`Status` BIGINT NULL,
`UpdatedAt` DATETIME NULL DEFAULT NULL,
`y` INT NULL,
`dateAdded` DATETIME NULL DEFAULT NULL,
`lastUpdated` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`Id`));
CREATE TABLE `User_Settings` (
`Id` VARCHAR(128) NOT NULL,
`Setting` VARCHAR(45) NOT NULL,
`Value` LONGTEXT NULL DEFAULT NULL,
PRIMARY KEY (`Id`, `Setting`));
ALTER TABLE `ServerLogs`
ADD FULLTEXT INDEX `ft_message` (`Message`) VISIBLE;
CREATE TABLE `Relation_Game_Platforms` (
`GameId` BIGINT NOT NULL,
`PlatformsId` BIGINT NOT NULL,
PRIMARY KEY (`GameId`, `PlatformsId`),
INDEX `idx_PrimaryColumn` (`GameId` ASC) VISIBLE
);

View File

@@ -46,6 +46,8 @@
<None Remove="Support\Database\MySQL\gaseous-1003.sql" /> <None Remove="Support\Database\MySQL\gaseous-1003.sql" />
<None Remove="Support\Database\MySQL\gaseous-1004.sql" /> <None Remove="Support\Database\MySQL\gaseous-1004.sql" />
<None Remove="Support\Database\MySQL\gaseous-1005.sql" /> <None Remove="Support\Database\MySQL\gaseous-1005.sql" />
<None Remove="Support\Database\MySQL\gaseous-1006.sql" />
<None Remove="Support\Database\MySQL\gaseous-1007.sql" />
<None Remove="Classes\Metadata\" /> <None Remove="Classes\Metadata\" />
<None Remove="Assets\" /> <None Remove="Assets\" />
<None Remove="Assets\Ratings\" /> <None Remove="Assets\Ratings\" />
@@ -174,5 +176,7 @@
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1003.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1004.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" /> <EmbeddedResource Include="Support\Database\MySQL\gaseous-1005.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1006.sql" />
<EmbeddedResource Include="Support\Database\MySQL\gaseous-1007.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

View File

@@ -5,7 +5,7 @@
<script src="/api/v1.1/System/VersionFile"></script> <script src="/api/v1.1/System/VersionFile"></script>
<link type="text/css" rel="stylesheet" dat-href="/styles/style.css" /> <link type="text/css" rel="stylesheet" dat-href="/styles/style.css" />
<script src="/scripts/jquery-3.6.0.min.js"></script> <script src="/scripts/jquery-3.6.0.min.js"></script>
<script src="/scripts/moment.js"></script> <script src="/scripts/moment-with-locales.min.js"></script>
<link href="/styles/select2.min.css" rel="stylesheet" /> <link href="/styles/select2.min.css" rel="stylesheet" />
<link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" /> <link href="/styles/dropzone.min.css" rel="stylesheet" type="text/css" />
<script src="/scripts/jquery.lazy.min.js"></script> <script src="/scripts/jquery.lazy.min.js"></script>

View File

@@ -27,6 +27,9 @@
<tr> <tr>
<td colspan="2" id="settings_users_edit_label"></td> <td colspan="2" id="settings_users_edit_label"></td>
</tr> </tr>
<tr>
<td colspan="2" id="settings_users_edit_errorlabel" style="color: red;"></td>
</tr>
</table> </table>
</div> </div>
@@ -339,6 +342,7 @@
var newPassword = document.getElementById('settings_users_edit_password').value; var newPassword = document.getElementById('settings_users_edit_password').value;
if (newPassword.length > 0) { if (newPassword.length > 0) {
console.log("New password value is long enough to commit to database");
var model = { var model = {
"newPassword": newPassword, "newPassword": newPassword,
"confirmPassword": newPassword "confirmPassword": newPassword
@@ -348,12 +352,10 @@
'/api/v1.1/Account/Users/' + modalVariables + '/Password', '/api/v1.1/Account/Users/' + modalVariables + '/Password',
'POST', 'POST',
function(result) { function(result) {
console.log(JSON.stringify(result)); savePasswordsCallback(result);
savePropertiesCallback();
}, },
function(error) { function(error) {
console.log(JSON.stringify(error)); savePasswordsCallback(error);
savePropertiesCallback();
}, },
JSON.stringify(model) JSON.stringify(model)
); );
@@ -366,6 +368,21 @@
} }
} }
function savePasswordsCallback(result) {
console.log(result);
if (result.succeeded == true) {
savePropertiesCallback();
} else {
var errorBox = document.getElementById('settings_users_edit_errorlabel');
errorBox.innerHTML = '';
for (var i = 0; i < result.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.errors[i].description;
errorBox.appendChild(errorMessage);
}
}
}
function savePropertiesCallback() { function savePropertiesCallback() {
GetUsers(); GetUsers();
closeDialog(); closeDialog();

View File

@@ -28,6 +28,9 @@
<tr> <tr>
<td colspan="2" id="settings_users_new_label"></td> <td colspan="2" id="settings_users_new_label"></td>
</tr> </tr>
<tr>
<td colspan="2" id="settings_users_new_errors" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="text-align: right; padding-top: 10px;"> <td colspan="2" style="text-align: right; padding-top: 10px;">
<button value="OK" id="settings_users_new_okbutton" disabled="disabled" onclick="createUser();">OK</button><button value="Cancel" onclick="closeSubDialog();">Cancel</button> <button value="OK" id="settings_users_new_okbutton" disabled="disabled" onclick="createUser();">OK</button><button value="Cancel" onclick="closeSubDialog();">Cancel</button>
@@ -75,14 +78,27 @@
'/api/v1.1/Account/Users', '/api/v1.1/Account/Users',
'POST', 'POST',
function(result) { function(result) {
GetUsers(); createUserCallback(result);
closeSubDialog();
}, },
function(error) { function(error) {
GetUsers(); createUserCallback(result);
closeSubDialog();
}, },
JSON.stringify(model) JSON.stringify(model)
); );
} }
function createUserCallback(result) {
if (result.succeeded == true) {
GetUsers();
closeSubDialog();
} else {
var errorBox = document.getElementById('settings_users_new_errors');
errorBox.innerHTML = '';
for (var i = 0; i < result.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.errors[i].description;
errorBox.appendChild(errorMessage);
}
}
}
</script> </script>

View File

@@ -1,8 +1,79 @@
<div id="properties_toc"> <div id="properties_toc">
<div id="properties_profile_toc_general" name="properties_profile_toc_item" onclick="ProfileSelectTab('general');">Account</div> <div id="properties_profile_toc_general" name="properties_profile_toc_item" onclick="ProfileSelectTab('general');">Preferences</div>
<div id="properties_profile_toc_account" name="properties_profile_toc_item" onclick="ProfileSelectTab('account');">Account</div>
</div> </div>
<div id="properties_bodypanel"> <div id="properties_bodypanel">
<div id="properties_bodypanel_general" name="properties_profile_tab" style="display: none;"> <div id="properties_bodypanel_general" name="properties_profile_tab" style="display: none;">
<h3>Game Library</h3>
<table style="width: 100%;">
<tr>
<th>
Library
</th>
</tr>
<tr>
<td>
Pagination mode:
</td>
</tr>
<tr>
<td>
<select id="profile_pref-LibraryPagination" data-pref="LibraryPagination" onchange="SavePrefValue_Value(this);">
<option value="paged">Paged</option>
<option value="infinite">Infinite scrolling</option>
</select>
</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<th>
Game Icons
</th>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameTitle" data-pref="LibraryShowGameTitle" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameTitle"> Show title</label>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameRating" data-pref="LibraryShowGameRating" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameRating"> Show rating</label>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="profile_pref_LibraryShowGameClassification" data-pref="LibraryShowGameClassification" onchange="SavePrefValue_Checkbox(this);"><label for="profile_pref_LibraryShowGameClassification"> Show age classification badges</label>
</td>
</tr>
<tr>
<td>
<table id="profile_pref_LibraryClassificationBadgeSelect">
<tr>
<td>Use classification badges from:</td>
</tr>
<tr>
<td>
<select id="profile_pref_LibraryPrimaryClassificationBadge" data-primary="primary" onchange="SavePrefValue_ClassBadge(this);">
</select>
</td>
</tr>
<tr>
<td>Fallback to classification badges from:</td>
</tr>
<tr>
<td>
<select id="profile_pref_LibraryFallbackClassificationBadge" onchange="SavePrefValue_ClassBadge(this);">
</select>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div id="properties_bodypanel_account" name="properties_profile_tab" style="display: none;">
<h3>Reset Password</h3> <h3>Reset Password</h3>
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tr>
@@ -20,6 +91,9 @@
<tr> <tr>
<td colspan="2" id="profile_passwordnotice"></td> <td colspan="2" id="profile_passwordnotice"></td>
</tr> </tr>
<tr>
<td colspan="2" id="profile_passworderrors" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="text-align: right;"> <td colspan="2" style="text-align: right;">
<button id="profile_resetpassword" value="Reset Password" disabled="disabled" onclick="ResetPassword();">Reset Password</button> <button id="profile_resetpassword" value="Reset Password" disabled="disabled" onclick="ResetPassword();">Reset Password</button>
@@ -52,6 +126,125 @@
} }
} }
function GetPrefInitialValues() {
var paginationMode = document.getElementById('profile_pref-LibraryPagination');
paginationMode.value = GetPreference('LibraryPagination', 'paged');
ConfigurePrefInitialValue_Checkbox("LibraryShowGameTitle", GetPreference("LibraryShowGameTitle", true));
ConfigurePrefInitialValue_Checkbox("LibraryShowGameRating", GetPreference("LibraryShowGameRating", true));
ConfigurePrefInitialValue_Checkbox("LibraryShowGameClassification", GetPreference("LibraryShowGameClassification", true));
var primary = document.getElementById('profile_pref_LibraryPrimaryClassificationBadge');
var secondary = document.getElementById('profile_pref_LibraryFallbackClassificationBadge');
PopulateClassificationMenus(primary);
PopulateClassificationMenus(secondary, true);
var classificationOrder = JSON.parse(GetPreference("LibraryGameClassificationDisplayOrder", JSON.stringify([ "ESRB" ])));
primary.value = classificationOrder[0];
if (classificationOrder[1]) {
secondary.value = classificationOrder[1];
}
for (var i = 0; i < secondary.childNodes.length; i++) {
if (secondary.childNodes[i].value == primary.value) {
secondary.childNodes[i].setAttribute('disabled', 'disabled');
} else {
secondary.childNodes[i].removeAttribute('disabled');
}
}
}
function PopulateClassificationMenus(targetSelector, IsSecondary) {
targetSelector.innerHTML = '';
if (IsSecondary == true) {
var defaultOpt = document.createElement('option');
defaultOpt.value = '-';
defaultOpt.innerHTML = 'None';
targetSelector.appendChild(defaultOpt);
}
for (const [key, value] of Object.entries(ClassificationBoards)) {
var opt = document.createElement('option');
opt.value = key;
opt.innerHTML = value;
targetSelector.appendChild(opt);
}
}
function ConfigurePrefInitialValue_Checkbox(ValueName, ValueSetting) {
var valueCheckbox = document.getElementById("profile_pref_" + ValueName);
if (ValueSetting == "true" || ValueSetting == true) {
valueCheckbox.checked = true;
updateDisplay(ValueName, true);
} else {
valueCheckbox.checked = false;
updateDisplay(ValueName, false);
}
}
function SavePrefValue_Checkbox(e) {
var ValueName = e.getAttribute("data-pref");
SetPreference(ValueName, e.checked);
updateDisplay(ValueName, e.checked);
executeFilter1_1(1);
}
function SavePrefValue_Value(e) {
var ValueName = e.getAttribute("data-pref");
SetPreference(ValueName, e.value);
executeFilter1_1(1);
}
function updateDisplay(ValueName, ValueSetting) {
switch(ValueName) {
case "LibraryShowGameClassification":
var badgeSelector = document.getElementById("profile_pref_LibraryClassificationBadgeSelect");
if (ValueSetting == true || ValueSetting == "true") {
badgeSelector.style.display = '';
} else {
badgeSelector.style.display = 'none';
}
break;
}
}
function SavePrefValue_ClassBadge(e) {
var primary = document.getElementById('profile_pref_LibraryPrimaryClassificationBadge');
var secondary = document.getElementById('profile_pref_LibraryFallbackClassificationBadge');
if (e.getAttribute('data-primary') == 'primary') {
// reset secondary to "none" if the same board is selected in both
if (primary.value == secondary.value) {
secondary.value = '-';
}
// disable in secondary board selected in primary
for (var i = 0; i < secondary.childNodes.length; i++) {
if (secondary.childNodes[i].value == primary.value) {
secondary.childNodes[i].setAttribute('disabled', 'disabled');
} else {
secondary.childNodes[i].removeAttribute('disabled');
}
}
}
// save values
var model = [];
if (secondary.value == '-') {
model = [ primary.value ];
} else {
model = [ primary.value, secondary.value ];
}
SetPreference('LibraryGameClassificationDisplayOrder', JSON.stringify(model));
executeFilter1_1(1);
}
function checkPasswordsMatch() { function checkPasswordsMatch() {
var oldPassword = document.getElementById('profile_oldpassword').value; var oldPassword = document.getElementById('profile_oldpassword').value;
var newPassword = document.getElementById('profile_newpassword').value; var newPassword = document.getElementById('profile_newpassword').value;
@@ -106,9 +299,16 @@
function ResetPasswordCallback(result) { function ResetPasswordCallback(result) {
var errorLabel = document.getElementById('profile_passwordnotice'); var errorLabel = document.getElementById('profile_passwordnotice');
var errorBox = document.getElementById('profile_passworderrors');
errorBox.innerHTML = '';
if (result.succeeded == false) { console.log(result);
errorLabel.innerHTML = result.errors.description; if (result.responseJSON.succeeded == false) {
for (var i = 0; i < result.responseJSON.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.responseJSON.errors[i].description;
errorBox.appendChild(errorMessage);
}
} else { } else {
document.getElementById('profile_oldpassword').value = ''; document.getElementById('profile_oldpassword').value = '';
document.getElementById('profile_newpassword').value = ''; document.getElementById('profile_newpassword').value = '';
@@ -119,4 +319,5 @@
} }
ProfileSelectTab('general'); ProfileSelectTab('general');
GetPrefInitialValues();
</script> </script>

View File

@@ -63,7 +63,7 @@
<table style="width: 100%; margin-top: 20px;" cellpadding="5px"> <table style="width: 100%; margin-top: 20px;" cellpadding="5px">
<tr> <tr>
<td colspan="2" style="font-size: 18px;">Create your account.</td> <td colspan="2" style="font-size: 18px;">Create your administrator account.</td>
</tr> </tr>
<tr> <tr>
<th>Email</th> <th>Email</th>
@@ -80,6 +80,9 @@
<tr> <tr>
<td colspan="2" id="login_passwordnotice">&nbsp;</td> <td colspan="2" id="login_passwordnotice">&nbsp;</td>
</tr> </tr>
<tr>
<td colspan="2" id="login_passworderrors" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="padding-top: 20px;"> <td colspan="2" style="padding-top: 20px;">
<button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Create Account</button> <button id="login_createaccount" type="button" value="Create Account" onclick="registerAccount();" disabled="disabled" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Create Account</button>
@@ -154,13 +157,19 @@
} }
function loginCallback(result) { function loginCallback(result) {
switch(result.status) { var errorLabel = document.getElementById('login_passwordnotice');
case 200: var errorBox = document.getElementById('login_passworderrors');
errorBox.innerHTML = '';
console.log(result);
if (result.succeeded == false) {
for (var i = 0; i < result.errors.length; i++) {
var errorMessage = document.createElement('p');
errorMessage.innerHTML = result.errors[i].description;
errorBox.appendChild(errorMessage);
}
} else {
window.location.replace('/index.html'); window.location.replace('/index.html');
break;
default:
// login failed
break;
} }
} }
</script> </script>

View File

@@ -35,6 +35,9 @@
<div id="gamesummary"> <div id="gamesummary">
<div id="gamesummary_cover"></div> <div id="gamesummary_cover"></div>
<div id="gamesummary_firstrelease">
<h3>First Released</h3>
</div>
<div id="gamesumarry_genres"> <div id="gamesumarry_genres">
<h3>Genres</h3> <h3>Genres</h3>
</div> </div>
@@ -83,6 +86,10 @@
</div> </div>
</div> </div>
</div> </div>
<div id="gamesummarysimilar">
<h3>Similar Games</h3>
<div id="gamesummarysimilarcontent"></div>
</div>
</div> </div>
</div> </div>
@@ -234,6 +241,16 @@
} }
gameSummaryCover.appendChild(gameImage); gameSummaryCover.appendChild(gameImage);
// load release date
var gameSummaryRelease = document.getElementById('gamesummary_firstrelease');
if (result.firstReleaseDate) {
var firstRelease = document.createElement('p');
firstRelease.innerHTML = '<p>' + moment(result.firstReleaseDate).format('LL') + ' (' + moment(result.firstReleaseDate).fromNow() + ')</p>';
gameSummaryRelease.appendChild(firstRelease);
} else {
gameSummaryRelease.setAttribute('style', 'display: none;');
}
// load ratings // load ratings
var gameSummaryRatings = document.getElementById('gamesummary_ratings'); var gameSummaryRatings = document.getElementById('gamesummary_ratings');
if (result.ageRatings) { if (result.ageRatings) {
@@ -344,6 +361,26 @@
gamescreenshots.setAttribute('style', 'display: none;'); gamescreenshots.setAttribute('style', 'display: none;');
} }
// load similar
var gameSummarySimilar = document.getElementById('gamesummarysimilar');
ajaxCall('/api/v1.1/Games/' + gameId + '/Related', 'GET', function (result) {
if (result.games.length > 0) {
var gameSummarySimilarContent = document.getElementById('gamesummarysimilar');
for (var i = 0; i < result.games.length; i++) {
var similarObject = renderGameIcon(result.games[i], false, false, false, null, true);
gameSummarySimilarContent.appendChild(similarObject);
}
$('.lazy').Lazy({
scrollDirection: 'vertical',
effect: 'fadeIn',
visibleOnly: true
});
} else {
gameSummarySimilar.setAttribute('style', 'display: none;');
}
});
// load roms // load roms
loadRoms(); loadRoms();
}); });

View File

@@ -2,9 +2,34 @@
<div id="bgImage_Opacity"></div> <div id="bgImage_Opacity"></div>
</div> </div>
<div id="games_home"> <div id="games_filter_scroller">
<div id="games_filter"></div> <div id="games_filter"></div>
</div>
<div id="games_home">
<div id="games_home_box">
<div id="games_library_controls">
<div id="games_library_orderby" class="games_library_controlblock">
<span>Order by: </span>
<select id="games_library_orderby_select" onchange="executeFilter1_1();">
<option selected="selected" value="NameThe">Name, The</option>
<option value="Name">Name</option>
<option value="Rating">User Rating</option>
<option value="RatingCount">User Rating Count</option>
</select>
<select id="games_library_orderby_direction_select" onchange="executeFilter1_1();">
<option selected="selected" value="Ascending">Ascending</option>
<option value="Descending">Descending</option>
</select>
</div>
<div id="games_library_recordcount" class="games_library_controlblock"></div>
</div>
<div id="games_library"></div> <div id="games_library"></div>
<div id="games_library_pagerstore" style="display: none;">0</div>
</div>
</div>
<div id="games_pager" style="display: none;">
&lt; 1 2 3 4 5 &gt;
</div> </div>
<script type="text/javascript"> <script type="text/javascript">

View File

@@ -66,6 +66,9 @@
<input type="checkbox" id="login_rememberme"> <label for="login_rememberme">Remember Me</label> <input type="checkbox" id="login_rememberme"> <label for="login_rememberme">Remember Me</label>
</td> </td>
</tr> </tr>
<tr>
<td colspan="2" id="login_errorlabel" style="color: red;"></td>
</tr>
<tr> <tr>
<td colspan="2" style="padding-top: 20px;"> <td colspan="2" style="padding-top: 20px;">
<button type="button" value="Sign In" onclick="UserLogin();" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Sign In</button> <button type="button" value="Sign In" onclick="UserLogin();" style="margin-top: 10px; width: 100%; font-size: 16px; border-radius: 10px; padding-top: 10px; padding-bottom: 10px;">Sign In</button>
@@ -120,6 +123,7 @@
break; break;
default: default:
// login failed // login failed
document.getElementById('login_errorlabel').innerHTML = 'Incorrect password';
break; break;
} }
} }

View File

@@ -2,60 +2,121 @@
<h1 id="gametitle_label">Firmware</h1> <h1 id="gametitle_label">Firmware</h1>
</div> </div>
<h3>Firmware Availablility</h3> <h3>Firmware Availablility <span id="firmware_totalcount" style="float: right;"></span></h3>
<p>
Display:
<ul style="list-style-type:none;">
<li><input type="checkbox" id="firmware_showavailable" checked="checked" onclick="displayFirmwareList();"/><label for="firmware_showavailable"> Available</label></li>
<li><input type="checkbox" id="firmware_showunavailable" checked="checked" onclick="displayFirmwareList();"/><label for="firmware_showunavailable"> Unavailable</label></li>
</ul>
</p>
<table id="table_firmware" class="romtable" cellspacing="0"> <table id="table_firmware" class="romtable" cellspacing="0">
</table> </table>
<script type="text/javascript"> <script type="text/javascript">
var biosDict = {};
ajaxCall('/api/v1.1/Bios', 'GET', function (result) { ajaxCall('/api/v1.1/Bios', 'GET', function (result) {
result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0)); result.sort((a, b) => a.platformname.charCodeAt(0) - b.platformname.charCodeAt(0));
// sort into a dictionary
for (var i = 0; i < result.length; i++) {
var tempArray = [];
if (biosDict.hasOwnProperty(result[i].platformname)) {
tempArray = biosDict[result[i].platformname];
tempArray.push(result[i]);
} else {
tempArray.push(result[i]);
biosDict[result[i].platformname] = tempArray;
}
biosDict[result[i].platformname] = tempArray;
}
console.log(biosDict);
displayFirmwareList();
});
function displayFirmwareList() {
var lastPlatform = ''; var lastPlatform = '';
var newTable = document.getElementById('table_firmware'); var newTable = document.getElementById('table_firmware');
newTable.innerHTML = '';
newTable.appendChild(createTableRow(true, ['Description', 'File name', 'MD5 Hash', 'Available'])); newTable.appendChild(createTableRow(true, ['Description', 'File name', 'MD5 Hash', 'Available']));
for (var i = 0; i < result.length; i++) { var totalAvailable = 0;
if (result[i].platformname != lastPlatform) { var totalCount = 0;
lastPlatform = result[i].platformname;
for (const [key, value] of Object.entries(biosDict)) {
// new platform - show a header
var platformRow = document.createElement('tr'); var platformRow = document.createElement('tr');
var platformHeader = document.createElement('th'); var platformHeader = document.createElement('th');
platformHeader.setAttribute('colspan', 4); platformHeader.setAttribute('colspan', 4);
platformHeader.innerHTML = result[i].platformname;
var platformHeaderValue = document.createElement('span');
platformHeaderValue.innerHTML = key;
platformHeader.appendChild(platformHeaderValue);
var platformHeaderCounter = document.createElement('span');
platformHeaderCounter.style.float = 'right';
platformHeader.appendChild(platformHeaderCounter);
platformRow.appendChild(platformHeader); platformRow.appendChild(platformHeader);
newTable.appendChild(platformRow); newTable.appendChild(platformRow);
var totalPlatformAvailable = 0;
var showAvailable = document.getElementById('firmware_showavailable').checked;
var showUnavailable = document.getElementById('firmware_showunavailable').checked;
for (var i = 0; i < value.length; i++) {
// update counters
if (value[i].available == true) {
totalAvailable += 1;
totalPlatformAvailable += 1;
} }
if (
(value[i].available == true && showAvailable == true) ||
(value[i].available == false && showUnavailable == true)
) {
var biosFilename = document.createElement('a'); var biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.1/Bios/' + result[i].platformid + '/' + result[i].filename; biosFilename.href = '/api/v1.1/Bios/' + value[i].platformid + '/' + value[i].filename;
biosFilename.innerHTML = result[i].filename; biosFilename.innerHTML = value[i].filename;
biosFilename.className = 'romlink'; biosFilename.className = 'romlink';
var availableText = document.createElement('span'); var availableText = document.createElement('span');
if (result[i].available == true) { if (value[i].available == true) {
availableText.innerHTML = 'Available'; availableText.innerHTML = 'Available';
availableText.className = 'greentext'; availableText.className = 'greentext';
biosFilename = document.createElement('a'); biosFilename = document.createElement('a');
biosFilename.href = '/api/v1.1/Bios/' + result[i].platformid + '/' + result[i].filename; biosFilename.href = '/api/v1.1/Bios/' + value[i].platformid + '/' + value[i].filename;
biosFilename.innerHTML = result[i].filename; biosFilename.innerHTML = value[i].filename;
biosFilename.className = 'romlink'; biosFilename.className = 'romlink';
} else { } else {
availableText.innerHTML = 'Unavailable'; availableText.innerHTML = 'Unavailable';
availableText.className = 'redtext'; availableText.className = 'redtext';
biosFilename = document.createElement('span'); biosFilename = document.createElement('span');
biosFilename.innerHTML = result[i].filename; biosFilename.innerHTML = value[i].filename;
} }
var newRow = [ var newRow = [
result[i].description, value[i].description,
biosFilename, biosFilename,
result[i].hash, value[i].hash,
availableText availableText
]; ];
newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell')); newTable.appendChild(createTableRow(false, newRow, 'romrow', 'romcell'));
} }
}); totalCount += 1;
}
platformHeaderCounter.innerHTML = totalPlatformAvailable + ' / ' + value.length + ' available';
}
document.getElementById('firmware_totalcount').innerHTML = totalAvailable + ' / ' + totalCount + ' available';
}
</script> </script>

View File

@@ -2,7 +2,34 @@
<h1 id="gametitle_label">Logs</h1> <h1 id="gametitle_label">Logs</h1>
</div> </div>
<a href="#" class="romlink" onclick="loadLogs();" style="float: right;"><img src="/images/refresh.svg" alt="Refresh" title="Refresh" class="banner_button_image" /></a> <table style="width: 960px; max-width: 960px;" cellspacing="0">
<tr>
<td>
<input type="datetime-local" id="logs_startdate" />
</td>
<td>
<input type="datetime-local" id="logs_enddate" />
</td>
<td>
<input type="checkbox" id="logs_type_info"><label for="logs_type_info">Information</label>
</td>
<td>
<input type="checkbox" id="logs_type_warning"><label for="logs_type_warning">Warning</label>
</td>
<td>
<input type="checkbox" id="logs_type_critical"><label for="logs_type_critical">Critical</label>
</td>
<td>
<input type="text" id="logs_textsearch" placeholder="Search" />
</td>
<td>
<button onclick="loadLogs();">Search</button>
<button onclick="resetFilters();">Reset</button>
</td>
</tr>
</table>
<!-- <a href="#" class="romlink" onclick="loadLogs();" style="float: right;"><img src="/images/refresh.svg" alt="Refresh" title="Refresh" class="banner_button_image" /></a> -->
<table id="settings_events_table" style="width: 960px; max-width: 960px;" cellspacing="0"> <table id="settings_events_table" style="width: 960px; max-width: 960px;" cellspacing="0">
</table> </table>
@@ -14,20 +41,64 @@
<script type="text/javascript"> <script type="text/javascript">
var lastStartIndex = 0; var lastStartIndex = 0;
var currentPage = 1; var currentPage = 1;
var searchModel = {};
function resetFilters() {
document.getElementById('logs_startdate').value = '';
document.getElementById('logs_enddate').value = '';
document.getElementById('logs_type_info').checked = false;
document.getElementById('logs_type_warning').checked = false;
document.getElementById('logs_type_critical').checked = false;
document.getElementById('logs_textsearch').value = '';
loadLogs();
}
function loadLogs(StartIndex, PageNumber) { function loadLogs(StartIndex, PageNumber) {
var apiQuery = ''; var model = {}
if (StartIndex && PageNumber) { if (StartIndex && PageNumber) {
currentPage += 1; currentPage += 1;
apiQuery = '?StartIndex=' + StartIndex + '&PageNumber=' + PageNumber;
// get saved search model
model = searchModel;
model.StartIndex = StartIndex;
model.PageNumber = PageNumber;
} else { } else {
currentPage = 1; currentPage = 1;
// create search model
var statusList = [];
if (document.getElementById('logs_type_info').checked == true) { statusList.push(0); }
if (document.getElementById('logs_type_warning').checked == true) { statusList.push(2); }
if (document.getElementById('logs_type_critical').checked == true) { statusList.push(3); }
var startDate = null;
var startDateObj = document.getElementById('logs_startdate');
if (startDateObj.value != null) { startDate = new Date(startDateObj.value); }
var endDate = null;
var endDateObj = document.getElementById('logs_enddate');
if (endDateObj.value != null) { endDate = new Date(endDateObj.value); }
var searchText = null;
var searchTextObj = document.getElementById('logs_textsearch');
if (searchTextObj.value != null) { searchText = searchTextObj.value; }
model = {
"StartIndex": StartIndex,
"PageNumber": PageNumber,
"PageSize": 100,
"Status": statusList,
"StartDateTime": startDate,
"EndDateTime": endDate,
"SearchText": searchText
}
searchModel = model;
} }
console.log(model);
ajaxCall( ajaxCall(
'/api/v1.1/Logs' + apiQuery, '/api/v1.1/Logs',
'GET', 'POST',
function (result) { function (result) {
var newTable = document.getElementById('settings_events_table'); var newTable = document.getElementById('settings_events_table');
if (currentPage == 1) { if (currentPage == 1) {
@@ -54,7 +125,7 @@
var newRow = [ var newRow = [
//result[i].id, //result[i].id,
moment(result[i].eventTime).format("YYYY-MM-DD H:mm:ss"), moment(result[i].eventTime).format("YYYY-MM-DD h:mm:ss a"),
result[i].eventType, result[i].eventType,
result[i].process, result[i].process,
result[i].message result[i].message
@@ -74,7 +145,11 @@
newTable.appendChild(exRow); newTable.appendChild(exRow);
} }
} }
} },
function (error) {
},
JSON.stringify(model)
); );
} }

View File

@@ -8,6 +8,49 @@
</table> </table>
<div style="text-align: right;"><button id="settings_newlibrary" onclick="showSubDialog('librarynew');">New Library</button></div> <div style="text-align: right;"><button id="settings_newlibrary" onclick="showSubDialog('librarynew');">New Library</button></div>
<h2>Advanced Settings</h2>
<p><strong>Warning</strong> Do not modify the below settings unless you know what you're doing.</p>
<h3>Background Task Timers</h3>
<p>All intervals are in minutes.</p>
<table id="settings_tasktimers" class="romtable" style="width: 100%;" cellspacing="0">
</table>
<div style="text-align: right;"><button id="settings_tasktimers_default" onclick="defaultTaskTimers();">Reset to Default</button><button id="settings_tasktimers_new" onclick="saveTaskTimers();">Save</button></div>
<h3>Logging</h3>
<table cellspacing="0" style="width: 100%;">
<tr>
<th>
Write logs
</th>
<td>
<input type="radio" name="settings_logs_write" id="settings_logs_write_db" value="false" checked="checked"><label for="settings_logs_write_db"> To database only (default)</label>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="radio" name="settings_logs_write" id="settings_logs_write_fs" value="true"><label for="settings_logs_write_fs"> To database and disk</label>
</td>
</tr>
<tr>
<td colspan="2">&nbsp;</td>
</tr>
<tr>
<th>
Minimum log retention (days):
</th>
<td>
<input type="number" min="1" id="settings_logs_retention" />
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<button id="settings_tasktimers_new" onclick="setLoggingSettings();">Save</button>
</td>
</tr>
</table>
<script type="text/javascript"> <script type="text/javascript">
function drawLibrary() { function drawLibrary() {
ajaxCall( ajaxCall(
@@ -59,5 +102,116 @@
); );
} }
function getBackgroundTaskTimers() {
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Intervals',
'GET',
function(result) {
var targetTable = document.getElementById('settings_tasktimers');
targetTable.innerHTML = '';
targetTable.appendChild(
createTableRow(true, ['Background Task', 'Timer Interval', 'Default Interval', 'Minimum Allowed Interval'])
);
for (const [key, value] of Object.entries(result)) {
var newTableRow = createTableRow(
false,
[
GetTaskFriendlyName(value.task),
'<input id="settings_tasktimers_' + value.task + '" name="settings_tasktimers_values" data-name="' + value.task + '" data-default="' + value.defaultInterval + '" type="number" placeholder="0" min="' + value.minimumAllowedValue + '" value="' + value.interval + '" />',
value.defaultInterval,
value.minimumAllowedValue
],
'romrow',
'romcell'
);
targetTable.appendChild(newTableRow);
}
}
);
}
function saveTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_values');
var model = {};
for (var i = 0; i < timerValues.length; i++) {
model[timerValues[i].getAttribute('data-name')] = timerValues[i].value;
}
ajaxCall(
'/api/v1/System/Settings/BackgroundTasks/Intervals',
'POST',
function(result) {
getBackgroundTaskTimers();
},
function(error) {
getBackgroundTaskTimers();
},
JSON.stringify(model)
);
}
function defaultTaskTimers() {
var timerValues = document.getElementsByName('settings_tasktimers_values');
for (var i = 0; i < timerValues.length; i++) {
timerValues[i].value = timerValues[i].getAttribute('data-default');
}
saveTaskTimers();
}
function getLoggingSettings() {
ajaxCall(
'/api/v1/System/Settings/System',
'GET',
function(result) {
var optionToSelect = 'settings_logs_write_db';
if (result.alwaysLogToDisk == true) {
optionToSelect = 'settings_logs_write_fs';
}
document.getElementById(optionToSelect).checked = true;
document.getElementById('settings_logs_retention').value = result.minimumLogRetentionPeriod;
}
);
}
function setLoggingSettings() {
var alwaysLogToDisk = false;
if ($("input[type='radio'][name='settings_logs_write']:checked").val() == "true") {
alwaysLogToDisk = true;
}
var retention = document.getElementById('settings_logs_retention');
var retentionValue = 0;
if (retention.value) {
retentionValue = retention.value;
} else {
retentionValue = 7;
}
var model = {
"alwaysLogToDisk": alwaysLogToDisk,
"minimumLogRetentionPeriod": retentionValue
};
ajaxCall(
'/api/v1/System/Settings/System',
'POST',
function(result) {
getLoggingSettings();
},
function(error) {
getLoggingSettings();
},
JSON.stringify(model)
);
}
drawLibrary(); drawLibrary();
getBackgroundTaskTimers();
getLoggingSettings();
</script> </script>

View File

@@ -34,33 +34,7 @@
if (result) { if (result) {
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
var itemTypeName; var itemTypeName = GetTaskFriendlyName(result[i].itemType, result[i].options);
switch (result[i].itemType) {
case 'SignatureIngestor':
itemTypeName = "Signature import";
break;
case 'TitleIngestor':
itemTypeName = "Title import";
break;
case 'MetadataRefresh':
itemTypeName = "Metadata refresh"
break;
case 'OrganiseLibrary':
itemTypeName = "Organise library";
break;
case 'LibraryScan':
itemTypeName = "Library scan";
break;
case 'CollectionCompiler':
itemTypeName = "Compress collection id: " + result[i].options;
break;
case 'BackgroundDatabaseUpgrade':
itemTypeName = "Background database upgrade";
break;
default:
itemTypeName = result[i].itemType;
break;
}
var itemStateName; var itemStateName;
var itemLastStart; var itemLastStart;

View File

@@ -19,9 +19,25 @@
panel.appendChild(containerPanelSearch); panel.appendChild(containerPanelSearch);
panel.appendChild(buildFilterPanelHeader('userrating', 'User Rating')); panel.appendChild(buildFilterPanelHeader('userrating', 'User Rating', true, false));
var containerPanelUserRating = document.createElement('div'); var containerPanelUserRating = document.createElement('div');
containerPanelUserRating.id = 'filter_panel_box_userrating';
containerPanelUserRating.className = 'filter_panel_box'; containerPanelUserRating.className = 'filter_panel_box';
var containerPanelUserRatingCheckBox = document.createElement('input');
containerPanelUserRatingCheckBox.id = 'filter_panel_userrating_enabled';
containerPanelUserRatingCheckBox.type = 'checkbox';
containerPanelUserRatingCheckBox.setAttribute('oninput', 'executeFilterDelayed();');
var ratingEnabledCookie = getCookie('filter_panel_userrating_enabled');
if (ratingEnabledCookie) {
if (ratingEnabledCookie == "true") {
containerPanelUserRatingCheckBox.checked = true;
} else {
containerPanelUserRatingCheckBox.checked = false;
}
}
containerPanelUserRating.appendChild(containerPanelUserRatingCheckBox);
var containerPanelUserRatingMinField = document.createElement('input'); var containerPanelUserRatingMinField = document.createElement('input');
var minRatingCookie = getCookie('filter_panel_userrating_min'); var minRatingCookie = getCookie('filter_panel_userrating_min');
if (minRatingCookie) { if (minRatingCookie) {
@@ -72,7 +88,23 @@
buildFilterPanel(panel, 'theme', 'Themes', result.themes, true, false); buildFilterPanel(panel, 'theme', 'Themes', result.themes, true, false);
} }
if (result.agegroupings) {
if (result.agegroupings.length > 1) {
buildFilterPanel(panel, 'agegroupings', 'Age Groups', result.agegroupings, true, false);
}
}
targetElement.appendChild(panel); targetElement.appendChild(panel);
// set order by values
var orderByCookie = getCookie('games_library_orderby_select');
if (orderByCookie) {
document.getElementById('games_library_orderby_select').value = orderByCookie;
}
var orderByDirectionCookie = getCookie('games_library_orderby_direction_select');
if (orderByDirectionCookie) {
document.getElementById('games_library_orderby_direction_select').value = orderByDirectionCookie;
}
} }
function buildFilterPanel(targetElement, headerString, friendlyHeaderString, valueList, showToggle, initialDisplay) { function buildFilterPanel(targetElement, headerString, friendlyHeaderString, valueList, showToggle, initialDisplay) {
@@ -80,7 +112,6 @@ function buildFilterPanel(targetElement, headerString, friendlyHeaderString, val
var displayCookie = getCookie('filter_panel_box_' + headerString); var displayCookie = getCookie('filter_panel_box_' + headerString);
if (displayCookie) { if (displayCookie) {
initialDisplay = (displayCookie === 'true'); initialDisplay = (displayCookie === 'true');
console.log(displayCookie);
} }
targetElement.appendChild(buildFilterPanelHeader(headerString, friendlyHeaderString, showToggle, initialDisplay)); targetElement.appendChild(buildFilterPanelHeader(headerString, friendlyHeaderString, showToggle, initialDisplay));
@@ -92,14 +123,13 @@ function buildFilterPanel(targetElement, headerString, friendlyHeaderString, val
} }
for (var i = 0; i < valueList.length; i++) { for (var i = 0; i < valueList.length; i++) {
var tags; var tags;
switch(headerString) {
case 'platform': if (valueList[i].gameCount) {
tags = [ tags = [
{ {
'label': valueList[i].gameCount 'label': valueList[i].gameCount
} }
]; ];
break;
} }
containerPanel.appendChild(buildFilterPanelItem(headerString, valueList[i].id, valueList[i].name, tags)); containerPanel.appendChild(buildFilterPanelItem(headerString, valueList[i].id, valueList[i].name, tags));
} }
@@ -201,85 +231,6 @@ function executeFilterDelayed() {
filterExecutor = setTimeout(executeFilter1_1, 1000); filterExecutor = setTimeout(executeFilter1_1, 1000);
} }
function executeFilter() {
// build filter lists
var queries = [];
var platforms = '';
var genres = '';
var searchString = document.getElementById('filter_panel_search');
if (searchString.value.length > 0) {
queries.push('name=' + searchString.value);
}
setCookie(searchString.id, searchString.value);
var minUserRating = 0;
var minUserRatingInput = document.getElementById('filter_panel_userrating_min');
if (minUserRatingInput.value) {
minUserRating = minUserRatingInput.value;
queries.push('minrating=' + minUserRating);
}
setCookie(minUserRatingInput.id, minUserRatingInput.value);
var maxUserRating = 100;
var maxUserRatingInput = document.getElementById('filter_panel_userrating_max');
if (maxUserRatingInput.value) {
maxUserRating = maxUserRatingInput.value;
queries.push('maxrating=' + maxUserRating);
}
setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
queries.push(GetFilterQuery('platform'));
queries.push(GetFilterQuery('genre'));
queries.push(GetFilterQuery('gamemode'));
queries.push(GetFilterQuery('playerperspective'));
queries.push(GetFilterQuery('theme'));
var queryString = '';
for (var i = 0; i < queries.length; i++) {
if (queries[i].length > 0) {
if (queryString.length == 0) {
queryString = '?';
} else {
queryString += '&';
}
queryString += queries[i];
}
}
console.log('Query string = ' + queryString);
ajaxCall('/api/v1.0/Games' + queryString, 'GET', function (result) {
var gameElement = document.getElementById('games_library');
formatGamesPanel(gameElement, result);
});
}
function GetFilterQuery(filterName) {
var Filters = document.getElementsByName('filter_' + filterName);
var queryString = '';
for (var i = 0; i < Filters.length; i++) {
if (Filters[i].checked) {
setCookie(Filters[i].id, true);
if (queryString.length > 0) {
queryString += ',';
}
queryString += Filters[i].getAttribute('filter_id');
} else {
setCookie(Filters[i].id, false);
}
}
if (queryString.length > 0) {
queryString = filterName + '=' + queryString;
}
return queryString;
}
function buildFilterTag(tags) { function buildFilterTag(tags) {
// accepts an array of numbers + classes for styling (optional) // accepts an array of numbers + classes for styling (optional)
// example [ { label: "G: 13", class: "tag_Green" }, { label: "R: 17", class: "tag_Orange" } ] // example [ { label: "G: 13", class: "tag_Green" }, { label: "R: 17", class: "tag_Orange" } ]
@@ -301,12 +252,23 @@ function buildFilterTag(tags) {
return boundingDiv; return boundingDiv;
} }
function executeFilter1_1() { function executeFilter1_1(pageNumber, pageSize) {
console.log("Execute filter 1.1"); if (!pageNumber) {
pageNumber = 1;
}
if (!pageSize) {
pageSize = 30;
}
// user ratings
var userRatingEnabled = document.getElementById('filter_panel_userrating_enabled');
var minUserRating = -1; var minUserRating = -1;
var minUserRatingInput = document.getElementById('filter_panel_userrating_min'); var minUserRatingInput = document.getElementById('filter_panel_userrating_min');
if (minUserRatingInput.value) { if (minUserRatingInput.value) {
minUserRating = minUserRatingInput.value; minUserRating = minUserRatingInput.value;
userRatingEnabled.checked = true;
} }
setCookie(minUserRatingInput.id, minUserRatingInput.value); setCookie(minUserRatingInput.id, minUserRatingInput.value);
@@ -314,15 +276,51 @@ function executeFilter1_1() {
var maxUserRatingInput = document.getElementById('filter_panel_userrating_max'); var maxUserRatingInput = document.getElementById('filter_panel_userrating_max');
if (maxUserRatingInput.value) { if (maxUserRatingInput.value) {
maxUserRating = maxUserRatingInput.value; maxUserRating = maxUserRatingInput.value;
userRatingEnabled.checked = true;
} }
setCookie(maxUserRatingInput.id, maxUserRatingInput.value); setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
if (minUserRating == -1 && maxUserRating == -1) {
userRatingEnabled.checked = false;
}
if (userRatingEnabled.checked == false) {
setCookie("filter_panel_userrating_enabled", false);
minUserRating = -1;
minUserRatingInput.value = "";
setCookie(minUserRatingInput.id, minUserRatingInput.value);
maxUserRating = -1;
maxUserRatingInput.value = "";
setCookie(maxUserRatingInput.id, maxUserRatingInput.value);
} else {
setCookie("filter_panel_userrating_enabled", true);
}
// get order by
var orderBy = document.getElementById('games_library_orderby_select').value;
setCookie('games_library_orderby_select', orderBy);
var orderByDirection = true;
var orderByDirectionSelect = document.getElementById('games_library_orderby_direction_select').value;
if (orderByDirectionSelect == "Ascending") {
orderByDirection = true;
} else {
orderByDirection = false;
}
setCookie('games_library_orderby_direction_select', orderByDirectionSelect);
// build filter model // build filter model
var ratingAgeGroups = GetFilterQuery1_1('agegroupings');
var ratingIncludeUnrated = false;
if (ratingAgeGroups.includes("0")) {
ratingIncludeUnrated = true;
}
var model = { var model = {
"Name": document.getElementById('filter_panel_search').value, "Name": document.getElementById('filter_panel_search').value,
"Platform": GetFilterQuery1_1('platform'), "Platform": GetFilterQuery1_1('platform'),
"Genre": GetFilterQuery1_1('genre'), "Genre": GetFilterQuery1_1('genre'),
"GameMode": GetFilterQuery1_1('gamemmode'), "GameMode": GetFilterQuery1_1('gamemode'),
"PlayerPerspective": GetFilterQuery1_1('playerperspective'), "PlayerPerspective": GetFilterQuery1_1('playerperspective'),
"Theme": GetFilterQuery1_1('theme'), "Theme": GetFilterQuery1_1('theme'),
"GameRating": { "GameRating": {
@@ -330,31 +328,24 @@ function executeFilter1_1() {
"MinimumRatingCount": -1, "MinimumRatingCount": -1,
"MaximumRating": maxUserRating, "MaximumRating": maxUserRating,
"MaximumRatingCount": -1, "MaximumRatingCount": -1,
"IncludeUnrated": true "IncludeUnrated": !userRatingEnabled
}, },
"GameAgeRating": { "GameAgeRating": {
"AgeGroupings": [ "AgeGroupings": ratingAgeGroups,
"Child", "IncludeUnrated": ratingIncludeUnrated
"Teen",
"Mature",
"Adult"
],
"IncludeUnrated": true
}, },
"Sorting": { "Sorting": {
"SortBy": "NameThe", "SortBy": orderBy,
"SortAscenting": true "SortAscending": orderByDirection
} }
}; };
console.log('Search model = ' + JSON.stringify(model));
ajaxCall( ajaxCall(
'/api/v1.1/Games', '/api/v1.1/Games?pageNumber=' + pageNumber + '&pageSize=' + pageSize,
'POST', 'POST',
function (result) { function (result) {
var gameElement = document.getElementById('games_library'); var gameElement = document.getElementById('games_library');
formatGamesPanel(gameElement, result); formatGamesPanel(gameElement, result, pageNumber, pageSize);
}, },
function (error) { function (error) {
console.log('An error occurred: ' + JSON.stringify(error)); console.log('An error occurred: ' + JSON.stringify(error));

View File

@@ -1,7 +1,50 @@
function formatGamesPanel(targetElement, result) { var ClassificationBoards = {
"ESRB": "Entertainment Software Rating Board (ESRB)",
"PEGI": "Pan European Game Information (PEGI)",
"CERO": "Computer Entertainment Rating Organisation (CERO)",
"USK": "Unterhaltungssoftware Selbstkontrolle (USK)",
"GRAC": "Game Rating and Administration Committee (GRAC)",
"CLASS_IND": "Brazilian advisory rating system",
"ACB": "Australian Classification Board (ACB)"
};
function formatGamesPanel(targetElement, result, pageNumber, pageSize) {
console.log("Displaying page: " + pageNumber);
console.log("Page size: " + pageSize);
var pageMode = GetPreference('LibraryPagination', 'paged');
if (pageNumber == 1 || pageMode == 'paged') {
targetElement.innerHTML = ''; targetElement.innerHTML = '';
for (var i = 0; i < result.length; i++) { }
var game = renderGameIcon(result[i], true, false);
var pagerCheck = document.getElementById('games_library_pagerstore');
if (pageNumber == 1) {
pagerCheck.innerHTML = "0";
}
if (pageNumber > Number(pagerCheck.innerHTML) || pageMode == 'paged') {
pagerCheck.innerHTML = pageNumber;
document.getElementById('games_library_recordcount').innerHTML = result.count + ' games';
var existingLoadPageButton = document.getElementById('games_library_loadmore');
if (existingLoadPageButton) {
existingLoadPageButton.parentNode.removeChild(existingLoadPageButton);
}
// setup preferences
var showTitle = GetPreference("LibraryShowGameTitle", true);
var showRatings = GetPreference("LibraryShowGameRating", true);
var showClassification = GetPreference("LibraryShowGameClassification", true);
var classificationDisplayOrderString = GetPreference("LibraryGameClassificationDisplayOrder", JSON.stringify([ "ESRB" ]));
var classificationDisplayOrder = JSON.parse(classificationDisplayOrderString);
if (showTitle == "true") { showTitle = true; } else { showTitle = false; }
if (showRatings == "true") { showRatings = true; } else { showRatings = false; }
if (showClassification == "true") { showClassification = true; } else { showClassification = false; }
for (var i = 0; i < result.games.length; i++) {
var game = renderGameIcon(result.games[i], showTitle, showRatings, showClassification, classificationDisplayOrder, false);
targetElement.appendChild(game); targetElement.appendChild(game);
} }
@@ -10,22 +53,178 @@
effect: 'fadeIn', effect: 'fadeIn',
visibleOnly: true visibleOnly: true
}); });
var pager = document.getElementById('games_pager');
pager.style.display = 'none';
switch(pageMode) {
case 'infinite':
if (result.games.length == pageSize) {
var loadPageButton = document.createElement("div");
loadPageButton.id = 'games_library_loadmore';
loadPageButton.innerHTML = 'Load More';
loadPageButton.setAttribute('onclick', 'executeFilter1_1(' + (pageNumber + 1) + ', ' + pageSize + ');');
loadPageButton.setAttribute('data-pagenumber', Number(pageNumber + 1));
loadPageButton.setAttribute('data-pagesize', pageSize);
targetElement.appendChild(loadPageButton);
}
break;
case 'paged':
if (result.count > pageSize) {
// add some padding to the bottom of the games list
var loadPageButton = document.createElement("div");
loadPageButton.id = 'games_library_padding';
targetElement.appendChild(loadPageButton);
var pageCount = Math.ceil(result.count / pageSize);
// add previous page button
var prevPage = document.createElement('span');
prevPage.innerHTML = '&lt;';
if (pageNumber == 1) {
prevPage.className = 'games_pager_number_disabled';
} else {
prevPage.className = 'games_pager_number';
prevPage.setAttribute('onclick', 'executeFilter1_1(' + (pageNumber - 1) + ');');
} }
function renderGameIcon(gameObject, showTitle, showRatings) { // add page numbers
var pageNumbers = document.createElement('span');
for (var i = 1; i <= pageCount; i++) {
var pageNum = document.createElement('span');
if (Number(pagerCheck.innerHTML) == i) {
pageNum.className = 'games_pager_number_disabled';
} else {
pageNum.className = 'games_pager_number';
pageNum.setAttribute('onclick', 'executeFilter1_1(' + i + ');');
}
pageNum.innerHTML = i;
pageNumbers.appendChild(pageNum);
}
// add next page button
var nextPage = document.createElement('span');
nextPage.innerHTML = '&gt;';
if (pageNumber == pageCount) {
nextPage.className = 'games_pager_number_disabled';
} else {
nextPage.className = 'games_pager_number';
nextPage.setAttribute('onclick', 'executeFilter1_1(' + (pageNumber + 1) + ');');
}
pager.innerHTML = '';
pager.appendChild(prevPage);
pager.appendChild(pageNumbers);
pager.appendChild(nextPage);
pager.style.display = '';
}
break;
}
}
}
function isScrolledIntoView(elem) {
if (elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
}
function IsInView() {
var loadElement = document.getElementById('games_library_loadmore');
if (loadElement) {
if (isScrolledIntoView(loadElement)) {
var pageNumber = Number(document.getElementById('games_library_loadmore').getAttribute('data-pagenumber'));
var pageSize = document.getElementById('games_library_loadmore').getAttribute('data-pagesize');
executeFilter1_1(pageNumber, pageSize);
}
}
}
$(window).scroll(IsInView);
function renderGameIcon(gameObject, showTitle, showRatings, showClassification, classificationDisplayOrder, useSmallCover) {
var gameBox = document.createElement('div'); var gameBox = document.createElement('div');
gameBox.id = "game_tile_" + gameObject.id;
if (useSmallCover == true) {
gameBox.className = 'game_tile game_tile_small';
} else {
gameBox.className = 'game_tile'; gameBox.className = 'game_tile';
}
gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";'); gameBox.setAttribute('onclick', 'window.location.href = "/index.html?page=game&id=' + gameObject.id + '";');
var gameImageBox = document.createElement('div');
gameImageBox.className = 'game_tile_box';
var gameImage = document.createElement('img'); var gameImage = document.createElement('img');
if (useSmallCover == true) {
gameImage.className = 'game_tile_image game_tile_image_small lazy';
} else {
gameImage.className = 'game_tile_image lazy'; gameImage.className = 'game_tile_image lazy';
}
gameImage.src = '/images/unknowngame.png';
if (gameObject.cover) { if (gameObject.cover) {
gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.id + '/cover/image'); gameImage.setAttribute('data-src', '/api/v1.1/Games/' + gameObject.id + '/cover/image');
} else { } else {
gameImage.src = '/images/unknowngame.png';
gameImage.className = 'game_tile_image unknown'; gameImage.className = 'game_tile_image unknown';
} }
gameBox.appendChild(gameImage); gameImageBox.appendChild(gameImage);
var classificationPath = '';
var displayClassification = false;
var shownClassificationBoard = '';
if (showClassification == true) {
for (var b = 0; b < classificationDisplayOrder.length; b++) {
if (shownClassificationBoard == '') {
for (var c = 0; c < gameObject.ageRatings.length; c++) {
if (gameObject.ageRatings[c].category == classificationDisplayOrder[b]) {
shownClassificationBoard = classificationDisplayOrder[b];
displayClassification = true;
classificationPath = '/api/v1.1/Ratings/Images/' + classificationDisplayOrder[b] + '/' + getKeyByValue(AgeRatingStrings, gameObject.ageRatings[c].rating) + '/image.svg';
}
}
} else {
break;
}
}
}
if (gameObject.totalRating || displayClassification == true) {
var gameImageRatingBanner = document.createElement('div');
gameImageRatingBanner.className = 'game_tile_box_ratingbanner';
if (showRatings == true || displayClassification == true) {
if (showRatings == true) {
if (gameObject.totalRating) {
var gameImageRatingBannerLogo = document.createElement('img');
gameImageRatingBannerLogo.src = '/images/IGDB_logo.svg';
gameImageRatingBannerLogo.setAttribute('style', 'filter: invert(100%); height: 10px; margin-right: 5px; padding-top: 4px;');
gameImageRatingBanner.appendChild(gameImageRatingBannerLogo);
var gameImageRatingBannerValue = document.createElement('span');
gameImageRatingBannerValue.innerHTML = Math.floor(gameObject.totalRating) + '% / ' + gameObject.totalRatingCount;
gameImageRatingBanner.appendChild(gameImageRatingBannerValue);
}
}
gameImageBox.appendChild(gameImageRatingBanner);
if (displayClassification == true) {
var gameImageClassificationLogo = document.createElement('img');
gameImageClassificationLogo.src = classificationPath;
gameImageClassificationLogo.className = 'rating_image_overlay';
gameImageBox.appendChild(gameImageClassificationLogo);
}
}
}
gameBox.appendChild(gameImageBox);
if (showTitle == true) { if (showTitle == true) {
var gameBoxTitle = document.createElement('div'); var gameBoxTitle = document.createElement('div');
@@ -34,19 +233,5 @@ function renderGameIcon(gameObject, showTitle, showRatings) {
gameBox.appendChild(gameBoxTitle); gameBox.appendChild(gameBoxTitle);
} }
if (showRatings == true) {
if (gameObject.ageRatings) {
var ratingsSection = document.createElement('div');
ratingsSection.id = 'ratings_section';
for (var i = 0; i < gameObject.ageRatings.ids.length; i++) {
var ratingImage = document.createElement('img');
ratingImage.src = '/api/v1.1/Games/' + gameObject.id + '/agerating/' + gameObject.ageRatings.ids[i] + '/image';
ratingImage.className = 'rating_image_mini';
ratingsSection.appendChild(ratingImage);
}
gameBox.appendChild(ratingsSection);
}
}
return gameBox; return gameBox;
} }

View File

@@ -1,4 +1,8 @@
function ajaxCall(endpoint, method, successFunction, errorFunction, body) { var locale = window.navigator.userLanguage || window.navigator.language;
console.log(locale);
moment.locale(locale);
function ajaxCall(endpoint, method, successFunction, errorFunction, body) {
$.ajax({ $.ajax({
// Our sample url to make request // Our sample url to make request
@@ -389,3 +393,80 @@ function CreateBadge(BadgeText, ColourOverride) {
} }
return badgeItem; return badgeItem;
} }
function GetTaskFriendlyName(TaskName, options) {
switch (TaskName) {
case 'SignatureIngestor':
return "Signature import";
case 'TitleIngestor':
return "Title import";
case 'MetadataRefresh':
return "Metadata refresh";
case 'OrganiseLibrary':
return "Organise library";
case 'LibraryScan':
return "Library scan";
case 'CollectionCompiler':
return "Compress collection id: " + options;
case 'BackgroundDatabaseUpgrade':
return "Background database upgrade";
default:
return TaskName;
}
}
function getKeyByValue(object, value) {
return Object.keys(object).find(key => object[key] === value);
}
function GetPreference(Setting, DefaultValue) {
if (userProfile.userPreferences) {
for (var i = 0; i < userProfile.userPreferences.length; i++) {
if (userProfile.userPreferences[i].setting == Setting) {
return userProfile.userPreferences[i].value.toString();
}
}
}
SetPreference(Setting, DefaultValue);
return DefaultValue;
}
function SetPreference(Setting, Value) {
var model = [
{
"setting": Setting,
"value": Value.toString()
}
];
ajaxCall(
'/api/v1.1/Account/Preferences',
'POST',
function(result) {
SetPreference_Local(Setting, Value);
},
function(error) {
SetPreference_Local(Setting, Value);
},
JSON.stringify(model)
);
}
function SetPreference_Local(Setting, Value) {
if (userProfile.userPreferences) {
var prefFound = false;
for (var i = 0; i < userProfile.userPreferences.length; i++) {
if (userProfile.userPreferences[i].setting == Setting) {
userProfile.userPreferences[i].value = Value;
prefFound = true;
break;
}
}
if (prefFound == false) {
userProfile.userPreferences.push(model);
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -187,12 +187,28 @@ h3 {
z-index: -100; z-index: -100;
} }
#games_filter_scroller {
position: fixed;
width: 210px;
overflow-y: hidden;
overflow-x: hidden;
overscroll-behavior: contain;
top: 60px;
bottom: 20px;
/* margin-right: 10px; */
}
#games_filter { #games_filter {
width: 200px; width: 200px;
border-style: solid; /* border-style: solid;
border-width: 1px; border-width: 1px;
border-color: #2b2b2b; border-color: #2b2b2b; */
margin-right: 10px; overflow: hidden;
}
#games_filter_scroller:hover {
overflow-y: auto;
} }
.filter_header { .filter_header {
@@ -207,7 +223,7 @@ h3 {
z-index: 1; z-index: 1;
} }
input[type='text'], input[type='number'], input[type="email"], input[type="password"] { input[type='text'], input[type='number'], input[type="email"], input[type="password"], input[type="datetime-local"] {
background-color: #2b2b2b; background-color: #2b2b2b;
color: white; color: white;
padding: 5px; padding: 5px;
@@ -266,8 +282,51 @@ input[id='filter_panel_userrating_max'] {
} }
#games_home { #games_home {
display: flex; display: absolute;
margin-left: 210px;
margin-top: 20px; margin-top: 20px;
right: 20px;
}
#games_home_box {
display: relative;
}
#games_pager {
position: fixed;
bottom: 50px;
left: 50%;
transform: translate(-50%, 0);
border-style: solid;
border-width: 1px;
border-radius: 7px;
border-color: rgba(0, 22, 56, 0.8);
padding-left: 20px;
padding-right: 20px;
padding-top: 15px;
padding-bottom: 15px;
font-size: 18px;
font-family: Commodore64;
background-color: rgba(0, 22, 56, 0.8);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
}
.games_pager_number {
padding: 5px;
}
.games_pager_number:hover {
background-color: blue;
cursor: pointer;
}
.games_pager_number_disabled {
padding: 5px;
color: grey;
} }
.filter_panel_item { .filter_panel_item {
@@ -285,18 +344,52 @@ input[id='filter_panel_userrating_max'] {
margin-right: 15px; margin-right: 15px;
} }
#games_library { #games_library_controls {
width: 90%; display: absolute;
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
border-color: #2b2b2b; border-color: #2b2b2b;
border-radius: 5px;
margin-bottom: 10px;
padding: 10px; padding: 10px;
text-align: right;
}
.games_library_controlblock {
margin-left: 20px;
display: inline-block;
}
#games_library {
display: block;
/* width: 90%; */
/* border-style: solid;
border-width: 1px;
border-color: #2b2b2b; */
/* padding: 10px; */
}
#games_library_loadmore {
display: block;
width: 100%;
text-align: center;
padding-top: 10px;
height: 50px;
}
#games_library_padding {
display: block;
width: 100%;
text-align: center;
padding-top: 10px;
height: 100px;
} }
.game_tile { .game_tile {
padding: 5px; padding: 5px;
display: inline-block; display: inline-block;
width: 220px; width: 220px;
min-height: 200px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
@@ -308,6 +401,12 @@ input[id='filter_panel_userrating_max'] {
border: 1px solid transparent; border: 1px solid transparent;
} }
.game_tile_small {
min-height: 50px;
min-width: 50px;
width: 105px;
}
.game_tile:hover { .game_tile:hover {
cursor: pointer; cursor: pointer;
text-decoration: underline; text-decoration: underline;
@@ -318,19 +417,57 @@ input[id='filter_panel_userrating_max'] {
border: 1px solid #2b2b2b; border: 1px solid #2b2b2b;
} }
.game_tile_image { .game_tile_small:hover {
cursor: pointer;
text-decoration: underline;
background-color: #2b2b2b;
border-radius: 10px 10px 10px 10px;
-webkit-border-radius: 10px 10px 10px 10px;
-moz-border-radius: 10px 10px 10px 10px;
border: 1px solid #2b2b2b;
}
.game_tile_box {
position: relative;
display: inline-block;
max-width: 200px; max-width: 200px;
max-height: 200px; max-height: 200px;
}
.game_tile_box_ratingbanner {
position: absolute;
bottom: 0px;
left: 0px;
right: 0px;
text-align: right;
padding-top: 2px;
padding-bottom: 2px;
padding-left: 5px;
padding-right: 5px;
height: 19px;
background-color: rgba(0, 22, 56, 0.8);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
.game_tile_image {
max-width: 200px;
min-width: 150px;
max-height: 200px;
min-height: 200px;
background-color: transparent;
box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44); box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44); -webkit-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
-moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44); -moz-box-shadow: 5px 5px 19px 0px rgba(0,0,0,0.44);
} }
.game_tile_image, .unknown { .game_tile_image, .unknown {
background-color: white; background-color: transparent;
} }
.game_tile_image_small { .game_tile_image_small {
min-width: 50px;
min-height: 50px;
max-width: 100px; max-width: 100px;
max-height: 100px; max-height: 100px;
} }
@@ -408,10 +545,18 @@ input[id='filter_panel_userrating_max'] {
} }
.rating_image_mini { .rating_image_mini {
display: inline-block;
max-width: 32px; max-width: 32px;
max-height: 32px; max-height: 32px;
margin-right: 10px; margin-right: 5px;
margin-bottom: 5px;
}
.rating_image_overlay {
position: absolute;
max-width: 32px;
max-height: 32px;
left: 5px;
bottom: 5px;
} }
#gamescreenshots { #gamescreenshots {
@@ -947,7 +1092,7 @@ button:disabled {
top: 0px; top: 0px;
right: 0px; right: 0px;
text-align: center; text-align: center;
font-size: 8px; font-size: 10px;
} }
.tagBoxItem { .tagBoxItem {