diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-11-24 13:49:30 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-11-24 13:49:30 -0500 |
commit | a3f21685049cabf2d824c8060dc0b1de47e9449e (patch) | |
tree | ad9add30e9da2a50e0ea0245f1546b7378f0d282 /src/SMAPI.Web/Views | |
parent | 6521df7b131924835eb797251c1e956fae0d6e13 (diff) | |
parent | 277bf082675b98b95bf6184fe3c7a45b969c7ac2 (diff) | |
download | SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.gz SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.bz2 SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Web/Views')
-rw-r--r-- | src/SMAPI.Web/Views/Index/Index.cshtml | 7 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/Index/Privacy.cshtml | 4 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/JsonValidator/Index.cshtml | 151 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/LogParser/Index.cshtml | 22 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/Mods/Index.cshtml | 167 | ||||
-rw-r--r-- | src/SMAPI.Web/Views/Shared/_Layout.cshtml | 9 |
6 files changed, 272 insertions, 88 deletions
diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml index 249dc9d1..f42dde3b 100644 --- a/src/SMAPI.Web/Views/Index/Index.cshtml +++ b/src/SMAPI.Web/Views/Index/Index.cshtml @@ -52,7 +52,7 @@ <h2 id="help">Get help</h2> <ul> <li><a href="@SiteConfig.Value.ModListUrl">Mod compatibility list</a></li> - <li>Get help <a href="https://stardewvalleywiki.com/Modding:Community#Discord">on Discord</a> or <a href="https://community.playstarbound.com/threads/smapi-stardew-modding-api.108375/">in the forums</a></li> + <li>Get help <a href="https://smapi.io/community">on Discord or in the forums</a></li> </ul> @if (Model.BetaVersion == null) @@ -105,13 +105,12 @@ else <p> Special thanks to - <a href="https://www.nexusmods.com/stardewvalley/users/31393530">ChefRude</a>, - <a href="https://github.com/dittusch">dittusch</a>, hawkfalcon, <a href="https://twitter.com/iKeychain">iKeychain</a>, jwdred, <a href="https://www.nexusmods.com/users/12252523">Karmylla</a>, Pucklynn, + Renorien, Robby LaFarge, and a few anonymous users for their ongoing support on Patreon; you're awesome! </p> @@ -124,5 +123,5 @@ else <li><a href="@Model.BetaVersion.DevDownloadUrl">SMAPI @Model.BetaVersion.Version for developers</a> (includes <a href="https://docs.microsoft.com/en-us/visualstudio/ide/using-intellisense">intellisense</a> and full console output)</li> } <li><a href="https://stardewvalleywiki.com/Modding:Index">Modding documentation</a></li> - <li>Need help? Come <a href="https://stardewvalleywiki.com/Modding:Community#Discord">chat on Discord</a>.</li> + <li>Need help? Come <a href="https://smapi.io/community">chat on Discord</a>.</li> </ul> diff --git a/src/SMAPI.Web/Views/Index/Privacy.cshtml b/src/SMAPI.Web/Views/Index/Privacy.cshtml index ca99eef6..914384a8 100644 --- a/src/SMAPI.Web/Views/Index/Privacy.cshtml +++ b/src/SMAPI.Web/Views/Index/Privacy.cshtml @@ -24,12 +24,12 @@ <p>This website and SMAPI's web API are hosted by Amazon Web Services. Their servers may automatically collect diagnostics like your IP address, but this information is not visible to SMAPI's web application or developers. For more information, see the <a href="https://aws.amazon.com/privacy/">Amazon Privacy Notice</a>.</p> <h3>Update checks</h3> -<p>SMAPI notifies you when there's a new version of SMAPI or your mods available. To do so, it sends your SMAPI and mod versions to its web API. No personal information is stored by the web application, but see <em><a href="#web-logging">web logging</a></em>.</p> +<p>SMAPI notifies you when there's a new version of SMAPI or your mods available. To do so, it sends your game/SMAPI/mod versions and platform type to its web API. No personal information is stored by the web application, but see <em><a href="#web-logging">web logging</a></em>.</p> <p>You can disable update checks, and no information will be transmitted to the web API. To do so:</p> <ol> <li><a href="https://stardewvalleywiki.com/Modding:Game_folder">find your game folder</a>;</li> - <li>open the <code>smapi-internal/StardewModdingAPI.config.json</code> file in a text editor;</li> + <li>open the <code>smapi-internal/config.json</code> file in a text editor;</li> <li>change <code>"CheckForUpdates": true</code> to <code>"CheckForUpdates": false</code>.</li> </ol> diff --git a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml new file mode 100644 index 00000000..3143fad9 --- /dev/null +++ b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml @@ -0,0 +1,151 @@ +@using StardewModdingAPI.Web.ViewModels.JsonValidator +@model JsonValidatorModel + +@{ + // get view data + string curPageUrl = new Uri(new Uri(Model.SectionUrl), $"{Model.SchemaName}/{Model.PasteID}").ToString(); + string newUploadUrl = Model.SchemaName != null ? new Uri(new Uri(Model.SectionUrl), Model.SchemaName).ToString() : Model.SectionUrl; + string schemaDisplayName = null; + bool isValidSchema = Model.SchemaName != null && Model.SchemaFormats.TryGetValue(Model.SchemaName, out schemaDisplayName) && schemaDisplayName != "None"; + + // build title + ViewData["Title"] = "JSON validator"; + @if (Model.PasteID != null) + { + ViewData["ViewTitle"] = ViewData["Title"]; + ViewData["Title"] += + " (" + + string.Join(", ", new[] { isValidSchema ? schemaDisplayName : null, Model.PasteID }.Where(p => p != null)) + + ")"; + } +} + +@section Head { + @if (Model.PasteID != null) + { + <meta name="robots" content="noindex" /> + } + <link rel="stylesheet" href="~/Content/css/json-validator.css" /> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/themes/sunlight.default.min.css" /> + + <script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/sunlight.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/plugins/sunlight-plugin.linenumbers.min.js" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/gh/tmont/sunlight@1.22.0/src/lang/sunlight.javascript.min.js" crossorigin="anonymous"></script> + <script src="~/Content/js/json-validator.js"></script> + <script> + $(function () { + smapi.jsonValidator(@Json.Serialize(Model.SectionUrl), @Json.Serialize(Model.PasteID)); + }); + </script> +} + +@* upload result banner *@ +@if (Model.UploadError != null) +{ + <div class="banner error"> + <strong>Oops, the server ran into trouble saving that file.</strong><br /> + <small>Error details: @Model.UploadError</small> + </div> +} +else if (Model.ParseError != null) +{ + <div class="banner error"> + <strong>Oops, couldn't parse that JSON.</strong><br /> + Share this link to let someone see this page: <code>@curPageUrl</code><br /> + (Or <a href="@newUploadUrl">validate a new file</a>.)<br /> + <br /> + <small v-pre>Error details: @Model.ParseError</small> + </div> +} +else if (Model.PasteID != null) +{ + <div class="banner success"> + <strong>Share this link to let someone else see this page:</strong> <code>@curPageUrl</code><br /> + (Or <a href="@newUploadUrl">validate a new file</a>.) + </div> +} + +@* upload new file *@ +@if (Model.Content == null) +{ + <h2>Upload a JSON file</h2> + <form action="@Model.SectionUrl" method="post"> + <ol> + <li> + Choose the JSON format:<br /> + <select id="format" name="SchemaName"> + @foreach (var pair in Model.SchemaFormats) + { + <option value="@pair.Key" selected="@(Model.SchemaName == pair.Key)">@pair.Value</option> + } + </select> + </li> + <li> + Drag the file onto this textbox (or paste the text in):<br /> + <textarea id="input" name="Content" placeholder="paste file here"></textarea> + </li> + <li> + Click this button:<br /> + <input type="submit" id="submit" value="save & validate file" /> + </li> + </ol> + </form> +} + +@* validation results *@ +@if (Model.Content != null) +{ + <div id="output"> + @if (Model.UploadError == null) + { + <div> + Change JSON format: + <select id="format" name="format"> + @foreach (var pair in Model.SchemaFormats) + { + <option value="@pair.Key" selected="@(Model.SchemaName == pair.Key)">@pair.Value</option> + } + </select> + </div> + + <h2>Validation errors</h2> + @if (Model.FormatUrl != null) + { + <p>See <a href="@Model.FormatUrl">format documentation</a>.</p> + } + + @if (Model.Errors.Any()) + { + <table id="metadata" class="table"> + <tr> + <th>Line</th> + <th>Field</th> + <th>Error</th> + </tr> + + @foreach (JsonValidatorErrorModel error in Model.Errors) + { + <tr data-schema-error="@error.SchemaErrorType"> + <td><a href="#L@(error.Line)">@error.Line</a></td> + <td>@error.Path</td> + <td>@error.Message</td> + </tr> + } + </table> + } + else + { + <p>No errors found.</p> + } + } + + <h2>Content</h2> + <pre id="raw-content" class="sunlight-highlight-javascript">@Model.Content</pre> + + @if (isValidSchema) + { + <p class="footer-tip">(Tip: you can <a href="https://github.com/Pathoschild/SMAPI/blob/develop/docs/technical/web.md#using-a-schema-file-directly">validate directly in your text editor</a> if it supports JSON Schema.)</p> + } + </div> +} diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index 1b40cfa9..f98ffdf9 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -1,4 +1,5 @@ @using Newtonsoft.Json +@using StardewModdingAPI.Toolkit.Utilities @using StardewModdingAPI.Web.Framework.LogParsing.Models @model StardewModdingAPI.Web.ViewModels.LogParserModel @@ -67,12 +68,15 @@ else if (Model.ParsedLog?.IsValid == true) <h2>Where do I find my SMAPI log?</h2> <div>What system do you use?</div> <ul id="os-list"> - <li><input type="radio" name="os" value="android" id="os-android" /> <label for="os-android">Android</label></li> - <li><input type="radio" name="os" value="linux" id="os-linux" /> <label for="os-linux">Linux</label></li> - <li><input type="radio" name="os" value="mac" id="os-mac" /> <label for="os-mac">Mac</label></li> - <li><input type="radio" name="os" value="windows" id="os-windows" /> <label for="os-windows">Windows</label></li> + @foreach (Platform platform in new[] { Platform.Android, Platform.Linux, Platform.Mac, Platform.Windows }) + { + <li> + <input type="radio" name="os" value="@platform" id="os-@platform" checked="@(Model.DetectedPlatform == platform)"/> + <label for="os-@platform">@platform</label> + </li> + } </ul> - <div data-os="android"> + <div data-os="@Platform.Android"> On Android: <ol> <li>Open a file app (like My Files or MT Manager).</li> @@ -81,7 +85,7 @@ else if (Model.ParsedLog?.IsValid == true) <li>The log file is <code>SMAPI-crash.txt</code> if it exists, otherwise <code>SMAPI-latest.txt</code>.</li> </ol> </div> - <div data-os="linux"> + <div data-os="@Platform.Linux"> On Linux: <ol> <li>Open the Files app.</li> @@ -91,7 +95,7 @@ else if (Model.ParsedLog?.IsValid == true) <li>The log file is <code>SMAPI-crash.txt</code> if it exists, otherwise <code>SMAPI-latest.txt</code>.</li> </ol> </div> - <div data-os="mac"> + <div data-os="@Platform.Mac"> On Mac: <ol> <li>Open the Finder app.</li> @@ -100,7 +104,7 @@ else if (Model.ParsedLog?.IsValid == true) <li>The log file is <code>SMAPI-crash.txt</code> if it exists, otherwise <code>SMAPI-latest.txt</code>.</li> </ol> </div> - <div data-os="windows"> + <div data-os="@Platform.Windows"> On Windows: <ol> <li>Press the <code>Windows</code> and <code>R</code> buttons at the same time.</li> @@ -118,7 +122,7 @@ else if (Model.ParsedLog?.IsValid == true) </li> <li> Click this button:<br /> - <input type="submit" id="submit" value="save log" /> + <input type="submit" id="submit" value="save & parse log" /> </li> <li>On the new page, copy the URL and send it to the person helping you.</li> </ol> diff --git a/src/SMAPI.Web/Views/Mods/Index.cshtml b/src/SMAPI.Web/Views/Mods/Index.cshtml index 8293fbe2..50b59b45 100644 --- a/src/SMAPI.Web/Views/Mods/Index.cshtml +++ b/src/SMAPI.Web/Views/Mods/Index.cshtml @@ -1,7 +1,11 @@ +@using Humanizer +@using Humanizer.Localisation @using Newtonsoft.Json @model StardewModdingAPI.Web.ViewModels.ModListModel @{ ViewData["Title"] = "Mod compatibility"; + + TimeSpan staleAge = DateTimeOffset.UtcNow - Model.LastUpdated; } @section Head { <link rel="stylesheet" href="~/Content/css/mods.css?r=20190302" /> @@ -18,83 +22,104 @@ </script> } -<div id="app"> - <div id="intro"> - <p>This page shows all known SMAPI mods and (incompatible) content packs, whether they work with the latest versions of Stardew Valley and SMAPI, and how to fix them if not. If a mod doesn't work after following the instructions below, check <a href="https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting">the troubleshooting guide</a> or <a href="https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting#Ask_for_help">ask for help</a>.</p> +@if (!Model.HasData) +{ + <div class="error">↻ The mod data hasn't been fetched yet; please try again in a few minutes.</div> +} +else +{ + @if (Model.IsStale) + { + <div class="error">Showing data from @staleAge.Humanize(maxUnit: TimeUnit.Hour, minUnit: TimeUnit.Minute) ago. (Couldn't fetch newer data; the wiki API may be offline.)</div> + } - <p>The list is updated every few days (you can help <a href="https://stardewvalleywiki.com/Modding:Mod_compatibility">update it</a>!). It doesn't include XNB mods (see <a href="https://stardewvalleywiki.com/Modding:Using_XNB_mods"><em>using XNB mods</em> on the wiki</a> instead) or compatible content packs.</p> + <div id="app"> + <div id="intro"> + <p>This page shows all known SMAPI mods and (incompatible) content packs, whether they work with the latest versions of Stardew Valley and SMAPI, and how to fix them if not. If a mod doesn't work after following the instructions below, check <a href="https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting">the troubleshooting guide</a> or <a href="https://stardewvalleywiki.com/Modding:Player_Guide/Troubleshooting#Ask_for_help">ask for help</a>.</p> - @if (Model.BetaVersion != null) - { - <p id="beta-blurb" v-show="showAdvanced"><strong>Note:</strong> "SDV @Model.BetaVersion only" lines are for an unreleased version of the game, not the stable version most players have. If a mod doesn't have that line, the info applies to both versions of the game.</p> - } - </div> + <p>The list is updated every few days (you can help <a href="https://stardewvalleywiki.com/Modding:Mod_compatibility">update it</a>!). It doesn't include XNB mods (see <a href="https://stardewvalleywiki.com/Modding:Using_XNB_mods"><em>using XNB mods</em> on the wiki</a> instead) or compatible content packs.</p> - <div id="options"> - <div> - <label for="search-box">Search: </label> - <input type="text" id="search-box" v-model="search" v-on:input="applyFilters" /> + @if (Model.BetaVersion != null) + { + <p id="beta-blurb" v-show="showAdvanced"><strong>Note:</strong> "SDV @Model.BetaVersion only" lines are for an unreleased version of the game, not the stable version most players have. If a mod doesn't have that line, the info applies to both versions of the game.</p> + } </div> - <div id="filter-area"> - <input type="checkbox" id="show-advanced" v-model="showAdvanced" /> - <label for="show-advanced">show advanced info and options</label> - <div id="filters" v-show="showAdvanced"> - <div v-for="(filterGroup, key) in filters"> - {{filterGroup.label}}: <span v-for="filter in filterGroup.value" v-bind:class="{ active: filter.value }"><input type="checkbox" v-bind:id="filter.id" v-model="filter.value" v-on:change="applyFilters" /> <label v-bind:for="filter.id">{{filter.label}}</label></span> + + <div id="options"> + <div> + <label for="search-box">Search: </label> + <input type="text" id="search-box" v-model="search" v-on:input="applyFilters" /> + </div> + <div id="filter-area"> + <input type="checkbox" id="show-advanced" v-model="showAdvanced" /> + <label for="show-advanced">show advanced info and options</label> + <div id="filters" v-show="showAdvanced"> + <div v-for="(filterGroup, key) in filters"> + {{filterGroup.label}}: <span v-for="filter in filterGroup.value" v-bind:class="{ active: filter.value }"><input type="checkbox" v-bind:id="filter.id" v-model="filter.value" v-on:change="applyFilters" /> <label v-bind:for="filter.id">{{filter.label}}</label></span> + </div> </div> </div> </div> - </div> - <div id="mod-count" v-show="showAdvanced"> - <div v-if="visibleStats.total > 0"> - {{visibleStats.total}} mods shown ({{Math.round((visibleStats.compatible + visibleStats.workaround) / visibleStats.total * 100)}}% compatible or have a workaround, {{Math.round((visibleStats.soon + visibleStats.broken) / visibleStats.total * 100)}}% broken, {{Math.round(visibleStats.abandoned / visibleStats.total * 100)}}% obsolete). + <div id="mod-count" v-show="showAdvanced"> + <div v-if="visibleStats.total > 0"> + {{visibleStats.total}} mods shown ({{Math.round((visibleStats.compatible + visibleStats.workaround) / visibleStats.total * 100)}}% compatible or have a workaround, {{Math.round((visibleStats.soon + visibleStats.broken) / visibleStats.total * 100)}}% broken, {{Math.round(visibleStats.abandoned / visibleStats.total * 100)}}% obsolete). + </div> + <span v-else>No matching mods found.</span> </div> - <span v-else>No matching mods found.</span> + <table class="wikitable" id="mod-list"> + <thead> + <tr> + <th>mod name</th> + <th>links</th> + <th>author</th> + <th>compatibility</th> + <th v-show="showAdvanced">broke in</th> + <th v-show="showAdvanced">code</th> + <th> </th> + </tr> + </thead> + <tbody> + <tr v-for="mod in mods" :key="mod.Name" v-bind:id="mod.Slug" :key="mod.Slug" v-bind:data-status="mod.Compatibility.Status" v-show="mod.Visible"> + <td> + {{mod.Name}} + <small class="mod-alt-names" v-if="mod.AlternateNames">(aka {{mod.AlternateNames}})</small> + </td> + <td class="mod-page-links"> + <span v-for="(link, i) in mod.ModPages"> + <a v-bind:href="link.Url">{{link.Text}}</a>{{i < mod.ModPages.length - 1 ? ', ' : ''}} + </span> + </td> + <td> + {{mod.Author}} + <small class="mod-alt-authors" v-if="mod.AlternateAuthors">(aka {{mod.AlternateAuthors}})</small> + </td> + <td> + <div v-html="mod.Compatibility.Summary"></div> + <div v-if="mod.BetaCompatibility" v-show="showAdvanced"> + <strong v-if="mod.BetaCompatibility">SDV @Model.BetaVersion only:</strong> + <span v-html="mod.BetaCompatibility.Summary"></span> + </div> + <div v-for="(warning, i) in mod.Warnings">⚠ {{warning}}</div> + </td> + <td class="mod-broke-in" v-html="mod.LatestCompatibility.BrokeIn" v-show="showAdvanced"></td> + <td v-show="showAdvanced"> + <span v-if="mod.SourceUrl"><a v-bind:href="mod.SourceUrl">source</a></span> + <span v-else class="mod-closed-source">no source</span> + </td> + <td> + <small> + <a v-bind:href="'#' + mod.Slug">#</a> + <span v-show="showAdvanced"> + <template v-for="(link, i) in mod.MetadataLinks"> + <a v-bind:href="link.Item1">{{link.Item2}}</a> + </template> + + <abbr v-bind:title="mod.DevNote" v-show="mod.DevNote">[dev note]</abbr> + </span> + </small> + </td> + </tr> + </tbody> + </table> </div> - <table class="wikitable" id="mod-list"> - <thead> - <tr> - <th>mod name</th> - <th>links</th> - <th>author</th> - <th>compatibility</th> - <th v-show="showAdvanced">broke in</th> - <th v-show="showAdvanced">code</th> - <th> </th> - </tr> - </thead> - <tbody> - <tr v-for="mod in mods" :key="mod.Name" v-bind:id="mod.Slug" :key="mod.Slug" v-bind:data-status="mod.Compatibility.Status" v-show="mod.Visible"> - <td> - {{mod.Name}} - <small class="mod-alt-names" v-if="mod.AlternateNames">(aka {{mod.AlternateNames}})</small> - </td> - <td class="mod-page-links"> - <span v-for="(link, i) in mod.ModPages"> - <a v-bind:href="link.Url">{{link.Text}}</a>{{i < mod.ModPages.length - 1 ? ', ' : ''}} - </span> - </td> - <td> - {{mod.Author}} - <small class="mod-alt-authors" v-if="mod.AlternateAuthors">(aka {{mod.AlternateAuthors}})</small> - </td> - <td> - <div v-html="mod.Compatibility.Summary"></div> - <div v-if="mod.BetaCompatibility" v-show="showAdvanced"> - <strong v-if="mod.BetaCompatibility">SDV @Model.BetaVersion only:</strong> - <span v-html="mod.BetaCompatibility.Summary"></span> - </div> - <div v-for="(warning, i) in mod.Warnings">⚠ {{warning}}</div> - </td> - <td class="mod-broke-in" v-html="mod.LatestCompatibility.BrokeIn" v-show="showAdvanced"></td> - <td v-show="showAdvanced"> - <span v-if="mod.SourceUrl"><a v-bind:href="mod.SourceUrl">source</a></span> - <span v-else class="mod-closed-source">no source</span> - </td> - <td> - <small><a v-bind:href="'#' + mod.Slug">#</a></small> - </td> - </tr> - </tbody> - </table> -</div> +} diff --git a/src/SMAPI.Web/Views/Shared/_Layout.cshtml b/src/SMAPI.Web/Views/Shared/_Layout.cshtml index 4c602b29..87a22f06 100644 --- a/src/SMAPI.Web/Views/Shared/_Layout.cshtml +++ b/src/SMAPI.Web/Views/Shared/_Layout.cshtml @@ -16,14 +16,19 @@ <h4>SMAPI</h4> <ul> <li><a href="@SiteConfig.Value.RootUrl">About SMAPI</a></li> + <li><a href="https://stardewvalleywiki.com/Modding:Index">Modding docs</a></li> + </ul> + + <h4>Tools</h4> + <ul> <li><a href="@SiteConfig.Value.ModListUrl">Mod compatibility</a></li> <li><a href="@SiteConfig.Value.LogParserUrl">Log parser</a></li> - <li><a href="https://stardewvalleywiki.com/Modding:Index">Docs</a></li> + <li><a href="@SiteConfig.Value.JsonValidatorUrl">JSON validator</a></li> </ul> </div> <div id="content-column"> <div id="content"> - <h1>@ViewData["Title"]</h1> + <h1>@(ViewData["ViewTitle"] ?? ViewData["Title"])</h1> @RenderBody() </div> <div id="footer"> |