From e94aaaf7c8dccd0910d6275860821b16c90ef6c6 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 27 Oct 2018 20:37:42 -0400 Subject: update for changes to wiki compatibility list (#597) --- src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs | 12 ++- src/SMAPI.Web/ViewModels/ModModel.cs | 12 +-- src/SMAPI.Web/Views/Mods/Index.cshtml | 2 +- .../Clients/WebApi/ModExtendedMetadataModel.cs | 2 +- .../Framework/Clients/Wiki/WikiClient.cs | 112 ++++++++++++--------- .../Clients/Wiki/WikiCompatibilityInfo.cs | 3 + .../Framework/Clients/Wiki/WikiModEntry.cs | 22 ++-- 7 files changed, 92 insertions(+), 73 deletions(-) (limited to 'src') diff --git a/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs index d331c093..61756176 100644 --- a/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs +++ b/src/SMAPI.Web/ViewModels/ModCompatibilityModel.cs @@ -11,12 +11,15 @@ namespace StardewModdingAPI.Web.ViewModels /// 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; } + /// The game or SMAPI version which broke this mod (if applicable). + public string BrokeIn { get; set; } + + /// A link to the unofficial version which fixes compatibility, if any. + public ModLinkModel UnofficialVersion { get; set; } + /********* ** Public methods @@ -26,9 +29,10 @@ namespace StardewModdingAPI.Web.ViewModels public ModCompatibilityModel(WikiCompatibilityInfo info) { this.Status = info.Status.ToString(); + this.Summary = info.Summary; + this.BrokeIn = info.BrokeIn; if (info.UnofficialVersion != null) this.UnofficialVersion = new ModLinkModel(info.UnofficialUrl, info.UnofficialVersion.ToString()); - this.Summary = info.Summary; } } } diff --git a/src/SMAPI.Web/ViewModels/ModModel.cs b/src/SMAPI.Web/ViewModels/ModModel.cs index 4fb9d5b5..1199fe20 100644 --- a/src/SMAPI.Web/ViewModels/ModModel.cs +++ b/src/SMAPI.Web/ViewModels/ModModel.cs @@ -34,9 +34,6 @@ namespace StardewModdingAPI.Web.ViewModels /// 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; } @@ -49,15 +46,14 @@ namespace StardewModdingAPI.Web.ViewModels public ModModel(WikiModEntry entry) { // basic info - this.Name = entry.Name; - this.AlternateNames = entry.AlternateNames; - this.Author = entry.Author; - this.AlternateAuthors = entry.AlternateAuthors; + this.Name = entry.Name.FirstOrDefault(); + this.AlternateNames = string.Join(", ", entry.Name.Skip(1).ToArray()); + this.Author = entry.Author.FirstOrDefault(); + this.AlternateAuthors = string.Join(", ", entry.Author.Skip(1).ToArray()); 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; } diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml index b2ab61d7..b2e20c7a 100644 --- a/src/SMAPI.Web/Views/Mods/Index.cshtml +++ b/src/SMAPI.Web/Views/Mods/Index.cshtml @@ -71,7 +71,7 @@ - + source no source diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs index e6f2e4b4..247730d7 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs @@ -74,7 +74,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.WebApi if (wiki != null) { this.ID = wiki.ID; - this.Name = wiki.Name; + this.Name = wiki.Name.FirstOrDefault(); this.NexusID = wiki.NexusID; this.ChucklefishID = wiki.ChucklefishID; this.GitHubRepo = wiki.GitHubRepo; diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs index 9be760f8..7197bf2c 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiClient.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Net; using System.Threading.Tasks; using HtmlAgilityPack; using Pathoschild.Http.Client; @@ -84,40 +85,40 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki foreach (HtmlNode node in nodes) { // extract fields - string name = this.GetMetadataField(node, "mod-name"); - string alternateNames = this.GetMetadataField(node, "mod-name2"); - string author = this.GetMetadataField(node, "mod-author"); - string alternateAuthors = this.GetMetadataField(node, "mod-author2"); - string[] ids = this.GetMetadataField(node, "mod-id")?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray() ?? new string[0]; - int? nexusID = this.GetNullableIntField(node, "mod-nexus-id"); - int? chucklefishID = this.GetNullableIntField(node, "mod-cf-id"); - string githubRepo = this.GetMetadataField(node, "mod-github"); - string customSourceUrl = this.GetMetadataField(node, "mod-custom-source"); - string customUrl = this.GetMetadataField(node, "mod-url"); - string brokeIn = this.GetMetadataField(node, "mod-broke-in"); - string anchor = this.GetMetadataField(node, "mod-anchor"); + string[] names = this.GetAttributeAsCsv(node, "data-name"); + string[] authors = this.GetAttributeAsCsv(node, "data-author"); + string[] ids = this.GetAttributeAsCsv(node, "data-id"); + string[] warnings = this.GetAttributeAsCsv(node, "data-warnings"); + int? nexusID = this.GetAttributeAsNullableInt(node, "data-nexus-id"); + int? chucklefishID = this.GetAttributeAsNullableInt(node, "data-cf-id"); + string githubRepo = this.GetAttribute(node, "data-github"); + string customSourceUrl = this.GetAttribute(node, "data-custom-source"); + string customUrl = this.GetAttribute(node, "data-url"); + string anchor = this.GetAttribute(node, "id"); // parse stable compatibility WikiCompatibilityInfo compatibility = new WikiCompatibilityInfo { - Status = this.GetStatusField(node, "mod-status") ?? WikiCompatibilityStatus.Ok, - UnofficialVersion = this.GetSemanticVersionField(node, "mod-unofficial-version"), - UnofficialUrl = this.GetMetadataField(node, "mod-unofficial-url"), - Summary = this.GetMetadataField(node, "mod-summary")?.Trim() + Status = this.GetAttributeAsStatus(node, "data-status") ?? WikiCompatibilityStatus.Ok, + BrokeIn = this.GetAttribute(node, "data-broke-in"), + UnofficialVersion = this.GetAttributeAsSemanticVersion(node, "data-unofficial-version"), + UnofficialUrl = this.GetAttribute(node, "data-unofficial-url"), + Summary = this.GetInnerHtml(node, "mod-summary")?.Trim() }; // parse beta compatibility WikiCompatibilityInfo betaCompatibility = null; { - WikiCompatibilityStatus? betaStatus = this.GetStatusField(node, "mod-beta-status"); + WikiCompatibilityStatus? betaStatus = this.GetAttributeAsStatus(node, "data-beta-status"); if (betaStatus.HasValue) { betaCompatibility = new WikiCompatibilityInfo { Status = betaStatus.Value, - UnofficialVersion = this.GetSemanticVersionField(node, "mod-beta-unofficial-version"), - UnofficialUrl = this.GetMetadataField(node, "mod-beta-unofficial-url"), - Summary = this.GetMetadataField(node, "mod-beta-summary") + BrokeIn = this.GetAttribute(node, "data-beta-broke-in"), + UnofficialVersion = this.GetAttributeAsSemanticVersion(node, "data-beta-unofficial-version"), + UnofficialUrl = this.GetAttribute(node, "data-beta-unofficial-url"), + Summary = this.GetInnerHtml(node, "mod-beta-summary") }; } } @@ -126,37 +127,50 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki yield return new WikiModEntry { ID = ids, - Name = name, - AlternateNames = alternateNames, - Author = author, - AlternateAuthors = alternateAuthors, + Name = names, + Author = authors, NexusID = nexusID, ChucklefishID = chucklefishID, GitHubRepo = githubRepo, CustomSourceUrl = customSourceUrl, CustomUrl = customUrl, - BrokeIn = brokeIn, Compatibility = compatibility, BetaCompatibility = betaCompatibility, + Warnings = warnings, Anchor = anchor }; } } - /// Get the value of a metadata field. - /// The metadata container. - /// The field name. - private string GetMetadataField(HtmlNode container, string name) + /// Get an attribute value. + /// The element whose attributes to read. + /// The attribute name. + private string GetAttribute(HtmlNode element, string name) { - return container.Descendants().FirstOrDefault(p => p.HasClass(name))?.InnerHtml; + string value = element.GetAttributeValue(name, null); + if (string.IsNullOrWhiteSpace(value)) + return null; + + return WebUtility.HtmlDecode(value); } - /// Get the value of a metadata field as a compatibility status. - /// The metadata container. - /// The field name. - private WikiCompatibilityStatus? GetStatusField(HtmlNode container, string name) + /// Get an attribute value and parse it as a comma-delimited list of strings. + /// The element whose attributes to read. + /// The attribute name. + private string[] GetAttributeAsCsv(HtmlNode element, string name) + { + string raw = this.GetAttribute(element, name); + return !string.IsNullOrWhiteSpace(raw) + ? raw.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToArray() + : new string[0]; + } + + /// Get an attribute value and parse it as a compatibility status. + /// The element whose attributes to read. + /// The attribute name. + private WikiCompatibilityStatus? GetAttributeAsStatus(HtmlNode element, string name) { - string raw = this.GetMetadataField(container, name); + string raw = this.GetAttribute(element, name); if (raw == null) return null; if (!Enum.TryParse(raw, true, out WikiCompatibilityStatus status)) @@ -164,28 +178,36 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki return status; } - /// Get the value of a metadata field as a semantic version. - /// The metadata container. - /// The field name. - private ISemanticVersion GetSemanticVersionField(HtmlNode container, string name) + /// Get an attribute value and parse it as a semantic version. + /// The element whose attributes to read. + /// The attribute name. + private ISemanticVersion GetAttributeAsSemanticVersion(HtmlNode element, string name) { - string raw = this.GetMetadataField(container, name); + string raw = this.GetAttribute(element, name); return SemanticVersion.TryParse(raw, out ISemanticVersion version) ? version : null; } - /// Get the value of a metadata field as a nullable integer. - /// The metadata container. - /// The field name. - private int? GetNullableIntField(HtmlNode container, string name) + /// Get an attribute value and parse it as a nullable int. + /// The element whose attributes to read. + /// The attribute name. + private int? GetAttributeAsNullableInt(HtmlNode element, string name) { - string raw = this.GetMetadataField(container, name); + string raw = this.GetAttribute(element, name); if (raw != null && int.TryParse(raw, out int value)) return value; return null; } + /// Get the text of an element with the given class name. + /// The metadata container. + /// The field name. + private string GetInnerHtml(HtmlNode container, string className) + { + return container.Descendants().FirstOrDefault(p => p.HasClass(className))?.InnerHtml; + } + /// The response model for the MediaWiki parse API. [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs index 2725df1a..204acd2b 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityInfo.cs @@ -12,6 +12,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki /// The human-readable summary of the compatibility status or workaround, without HTML formatting. public string Summary { get; set; } + /// The game or SMAPI version which broke this mod (if applicable). + public string BrokeIn { get; set; } + /// The version of the latest unofficial update, if applicable. public ISemanticVersion UnofficialVersion { get; set; } diff --git a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs index 752b526c..ce8d6c5f 100644 --- a/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs +++ b/src/StardewModdingAPI.Toolkit/Framework/Clients/Wiki/WikiModEntry.cs @@ -6,20 +6,14 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki /********* ** Accessors *********/ - /// The mod's unique ID. A mod may have multiple current IDs in rare cases (e.g. due to parallel releases or unofficial updates). + /// The mod's unique ID. If the mod has alternate/old IDs, they're listed in latest to newest order. public string[] ID { get; set; } - /// The mod's display name. - public string Name { get; set; } + /// The mod's display name. If the mod has multiple names, the first one is the most canonical name. + public string[] Name { get; set; } - /// The mod's alternative names, if any. - public string AlternateNames { get; set; } - - /// The mod's author name. - public string Author { get; set; } - - /// The mod's alternative author names, if any. - public string AlternateAuthors { get; set; } + /// The mod's author name. If the author has multiple names, the first one is the most canonical name. + public string[] Author { get; set; } /// The mod ID on Nexus. public int? NexusID { get; set; } @@ -36,9 +30,6 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki /// The custom mod page URL (if applicable). public string CustomUrl { get; set; } - /// The game or SMAPI version which broke this mod (if applicable). - public string BrokeIn { get; set; } - /// The mod's compatibility with the latest stable version of the game. public WikiCompatibilityInfo Compatibility { get; set; } @@ -48,6 +39,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki /// Whether a Stardew Valley or SMAPI beta which affects mod compatibility is in progress. If this is true, should be used for beta versions of SMAPI instead of . public bool HasBetaInfo => this.BetaCompatibility != null; + /// The human-readable warnings for players about this mod. + public string[] Warnings { get; set; } + /// The link anchor for the mod entry in the wiki compatibility list. public string Anchor { get; set; } } -- cgit