Skip to content

Commit 6075dde

Browse files
authored
Merge pull request #39 from AdamG100/master
Add releases plugin and list component
2 parents f2f7753 + df3fd68 commit 6075dde

9 files changed

Lines changed: 160 additions & 42 deletions

File tree

docusaurus.config.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,28 @@ const config = {
3535
routeBasePath: "/",
3636
sidebarPath: "./sidebars.js",
3737
showLastUpdateTime: true,
38+
// @ts-ignore
39+
async sidebarItemsGenerator({ defaultSidebarItemsGenerator, ...args }) {
40+
const items = await defaultSidebarItemsGenerator(args);
41+
/** @param {any[]} items */
42+
function sortItems(items) {
43+
return items.map((/** @type {any} */ item) => {
44+
if (item.type === "category") {
45+
const sorted = /** @type {any[]} */ (sortItems(item.items));
46+
if (item.label === "Release Notes") {
47+
sorted.sort((/** @type {any} */ a, /** @type {any} */ b) => {
48+
const la = a.label ?? "";
49+
const lb = b.label ?? "";
50+
return lb < la ? -1 : lb > la ? 1 : 0;
51+
});
52+
}
53+
return { ...item, items: sorted };
54+
}
55+
return item;
56+
});
57+
}
58+
return sortItems(items);
59+
},
3860
editUrl:
3961
"https://github.com/TotalControlAdmin/tcadmin-docs/blob/master/",
4062
lastVersion: "2",
@@ -58,6 +80,10 @@ const config = {
5880
],
5981
],
6082

83+
plugins: [
84+
"./plugins/releases-data-plugin",
85+
],
86+
6187
themes: ["docusaurus-theme-search-typesense"],
6288

