Skip to content

Commit c427277

Browse files
psjamespclaudeCopilotHeyItsGilbert
authored
Add local user groups directory (#71)
## Summary Adds a **User Groups** section off the community page where local PowerShell user groups get their own page, plus a listing that shows all of them — modeled on how events and authors already work so groups can be added with a quick submission. ## What's included - **New `/user-groups/` section** with `list` + `single` layouts: card-grid directory, per-group pages with a meeting-details sidebar (location, format, schedule, organizers, links), logo support, and multiple organizers. - **"User Groups" card** added to the `community` menu — appears on the community page grid *and* in the Community nav dropdown. - **Self-service submission** (mirrors the community-event flow): - `.github/ISSUE_TEMPLATE/user-group.yml` — a web form; no git/markdown needed. - `.github/workflows/add-user-group.yml` — on the `approved` label, parses the issue, generates `content/user-groups/<slug>.md`, and opens a PR. Includes the same raw-HTML stripping XSS guard as `add-event.yml`. - `archetypes/user-groups.md` scaffold for the direct-PR path. - **Seed groups:** - **PowerShell Ohio User Group** — real details from their Meetup (online, first Thursday monthly at 7 PM ET, Meetup link) plus their badge logo, self-hosted at `static/images/user-groups/ohio-powershell.webp` (center-cropped to 512×512). - **Research Triangle PowerShell User Group.** - **Calendar:** Ohio's real upcoming session ("PowerShell MythBusters w/ Matthew Dowst", Sep 3 2026) added. - Regenerated the purged Tailwind CSS and Font Awesome icon subsets for the new classes/icons. ## Verification - Parsing logic tested against a realistic issue body (correct slug, multiple organizers, links, `<script>` stripped). - Generated content renders through Hugo at the correct URL; clean production build passes. - Both new YAML files parse; directory, group pages, and calendar entry verified in the browser. ## Notes / follow-ups - The workflow relies on an **`approved`** label existing in the repo (same as the event workflow). - **Ohio organizer name(s)** aren't listed on Meetup — left as a `# TODO` in the group's front matter. - Only the Sep 3 meeting was added to the calendar; the group meets monthly, so future dates can come in via the event form. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: HeyItsGilbert <615265+HeyItsGilbert@users.noreply.github.com>
1 parent e1393d7 commit c427277

16 files changed

Lines changed: 530 additions & 1 deletion

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: User Group Submission
2+
description: Add a local PowerShell user group to the community directory
3+
title: "[User Group] "
4+
labels: ["user-group"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Thanks for adding your PowerShell user group to the [community directory](https://powershell.org/user-groups/)!
10+
Fill out the details below. A maintainer will review and approve it, and a
11+
pull request will be opened automatically.
12+
13+
- type: input
14+
id: group_name
15+
attributes:
16+
label: Group Name
17+
placeholder: "e.g., Ohio PowerShell User Group"
18+
validations:
19+
required: true
20+
21+
- type: input
22+
id: location
23+
attributes:
24+
label: Location
25+
description: "City, State/Region, Country — use 'Online — worldwide' for online-only groups."
26+
placeholder: "Columbus, OH, USA"
27+
validations:
28+
required: true
29+
30+
- type: dropdown
31+
id: format
32+
attributes:
33+
label: Meeting Format
34+
options:
35+
- "In person"
36+
- "Online"
37+
- "Hybrid"
38+
validations:
39+
required: true
40+
41+
- type: input
42+
id: meeting_schedule
43+
attributes:
44+
label: Meeting Schedule
45+
description: "When the group meets."
46+
placeholder: "First Tuesday of each month, 6:00 PM ET"
47+
validations:
48+
required: true
49+
50+
- type: input
51+
id: website
52+
attributes:
53+
label: Website
54+
description: "The group's main website or landing page."
55+
placeholder: "https://..."
56+
validations:
57+
required: false
58+
59+
- type: input
60+
id: meetup_url
61+
attributes:
62+
label: Meetup / Registration URL
63+
description: "Where people sign up or find the next meeting (Meetup, Eventbrite, etc.)."
64+
placeholder: "https://www.meetup.com/your-group"
65+
validations:
66+
required: false
67+
68+
- type: input
69+
id: organizers
70+
attributes:
71+
label: Organizer(s)
72+
description: "Name(s) of the people running the group. Separate multiple names with commas."
73+
placeholder: "Jane Doe, John Smith"
74+
validations:
75+
required: true
76+
77+
- type: input
78+
id: logo_url
79+
attributes:
80+
label: Logo URL
81+
description: "Optional. A direct link to the group's logo image. You can drag-and-drop an image into this issue and paste the URL GitHub generates."
82+
placeholder: "https://..."
83+
validations:
84+
required: false
85+
86+
- type: textarea
87+
id: description
88+
attributes:
89+
label: Short Description
90+
description: "One line shown on the group's card in the directory."
91+
placeholder: "The PowerShell community for the Columbus, Ohio area."
92+
validations:
93+
required: true
94+
95+
- type: textarea
96+
id: about
97+
attributes:
98+
label: About the Group
99+
description: "Optional. A longer description shown on the group's own page — who it's for, what meetings look like, how to get involved."
100+
placeholder: "Tell people about your user group."
101+
validations:
102+
required: false
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
name: Add User Group
2+
3+
on:
4+
issues:
5+
types: [labeled]
6+
7+
jobs:
8+
add-user-group:
9+
if: |
10+
github.event.label.name == 'approved' &&
11+
contains(github.event.issue.labels.*.name, 'user-group')
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: write
15+
pull-requests: write
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Parse issue and create user group content file
22+
id: parse
23+
env:
24+
ISSUE_BODY: ${{ github.event.issue.body }}
25+
ISSUE_NUMBER: ${{ github.event.issue.number }}
26+
ISSUE_TITLE: ${{ github.event.issue.title }}
27+
run: |
28+
python3 - <<'PYEOF'
29+
import os, re, yaml
30+
31+
body = os.environ['ISSUE_BODY']
32+
33+
def field(label):
34+
m = re.search(rf'### {re.escape(label)}\s+(.+?)(?=\n###|\Z)', body, re.DOTALL)
35+
if not m:
36+
return ''
37+
val = m.group(1).strip()
38+
return '' if val in ('_No response_', '') else val
39+
40+
name = field('Group Name')
41+
location = field('Location')
42+
fmt = field('Meeting Format')
43+
schedule = field('Meeting Schedule')
44+
website = field('Website')
45+
meetup_url = field('Meetup / Registration URL')
46+
organizers = field('Organizer(s)')
47+
logo_url = field('Logo URL')
48+
description = field('Short Description')
49+
about = field('About the Group')
50+
51+
def strip_html(s):
52+
# Strip raw HTML to prevent stored XSS via goldmark unsafe mode.
53+
return re.sub(r'<[^>]+>', '', s).strip()
54+
55+
# Organizers: split on commas / newlines into a clean list.
56+
org_list = [o.strip() for o in re.split(r'[,\n]+', organizers) if o.strip()]
57+
58+
# Links: only reference Font Awesome icons already in the committed subset
59+
# (fas fa-globe, fab fa-meetup). New icons would need `npm run build:icons`.
60+
links = []
61+
if website:
62+
links.append({'name': 'Website', 'url': website, 'icon': 'fas fa-globe'})
63+
if meetup_url:
64+
if 'meetup.com' in meetup_url.lower():
65+
links.append({'name': 'Meetup', 'url': meetup_url, 'icon': 'fab fa-meetup'})
66+
else:
67+
links.append({'name': 'Register', 'url': meetup_url, 'icon': 'fas fa-globe'})
68+
69+
slug = re.sub(r'[^a-z0-9]+', '-', name.lower()).strip('-')
70+
71+
fm = {
72+
'title': name,
73+
'description': strip_html(description),
74+
'location': location,
75+
'format': fmt,
76+
'meeting_schedule': schedule,
77+
}
78+
if org_list:
79+
fm['organizers'] = org_list
80+
if logo_url:
81+
fm['logo'] = logo_url
82+
if links:
83+
fm['links'] = links
84+
85+
content = '---\n' + yaml.safe_dump(fm, default_flow_style=False, allow_unicode=True, sort_keys=False) + '---\n'
86+
about = strip_html(about)
87+
if about:
88+
content += about + '\n'
89+
90+
filepath = f'content/user-groups/{slug}.md'
91+
os.makedirs('content/user-groups', exist_ok=True)
92+
with open(filepath, 'w') as f:
93+
f.write(content)
94+
95+
with open(os.environ['GITHUB_OUTPUT'], 'a') as out:
96+
out.write(f'slug={slug}\n')
97+
out.write(f'filepath={filepath}\n')
98+
out.write(f'group_name={name}\n')
99+
100+
print(f'Created {filepath}')
101+
PYEOF
102+
103+
- name: Open PR
104+
env:
105+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
106+
SLUG: ${{ steps.parse.outputs.slug }}
107+
FILEPATH: ${{ steps.parse.outputs.filepath }}
108+
GROUP_NAME: ${{ steps.parse.outputs.group_name }}
109+
ISSUE_NUMBER: ${{ github.event.issue.number }}
110+
run: |
111+
BRANCH="user-group/issue-${ISSUE_NUMBER}-${SLUG}"
112+
git config user.name "github-actions[bot]"
113+
git config user.email "github-actions[bot]@users.noreply.github.com"
114+
git checkout -b "$BRANCH"
115+
git add "$FILEPATH"
116+
git commit -m "Add user group: ${GROUP_NAME} (closes #${ISSUE_NUMBER})"
117+
git push origin "$BRANCH"
118+
gh pr create \
119+
--title "Add user group: ${GROUP_NAME}" \
120+
--body "Closes #${ISSUE_NUMBER}
121+
122+
Auto-generated from user group submission." \
123+
--base main \
124+
--head "$BRANCH"

archetypes/user-groups.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: '{{ replace .File.ContentBaseName "-" " " | title }}'
3+
description: "A short one-line summary of the group."
4+
location: "City, State/Region, Country"
5+
format: "In person" # In person | Online | Hybrid
6+
meeting_schedule: "First Tuesday of each month"
7+
# Optional: path to the group's logo (put the image in static/images/user-groups/).
8+
logo: "/images/user-groups/your-group.png"
9+
# List one or more organizers.
10+
organizers:
11+
- "Organizer Name"
12+
- "Second Organizer"
13+
links:
14+
- name: "Website"
15+
url: "https://example.com"
16+
icon: "fas fa-globe"
17+
- name: "Meetup"
18+
url: "https://www.meetup.com/your-group"
19+
icon: "fab fa-meetup"
20+
---
21+
22+
Tell people about your user group: who it's for, what a typical meeting looks
23+
like, and how to get involved.

assets/css/fontawesome-subset.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
.far,.fa-regular{font-family:"Font Awesome 6 Free";font-weight:400}
44
.fab,.fa-brands{font-family:"Font Awesome 6 Brands";font-weight:400}
55
.fa-apple:before{content:"\f179"}
6+
.fa-arrow-left:before{content:"\f060"}
67
.fa-arrow-right:before{content:"\f061"}
78
.fa-arrow-up:before{content:"\f062"}
89
.fa-arrow-up-right-from-square:before{content:"\f08e"}
@@ -16,6 +17,7 @@
1617
.fa-calendar:before{content:"\f133"}
1718
.fa-calendar-alt:before{content:"\f073"}
1819
.fa-calendar-check:before{content:"\f274"}
20+
.fa-calendar-day:before{content:"\f783"}
1921
.fa-calendar-days:before{content:"\f073"}
2022
.fa-calendar-plus:before{content:"\f271"}
2123
.fa-calendar-times:before{content:"\f273"}
@@ -55,7 +57,9 @@
5557
.fa-location-dot:before{content:"\f3c5"}
5658
.fa-map-marker-alt:before{content:"\f3c5"}
5759
.fa-mastodon:before{content:"\f4f6"}
60+
.fa-meetup:before{content:"\f2e0"}
5861
.fa-pen-nib:before{content:"\f5ad"}
62+
.fa-people-group:before{content:"\e533"}
5963
.fa-play:before{content:"\f04b"}
6064
.fa-play-circle:before{content:"\f144"}
6165
.fa-plug:before{content:"\f1e6"}
@@ -73,6 +77,7 @@
7377
.fa-times:before{content:"\f00d"}
7478
.fa-twitter:before{content:"\f099"}
7579
.fa-user-circle:before{content:"\f2bd"}
80+
.fa-user-group:before{content:"\f500"}
7681
.fa-users:before{content:"\f0c0"}
7782
.fa-video:before{content:"\f03d"}
7883
.fa-windows:before{content:"\f17a"}

assets/css/tailwind.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
416 Bytes
Binary file not shown.

assets/fonts/fa-solid-subset.woff2

300 Bytes
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: "PowerShell MythBusters w/ Matthew Dowst (Ohio PowerShell User Group)"
3+
startDate: '2026-09-03'
4+
endDate: ""
5+
where: "Online"
6+
externalUrl: "https://www.meetup.com/powershellohio/events/316089030/"
7+
virtual: true
8+
---
9+
The Ohio PowerShell User Group puts common PowerShell wisdom to the test —
10+
benchmarking competing approaches and exploring performance, the pipeline,
11+
parallel execution, and output behavior. See the
12+
[group page](/user-groups/ohio-powershell/) for more.

content/user-groups/_index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: "PowerShell User Groups"
3+
description: "Find a local PowerShell user group near you — or add your own."
4+
---
5+
6+
Local user groups are where the PowerShell community meets face to face. Whether
7+
you're looking to learn, share what you know, or just talk shop with fellow
8+
scripters, there's a good chance there's a group near you.
9+
10+
Don't see your group listed?
11+
[Add it in a couple of minutes](https://github.com/PowerShellOrg/PowerShellOrgWebsite/issues/new?template=user-group.yml)
12+
— just fill out a short form and a maintainer will take care of the rest.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: "PowerShell Ohio User Group"
3+
description: "An online PowerShell user group serving the Ohio community and beyond."
4+
location: "Ohio, USA"
5+
format: "Online"
6+
meeting_schedule: "First Thursday of each month, 7:00 PM ET"
7+
logo: "/images/user-groups/ohio-powershell.webp"
8+
organizers:
9+
- "Stephen Valdinger"
10+
links:
11+
- name: "Meetup"
12+
url: "https://www.meetup.com/powershellohio/"
13+
icon: "fab fa-meetup"
14+
---
15+
16+
The Ohio PowerShell User Group is a brand-new, online-first community for
17+
PowerShell scripters, automators, and IT pros in Ohio and beyond. The group meets
18+
online on the first Thursday of each month for talks, demos, and discussion.
19+
20+
The group kicked off in August 2026 with its first meeting, and hosts community
21+
speakers each month. Join through [Meetup](https://www.meetup.com/powershellohio/)
22+
to RSVP and get notified about upcoming sessions.

0 commit comments

Comments
 (0)