SEO: robust per-page metadata — https OG/canonical, descriptions, ORCID/Scholar, JSON-LD (#1236, #1142, #1324)#1330
Merged
Conversation
…xy (#1236) Replace the duplicated, per-template Open Graph blocks with a single source of truth in base.html driven by an optional `page_meta` context dict (title, description, og_type, canonical_path) with lab-wide defaults. Every page now emits a self-referential canonical link, a complete Open Graph block (og:site_name, og:locale, og:image:width/height/alt, …), and Twitter Card tags (previously absent) — with the meta description computed once and reused. Fixes #1236: absolute URLs (og:url, og:image, canonical) were built from request.scheme, which is http behind UW CSE's TLS-terminating Apache proxy, so social/canonical URLs advertised http://. A new `site_scheme` context processor pins https when not DEBUG (local dev stays http). The cleaner long-term fix (SECURE_PROXY_SSL_HEADER) is left to a separate IT-facing issue. Detail views (project/member/news) populate page_meta and the templates override only the share image + structural tags (og:profile:*, og:article:*). The project canonical is built with reverse('website:project', …) so it is byte-for-byte identical to the URL website/sitemaps.py advertises (singular /project/<name>/), collapsing the /projects/ alias to one indexable URL. Lays the groundwork for #1142 (indexing) and #1324 (metadata audit). Adds website/tests/test_page_metadata.py (canonical/OG/Twitter presence, the #1236 https behavior via DEBUG toggling, and per-page title/description/type). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…g pages (#1142, #1324) Every page previously shipped the same generic <meta name="description">, a classic driver of Google's "Crawled - currently not indexed". Give the People, Projects, Publications, Awards, and News listing pages each a hand-written, distinct description and og:title via the page_meta context dict introduced in the prior commit. (The home page already uses the canonical lab description, so it's left on the default.) Extends website/tests/test_page_metadata.py to pin each listing page's distinct description/og:title and that none falls back to the generic default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… pages (#1324) Add `orcid` and `google_scholar` URLField(blank, null) to Person, expose them in the admin "Socials" fieldset, and render them on the member page as academicons icons (ai-orcid / ai-google-scholar; academicons is already loaded in base.html) following the existing per-link a11y pattern (aria-label + text). These also enrich the Person JSON-LD `sameAs` planned in the next commit. While here, fix two latent gaps in the same surfaces: - has_website_links() only checked personal_website/github/twitter, so a person whose only link was bluesky/mastodon/threads/linkedin (and now orcid/scholar) had their entire profile-links nav hidden. It now returns True for any link. - the admin "Socials" fieldset was missing `bluesky`, so it couldn't be edited even though the field existed and rendered on the page. Migrations are gitignored / generated at container start, so no migration file is committed; the test DB is built from models via the settings_test shim. Adds MemberSocialLinkTests to website/tests/test_views.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#1324) Emit structured data on the highest-value page types: - Organization on the home page (name, url, logo, description, parentOrganization = Allen School/UW, sameAs = lab social profiles) to help Google build a knowledge panel for "Makeability Lab". - Person on member pages, with sameAs linking the profile to the person's external identities (ORCID, Google Scholar, GitHub, …) — strengthens "<name> Makeability Lab" queries. - NewsArticle on news detail (headline, datePublished, author, publisher). Dicts are built in the views and serialized by a new render_jsonld() helper that escapes <, >, & as \uXXXX so a value containing literal "</script>" can't break out of the tag (covered by a regression test). base.html renders the optional `jsonld` context var inside <script type="application/ld+json">, so a view opts in just by setting context['jsonld'] — no per-template override. Adds JsonLdTests (presence + json.loads validity + the breakout guard) to website/tests/test_page_metadata.py. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reference #1329 (enable SECURE_PROXY_SSL_HEADER) from the in-app https workaround so the temporary code points at the durable fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…text examples Replace the placeholder ORCID/Scholar example URLs in the Person admin help_text with Jon Froehlich's actual profiles so editors have a concrete, working example to mirror. (& encoded as & in the Scholar URL for valid help_text HTML.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Member
Author
This was referenced Jun 18, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Overhauls on-page SEO and social-sharing metadata across the site. One cohesive
effort built on a shared
page_metainfrastructure, so the commits are kept ina single PR. Grew out of the planning doc
docs/plans/issue-1142-1324-seo.md.Closes #1236. Substantially addresses #1142 (indexing) and #1324 (metadata audit).
What changed (commit by commit)
<head>metadata + fix Project page social media (opengraph) not working perfectly #1236 — replaced the duplicatedper-template Open Graph blocks with a single source of truth in
base.htmldriven by an optional
page_metacontext dict. Added self-referentialcanonical links, a complete Open Graph block (
og:site_name,og:locale,og:image:width/height/alt, …), and Twitter Cards (previouslyabsent). A new
site_schemecontext processor pinshttpsoutside DEBUG, soog:url/og:image/canonical no longer advertisehttp://behind UW CSE'sTLS-terminating Apache proxy — the root cause of Project page social media (opengraph) not working perfectly #1236.
Publications / Awards / News (every page previously shipped the same generic
description — a classic "Crawled – currently not indexed" driver).
PersonORCID + Google Scholar fields (model + admin + member page,rendered with academicons). Also fixes two latent bugs found in the same
surfaces:
has_website_links()ignored bluesky/mastodon/threads/linkedin, andthe admin "Socials" fieldset was missing
bluesky.Organization(home),PersonwithsameAslinking ORCID/Scholar/socials (member),
NewsArticle(news). Arender_jsonld()helper escapes
</>/&so a value containing literal</script>can'tbreak out of the tag.
Canonical URL note
Project canonical is singular
/project/<short_name>/, built with the samereverse('website:project', …)the sitemap uses, so canonical and sitemap arebyte-for-byte identical and the
/projects/alias collapses to one indexable URL.Follow-up (not in this PR)
SECURE_PROXY_SSL_HEADERso Django seesthe real https scheme, superseding the in-app
site_schemeworkaround. Needsconfirmation that Apache sends and strips
X-Forwarded-Proto.Testing
website/tests/test_page_metadata.py(new): canonical/OG/Twitter presence, theProject page social media (opengraph) not working perfectly #1236 https behavior via DEBUG toggling, per-page descriptions, JSON-LD validity
(
json.loads) + the</script>breakout guard.MemberSocialLinkTestsintest_views.py: ORCID/Scholar rendering +has_website_links().--settings=makeabilitylab.settings_test).0036_person_…is gitignored per repo design; servers regenerate iton container start.
server (opengraph.xyz / LinkedIn Post Inspector against a
makeabilitylab-test.cs.washington.eduURL) thatog:url/og:imageemit https.backlog), not regressions from this PR.
🤖 Generated with Claude Code