171 lines
8.6 KiB
C#
171 lines
8.6 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using PlotLine.Services;
|
|
using PlotLine.ViewModels;
|
|
|
|
namespace PlotLine.Controllers;
|
|
|
|
[AllowAnonymous]
|
|
[Route("")]
|
|
public sealed class PublicController(IConfiguration configuration) : Controller
|
|
{
|
|
private const string SupportEmail = "hello@plotdirector.com";
|
|
private const string PolicyDate = "June 24, 2026";
|
|
private const string Keywords = "novel planning software, writing software, story planning app, author tools, plot tracking, character tracking, story timeline, novel organisation, fiction writing software, series writing software, continuity tracking, plot threads, story bible";
|
|
private const string FeaturesDescription = "Explore PlotDirector's features for novel planning, story timelines, character tracking, continuity management, scene organisation, writing schedules, story bibles, floor plans and Microsoft Word integration.";
|
|
private const string FeaturesKeywords = "novel planning software, story planning software, writing software, book planning tool, story timeline software, character tracking software, continuity software for writers, story bible software, novel writing app, manuscript planning software, plot tracking software, character relationship map, story structure software, writing schedule planner, Microsoft Word writing companion, series bible software, worldbuilding software, fiction writing software, author tools";
|
|
|
|
[HttpGet("features", Name = PublicRouteNames.Features)]
|
|
public IActionResult Features()
|
|
{
|
|
var featuresUrl = PublicSeo.AbsoluteUrl(configuration, "/features");
|
|
var homeUrl = PublicSeo.AbsoluteUrl(configuration, "/");
|
|
var pricingUrl = PublicSeo.AbsoluteUrl(configuration, "/pricing");
|
|
PublicSeo.Apply(ViewData, configuration, new SeoMetadata
|
|
{
|
|
Title = "PlotDirector Features | Novel Planning, Continuity & Story Writing Software",
|
|
Description = FeaturesDescription,
|
|
Keywords = FeaturesKeywords,
|
|
CanonicalPath = "/features",
|
|
OgTitle = "PlotDirector Features | Plan, Write and Manage Your Story",
|
|
OgDescription = "Discover the connected story planning tools inside PlotDirector, including timelines, scene management, characters, relationships, continuity tracking, story health, floor plans, exports and Word integration.",
|
|
OgType = "website",
|
|
OgImage = "https://plotdirector.com/images/og/plotdirector-features-og.webp",
|
|
TwitterCard = "summary_large_image",
|
|
JsonLd =
|
|
[
|
|
PublicSeo.JsonLd(new Dictionary<string, object?>
|
|
{
|
|
["@context"] = "https://schema.org",
|
|
["@type"] = "WebPage",
|
|
["name"] = "PlotDirector Features",
|
|
["description"] = FeaturesDescription,
|
|
["url"] = featuresUrl,
|
|
["isPartOf"] = new Dictionary<string, object?>
|
|
{
|
|
["@type"] = "WebSite",
|
|
["name"] = "PlotDirector",
|
|
["url"] = homeUrl
|
|
}
|
|
}),
|
|
PublicSeo.JsonLd(new Dictionary<string, object?>
|
|
{
|
|
["@context"] = "https://schema.org",
|
|
["@type"] = "SoftwareApplication",
|
|
["name"] = "PlotDirector",
|
|
["applicationCategory"] = "WritingApplication",
|
|
["operatingSystem"] = "Web",
|
|
["description"] = "PlotDirector is story planning and writing software for authors, helping writers manage novels, characters, timelines, continuity, story bibles, floor plans, writing schedules and Microsoft Word integration.",
|
|
["url"] = homeUrl,
|
|
["offers"] = new Dictionary<string, object?>
|
|
{
|
|
["@type"] = "Offer",
|
|
["url"] = pricingUrl
|
|
}
|
|
}),
|
|
PublicSeo.BreadcrumbJsonLd(
|
|
("Home", homeUrl),
|
|
("Features", featuresUrl)),
|
|
PublicSeo.JsonLd(new Dictionary<string, object?>
|
|
{
|
|
["@context"] = "https://schema.org",
|
|
["@type"] = "FAQPage",
|
|
["mainEntity"] = new[]
|
|
{
|
|
Faq("What is PlotDirector?", "PlotDirector is story planning and writing software for authors. It helps writers organise books, chapters, scenes, characters, locations, timelines, plot threads, continuity, writing schedules and story bibles in one connected workspace."),
|
|
Faq("Can PlotDirector help with story continuity?", "Yes. PlotDirector includes continuity tools for tracking characters, locations, story assets, knowledge, custody, warnings and story state across scenes."),
|
|
Faq("Does PlotDirector work with Microsoft Word?", "PlotDirector includes a Microsoft Word Companion designed to connect your manuscript with your planning data, helping sync chapters, scenes, word counts and story details."),
|
|
Faq("Can I use PlotDirector for a series?", "Yes. PlotDirector supports projects, books, chapters and scenes, making it suitable for novels, series, screenplays and larger story worlds."),
|
|
Faq("Does PlotDirector include character and relationship tracking?", "Yes. PlotDirector includes character profiles, aliases, appearances, relationship tracking, relationship maps and changes over time."),
|
|
Faq("Can PlotDirector create a story bible?", "Yes. PlotDirector can produce story bible exports containing key information about characters, settings, plot threads, timelines and other story details.")
|
|
}
|
|
})
|
|
]
|
|
});
|
|
ViewData["SeoTitle"] = "PlotDirector Features | Novel Planning, Continuity & Story Writing Software";
|
|
ViewData["OgTitle"] = "PlotDirector Features | Plan, Write and Manage Your Story";
|
|
|
|
return View();
|
|
}
|
|
|
|
[HttpGet("about", Name = PublicRouteNames.About)]
|
|
public IActionResult About()
|
|
{
|
|
SetSeo(
|
|
"About",
|
|
"Learn why PlotDirector was created by a novelist to help writers organise complex stories, manage continuity, and finish their books with confidence.",
|
|
"/about",
|
|
"AboutPage",
|
|
"About PlotDirector");
|
|
return View(CreateModel());
|
|
}
|
|
|
|
[HttpGet("contact", Name = PublicRouteNames.Contact)]
|
|
public IActionResult Contact()
|
|
{
|
|
SetSeo(
|
|
"Contact",
|
|
"Contact PlotDirector for support, questions, feedback, or feature suggestions about our novel planning and story organisation platform.",
|
|
"/contact",
|
|
"ContactPage",
|
|
"Contact PlotDirector");
|
|
return View(CreateModel());
|
|
}
|
|
|
|
[HttpGet("privacy", Name = PublicRouteNames.Privacy)]
|
|
public IActionResult Privacy()
|
|
{
|
|
SetSeo(
|
|
"Privacy Policy",
|
|
"Read the PlotDirector Privacy Policy to learn how we protect your account information, subscription data, and story content.",
|
|
"/privacy",
|
|
"PrivacyPolicy",
|
|
"Privacy Policy");
|
|
return View(CreateModel());
|
|
}
|
|
|
|
[HttpGet("terms", Name = PublicRouteNames.Terms)]
|
|
public IActionResult Terms()
|
|
{
|
|
SetSeo(
|
|
"Terms of Service",
|
|
"Read the PlotDirector Terms of Service for information about subscriptions, user content, acceptable use, and account responsibilities.",
|
|
"/terms",
|
|
"WebPage",
|
|
"Terms of Service");
|
|
return View(CreateModel());
|
|
}
|
|
|
|
private void SetSeo(string title, string description, string path, string schemaType, string pageName)
|
|
{
|
|
PublicSeo.Apply(ViewData, configuration, new SeoMetadata
|
|
{
|
|
Title = title,
|
|
Description = description,
|
|
Keywords = Keywords,
|
|
CanonicalPath = path,
|
|
JsonLd =
|
|
[
|
|
PublicSeo.WebPageJsonLd(configuration, schemaType, pageName, path, description)
|
|
]
|
|
});
|
|
}
|
|
|
|
private static PublicPageViewModel CreateModel() => new()
|
|
{
|
|
SupportEmail = SupportEmail,
|
|
LastUpdated = PolicyDate
|
|
};
|
|
|
|
private static Dictionary<string, object?> Faq(string question, string answer) => new()
|
|
{
|
|
["@type"] = "Question",
|
|
["name"] = question,
|
|
["acceptedAnswer"] = new Dictionary<string, object?>
|
|
{
|
|
["@type"] = "Answer",
|
|
["text"] = answer
|
|
}
|
|
};
|
|
}
|