diff --git a/CONTEXT.md b/CONTEXT.md index dfc70edc0..0e7b41eb4 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -13,3 +13,22 @@ and used as a site section, "Module" means specifically these org-stewarded proj — not the generic sense of "any PowerShell module you `Install-Module`", which is the subject of the site as a whole. _Avoid (for this concept)_: project, tool, package + +**Author**: +A person credited as a contributor to site content (articles, podcast episodes) via +the `authors:` frontmatter. Each distinct Author is surfaced as a taxonomy term with +its own profile page, and may optionally describe themselves (avatar, tagline, bio, +links). An Author exists the moment they are credited on a piece of content; the +self-description is optional enrichment, not what makes someone an Author. +_Avoid (for this concept)_: contributor, writer, user, account + +The **author name** stored in `authors:` frontmatter is both the display byline and +the source of the Profile URL slug — it is the stable key. An Author may additionally +set a **preferred name** on their Profile, which overrides only how their name is +*displayed*; it never changes the slug or the byline key. Authors without a Profile +display their author name unchanged. + +**Profile**: +The per-Author page at `/authors//` listing that Author's content and, when +provided, their self-description. Distinct from the **Author list** — the index page +at `/authors/` showing every Author as a card. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca9c1f096..662c9aa30 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,6 +57,68 @@ If you're not comfortable with Git, you can pitch or submit your article through - Include a brief intro that tells readers what they'll learn - Test any code examples before submitting +## Adding Your Author Profile + +Once you've been credited as an author, you can give yourself a richer profile page at +`/authors//` — an avatar, a tagline, social links, and a bio. Profiles are +**opt-in**: if you don't add one, your name still works as a byline exactly as before. + +Your profile lives in a single file at `content/authors//_index.md`, where `` +**must** match your name as it appears in articles' `authors:` front matter (lowercased, +spaces become hyphens, punctuation dropped — e.g. `Jane Doe` → `jane-doe`). Getting the +slug wrong creates a page that attaches to nothing, so let the helper script do it: + +```powershell +./tools/new-author.ps1 "Jane Doe" +``` + +This scaffolds `content/authors/jane-doe/_index.md` with every field commented. Fill in +what you want and delete the rest: + +```yaml +--- +title: "Jane Doe" # required — keep this as your byline name +preferred_name: "Jane" # optional — changes only how your name is displayed +tagline: "Cloud automation, mostly." +# --- Avatar (first one set wins) --- +# avatar: "/images/authors/jane.jpg" # an image you host in this repo +gravatar_hash: "..." # MD5 of your lowercased email — keeps your email private +# email: "jane@example.com" # convenient, but stored publicly in this repo +# --- Links (full URLs) --- +github: "https://github.com/janedoe" +website: "https://janedoe.dev" +# twitter / mastodon / linkedin / bluesky also supported +--- + +Your bio in Markdown goes here. It shows on your profile page. +``` + +### Choosing an avatar + +The avatar is resolved in this order: `avatar` → `gravatar_hash` → `email` → an +auto-generated identicon. To use [Gravatar](https://gravatar.com/) without putting your +email in the repo, store the **MD5 hash** of your lowercased email instead: + +```powershell +$email = "jane@example.com" +[System.BitConverter]::ToString( + [System.Security.Cryptography.MD5]::Create().ComputeHash( + [System.Text.Encoding]::UTF8.GetBytes($email.Trim().ToLowerInvariant()) + ) +).Replace("-", "").ToLowerInvariant() +``` + +### Changed your name? + +If your byline ever needs to change across all your content (and your profile URL), use: + +```powershell +./tools/new-author.ps1 "Old Name" -To "New Name" +``` + +This rewrites the byline everywhere it appears and adds a redirect so your old profile +URL keeps working. Open a PR with the result. + ## Submitting a Community Event Add your PowerShell-related event to our community calendar: diff --git a/archetypes/authors.md b/archetypes/authors.md index 76764ce08..2ec9ed7e7 100644 --- a/archetypes/authors.md +++ b/archetypes/authors.md @@ -1,8 +1,34 @@ --- -title: '{{ replace .File.ContentBaseName "-" " " | title }}' -description: "" -layout: "authors" -website: "" -twitter: "" -github: "" +# The directory this file lives in MUST equal Hugo's slug of your author name +# exactly as it appears in articles' `authors:` frontmatter +# (lowercase, spaces -> hyphens, punctuation dropped). +# Use `tools/new-author.ps1 "Your Name"` to scaffold this with the correct slug. + +# `title` is required by Hugo. Keep it as your author name (the byline key). +title: "{{ replace .File.ContentBaseName "-" " " | title }}" + +# Optional: override only how your name is *displayed* (byline + slug are unchanged). +# preferred_name: "" + +# One short line shown on your card in the author list. +tagline: "" + +# --- Avatar (first match wins) ------------------------------------------------- +# 1. avatar: path or URL to an image you control (bypasses Gravatar) +# 2. gravatar_hash: MD5 of your lowercased email (keeps your email out of the repo) +# 3. email: plain email; hashed to a Gravatar at build time (stored publicly) +# If none are set, a stable identicon is generated from your name. +# avatar: "" +# gravatar_hash: "" +# email: "" + +# --- Links (full URLs) --------------------------------------------------------- +# website: "" +# github: "" +# twitter: "" +# mastodon: "" +# linkedin: "" +# bluesky: "" --- + + diff --git a/content/authors/gilbert-sanchez/_index.md b/content/authors/gilbert-sanchez/_index.md new file mode 100644 index 000000000..94dc2f6f8 --- /dev/null +++ b/content/authors/gilbert-sanchez/_index.md @@ -0,0 +1,16 @@ +--- +title: "Gilbert Sanchez" +tagline: "PowerShell nerd, open source contributor, and accidental homelab architect" +gravatar_hash: "8a5f80a8824ba8c72c0295b000476718" +website: "https://gilbertsanchez.com" +github: "https://github.com/HeyItsGilbert" +mastodon: "https://fosstodon.org/@HeyItsGilbert" +linkedin: "https://www.linkedin.com/in/gilbertsanchez" +bluesky: "https://bsky.app/profile/gilbertsanchez.com" +--- + +I maintain projects like psake, lead a team of engineers by day, and automate +everything in sight by night — the house runs on Home Assistant and I have +no regrets. I also care a lot about making tech more accessible and welcoming, +especially for neurodiverse folks. If it can be scripted or made more human, +it probably already has been. Find me at [gilbertsanchez.com](https://gilbertsanchez.com/). diff --git a/docs/adr/0002-author-profiles.md b/docs/adr/0002-author-profiles.md new file mode 100644 index 000000000..dabf9c00f --- /dev/null +++ b/docs/adr/0002-author-profiles.md @@ -0,0 +1,45 @@ +# Author profiles: enrich taxonomy term pages, keep the display name as the key + +Authors are a Hugo taxonomy derived from the `authors:` frontmatter on ~1,045 articles +and podcast episodes (see [[Author]] in `CONTEXT.md`). The term pages at `/authors//` +are auto-generated and show only a name and a content list. To let an Author describe +themselves (avatar, tagline, bio, links), we enrich each term page with an **optional** +content file at `content/authors//_index.md`. Profiles are **opt-in via PR** — not +pre-scaffolded — so most Authors have no file and must keep rendering correctly. + +The load-bearing decision is what gets stored in every article's `authors:` frontmatter. +We keep the **display name** there (`authors: [James Petty]`), exactly as today. The name +is both the byline and the source of the Profile slug — the stable key. A Profile may set +a **preferred name** that overrides only how the name is *displayed*; it never changes the +slug or the byline. Authors without a Profile render their author name unchanged. + +## Considered options + +- **Slug as the key** (`authors: [james-petty]`, display name rendered from the Profile) — + rejected. It decouples slug from name, but the display name would then live *only* in the + Profile. With ~70 of 80 Authors un-enriched at launch, their bylines and cards would fall + back to a humanized slug — `darren-mar-elia` → "Darren Mar Elia" (wrong hyphen), + `jasonmorgan` → "Jasonmorgan". It also forces a repo-wide migration of all 1,045 files. +- **Display name as the key, with an optional preferred-name override** — chosen. Zero + migration, correct bylines for every Author for free, and the changeable-display-name + benefit is preserved for those who opt in. The only thing it gives up is a slug decoupled + from the original name — but the slug is a URL we want stable anyway, and renaming it is a + redirect-and-find/replace job in *either* model. + +A central `data/authors.yaml` was also rejected in favor of taxonomy term content files: +term files bind to the existing `/authors//` page automatically and carry a Markdown +bio, which a data file cannot do naturally. + +## Consequences + +- The profile content file's directory **must** equal Hugo's slug of the exact author + string; a mismatch yields an orphan page that enriches nothing. A `new-author.ps1` + scaffolder generates the file with the right slug, and a build-time check flags author + content files whose slug matches no taxonomy term. +- Renaming a slug is the rare, deliberate case: `new-author.ps1` rewrites the term across + content and adds an `aliases` redirect on the Profile to preserve the old URL. +- Avatars resolve `avatar` → `gravatar_hash` → `email` → `identicon` fallback. Raw `email` + is convenient but lands in the public repo; `gravatar_hash` lets the privacy-conscious + avoid that, and an explicit `avatar` image bypasses Gravatar entirely. +- The Author list orders enriched profiles first, then by count, so a partially-filled grid + reads as intentional rather than sparse. diff --git a/themes/powershell-community/layouts/_default/authors.html b/themes/powershell-community/layouts/_default/authors.html index 0e68c1de2..fca105d32 100644 --- a/themes/powershell-community/layouts/_default/authors.html +++ b/themes/powershell-community/layouts/_default/authors.html @@ -18,7 +18,7 @@

