using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using PlotLine.Services; using PlotLine.ViewModels; using System.Net; namespace PlotLine.Controllers; [Route("help")] [AllowAnonymous] public sealed class HelpController(IHelpService help, IHelpMarkdownRenderer markdown) : Controller { [HttpGet("")] public IActionResult Index(string? q) { if (!IsCanonicalPath("/help")) { return RedirectPermanent(PreserveQuery("/help")); } ViewData["CanonicalUrl"] = "https://plotdirector.com/help"; var articles = help.GetAllArticles(); return View(new HelpCentreIndexViewModel { Query = q, ArticleCount = articles.Count, Categories = help.GetCategories() .Select(category => new HelpCategoryViewModel { CategoryName = category, CategorySlug = HelpService.Slugify(category), ArticleCount = help.GetByCategory(category).Count }) .ToList(), SearchResults = string.IsNullOrWhiteSpace(q) ? [] : help.Search(q), RecentArticles = articles.OrderByDescending(x => x.RelativePath).Take(5).ToList() }); } [HttpGet("{categorySlug}")] public IActionResult Category(string categorySlug) { var category = help.GetCategoryBySlug(categorySlug); if (category is null) { return NotFound(); } var canonicalPath = $"/help/{HelpService.Slugify(category)}"; if (!IsCanonicalPath(canonicalPath)) { return RedirectPermanent(canonicalPath); } var articles = help.GetByCategorySlug(categorySlug); ViewData["CanonicalUrl"] = $"https://plotdirector.com{canonicalPath}"; return View(new HelpCategoryArticlesViewModel { CategoryName = category, CategorySlug = HelpService.Slugify(category), Articles = articles }); } [HttpGet("{categorySlug}/{articleSlug}")] public IActionResult Article(string categorySlug, string articleSlug) { var article = help.GetBySlugs(categorySlug, articleSlug); if (article is null) { return View("ArticleNotFound", $"{categorySlug}/{articleSlug}"); } if (!IsCanonicalPath(article.Url)) { return RedirectPermanent(article.Url); } ViewData["CanonicalUrl"] = $"https://plotdirector.com{article.Url}"; return View(new HelpCentreArticleViewModel { Article = article, RenderedHtml = markdown.Render(article.MarkdownContent), RelatedArticles = help.GetRelatedArticles(article) }); } [HttpGet("/Help/Category/{category}")] public IActionResult LegacyCategory(string category) { var articles = help.GetByCategory(WebUtility.UrlDecode(category)); var firstArticle = articles.FirstOrDefault(); return firstArticle is null ? NotFound() : RedirectPermanent(firstArticle.CategoryUrl); } [HttpGet("/Help/Article/{key}")] public IActionResult LegacyArticle(string key) { var article = help.GetByContextKey(WebUtility.UrlDecode(key)); return article is null ? View("ArticleNotFound", key) : RedirectPermanent(article.Url); } private bool IsCanonicalPath(string path) => string.Equals(HttpContext.Request.Path.Value, path, StringComparison.Ordinal); private string PreserveQuery(string path) => string.IsNullOrWhiteSpace(HttpContext.Request.QueryString.Value) ? path : $"{path}{HttpContext.Request.QueryString.Value}"; }