6389
themeConfig: {

plugins/releases-data-plugin.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const fs = require("fs");
2+
const path = require("path");
3+
const matter = require("gray-matter");
4+
5+
const RELEASES_DIR_REL = path.join("versioned_docs", "version-3", "releases");
6+
7+
function escapeXml(str) {
8+
return String(str)
9+
.replace(/&/g, "&amp;")
10+
.replace(/</g, "&lt;")
11+
.replace(/>/g, "&gt;")
12+
.replace(/"/g, "&quot;");
13+
}
14+
15+
function generateDescription(content) {
16+
const featuresMatch = content.match(/###\s+New Features([\s\S]*?)(?:###|$)/);
17+
if (!featuresMatch) return "";
18+
const names = [];
19+
const re = /\*\*([^*]+)\*\*/g;
20+
let m;
21+
while ((m = re.exec(featuresMatch[1])) !== null) {
22+
names.push(m[1]);
23+
if (names.length === 5) break;
24+
}
25+
return names.length ? names.join(", ") + (names.length === 5 ? ", and more." : ".") : "";
26+
}
27+
28+
function getReleases(siteDir) {
29+
const releasesDir = path.join(siteDir, RELEASES_DIR_REL);
30+
return fs
31+
.readdirSync(releasesDir)
32+
.filter((f) => (f.endsWith(".mdx") || f.endsWith(".md")) && f !== "index.mdx")
33+
.sort()
34+
.reverse()
35+
.map((file) => {
36+
const raw = fs.readFileSync(path.join(releasesDir, file), "utf8");
37+
const { data, content } = matter(raw);
38+
const slug = path.basename(file).replace(/\.(mdx|md)$/, "");
39+
return {
40+
title: data.sidebar_label ?? data.title ?? slug,
41+
date: data.date ? new Date(data.date).toISOString() : null,
42+
description: data.description || generateDescription(content),
43+
slug,
44+
id: file,
45+
};
46+
});
47+
}
48+
49+
function generateRSS(releases, siteUrl) {
50+
const items = releases
51+
.filter((r) => r.date)
52+
.map(
53+
(r) =>
54+
`\n <item>\n <title>${escapeXml(r.title)}</title>\n <link>${siteUrl}/3/releases/${escapeXml(r.slug)}</link>\n <guid isPermaLink="true">${siteUrl}/3/releases/${escapeXml(r.slug)}</guid>\n <description>${escapeXml(r.description)}</description>\n <pubDate>${new Date(r.date).toUTCString()}</pubDate>\n </item>`
55+
)
56+
.join("");
57+
const lastBuild = releases[0]?.date ? new Date(releases[0].date).toUTCString() : new Date().toUTCString();
58+
const year = new Date().getFullYear();
59+
return `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n <channel>\n <title>TCAdmin v3 Releases</title>\n <link>${siteUrl}/3/releases</link>\n <description>Latest TCAdmin v3 release notes</description>\n <language>en</language>\n <lastBuildDate>${lastBuild}</lastBuildDate>\n <atom:link href="${siteUrl}/releases/rss.xml" rel="self" type="application/rss+xml"/>\n <copyright>Copyright \u00a9 ${year} TCADMIN.COM</copyright>${items}\n </channel>\n</rss>`;
60+
}
61+
62+
function generateAtom(releases, siteUrl) {
63+
const entries = releases
64+
.filter((r) => r.date)
65+
.map(
66+
(r) =>
67+
`\n <entry>\n <title>${escapeXml(r.title)}</title>\n <link href="${siteUrl}/3/releases/${escapeXml(r.slug)}"/>\n <id>${siteUrl}/3/releases/${escapeXml(r.slug)}</id>\n <updated>${r.date}</updated>\n <summary>${escapeXml(r.description)}</summary>\n </entry>`
68+
)
69+
.join("");
70+
const updated = releases.find((r) => r.date)?.date ?? new Date().toISOString();
71+
const year = new Date().getFullYear();
72+
return `<?xml version="1.0" encoding="UTF-8"?>\n<feed xmlns="http://www.w3.org/2005/Atom">\n <title>TCAdmin v3 Releases</title>\n <link href="${siteUrl}/3/releases"/>\n <link rel="self" href="${siteUrl}/releases/atom.xml"/>\n <id>${siteUrl}/releases/atom.xml</id>\n <updated>${updated}</updated>\n <rights>Copyright \u00a9 ${year} TCADMIN.COM</rights>${entries}\n</feed>`;
73+
}
74+
75+
module.exports = function releasesDataPlugin(context) {
76+
return {
77+
name: "releases-data-plugin",
78+
async contentLoaded({ actions }) {
79+
const { setGlobalData } = actions;
80+
setGlobalData({ releases: getReleases(context.siteDir) });
81+
},
82+
async postBuild({ outDir }) {
83+
const releases = getReleases(context.siteDir);
84+
const siteUrl = context.siteConfig.url;
85+
const feedsDir = path.join(outDir, "releases");
86+
fs.mkdirSync(feedsDir, { recursive: true });
87+
fs.writeFileSync(path.join(feedsDir, "rss.xml"), generateRSS(releases, siteUrl));
88+
fs.writeFileSync(path.join(feedsDir, "atom.xml"), generateAtom(releases, siteUrl));
89+
},
90+
};
91+
};

src/components/ReleasesList.jsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react";
2+
import { usePluginData } from "@docusaurus/useGlobalData";
3+
4+
export default function ReleasesList() {
5+
const { releases } = usePluginData("releases-data-plugin");
6+
7+
return (
8+
<div className="releases-grid">
9+
{releases.map(({ id, title, date, description, slug }) => (
10+
<a key={id} href={`./${slug}`} className="release-card">
11+
<div className="release-card__version">{title}</div>
12+
{date && (
13+
<div className="release-card__date">
14+
{new Date(date).toLocaleDateString("en-US", {
15+
year: "numeric",
16+
month: "long",
17+
day: "numeric",
18+
})}
19+
</div>
20+
)}
21+
<div className="release-card__summary">{description}</div>
22+
</a>
23+
))}
24+
</div>
25+
);
26+
}

versioned_docs/version-3/releases/3.0.829.41981.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
2-
sidebar_position: 4
32
sidebar_label: 3.0.829.41981
3+
date: 2026-02-06
4+
authors: [tcadmin]
45
---
56

67
# Version 3.0.829.41981

versioned_docs/version-3/releases/3.0.867.2669.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
2-
sidebar_position: 3
32
sidebar_label: 3.0.867.2669
3+
date: 2026-02-16
4+
authors: [tcadmin]
45
---
56

67
# Version 3.0.867.2669

versioned_docs/version-3/releases/3.0.891.56055.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
2-
sidebar_position: 2
32
sidebar_label: 3.0.891.56055
3+
date: 2026-02-24
4+
authors: [tcadmin]
45
---
56

67
# Version 3.0.891.56055

versioned_docs/version-3/releases/3.1.62.57465.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
2-
sidebar_position: 1
32
sidebar_label: 3.1.62.57465
3+
date: 2026-03-16
4+
authors: [tcadmin]
45
---
56

67
# Version 3.1.62.57465
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
tcadmin:
2+
name: TCAdmin
3+
title: TCAdmin Team
4+
url: https://www.tcadmin.com
5+
image_url: https://docs.tcadmin.com/img/tcadmin-logo.png

versioned_docs/version-3/releases/index.mdx

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: Release Notes
33
sidebar_label: Release Notes
44
---
55

6+
import ReleasesList from "@site/src/components/ReleasesList.jsx";
7+
68
# Release Notes
79

810
Release notes for TCAdmin v3 versions.
@@ -11,42 +13,6 @@ Release notes for TCAdmin v3 versions.
1113
See the [Update TCAdmin](../update-tcadmin.mdx) guide for step-by-step instructions on how to update your installation.
1214
:::
1315

14-
export const ReleaseCard = ({version, date, summary, link}) => (
15-
<a href={link} className="release-card">
16-
<div className="release-card__version">{version}</div>
17-
{date && <div className="release-card__date">{date}</div>}
18-
<div className="release-card__summary">{summary}</div>
19-
</a>
20-
);
21-
22-
<div className="releases-grid">
23-
24-
<ReleaseCard
25-
version="3.1.62.57465"
26-
date="March 16, 2026"
27-
summary="Virtual servers, .NET 10 upgrade, billing integration, S3 file provider, automatic server selection, datacenter regions, and more."
28-
link="./3.1.62.57465"
29-
/>
30-
31-
<ReleaseCard
32-
version="3.0.891.56055"
33-
date="February 24, 2026"
34-
summary="Config file editors in service wizard, resource limits, service settings pages, update health check, and bug fixes."
35-
link="./3.0.891.56055"
36-
/>
37-
38-
<ReleaseCard
39-
version="3.0.867.2669"
40-
date="February 16, 2026"
41-
summary="Encrypted credentials, private networks, remote desktop/shell, community plugin repository, and more."
42-
link="./3.0.867.2669"
43-
/>
44-
45-
<ReleaseCard
46-
version="3.0.829.41981"
47-
date="February 6, 2026"
48-
summary="Initial preview release — redesigned Blazor interface and modular SDK architecture."
49-
link="./3.0.829.41981"
50-
/>
16+
Subscribe to release updates: [RSS](https://docs.tcadmin.com/releases/rss.xml) · [Atom](https://docs.tcadmin.com/releases/atom.xml)
5117

52-
</div>
18+
<ReleasesList />

0 commit comments

Comments
 (0)