From 8b8d0cc59de9ecc7e7201722edf8abce5b7e224b Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 16 Dec 2025 15:35:21 -0500
Subject: [PATCH v1 2/3] Add public contributor profiles

Now anyone can visit postgresql.org/community/contributors/[username] to
see a user's self-described contributions. This commit adds the profile
view and also links recognized contributors' names to their profiles.

The urls are based on username, which may not be the right design
because not all contributors have user profiles, but it is a starting
point.
---
 pgweb/contributors/views.py         | 10 +++++-
 pgweb/urls.py                       |  1 +
 templates/contributors/list.html    |  6 ++--
 templates/contributors/profile.html | 51 +++++++++++++++++++++++++++++
 4 files changed, 64 insertions(+), 4 deletions(-)
 create mode 100644 templates/contributors/profile.html

diff --git a/pgweb/contributors/views.py b/pgweb/contributors/views.py
index 45b8551e..60fadbd9 100644
--- a/pgweb/contributors/views.py
+++ b/pgweb/contributors/views.py
@@ -1,6 +1,7 @@
+from django.shortcuts import get_object_or_404
 from pgweb.util.contexts import render_pgweb
 
-from .models import ContributorType
+from .models import ContributorType, Contributor
 
 
 def completelist(request):
@@ -8,3 +9,10 @@ def completelist(request):
     return render_pgweb(request, 'community', 'contributors/list.html', {
         'contributortypes': contributortypes,
     })
+
+
+def contributor_profile(request, username):
+    contributor = get_object_or_404(Contributor, user__username=username)
+    return render_pgweb(request, 'community', 'contributors/profile.html', {
+        'contributor': contributor,
+    })
diff --git a/pgweb/urls.py b/pgweb/urls.py
index d9b81f75..d4a946ea 100644
--- a/pgweb/urls.py
+++ b/pgweb/urls.py
@@ -70,6 +70,7 @@ urlpatterns = [
 
     re_path(r'^community/$', pgweb.core.views.community),
     re_path(r'^community/contributors/$', pgweb.contributors.views.completelist),
+    re_path(r'^community/contributors/([^/]+)/$', pgweb.contributors.views.contributor_profile),
     re_path(r'^community/lists/$', RedirectView.as_view(url='/list/', permanent=True)),
     re_path(r'^community/lists/subscribe/$', RedirectView.as_view(url='https://lists.postgresql.org/', permanent=True)),
 
diff --git a/templates/contributors/list.html b/templates/contributors/list.html
index 068fc949..e713cee2 100644
--- a/templates/contributors/list.html
+++ b/templates/contributors/list.html
@@ -32,7 +32,7 @@
     {%for c in t.contributor_set.all %}
      {%if t.detailed%}
       <tr>
-       <td>{{c.firstname}} {{c.lastname}} {%if t.showemail and c.email%}({{c.email|hidemail}}){%endif%}
+       <td>{% if c.user %}<a href="/community/contributors/{{c.user.username}}/">{{c.firstname}} {{c.lastname}}</a>{% else %}{{c.firstname}} {{c.lastname}}{% endif %} {%if t.showemail and c.email%}({{c.email|hidemail}}){%endif%}
           {%if c.company %}
           <br/>
             {% if c.companyurl %}
@@ -49,13 +49,13 @@
      {%else%}
       {%if forloop.counter0|divisibleby:"2" %}
        <tr>
-        <td>{{c.firstname}} {{c.lastname}}{%if t.showemail and c.email%} ({{c.email|hidemail}}){%endif%}</td>
+        <td>{% if c.user %}<a href="/community/contributors/{{c.user.username}}/">{{c.firstname}} {{c.lastname}}</a>{% else %}{{c.firstname}} {{c.lastname}}{% endif %}{%if t.showemail and c.email%} ({{c.email|hidemail}}){%endif%}</td>
         {%if forloop.last%}
         <td></td>
        </tr>
         {%endif%}
       {%else%}
-        <td>{{c.firstname}} {{c.lastname}}{%if t.showemail and c.email%} ({{c.email|hidemail}}){%endif%}</td>
+        <td>{% if c.user %}<a href="/community/contributors/{{c.user.username}}/">{{c.firstname}} {{c.lastname}}</a>{% else %}{{c.firstname}} {{c.lastname}}{% endif %}{%if t.showemail and c.email%} ({{c.email|hidemail}}){%endif%}</td>
        </tr>
       {%endif%}
      {%endif%}
diff --git a/templates/contributors/profile.html b/templates/contributors/profile.html
new file mode 100644
index 00000000..6f476c3c
--- /dev/null
+++ b/templates/contributors/profile.html
@@ -0,0 +1,51 @@
+{%extends "base/page.html"%}
+{%load pgfilters%}
+{%block title%}{{contributor.firstname}} {{contributor.lastname}} - Contributor Profile{%endblock%}
+{%block contents%}
+<h1>{{contributor.firstname}} {{contributor.lastname}} <i class="fa fa-user"></i></h1>
+
+<div class="row">
+  <div class="col-md-8">
+    {%if contributor.contribution%}
+    <h2>Contribution</h2>
+    <p>{{contributor.contribution}}</p>
+    {%endif%}
+
+    <h2>Information</h2>
+    <dl class="row">
+      <dt class="col-sm-3">Contributor Type</dt>
+      <dd class="col-sm-9">{{contributor.ctype.typename}}</dd>
+
+      {%if contributor.email%}
+      <dt class="col-sm-3">Email</dt>
+      <dd class="col-sm-9">{{contributor.email|hidemail}}</dd>
+      {%endif%}
+
+      {%if contributor.location%}
+      <dt class="col-sm-3">Location</dt>
+      <dd class="col-sm-9">{{contributor.location}}</dd>
+      {%endif%}
+
+      {%if contributor.company%}
+      <dt class="col-sm-3">Company</dt>
+      <dd class="col-sm-9">
+        {%if contributor.companyurl%}
+          <a href="{{contributor.companyurl}}" target="_blank" rel="noopener">{{contributor.company}}</a>
+        {%else%}
+          {{contributor.company}}
+        {%endif%}
+      </dd>
+      {%endif%}
+    </dl>
+  </div>
+</div>
+
+<div class="mt-4">
+  <p>
+    <a href="mailto:contributors@postgresql.org?subject=Report contributor profile: {{contributor.firstname}} {{contributor.lastname}}" title="Report inappropriate profile that violates PostgreSQL Code of Conduct">
+      <i class="fa fa-flag"></i> Report this profile
+    </a>
+  </p>
+</div>
+
+{%endblock%}
-- 
2.51.2

