From da29f3f08f2192cdce2d3a9ed3c55680421e0caf Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 26 Aug 2018 12:25:25 -0400 Subject: make beta download blurb configurable (#585) --- src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs index 3d428015..c0e4c4c8 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs @@ -13,6 +13,9 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels public string LogParserUrl { get; set; } /// Whether to show SMAPI beta versions on the main page, if any. - public bool EnableSmapiBeta { get; set; } + public bool BetaEnabled { get; set; } + + /// A short sentence shown under the beta download button, if any. + public string BetaBlurb { get; set; } } } -- cgit From 093117d777a84e3f1e3aaa8a1337059fb805a7dd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 2 Sep 2018 19:06:37 -0400 Subject: add update key parsing to toolkit (#592) --- src/SMAPI.Web/Controllers/ModsApiController.cs | 44 ++++----------- .../Framework/ConfigModels/ModUpdateCheckConfig.cs | 9 --- .../Framework/ModRepositories/BaseRepository.cs | 6 +- .../ModRepositories/ChucklefishRepository.cs | 7 +-- .../Framework/ModRepositories/GitHubRepository.cs | 7 +-- .../Framework/ModRepositories/IModRepository.cs | 3 +- .../Framework/ModRepositories/NexusRepository.cs | 7 +-- src/SMAPI.Web/appsettings.json | 5 -- .../Framework/UpdateData/ModRepositoryKey.cs | 18 ++++++ .../Framework/UpdateData/UpdateKey.cs | 65 ++++++++++++++++++++++ 10 files changed, 109 insertions(+), 62 deletions(-) create mode 100644 src/StardewModdingAPI.Toolkit/Framework/UpdateData/ModRepositoryKey.cs create mode 100644 src/StardewModdingAPI.Toolkit/Framework/UpdateData/UpdateKey.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 3d05da16..5ca8c94c 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -12,6 +12,7 @@ using StardewModdingAPI.Toolkit; using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; using StardewModdingAPI.Toolkit.Framework.ModData; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.Chucklefish; using StardewModdingAPI.Web.Framework.Clients.GitHub; using StardewModdingAPI.Web.Framework.Clients.Nexus; @@ -29,7 +30,7 @@ namespace StardewModdingAPI.Web.Controllers ** Properties *********/ /// The mod repositories which provide mod metadata. - private readonly IDictionary Repositories; + private readonly IDictionary Repositories; /// The cache in which to store mod metadata. private readonly IMemoryCache Cache; @@ -73,11 +74,11 @@ namespace StardewModdingAPI.Web.Controllers this.Repositories = new IModRepository[] { - new ChucklefishRepository(config.ChucklefishKey, chucklefish), - new GitHubRepository(config.GitHubKey, github), - new NexusRepository(config.NexusKey, nexus) + new ChucklefishRepository(chucklefish), + new GitHubRepository(github), + new NexusRepository(nexus) } - .ToDictionary(p => p.VendorKey, StringComparer.CurrentCultureIgnoreCase); + .ToDictionary(p => p.VendorKey); } /// Fetch version metadata for the given mods. @@ -189,28 +190,6 @@ namespace StardewModdingAPI.Web.Controllers return result; } - /// Parse a namespaced mod ID. - /// The raw mod ID to parse. - /// The parsed vendor key. - /// The parsed mod ID. - /// Returns whether the value could be parsed. - private bool TryParseModKey(string raw, out string vendorKey, out string modID) - { - // split parts - string[] parts = raw?.Split(':'); - if (parts == null || parts.Length != 2) - { - vendorKey = null; - modID = null; - return false; - } - - // parse - vendorKey = parts[0].Trim(); - modID = parts[1].Trim(); - return true; - } - /// Get whether a version is newer than an version. /// The current version. /// The other version. @@ -244,17 +223,18 @@ namespace StardewModdingAPI.Web.Controllers private async Task GetInfoForUpdateKeyAsync(string updateKey) { // parse update key - if (!this.TryParseModKey(updateKey, out string vendorKey, out string modID)) + UpdateKey parsed = UpdateKey.Parse(updateKey); + if (!parsed.LooksValid) return new ModInfoModel($"The update key '{updateKey}' isn't in a valid format. It should contain the site key and mod ID like 'Nexus:541'."); // get matching repository - if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository)) - return new ModInfoModel($"There's no mod site with key '{vendorKey}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}]."); + if (!this.Repositories.TryGetValue(parsed.Repository, out IModRepository repository)) + return new ModInfoModel($"There's no mod site with key '{parsed.Repository}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}]."); // fetch mod info - return await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{modID}".ToLower(), async entry => + return await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{parsed.ID}".ToLower(), async entry => { - ModInfoModel result = await repository.GetModInfoAsync(modID); + ModInfoModel result = await repository.GetModInfoAsync(parsed.ID); if (result.Error != null) { if (result.Version == null) diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs index ce4f3cb5..5eef7c55 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs @@ -16,15 +16,6 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// Derived from SMAPI's SemanticVersion implementation. public string SemanticVersionRegex { get; set; } - /// The repository key for the Chucklefish mod site. - public string ChucklefishKey { get; set; } - - /// The repository key for Nexus Mods. - public string GitHubKey { get; set; } - - /// The repository key for Nexus Mods. - public string NexusKey { get; set; } - /// The web URL for the wiki compatibility list. public string WikiCompatibilityPageUrl { get; set; } } diff --git a/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs index 4a4a40cd..94256005 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; namespace StardewModdingAPI.Web.Framework.ModRepositories { @@ -10,7 +10,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Accessors *********/ /// The unique key for this vendor. - public string VendorKey { get; } + public ModRepositoryKey VendorKey { get; } /********* @@ -29,7 +29,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories *********/ /// Construct an instance. /// The unique key for this vendor. - protected RepositoryBase(string vendorKey) + protected RepositoryBase(ModRepositoryKey vendorKey) { this.VendorKey = vendorKey; } diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs index e6074a60..6e2a8814 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.Chucklefish; namespace StardewModdingAPI.Web.Framework.ModRepositories @@ -19,10 +19,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Public methods *********/ /// Construct an instance. - /// The unique key for this vendor. /// The underlying HTTP client. - public ChucklefishRepository(string vendorKey, IChucklefishClient client) - : base(vendorKey) + public ChucklefishRepository(IChucklefishClient client) + : base(ModRepositoryKey.Chucklefish) { this.Client = client; } diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs index 1d7e4fff..7ff22d0e 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.GitHub; namespace StardewModdingAPI.Web.Framework.ModRepositories @@ -19,10 +19,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Public methods *********/ /// Construct an instance. - /// The unique key for this vendor. /// The underlying GitHub API client. - public GitHubRepository(string vendorKey, IGitHubClient client) - : base(vendorKey) + public GitHubRepository(IGitHubClient client) + : base(ModRepositoryKey.GitHub) { this.Client = client; } diff --git a/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs index 09c59a86..68f754ae 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/IModRepository.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using StardewModdingAPI.Toolkit.Framework.UpdateData; namespace StardewModdingAPI.Web.Framework.ModRepositories { @@ -10,7 +11,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Accessors *********/ /// The unique key for this vendor. - string VendorKey { get; } + ModRepositoryKey VendorKey { get; } /********* diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs index 4afcda10..1e242c60 100644 --- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs +++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using StardewModdingAPI.Toolkit.Framework.Clients.WebApi; +using StardewModdingAPI.Toolkit.Framework.UpdateData; using StardewModdingAPI.Web.Framework.Clients.Nexus; namespace StardewModdingAPI.Web.Framework.ModRepositories @@ -19,10 +19,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories ** Public methods *********/ /// Construct an instance. - /// The unique key for this vendor. /// The underlying Nexus Mods API client. - public NexusRepository(string vendorKey, INexusClient client) - : base(vendorKey) + public NexusRepository(INexusClient client) + : base(ModRepositoryKey.Nexus) { this.Client = client; } diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index bd948aa0..2c6aa0cc 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -47,11 +47,6 @@ "SuccessCacheMinutes": 60, "ErrorCacheMinutes": 5, "SemanticVersionRegex": "^(?>(?0|[1-9]\\d*))\\.(?>(?0|[1-9]\\d*))(?>(?:\\.(?0|[1-9]\\d*))?)(?:-(?(?>[a-z0-9]+[\\-\\.]?)+))?$", - - "ChucklefishKey": "Chucklefish", - "GitHubKey": "GitHub", - "NexusKey": "Nexus", - "WikiCompatibilityPageUrl": "https://smapi.io/compat" } } diff --git a/src/StardewModdingAPI.Toolkit/Framework/UpdateData/ModRepositoryKey.cs b/src/StardewModdingAPI.Toolkit/Framework/UpdateData/ModRepositoryKey.cs new file mode 100644 index 00000000..7ca32f04 --- /dev/null +++ b/src/StardewModdingAPI.Toolkit/Framework/UpdateData/ModRepositoryKey.cs @@ -0,0 +1,18 @@ +namespace StardewModdingAPI.Toolkit.Framework.UpdateData +{ + /// A mod repository which SMAPI can check for updates. + public enum ModRepositoryKey + { + /// An unknown or invalid mod repository. + Unknown, + + /// The Chucklefish mod repository. + Chucklefish, + + /// A GitHub project containing releases. + GitHub, + + /// The Nexus Mods mod repository. + Nexus + } +} diff --git a/src/StardewModdingAPI.Toolkit/Framework/UpdateData/UpdateKey.cs b/src/StardewModdingAPI.Toolkit/Framework/UpdateData/UpdateKey.cs new file mode 100644 index 00000000..49b7a272 --- /dev/null +++ b/src/StardewModdingAPI.Toolkit/Framework/UpdateData/UpdateKey.cs @@ -0,0 +1,65 @@ +using System; + +namespace StardewModdingAPI.Toolkit.Framework.UpdateData +{ + /// A namespaced mod ID which uniquely identifies a mod within a mod repository. + public class UpdateKey + { + /********* + ** Accessors + *********/ + /// The raw update key text. + public string RawText { get; } + + /// The mod repository containing the mod. + public ModRepositoryKey Repository { get; } + + /// The mod ID within the repository. + public string ID { get; } + + /// Whether the update key seems to be valid. + public bool LooksValid { get; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The raw update key text. + /// The mod repository containing the mod. + /// The mod ID within the repository. + public UpdateKey(string rawText, ModRepositoryKey repository, string id) + { + this.RawText = rawText; + this.Repository = repository; + this.ID = id; + this.LooksValid = + repository != ModRepositoryKey.Unknown + && !string.IsNullOrWhiteSpace(id); + } + + /// Parse a raw update key. + /// The raw update key to parse. + public static UpdateKey Parse(string raw) + { + // split parts + string[] parts = raw?.Split(':'); + if (parts == null || parts.Length != 2) + return new UpdateKey(raw, ModRepositoryKey.Unknown, null); + + // extract parts + string repositoryKey = parts[0].Trim(); + string id = parts[1].Trim(); + if (string.IsNullOrWhiteSpace(id)) + id = null; + + // parse + if (!Enum.TryParse(repositoryKey, true, out ModRepositoryKey repository)) + return new UpdateKey(raw, ModRepositoryKey.Unknown, id); + if (id == null) + return new UpdateKey(raw, repository, null); + + return new UpdateKey(raw, repository, id); + } + } +} -- cgit From 7829df45cbe563ecbf95b84ffbc8cc86e06b1c92 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 8 Oct 2018 20:10:12 -0400 Subject: track game logs separately in log parser --- docs/release-notes.md | 5 ++++- src/SMAPI.Web/Framework/LogParsing/LogParser.cs | 25 ++++++++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/docs/release-notes.md b/docs/release-notes.md index 4e4c5dd9..6ce0169a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,7 +1,7 @@ # Release notes ## 2.8 (upcoming) * For players: - * Update checks now work even when the mod has no update keys in most cases. + * Update checks now work even for mods without update keys in most cases. * Reorganised SMAPI files: * You can now group mods into subfolders to organise them. * Most SMAPI files are now tucked into a `smapi-internal` subfolder. @@ -19,6 +19,9 @@ * Fixed some errors logged as SMAPI instead of the affected mod. * Updated compatibility list. +* For the web UI: + * The log parser now has a separate filter for game messages. + * For modders: * Added [data API](https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Data). * Added `IContentPack.WriteJsonFile` method. diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs index 013c6c47..f9b5ba76 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs @@ -70,6 +70,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing // parse log messages LogModInfo smapiMod = new LogModInfo { Name = "SMAPI", Author = "Pathoschild", Description = "" }; + LogModInfo gameMod = new LogModInfo { Name = "game", Author = "", Description = "" }; IDictionary mods = new Dictionary(); bool inModList = false; bool inContentPackList = false; @@ -78,10 +79,23 @@ namespace StardewModdingAPI.Web.Framework.LogParsing // collect stats if (message.Level == LogLevel.Error) { - if (message.Mod == "SMAPI") - smapiMod.Errors++; - else if (mods.ContainsKey(message.Mod)) - mods[message.Mod].Errors++; + switch (message.Mod) + { + case "SMAPI": + smapiMod.Errors++; + break; + + case "game": + gameMod.Errors++; + break; + + default: + { + if (mods.ContainsKey(message.Mod)) + mods[message.Mod].Errors++; + break; + } + } } // collect SMAPI metadata @@ -151,7 +165,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing } // finalise log - log.Mods = new[] { smapiMod }.Concat(mods.Values.OrderBy(p => p.Name)).ToArray(); + gameMod.Version = log.GameVersion; + log.Mods = new[] { gameMod, smapiMod }.Concat(mods.Values.OrderBy(p => p.Name)).ToArray(); return log; } catch (LogParseException ex) -- cgit From 28fdb9e4e7f5419947226171bf6d7efa273802c5 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 20 Oct 2018 15:10:44 -0400 Subject: add mod compatibility page (#597) --- src/SMAPI.Web/Controllers/ModsController.cs | 33 +++++++ src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs | 3 + src/SMAPI.Web/StardewModdingAPI.Web.csproj | 3 + src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs | 34 +++++++ src/SMAPI.Web/ViewModels/ModLinkModel.cs | 28 ++++++ src/SMAPI.Web/ViewModels/ModListModel.cs | 36 +++++++ src/SMAPI.Web/ViewModels/ModModel.cs | 107 +++++++++++++++++++++ src/SMAPI.Web/Views/Mods/Index.cshtml | 72 ++++++++++++++ src/SMAPI.Web/Views/Shared/_Layout.cshtml | 1 + src/SMAPI.Web/appsettings.Development.json | 1 + src/SMAPI.Web/appsettings.json | 1 + src/SMAPI.Web/wwwroot/Content/css/mods.css | 85 ++++++++++++++++ src/SMAPI.Web/wwwroot/Content/js/mods.js | 56 +++++++++++ 13 files changed, 460 insertions(+) create mode 100644 src/SMAPI.Web/Controllers/ModsController.cs create mode 100644 src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs create mode 100644 src/SMAPI.Web/ViewModels/ModLinkModel.cs create mode 100644 src/SMAPI.Web/ViewModels/ModListModel.cs create mode 100644 src/SMAPI.Web/ViewModels/ModModel.cs create mode 100644 src/SMAPI.Web/Views/Mods/Index.cshtml create mode 100644 src/SMAPI.Web/wwwroot/Content/css/mods.css create mode 100644 src/SMAPI.Web/wwwroot/Content/js/mods.js (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Controllers/ModsController.cs b/src/SMAPI.Web/Controllers/ModsController.cs new file mode 100644 index 00000000..99d19f76 --- /dev/null +++ b/src/SMAPI.Web/Controllers/ModsController.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using StardewModdingAPI.Toolkit; +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; +using StardewModdingAPI.Web.ViewModels; + +namespace StardewModdingAPI.Web.Controllers +{ + /// Provides user-friendly info about SMAPI mods. + internal class ModsController : Controller + { + /********* + ** Public methods + *********/ + /// Display information for all mods. + [HttpGet] + [Route("mods")] + public async Task Index() + { + WikiModEntry[] mods = await new ModToolkit().GetWikiCompatibilityListAsync(); + ModListModel viewModel = new ModListModel( + stableVersion: "1.3.28", + betaVersion: "1.3.31-beta", + mods: mods + .Select(mod => new ModModel(mod)) + .OrderBy(p => Regex.Replace(p.Name.ToLower(), "[^a-z0-9]", "")) // ignore case, spaces, and special characters when sorting + ); + return this.View("Index", viewModel); + } + } +} diff --git a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs index c0e4c4c8..d89a4260 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/SiteConfig.cs @@ -12,6 +12,9 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// The root URL for the log parser. public string LogParserUrl { get; set; } + /// The root URL for the mod list. + public string ModListUrl { get; set; } + /// Whether to show SMAPI beta versions on the main page, if any. public bool BetaEnabled { get; set; } diff --git a/src/SMAPI.Web/StardewModdingAPI.Web.csproj b/src/SMAPI.Web/StardewModdingAPI.Web.csproj index dc87cc98..4814d169 100644 --- a/src/SMAPI.Web/StardewModdingAPI.Web.csproj +++ b/src/SMAPI.Web/StardewModdingAPI.Web.csproj @@ -27,6 +27,9 @@ + + $(IncludeRazorContentInPack) + PreserveNewest diff --git a/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs new file mode 100644 index 00000000..d331c093 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs @@ -0,0 +1,34 @@ +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; + +namespace StardewModdingAPI.Web.ViewModels +{ + /// Metadata about a mod's compatibility with the latest versions of SMAPI and Stardew Valley. + public class ModCompatibilityModel + { + /********* + ** Accessors + *********/ + /// The compatibility status, as a string like "Broken". + public string Status { get; set; } + + /// A link to the unofficial version which fixes compatibility, if any. + public ModLinkModel UnofficialVersion { get; set; } + + /// The human-readable summary, as an HTML block. + public string Summary { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The mod metadata. + public ModCompatibilityModel(WikiCompatibilityInfo info) + { + this.Status = info.Status.ToString(); + if (info.UnofficialVersion != null) + this.UnofficialVersion = new ModLinkModel(info.UnofficialUrl, info.UnofficialVersion.ToString()); + this.Summary = info.Summary; + } + } +} diff --git a/src/SMAPI.Web/ViewModels/ModLinkModel.cs b/src/SMAPI.Web/ViewModels/ModLinkModel.cs new file mode 100644 index 00000000..97dd215c --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModLinkModel.cs @@ -0,0 +1,28 @@ +namespace StardewModdingAPI.Web.ViewModels +{ + /// Metadata about a link. + public class ModLinkModel + { + /********* + ** Accessors + *********/ + /// The URL of the linked page. + public string Url { get; set; } + + /// The suggested link text. + public string Text { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The URL of the linked page. + /// The suggested link text. + public ModLinkModel(string url, string text) + { + this.Url = url; + this.Text = text; + } + } +} diff --git a/src/SMAPI.Web/ViewModels/ModListModel.cs b/src/SMAPI.Web/ViewModels/ModListModel.cs new file mode 100644 index 00000000..3b87d393 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModListModel.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Linq; + +namespace StardewModdingAPI.Web.ViewModels +{ + /// Metadata for the mod list page. + public class ModListModel + { + /********* + ** Accessors + *********/ + /// The current stable version of the game. + public string StableVersion { get; set; } + + /// The current beta version of the game (if any). + public string BetaVersion { get; set; } + + /// The mods to display. + public ModModel[] Mods { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The current stable version of the game. + /// The current beta version of the game (if any). + /// The mods to display. + public ModListModel(string stableVersion, string betaVersion, IEnumerable mods) + { + this.StableVersion = stableVersion; + this.BetaVersion = betaVersion; + this.Mods = mods.ToArray(); + } + } +} diff --git a/src/SMAPI.Web/ViewModels/ModModel.cs b/src/SMAPI.Web/ViewModels/ModModel.cs new file mode 100644 index 00000000..4fb9d5b5 --- /dev/null +++ b/src/SMAPI.Web/ViewModels/ModModel.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Linq; +using StardewModdingAPI.Toolkit.Framework.Clients.Wiki; + +namespace StardewModdingAPI.Web.ViewModels +{ + /// Metadata about a mod. + public class ModModel + { + /********* + ** Accessors + *********/ + /// The mod name. + public string Name { get; set; } + + /// The mod's alternative names, if any. + public string AlternateNames { get; set; } + + /// The mod author's name. + public string Author { get; set; } + + /// The mod author's alternative names, if any. + public string AlternateAuthors { get; set; } + + /// The URL to the mod's source code, if any. + public string SourceUrl { get; set; } + + /// The compatibility status for the stable version of the game. + public ModCompatibilityModel Compatibility { get; set; } + + /// The compatibility status for the beta version of the game. + public ModCompatibilityModel BetaCompatibility { get; set; } + + /// Links to the available mod pages. + public ModLinkModel[] ModPages { get; set; } + + /// The game or SMAPI version which broke this mod (if applicable). + public string BrokeIn { get; set; } + + /// A unique identifier for the mod that can be used in an anchor URL. + public string Slug { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The mod metadata. + public ModModel(WikiModEntry entry) + { + // basic info + this.Name = entry.Name; + this.AlternateNames = entry.AlternateNames; + this.Author = entry.Author; + this.AlternateAuthors = entry.AlternateAuthors; + this.SourceUrl = this.GetSourceUrl(entry); + this.Compatibility = new ModCompatibilityModel(entry.Compatibility); + this.BetaCompatibility = entry.BetaCompatibility != null ? new ModCompatibilityModel(entry.BetaCompatibility) : null; + this.ModPages = this.GetModPageUrls(entry).ToArray(); + this.BrokeIn = entry.BrokeIn; + this.Slug = entry.Anchor; + } + + + /********* + ** Private methods + *********/ + /// Get the web URL for the mod's source code repository, if any. + /// The mod metadata. + private string GetSourceUrl(WikiModEntry entry) + { + if (!string.IsNullOrWhiteSpace(entry.GitHubRepo)) + return $"https://github.com/{entry.GitHubRepo}"; + if (!string.IsNullOrWhiteSpace(entry.CustomSourceUrl)) + return entry.CustomSourceUrl; + return null; + } + + /// Get the web URLs for the mod pages, if any. + /// The mod metadata. + private IEnumerable GetModPageUrls(WikiModEntry entry) + { + bool anyFound = false; + + // normal mod pages + if (entry.NexusID.HasValue) + { + anyFound = true; + yield return new ModLinkModel($"https://www.nexusmods.com/stardewvalley/mods/{entry.NexusID}", "Nexus"); + } + if (entry.ChucklefishID.HasValue) + { + anyFound = true; + yield return new ModLinkModel($"https://community.playstarbound.com/resources/{entry.ChucklefishID}", "Chucklefish"); + } + + // fallback + if (!anyFound && !string.IsNullOrWhiteSpace(entry.CustomUrl)) + { + anyFound = true; + yield return new ModLinkModel(entry.CustomUrl, "custom"); + } + if (!anyFound && !string.IsNullOrWhiteSpace(entry.GitHubRepo)) + yield return new ModLinkModel($"https://github.com/{entry.GitHubRepo}/releases", "GitHub"); + } + } +} diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml new file mode 100644 index 00000000..2b33fcaf --- /dev/null +++ b/src/SMAPI.Web/Views/Mods/Index.cshtml @@ -0,0 +1,72 @@ +@using Newtonsoft.Json +@model StardewModdingAPI.Web.ViewModels.ModListModel +@{ + ViewData["Title"] = "SMAPI mod compatibility"; +} +@section Head { + + + + + +} + +

This page lists all known SMAPI mods, whether they're compatible with the latest versions of Stardew Valley and SMAPI, and how to fix broken mods if possible. The list is updated every few days. (You can help edit this list!)

+ +@if (Model.BetaVersion != null) +{ +
+

Note: "SDV beta only" means Stardew Valley @Model.BetaVersion; if you didn't opt in to the beta, you have the stable version and can ignore that line. If a mod doesn't have a "SDV beta only" line, the compatibility applies to both versions of the game.

+
+} + +
+ + + + + + + + + + + + + + + + + + + + + +
mod namelinksauthorcompatibilitybroke incode 
+ {{mod.Name}} + (aka {{mod.AlternateNames}}) + + {{mod.Author}} + (aka {{mod.AlternateAuthors}}) + +
+
+ SDV beta only: + +
+
+ source + no source + + # +
+
diff --git a/src/SMAPI.Web/Views/Shared/_Layout.cshtml b/src/SMAPI.Web/Views/Shared/_Layout.cshtml index 29da9100..4c602b29 100644 --- a/src/SMAPI.Web/Views/Shared/_Layout.cshtml +++ b/src/SMAPI.Web/Views/Shared/_Layout.cshtml @@ -16,6 +16,7 @@

SMAPI

diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index dc22791b..db90a3de 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -19,6 +19,7 @@ "Site": { "RootUrl": "http://localhost:59482/", + "ModListUrl": "http://localhost:59482/mods/", "LogParserUrl": "http://localhost:59482/log/", "BetaEnabled": false, "BetaBlurb": null diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 2c6aa0cc..401b885f 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -16,6 +16,7 @@ "Site": { "RootUrl": null, // see top note + "ModListUrl": null, // see top note "LogParserUrl": null, // see top note "BetaEnabled": null, // see top note "BetaBlurb": null // see top note diff --git a/src/SMAPI.Web/wwwroot/Content/css/mods.css b/src/SMAPI.Web/wwwroot/Content/css/mods.css new file mode 100644 index 00000000..d250440f --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/css/mods.css @@ -0,0 +1,85 @@ +/********* +** Intro +*********/ +#content { + max-width: calc(100% - 2em); /* allow for wider table if room available */ +} + +#blurb { + margin-top: 0; + width: 50em; +} + +#beta-blurb { + width: 50em; + margin-bottom: 2em; + padding: 1em; + border: 3px solid darkgreen; +} + +table.wikitable { + background-color:#f8f9fa; + color:#222; + margin:1em 0; + border:1px solid #a2a9b1; + border-collapse:collapse +} + +table.wikitable > tr > th, +table.wikitable > tr > td, +table.wikitable > * > tr > th, +table.wikitable > * > tr > td { + border:1px solid #a2a9b1; + padding:0.2em 0.4em +} + +table.wikitable > tr > th, +table.wikitable > * > tr > th { + background-color:#eaecf0; +} + +table.wikitable > caption { + font-weight:bold +} + +#mod-list .mod-page-links, +#mod-list .mod-alt-authors, +#mod-list .mod-alt-names, +#mod-list .mod-broke-in { + font-size: 0.8em; +} + +#mod-list .mod-alt-authors, +#mod-list .mod-alt-names { + display: block; +} + +#mod-list tr { + font-size: 0.9em; +} + +#mod-list tr[data-status="Ok"], +#mod-list tr[data-status="Optional"] { + background: #9F9; +} + +#mod-list tr[data-status="Workaround"], +#mod-list tr[data-status="Unofficial"] { + background: #CF9; +} + +#mod-list tr[data-status="Broken"] { + background: #F99; +} + +#mod-list tr[data-status="Obsolete"], +#mod-list tr[data-status="Abandoned"] { + background: #999; + opacity: 0.7; +} + +#mod-list .mod-closed-source { + color: red; + font-size: 0.8em; + opacity: 0.5; +} diff --git a/src/SMAPI.Web/wwwroot/Content/js/mods.js b/src/SMAPI.Web/wwwroot/Content/js/mods.js new file mode 100644 index 00000000..1b15b622 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/js/mods.js @@ -0,0 +1,56 @@ +/* globals $ */ + +var smapi = smapi || {}; +var app; +smapi.modList = function (mods) { + // init data + var data = { mods: mods, search: "" }; + for (var i = 0; i < data.mods.length; i++) { + var mod = mods[i]; + + // set initial visibility + mod.Visible = true; + + // concatenate searchable text + mod.SearchableText = [mod.Name, mod.AlternateNames, mod.Author, mod.AlternateAuthors, mod.Compatibility.Summary, mod.BrokeIn]; + if (mod.Compatibility.UnofficialVersion) + mod.SearchableText.push(mod.Compatibility.UnofficialVersion); + if (mod.BetaCompatibility) { + mod.SearchableText.push(mod.BetaCompatibility.Summary); + if (mod.BetaCompatibility.UnofficialVersion) + mod.SearchableText.push(mod.BetaCompatibility.UnofficialVersion); + } + for (var p = 0; p < mod.ModPages; p++) + mod.SearchableField.push(mod.ModPages[p].Text); + mod.SearchableText = mod.SearchableText.join(" ").toLowerCase(); + } + + // init app + app = new Vue({ + el: "#app", + data: data, + methods: { + /** + * Update the visibility of all mods based on the current search text. + */ + applySearch: function () { + // get search terms + var words = data.search.toLowerCase().split(" "); + + // make sure all words match + for (var i = 0; i < data.mods.length; i++) { + var mod = data.mods[i]; + var match = true; + for (var w = 0; w < words.length; w++) { + if (mod.SearchableText.indexOf(words[w]) === -1) { + match = false; + break; + } + } + + mod.Visible = match; + } + } + } + }); +}; -- cgit From 724d3fccb1c7a5ec842314436126271c5697129a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 4 Nov 2018 23:48:55 -0500 Subject: update mod compatibility page URLs --- src/SMAPI.Web/Controllers/ModsApiController.cs | 10 +++++----- src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs | 2 +- src/SMAPI.Web/Startup.cs | 2 +- src/SMAPI.Web/appsettings.json | 2 +- src/SMAPI/Framework/ModLoading/ModResolver.cs | 2 +- src/SMAPI/Framework/SCore.cs | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 6e517a97..05568d5e 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -47,8 +47,8 @@ namespace StardewModdingAPI.Web.Controllers /// The internal mod metadata list. private readonly ModDatabase ModDatabase; - /// The web URL for the wiki compatibility list. - private readonly string WikiCompatibilityPageUrl; + /// The web URL for the compatibility list. + private readonly string CompatibilityPageUrl; /********* @@ -65,7 +65,7 @@ namespace StardewModdingAPI.Web.Controllers { this.ModDatabase = new ModToolkit().GetModDatabase(Path.Combine(environment.WebRootPath, "StardewModdingAPI.metadata.json")); ModUpdateCheckConfig config = configProvider.Value; - this.WikiCompatibilityPageUrl = config.WikiCompatibilityPageUrl; + this.CompatibilityPageUrl = config.CompatibilityPageUrl; this.Cache = cache; this.SuccessCacheMinutes = config.SuccessCacheMinutes; @@ -163,7 +163,7 @@ namespace StardewModdingAPI.Web.Controllers // get unofficial version if (wikiEntry?.Compatibility.UnofficialVersion != null && this.IsNewer(wikiEntry.Compatibility.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.Compatibility.UnofficialVersion, result.Optional?.Version)) - result.Unofficial = new ModEntryVersionModel(wikiEntry.Compatibility.UnofficialVersion, this.WikiCompatibilityPageUrl); + result.Unofficial = new ModEntryVersionModel(wikiEntry.Compatibility.UnofficialVersion, $"{this.CompatibilityPageUrl}/#{wikiEntry.Anchor}"); // get unofficial version for beta if (wikiEntry?.HasBetaInfo == true) @@ -174,7 +174,7 @@ namespace StardewModdingAPI.Web.Controllers if (wikiEntry.BetaCompatibility.UnofficialVersion != null) { result.UnofficialForBeta = (wikiEntry.BetaCompatibility.UnofficialVersion != null && this.IsNewer(wikiEntry.BetaCompatibility.UnofficialVersion, result.Main?.Version) && this.IsNewer(wikiEntry.BetaCompatibility.UnofficialVersion, result.Optional?.Version)) - ? new ModEntryVersionModel(wikiEntry.BetaCompatibility.UnofficialVersion, this.WikiCompatibilityPageUrl) + ? new ModEntryVersionModel(wikiEntry.BetaCompatibility.UnofficialVersion, $"{this.CompatibilityPageUrl}/#{wikiEntry.Anchor}") : null; } else diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs index 5eef7c55..bde566c0 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs @@ -17,6 +17,6 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels public string SemanticVersionRegex { get; set; } /// The web URL for the wiki compatibility list. - public string WikiCompatibilityPageUrl { get; set; } + public string CompatibilityPageUrl { get; set; } } } diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs index 82abf17d..60a16053 100644 --- a/src/SMAPI.Web/Startup.cs +++ b/src/SMAPI.Web/Startup.cs @@ -154,7 +154,7 @@ namespace StardewModdingAPI.Web // shortcut redirects redirects.Add(new RedirectToUrlRule(@"^/buildmsg(?:/?(.*))$", "https://github.com/Pathoschild/SMAPI/blob/develop/docs/mod-build-config.md#$1")); - redirects.Add(new RedirectToUrlRule(@"^/compat\.?$", "https://stardewvalleywiki.com/Modding:SMAPI_compatibility")); + redirects.Add(new RedirectToUrlRule(@"^/compat\.?$", "https://mods.smapi.io")); redirects.Add(new RedirectToUrlRule(@"^/docs\.?$", "https://stardewvalleywiki.com/Modding:Index")); redirects.Add(new RedirectToUrlRule(@"^/install\.?$", "https://stardewvalleywiki.com/Modding:Player_Guide/Getting_Started#Install_SMAPI")); diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 401b885f..aba8c448 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -48,6 +48,6 @@ "SuccessCacheMinutes": 60, "ErrorCacheMinutes": 5, "SemanticVersionRegex": "^(?>(?0|[1-9]\\d*))\\.(?>(?0|[1-9]\\d*))(?>(?:\\.(?0|[1-9]\\d*))?)(?:-(?(?>[a-z0-9]+[\\-\\.]?)+))?$", - "WikiCompatibilityPageUrl": "https://smapi.io/compat" + "CompatibilityPageUrl": "https://mods.smapi.io" } } diff --git a/src/SMAPI/Framework/ModLoading/ModResolver.cs b/src/SMAPI/Framework/ModLoading/ModResolver.cs index 0264f61f..ace84054 100644 --- a/src/SMAPI/Framework/ModLoading/ModResolver.cs +++ b/src/SMAPI/Framework/ModLoading/ModResolver.cs @@ -87,7 +87,7 @@ namespace StardewModdingAPI.Framework.ModLoading updateUrls.Add(mod.DataRecord.AlternativeUrl); // default update URL - updateUrls.Add("https://smapi.io/compat"); + updateUrls.Add("https://mods.smapi.io"); // build error string error = $"{reasonPhrase}. Please check for a "; diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs index f078acba..5f2e90aa 100644 --- a/src/SMAPI/Framework/SCore.cs +++ b/src/SMAPI/Framework/SCore.cs @@ -942,7 +942,7 @@ namespace StardewModdingAPI.Framework } catch (IncompatibleInstructionException) // details already in trace logs { - string[] updateUrls = new[] { modDatabase.GetModPageUrlFor(manifest.UniqueID), "https://smapi.io/compat" }.Where(p => p != null).ToArray(); + string[] updateUrls = new[] { modDatabase.GetModPageUrlFor(manifest.UniqueID), "https://mods.smapi.io" }.Where(p => p != null).ToArray(); errorReasonPhrase = $"it's no longer compatible. Please check for a new version at {string.Join(" or ", updateUrls)}."; return false; } -- cgit From 3a4ad38e05d1d403cff34d5717ad95df24df9b81 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 17 Nov 2018 11:12:11 -0500 Subject: fix broken link in docs, rm unneeded using --- docs/technical-docs.md | 2 +- src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/docs/technical-docs.md b/docs/technical-docs.md index 08590cba..4a8f45f1 100644 --- a/docs/technical-docs.md +++ b/docs/technical-docs.md @@ -16,7 +16,7 @@ mods, this section isn't relevant to you; see the main README to use or create m * [SMAPI web services](#smapi-web-services) * [Overview](#overview) * [Log parser](#log-parser) - * [Mods API](#mods-api) + * [Web API](#web-api) * [Development](#development-2) * [Local development](#local-development) * [Deploying to Amazon Beanstalk](#deploying-to-amazon-beanstalk) diff --git a/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs b/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs index 4ecf2f76..f4909155 100644 --- a/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs +++ b/src/SMAPI.Web/Framework/Clients/Nexus/NexusMod.cs @@ -1,5 +1,4 @@ using Newtonsoft.Json; -using StardewModdingAPI.Toolkit; namespace StardewModdingAPI.Web.Framework.Clients.Nexus { -- cgit