{{ .Title }}

- @@ -30,36 +30,58 @@

{{ .Title }}

- {{ $authorTaxonomy := .Site.Taxonomies.authors }} - {{ if $authorTaxonomy }} + {{ $pairs := .Site.Taxonomies.authors.ByCount }} + {{ if $pairs }} + {{/* Enriched profiles (backed by a content file) lead, then bare authors, + preserving ByCount order within each group. */}} + {{ $enriched := slice }} + {{ $bare := slice }} + {{ range $pairs }} + {{ if .Page.File }} + {{ $enriched = $enriched | append . }} + {{ else }} + {{ $bare = $bare | append . }} + {{ end }} + {{ end }} + {{ $ordered := $enriched | append $bare }}
- {{ range $authorTaxonomy.ByCount }} -
-
+ {{ range $ordered }} + {{ $page := .Page }} + {{ $name := $page.Title }} + {{ $display := $name }} + {{ with $page.Params.preferred_name }}{{ $display = . }}{{ end }} + {{ $tagline := $page.Params.tagline }} +
+
-
- -
+ {{ partial "author-avatar.html" (dict "page" $page "name" $name "size" 96 "class" "w-20 h-20 rounded-full object-cover ring-2 ring-blue-100") }}
-

- - {{ .Page.Title }} +

+ + {{ $display }}

+ {{ with $tagline }} +

{{ . }}

+ {{ end }} +

{{ .Count }}
- article{{ if gt .Count 1 }}s{{ end }} published + article{{ if ne .Count 1 }}s{{ end }} published

+ {{ partial "author-links.html" (dict "page" $page "class" "flex justify-center gap-4 text-lg mb-4") }} + - + View Profile

@@ -78,16 +100,16 @@

{{ define "scripts" }}