Skip to content

Instantly share code, notes, and snippets.

@jermdavis
Last active April 22, 2024 07:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jermdavis/469dbf0678ea5bc5ca7051955e1f5aba to your computer and use it in GitHub Desktop.
Save jermdavis/469dbf0678ea5bc5ca7051955e1f5aba to your computer and use it in GitHub Desktop.
Example code for expiring outdated links in Statiq. Example code for a blog post: https://blog.jermdavis.dev/posts/2024/expiring-outdated-blog-links - More info there.
using Statiq.App;
using Statiq.Common;
using StatiqGenerator.Customisations.ReadingTime;
namespace StatiqGenerator.Customisations.DisappearingLinks
{
public class ExpiringLinkConfigurator : IConfigurator<Bootstrapper>
{
public void Configure(Bootstrapper configurable)
{
configurable.ModifyPipeline("Content", p => {
p.ProcessModules.Insert(4, new ExpiringLinkProcessor());
});
}
}
}
using System;
using Statiq.Common;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Transactions;
namespace StatiqGenerator.Customisations.DisappearingLinks
{
public class ExpiringLinkProcessor : ParallelModule
{
private static readonly Regex _links = new Regex("<a\\s?(?<attrs>.*?)>(?<inner>.*?)</a>\\s?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex _date = new Regex("until=\"(?<date>.*?)\"", RegexOptions.Compiled | RegexOptions.IgnoreCase);
protected override async Task<IEnumerable<IDocument>> ExecuteInputAsync(IDocument input, IExecutionContext context)
{
if (input.Source.Extension == ".md")
{
var footnoteToAppend = string.Empty;
var hasChanged = false;
var content = await input.GetContentStringAsync();
var links = _links.Matches(content);
foreach(Match link in links)
{
var attrs = link.Groups["attrs"].Value;
var date = _date.Match(attrs);
if(date.Success)
{
var txt = date.Groups["date"].Value;
if(DateTime.TryParse(txt, out DateTime parsedDate))
{
if(parsedDate.Date < DateTime.Now.Date)
{
// remove link and return
content = content.Replace(link.Value, link.Groups["inner"].Value + "<a class=\"expired\" href=\"#expired\">**</a> ");
hasChanged = true;
footnoteToAppend += $"<li>{link.Value}</li>";
continue;
}
}
// remove until="??" and return
content = content.Replace($" {date.Value}", "");
hasChanged = true;
continue;
}
}
if(hasChanged)
{
if(footnoteToAppend.Length > 0)
{
content += "<blockquote><h4>Expired links</h4><div><a class=\"expired\" name=\"expired\">**</a> Some links in this page have expired. The originals are listed here, but they may no longer point to the correct content:</div><ol>" + footnoteToAppend + "</ol></blockquote>";
}
var mt = input.GetContentProvider().MediaType;
return input
.Clone(new MetadataItems { { "ChangedLinks", "true" } })
.Clone(context.GetContentProvider(content, mt))
.Yield();
}
}
return input.Yield();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment