namespace DocFx.Plugin.LastModified.Processors; using Docfx.Common; using Docfx.Plugins; using HtmlAgilityPack; /// /// An abstract processor for adding last modified date to articles, contains common methods. /// public abstract class AbstractProcessor : IProcessor { /// /// Determines whether this processor supports the given type. /// /// The type of manifest item. /// True if this processor supports the given type, false otherwise. public abstract bool Supports(string type); /// /// Processes the given manifest item. /// /// The that contains the manifest item. /// The to process. /// The output folder. public abstract void Process(Manifest manifest, ManifestItem manifestItem, string outputFolder); /// /// Attempts to process the given manifest item. /// /// The that contains the manifest item. /// The to process. /// The output folder. /// True if the manifest item was processed, false otherwise. public bool TryProcess(Manifest manifest, ManifestItem manifestItem, string outputFolder) { if (!Supports(manifestItem.Type)) { Logger.LogVerbose($"Skipping {manifestItem.Type}..."); return false; } Logger.LogDiagnostic($"Processing {manifestItem.Type}..."); Process(manifest, manifestItem, outputFolder); return true; } /// /// Builds the last modified info for the given file path. /// /// The file path. /// The for the given file path. protected abstract LastModifiedInfo GetLastModifiedInfo(string filePath); /// /// Modifies the given document with the given last modified info. /// /// The file path to the document. /// The to use. protected virtual void ModifyDocument(string filePath, LastModifiedInfo lastModifiedInfo) { var htmlDocument = new HtmlDocument(); htmlDocument.Load(filePath); var articleNode = htmlDocument.DocumentNode.SelectSingleNode("//article"); if (articleNode == null) { Logger.LogWarning($"No article node found in {filePath}."); return; } var separatorNode = htmlDocument.CreateElement("hr"); articleNode.AppendChild(separatorNode); var lastModifiedNode = htmlDocument.CreateElement("div"); lastModifiedNode.SetAttributeValue("class", "last-modified"); articleNode.AppendChild(lastModifiedNode); var paragraphNode = htmlDocument.CreateElement("p"); paragraphNode.InnerHtml = $"This page was last modified at {lastModifiedInfo.LastModified} (UTC)."; lastModifiedNode.AppendChild(paragraphNode); if (string.IsNullOrEmpty(lastModifiedInfo.CommitHeader)) { htmlDocument.Save(filePath); return; } var collapsibleNode = htmlDocument.CreateElement("details"); lastModifiedNode.AppendChild(collapsibleNode); var reasonHeaderNode = htmlDocument.CreateElement("summary"); reasonHeaderNode.SetAttributeValue("style", "display: list-item;"); reasonHeaderNode.InnerHtml = "Commit Message"; collapsibleNode.AppendChild(reasonHeaderNode); var reasonContainerNode = htmlDocument.CreateElement("div"); collapsibleNode.AppendChild(reasonContainerNode); var preCodeBlockNode = htmlDocument.CreateElement("pre"); var codeBlockNode = htmlDocument.CreateElement("code"); codeBlockNode.InnerHtml = lastModifiedInfo.CommitHeader; preCodeBlockNode.AppendChild(codeBlockNode); reasonContainerNode.AppendChild(preCodeBlockNode); if (!string.IsNullOrEmpty(lastModifiedInfo.CommitBody)) { preCodeBlockNode = htmlDocument.CreateElement("pre"); codeBlockNode = htmlDocument.CreateElement("code"); codeBlockNode.InnerHtml = lastModifiedInfo.CommitBody; preCodeBlockNode.AppendChild(codeBlockNode); reasonContainerNode.AppendChild(preCodeBlockNode); } htmlDocument.Save(filePath); } }