public inbox for [email protected]  
help / color / mirror / Atom feed
Public contributor profile pages
5+ messages / 3 participants
[nested] [flat]

* Public contributor profile pages
@ 2025-12-17 00:24 Melanie Plageman <[email protected]>
  2025-12-17 14:03 ` Re: Public contributor profile pages Magnus Hagander <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Melanie Plageman @ 2025-12-17 00:24 UTC (permalink / raw)
  To: [email protected]; +Cc: PostgreSQL Contributors <[email protected]>

Hello,

The Contributors Committee is working on a new program to recognize
specific contributions (e.g. "volunteered at PGConf.dev 2024" or
"contributed code to PostgreSQL 18"). These would be listed on an
individual contributor's profile. As a step toward that, we are
proposing to add public contributor profile pages.

Attached is a patch set which implements this by:
1) allowing all those with pgo user accounts to edit their
"contributions" (currently only enabled for recognized contributors)
2) adds a public contributor profile view accessible via
postgresql.org/community/contributors/[username]
3) links all recognized contributors on [1] to their public contributor profile

There are quite a few details to work out. A few I know of are:
- How should opting into a public profile work? What about existing
recognized contributors, do we opt them all in automatically? Or just
opt-in Major Contributors since their "Contributions" are already on a
public web page? (see patch 0003)
- How to make the public profile URL scheme work for contributors who
do not have a pgo user account? Or should it work at all?

This is my first real web patch, so I'm sure I did some things in a
non-pgweb-idiomatic (or non web-idiomatic) way and didn't think of
lots of things. I'll probably need lots of guidance.

- Melanie

[1] https://www.postgresql.org/community/contributors/


Attachments:

  [application/octet-stream] v1-0002-Add-public-contributor-profiles.patch (5.7K, ../../CAAKRu_Z42AAq7N=usSS3UPMtXqbVvsQzktNmH1X20oypyqA_Xg@mail.gmail.com/2-v1-0002-Add-public-contributor-profiles.patch)
  download | inline diff:
From 8b8d0cc59de9ecc7e7201722edf8abce5b7e224b Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
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:[email protected]?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



  [application/octet-stream] v1-0001-Allow-all-users-a-contributor-description.patch (7.1K, ../../CAAKRu_Z42AAq7N=usSS3UPMtXqbVvsQzktNmH1X20oypyqA_Xg@mail.gmail.com/3-v1-0001-Allow-all-users-a-contributor-description.patch)
  download | inline diff:
From f1c4be8509b46410f634f1f56ebf90876d4eb966 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Tue, 16 Dec 2025 15:18:44 -0500
Subject: [PATCH v1 1/3] Allow all users a contributor description

Previously only recognized contributors could add a description of their
contributions to Postgres. In order to support public contributor
profiles for all users (not just recognized contributors), contributors
must be able to add contributions.
---
 pgweb/account/views.py                        | 27 +++++++++----------
 .../0004_alter_contributor_ctype.py           | 24 +++++++++++++++++
 pgweb/contributors/models.py                  |  6 +++--
 templates/account/userprofileform.html        |  8 +++---
 4 files changed, 45 insertions(+), 20 deletions(-)
 create mode 100644 pgweb/contributors/migrations/0004_alter_contributor_ctype.py

diff --git a/pgweb/account/views.py b/pgweb/account/views.py
index 4f6cfa78..6900e04f 100644
--- a/pgweb/account/views.py
+++ b/pgweb/account/views.py
@@ -142,14 +142,14 @@ def profile(request):
     # accounts.
     can_change_email = (request.user.password != OAUTH_PASSWORD_STORE)
 
-    # We may have a contributor record - and we only show that part of the
-    # form if we have it for this user.
-    try:
-        contrib = Contributor.objects.get(user=request.user.pk)
-    except Contributor.DoesNotExist:
-        contrib = None
-
-    contribform = None
+    contrib, _ = Contributor.objects.get_or_create(
+        user=request.user,
+        defaults={
+            'firstname': request.user.first_name,
+            'lastname': request.user.last_name,
+            'email': request.user.email,
+        }
+    )
 
     secondaryaddresses = SecondaryEmail.objects.filter(user=request.user)
 
@@ -158,10 +158,9 @@ def profile(request):
         userform = UserForm(can_change_email, secondaryaddresses, data=request.POST, instance=request.user)
         profileform = UserProfileForm(data=request.POST, instance=profile)
         secondaryemailform = AddEmailForm(request.user, data=request.POST)
-        if contrib:
-            contribform = ContributorForm(data=request.POST, instance=contrib)
+        contribform = ContributorForm(data=request.POST, instance=contrib)
 
-        if userform.is_valid() and profileform.is_valid() and secondaryemailform.is_valid() and (not contrib or contribform.is_valid()):
+        if userform.is_valid() and profileform.is_valid() and secondaryemailform.is_valid() and contribform.is_valid():
             user = userform.save()
 
             # Email takes some magic special handling, since we only allow picking of existing secondary emails, but it's
@@ -179,8 +178,7 @@ def profile(request):
                 log.info("User {} changed primary email from {} to {}".format(user.username, oldemail, user.email))
 
             profileform.save()
-            if contrib:
-                contribform.save()
+            contribform.save()
             if secondaryemailform.cleaned_data.get('email1', ''):
                 sa = SecondaryEmail(user=request.user, email=secondaryemailform.cleaned_data['email1'], token=generate_random_token())
                 sa.save()
@@ -203,8 +201,7 @@ def profile(request):
         userform = UserForm(can_change_email, secondaryaddresses, instance=request.user)
         profileform = UserProfileForm(instance=profile)
         secondaryemailform = AddEmailForm(request.user)
-        if contrib:
-            contribform = ContributorForm(instance=contrib)
+        contribform = ContributorForm(instance=contrib)
 
     return render_pgweb(request, 'account', 'account/userprofileform.html', {
         'userform': userform,
diff --git a/pgweb/contributors/migrations/0004_alter_contributor_ctype.py b/pgweb/contributors/migrations/0004_alter_contributor_ctype.py
new file mode 100644
index 00000000..fdf15405
--- /dev/null
+++ b/pgweb/contributors/migrations/0004_alter_contributor_ctype.py
@@ -0,0 +1,24 @@
+# Generated by Django 5.2.8 on 2025-12-15 23:59
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contributors', '0003_make_email_nullable'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='contributor',
+            name='ctype',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contributors.contributortype', verbose_name='Contributor Type'),
+        ),
+        migrations.AlterField(
+            model_name='contributor',
+            name='contribution',
+            field=models.TextField(blank=True, help_text='Only displayed for Major Contributors', null=True),
+        ),
+    ]
diff --git a/pgweb/contributors/models.py b/pgweb/contributors/models.py
index 342cddb2..afecf2be 100644
--- a/pgweb/contributors/models.py
+++ b/pgweb/contributors/models.py
@@ -19,7 +19,9 @@ class ContributorType(models.Model):
 
 
 class Contributor(models.Model):
-    ctype = models.ForeignKey(ContributorType, on_delete=models.CASCADE, verbose_name='Contributor Type')
+    ctype = models.ForeignKey(ContributorType,
+                              on_delete=models.CASCADE,
+                              verbose_name='Contributor Type', null=True, blank=True)
     lastname = models.CharField(max_length=100, null=False, blank=False)
     firstname = models.CharField(max_length=100, null=False, blank=False)
     email = models.EmailField(null=False, blank=True)
@@ -27,7 +29,7 @@ class Contributor(models.Model):
     companyurl = models.URLField(max_length=100, null=True, blank=True, verbose_name='Company URL')
     location = models.CharField(max_length=100, null=True, blank=True)
     contribution = models.TextField(null=True, blank=True,
-                                    help_text='This description is currently used for major contributors only')
+                                    help_text='Only displayed for Major Contributors')
     user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
 
     send_notification = True
diff --git a/templates/account/userprofileform.html b/templates/account/userprofileform.html
index 96ed2779..12d6ace4 100644
--- a/templates/account/userprofileform.html
+++ b/templates/account/userprofileform.html
@@ -94,9 +94,12 @@
       </div>
     {%endfor%}
 
-  {% if contribform %}
     <h2>Edit contributor information</h2>
-    <p>You can edit the information that's shown on the <a href="/community/contributors/" target="_blank" rel="noopener">contributors</a> page. Please be careful as your changes will take effect immediately!
+    <p>You can edit the information that is shown on your contributor profile.
+      If you are a recognized Major Contributor, this is what will be displayed
+      on the
+      <a href="/community/contributors/" target="_blank" rel="noopener">contributors</a>
+      page. Please be careful as your changes will take effect immediately!
     </p>
     {% for field in contribform %}
       <div class="form-group row">
@@ -116,7 +119,6 @@
         </div>
       </div>
     {% endfor %}
-  {% endif %}
 
   <div class="submit-row">
     <input class="btn btn-primary" type="submit" value="Save" />
-- 
2.51.2



  [application/octet-stream] v1-0003-Make-public-contributor-profile-opt-in.patch (6.8K, ../../CAAKRu_Z42AAq7N=usSS3UPMtXqbVvsQzktNmH1X20oypyqA_Xg@mail.gmail.com/4-v1-0003-Make-public-contributor-profile-opt-in.patch)
  download | inline diff:
From cd32478c2bb49eed10eb56f2f21db9f10a34b989 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <[email protected]>
Date: Tue, 16 Dec 2025 18:00:57 -0500
Subject: [PATCH v1 3/3] Make public contributor profile opt-in

I'm not really sure if this is the right way to do this at all, so I
made it a separate patch. Basically, we probably don't want to just
suddenly have everybody with a user account at postgresql.org have a
public profile page. But, I don't know how it should work exactly --
both from a feature specification perspective and an implementation
perspective.

For one, we can't have all existing contributors not be displayed on the
recognized contributor page just because they didn't opt-in (which is
what I did here). But do we want all of them to get profiles without
first opting in?
---
 pgweb/account/forms.py                         |  3 +++
 .../migrations/0005_add_hidden_field.py        | 18 ++++++++++++++++++
 pgweb/contributors/models.py                   |  3 +++
 pgweb/contributors/views.py                    |  7 ++++++-
 pgweb/core/models.py                           |  3 +++
 templates/contributors/list.html               |  6 +++---
 6 files changed, 36 insertions(+), 4 deletions(-)
 create mode 100644 pgweb/contributors/migrations/0005_add_hidden_field.py

diff --git a/pgweb/account/forms.py b/pgweb/account/forms.py
index fb3641b0..5e8002ed 100644
--- a/pgweb/account/forms.py
+++ b/pgweb/account/forms.py
@@ -161,6 +161,9 @@ class ContributorForm(forms.ModelForm):
     class Meta:
         model = Contributor
         exclude = ('ctype', 'lastname', 'firstname', 'user', )
+        widgets = {
+            'hidden': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
+        }
 
 
 class AddEmailForm(forms.Form):
diff --git a/pgweb/contributors/migrations/0005_add_hidden_field.py b/pgweb/contributors/migrations/0005_add_hidden_field.py
new file mode 100644
index 00000000..ad5799da
--- /dev/null
+++ b/pgweb/contributors/migrations/0005_add_hidden_field.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.2.8 on 2025-12-16 23:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('contributors', '0004_alter_contributor_ctype'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='contributor',
+            name='hidden',
+            field=models.BooleanField(default=True, help_text='Hide your contributor profile from /community/contributors/[username]/', verbose_name='Hide public profile'),
+        ),
+    ]
diff --git a/pgweb/contributors/models.py b/pgweb/contributors/models.py
index afecf2be..592227ec 100644
--- a/pgweb/contributors/models.py
+++ b/pgweb/contributors/models.py
@@ -31,6 +31,9 @@ class Contributor(models.Model):
     contribution = models.TextField(null=True, blank=True,
                                     help_text='Only displayed for Major Contributors')
     user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
+    hidden = models.BooleanField(null=False, blank=False, default=True,
+                                 verbose_name='Hide public profile',
+                                 help_text='Check to hide your contributor profile. Uncheck to make it publicly accessible at /community/contributors/[username]/')
 
     send_notification = True
     purge_urls = ('/community/contributors/', )
diff --git a/pgweb/contributors/views.py b/pgweb/contributors/views.py
index 60fadbd9..767e7624 100644
--- a/pgweb/contributors/views.py
+++ b/pgweb/contributors/views.py
@@ -12,7 +12,12 @@ def completelist(request):
 
 
 def contributor_profile(request, username):
-    contributor = get_object_or_404(Contributor, user__username=username)
+    # Only show profiles that are not hidden
+    contributor = get_object_or_404(
+        Contributor,
+        user__username=username,
+        hidden=False
+    )
     return render_pgweb(request, 'community', 'contributors/profile.html', {
         'contributor': contributor,
     })
diff --git a/pgweb/core/models.py b/pgweb/core/models.py
index 54185917..2e88c04f 100644
--- a/pgweb/core/models.py
+++ b/pgweb/core/models.py
@@ -272,6 +272,9 @@ class UserProfile(models.Model):
     block_oauth = models.BooleanField(null=False, blank=False, default=False,
                                       verbose_name="Block OAuth login",
                                       help_text="Disallow login to this account using OAuth providers like Google or Microsoft.")
+    show_contributor_profile = models.BooleanField(null=False, blank=False, default=False,
+                                                   verbose_name="Show public contributor profile",
+                                                   help_text="Allow others to view your contributor profile at /community/contributors/[username]/")
 
 
 class UserSubmission(models.Model):
diff --git a/templates/contributors/list.html b/templates/contributors/list.html
index e713cee2..ef324572 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>{% 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 c.user and not c.hidden %}<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>{% 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>
+        <td>{% if c.user and not c.hidden %}<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>{% 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>
+        <td>{% if c.user and not c.hidden %}<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%}
-- 
2.51.2



^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: Public contributor profile pages
  2025-12-17 00:24 Public contributor profile pages Melanie Plageman <[email protected]>
@ 2025-12-17 14:03 ` Magnus Hagander <[email protected]>
  2025-12-17 15:18   ` Re: Public contributor profile pages Melanie Plageman <[email protected]>
  0 siblings, 1 reply; 5+ messages in thread

From: Magnus Hagander @ 2025-12-17 14:03 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: [email protected]; PostgreSQL Contributors <[email protected]>

On Wed, 17 Dec 2025 at 01:24, Melanie Plageman <[email protected]>
wrote:

> Hello,
>
> The Contributors Committee is working on a new program to recognize
> specific contributions (e.g. "volunteered at PGConf.dev 2024" or
> "contributed code to PostgreSQL 18"). These would be listed on an
> individual contributor's profile. As a step toward that, we are
> proposing to add public contributor profile pages.
>
> Attached is a patch set which implements this by:
> 1) allowing all those with pgo user accounts to edit their
> "contributions" (currently only enabled for recognized contributors)
> 2) adds a public contributor profile view accessible via
> postgresql.org/community/contributors/[username]
> <http://postgresql.org/community/contributors/%5Busername%5D;
> 3) links all recognized contributors on [1] to their public contributor
> profile
>
> There are quite a few details to work out. A few I know of are:
> - How should opting into a public profile work? What about existing
> recognized contributors, do we opt them all in automatically? Or just
> opt-in Major Contributors since their "Contributions" are already on a
> public web page? (see patch 0003)
> - How to make the public profile URL scheme work for contributors who
> do not have a pgo user account? Or should it work at all?
>
> This is my first real web patch, so I'm sure I did some things in a
> non-pgweb-idiomatic (or non web-idiomatic) way and didn't think of
> lots of things. I'll probably need lots of guidance.
>

Hi!

A couple of comments!

AFAICS with this patch, *every* user who visits their profile gets a
Contributor object created. And they do that even if they don't save their
profile? And the contributions part of the profile shows up for all users,
whether they are contributors at all or not -- not just "all levels of
contributors". That doesn't seem right, surely only those who are
recognized at *some* level should be getting that, and the change is to
list all levels, but not random non-contributors? (And even if it's shown
for non-contributors intentionally, we shouldn't create a database record
for people just viewing things)

Also, when making changes to a model that only changes fields that aren't
actually in the database (such as the change of help_text), instead of
making a migration for that it's better to "backpatch" it into the latest
existing migration. Otherwise we just collect a lot of no-op migrations
that over time will start costing time and annoyance.

And finally, when adding URLs that contain items from a model, you need to
add those URLs to purge_urls() otherwise it will stay cached when edited.
See for example news.NewsArticle as an example of how that's done
dynamically.


As for the questions - I think we should absolutely do opt-in, and I do
think it makes sense to auto-opt-in the current major contributors since
"it's been like that forever and they haven't complained", but listing the
others and in a more visible way there are sure to be some who might not
want it. In particular, maybe we should make the listing of *email* a
separate opt-in/opt-out? That is you can opt-out of being listed
completely, or you can say "list me, but not my email"?

I've thought a bit about it and I do think listing the profiles with the
username in the URL as you have done is in the end good. I don't think
people would care about exposing that one -- and it's already exposed on
for example the commitfest app. But based on that I would go with "does not
show at all if they don't have an account". They won't be able to edit any
information on it anyway, and if they care enough to do that they will
create an account. We will presumably continue to list them in the list of
contributors, just not provide a details page.

I wonder if we really need a separate "report inappropriate profile"? We
should be getting notifications for them, but in particular if we are
seriously worried about this case, we should make the records moderated.
But these are known contributors - I think we can trust them?


-- 
 Magnus Hagander
 Me: https://www.hagander.net/ <http://www.hagander.net/;
 Work: https://www.redpill-linpro.com/ <http://www.redpill-linpro.com/;


^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: Public contributor profile pages
  2025-12-17 00:24 Public contributor profile pages Melanie Plageman <[email protected]>
  2025-12-17 14:03 ` Re: Public contributor profile pages Magnus Hagander <[email protected]>
@ 2025-12-17 15:18   ` Melanie Plageman <[email protected]>
  2025-12-17 16:07     ` Re: Public contributor profile pages Christoph Berg <[email protected]>
  2025-12-18 12:25     ` Re: Public contributor profile pages Magnus Hagander <[email protected]>
  0 siblings, 2 replies; 5+ messages in thread

From: Melanie Plageman @ 2025-12-17 15:18 UTC (permalink / raw)
  To: Magnus Hagander <[email protected]>; +Cc: [email protected]; PostgreSQL Contributors <[email protected]>

Thanks for taking a look! A few follow-up questions before I edit the patch:

On Wed, Dec 17, 2025 at 9:03 AM Magnus Hagander <[email protected]> wrote:
>
> AFAICS with this patch, *every* user who visits their profile gets a Contributor object created. And they do that even if they don't save their profile? And the contributions part of the profile shows up for all users, whether they are contributors at all or not -- not just "all levels of contributors". That doesn't seem right, surely only those who are recognized at *some* level should be getting that, and the change is to list all levels, but not random non-contributors? (And even if it's shown for non-contributors intentionally, we shouldn't create a database record for people just viewing things)

What we are envisioning once we add "Contribution Badges" (or whatever
we call them) is that anybody who is approved for a badge (e.g.
volunteered at PGConf EU) would have that badge listed on their
profile. These may or may not be considered "Recognized Contributors",
and we aren't sure whether or not their names will be listed on the
Contributors Profile page or another page or not linked to a main pgo
page at all. But either way, it makes sense for these profiles to be
publicly viewable since they were awarded to the person.

Since we don't have the badges yet, we are considering the text of the
"Contribution" field to be the equivalent. So anyone that lists
"Contribution"s should have a publicly accessible profile. It isn't
approved by a moderator like badges would be, which does make it more
fraught, though.

The reason I thought every user profile should have a Contributor
object is that 1) it seemed easier from an implementation standpoint
than creating one only once someone has edited the contribution fields
of the form 2) most of the use cases for a pgo account seem related to
contributing (signing up for mailing lists, etc) -- I don't know if
they are used for other things like downloading postgres binaries etc.

Is there a good way to avoid making the Contributor object unless they
edit the "Contributions" field? Maybe there should be some kind of way
of saying "I contribute to Postgres" and "Contributions" is a required
field and then that makes the Contributor object and also kicks the
profile to some moderation queue for the Contributors team?

> Also, when making changes to a model that only changes fields that aren't actually in the database (such as the change of help_text), instead of making a migration for that it's better to "backpatch" it into the latest existing migration. Otherwise we just collect a lot of no-op migrations that over time will start costing time and annoyance.

The latest existing migration that includes that field or just the
latest existing migration?

> As for the questions - I think we should absolutely do opt-in, and I do think it makes sense to auto-opt-in the current major contributors since "it's been like that forever and they haven't complained", but listing the others and in a more visible way there are sure to be some who might not want it. In particular, maybe we should make the listing of *email* a separate opt-in/opt-out? That is you can opt-out of being listed completely, or you can say "list me, but not my email"?

So there would be a separate opt-in for being listed on the recognized
contributor page and for having a public individual contributor
profile? And we would only auto opt-in major contributors to the
public individual contributor profiles?

And email display would be separately opt-in and if you opt-in it
displays on both the individual contributor profile page and on the
main recognized contributor profile page for major contributors and we
would auto opt-in email display for major contributors?

> I've thought a bit about it and I do think listing the profiles with the username in the URL as you have done is in the end good. I don't think people would care about exposing that one -- and it's already exposed on for example the commitfest app. But based on that I would go with "does not show at all if they don't have an account". They won't be able to edit any information on it anyway, and if they care enough to do that they will create an account. We will presumably continue to list them in the list of contributors, just not provide a details page.

That makes sense. Maybe we should start calling the Contributor
Profiles page [1] the Recognized Contributors page or Sustained
Contributors page instead of the Contributor Profiles page to
disambiguate it from individual contributor profiles. If only to make
spec'ing out this feature easier.

> I wonder if we really need a separate "report inappropriate profile"? We should be getting notifications for them, but in particular if we are seriously worried about this case, we should make the records moderated. But these are known contributors - I think we can trust them?

I think we want people who are not recognized significant or major
contributors to be able to have profiles so that they can build them
up and eventually make the case for being a recognized significant or
major contributor. This would significantly increase the number of
people with profiles so moderation may get tricky.

What does it mean that we would get notifications for them? Is "we"
the contributors committee?

I would be okay with having them moderated if we thought the volume
would be like one a day (with more at the beginning). And maybe if it
is more, we could add another community member outside the
contributors committee to help moderate.

- Melanie

[1] https://www.postgresql.org/community/contributors/





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: Public contributor profile pages
  2025-12-17 00:24 Public contributor profile pages Melanie Plageman <[email protected]>
  2025-12-17 14:03 ` Re: Public contributor profile pages Magnus Hagander <[email protected]>
  2025-12-17 15:18   ` Re: Public contributor profile pages Melanie Plageman <[email protected]>
@ 2025-12-17 16:07     ` Christoph Berg <[email protected]>
  1 sibling, 0 replies; 5+ messages in thread

From: Christoph Berg @ 2025-12-17 16:07 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: Magnus Hagander <[email protected]>; [email protected]; PostgreSQL Contributors <[email protected]>

Re: Melanie Plageman
> And email display would be separately opt-in and if you opt-in it
> displays on both the individual contributor profile page and on the
> main recognized contributor profile page for major contributors and we
> would auto opt-in email display for major contributors?

Since the email is user-editable, and separate from the account email,
people could just leave that field empty. We just need to update the
description so they know when editing their profile. Same for all the
other fields)

> That makes sense. Maybe we should start calling the Contributor
> Profiles page [1] the Recognized Contributors page or Sustained
> Contributors page instead of the Contributor Profiles page to
> disambiguate it from individual contributor profiles. If only to make
> spec'ing out this feature easier.

We seem to have started using "Recognized Contributors" in the team,
even if we didn't ever consciously decide about it.

> > I wonder if we really need a separate "report inappropriate profile"? We should be getting notifications for them, but in particular if we are seriously worried about this case, we should make the records moderated. But these are known contributors - I think we can trust them?

That button looks a bit odd at the moment. Perhaps having it is
appropriate, but it should be much less prominent so the page looks
good when people show it around.

> I think we want people who are not recognized significant or major
> contributors to be able to have profiles so that they can build them
> up and eventually make the case for being a recognized significant or
> major contributor.

Maybe we could actually base our decisions on that...

> This would significantly increase the number of
> people with profiles so moderation may get tricky.
> 
> What does it mean that we would get notifications for them? Is "we"
> the contributors committee?

Perhaps passive moderation is good enough, i.e. things get put live
automatically, but we can review the notifications and kick anything
bad.

Christoph





^ permalink  raw  reply  [nested|flat] 5+ messages in thread

* Re: Public contributor profile pages
  2025-12-17 00:24 Public contributor profile pages Melanie Plageman <[email protected]>
  2025-12-17 14:03 ` Re: Public contributor profile pages Magnus Hagander <[email protected]>
  2025-12-17 15:18   ` Re: Public contributor profile pages Melanie Plageman <[email protected]>
@ 2025-12-18 12:25     ` Magnus Hagander <[email protected]>
  1 sibling, 0 replies; 5+ messages in thread

From: Magnus Hagander @ 2025-12-18 12:25 UTC (permalink / raw)
  To: Melanie Plageman <[email protected]>; +Cc: [email protected]; PostgreSQL Contributors <[email protected]>

On Wed, 17 Dec 2025 at 16:18, Melanie Plageman <[email protected]>
wrote:

> Thanks for taking a look! A few follow-up questions before I edit the
> patch:
>
> On Wed, Dec 17, 2025 at 9:03 AM Magnus Hagander <[email protected]>
> wrote:
> >
> > AFAICS with this patch, *every* user who visits their profile gets a
> Contributor object created. And they do that even if they don't save their
> profile? And the contributions part of the profile shows up for all users,
> whether they are contributors at all or not -- not just "all levels of
> contributors". That doesn't seem right, surely only those who are
> recognized at *some* level should be getting that, and the change is to
> list all levels, but not random non-contributors? (And even if it's shown
> for non-contributors intentionally, we shouldn't create a database record
> for people just viewing things)
>
> What we are envisioning once we add "Contribution Badges" (or whatever
> we call them) is that anybody who is approved for a badge (e.g.
> volunteered at PGConf EU) would have that badge listed on their
> profile. These may or may not be considered "Recognized Contributors",
> and we aren't sure whether or not their names will be listed on the
> Contributors Profile page or another page or not linked to a main pgo
> page at all. But either way, it makes sense for these profiles to be
> publicly viewable since they were awarded to the person.
>

If they are not based on the recognized contributors, they should probably
not be part of that in the data model either. But in particular, we
shouldn't start creating empty contributor profiles before we even *knoiw*
that it would.


Since we don't have the badges yet, we are considering the text of the
> "Contribution" field to be the equivalent. So anyone that lists
> "Contribution"s should have a publicly accessible profile. It isn't
> approved by a moderator like badges would be, which does make it more
> fraught, though.
>

So any user without any confirmation can just add themselves as a
contributor, without even moderation, and publish data? That sounds like a
*really* bad idea, tbh.

If it's data published by users where we have at one point added them to
the contributors list and therefore pre-moderated to some point that is
reasonable. But allowing random people to add records and then edit them
and then it turns out to be published is going to be a hard no -- we've
spent many hundreds of hours dealing with that on the wiki for example, and
we will definitely not open up another venue for that. If it's things that
not-specifically-approved users can add, then it *must* be moderated.


The reason I thought every user profile should have a Contributor
> object is that 1) it seemed easier from an implementation standpoint
> than creating one only once someone has edited the contribution fields
> of the form 2) most of the use cases for a pgo account seem related to
> contributing (signing up for mailing lists, etc) -- I don't know if
> they are used for other things like downloading postgres binaries etc.
>

No, the vast majority of pgo accounts are not in any way contributors.
Subscribers to mailinglists, by a large majority, don't
post/contribute. Attendees of conferences are mostly consumers and not
contributors, etc etc way outnumber those who are actually logging in to
the sites that are for contributors. We definitely don't have 175,000+
contributors...


Is there a good way to avoid making the Contributor object unless they
> edit the "Contributions" field? Maybe there should be some kind of way
> of saying "I contribute to Postgres" and "Contributions" is a required
> field and then that makes the Contributor object and also kicks the
> profile to some moderation queue for the Contributors team?
>

If the goal is the users can add themselves rather than the contributors
team adding them, then yeah I think the process should be more similar to
how users add a news article for example, or an organization (though they
should have a 1-1 mapping of course). That is, it should be a separate
"register me as a contributor" button which takes them to a separate form
that specifically creates it. This can then also easily be moderated -- see
the use of moderation mixins on for example organisations or professional
services (news are a little special because they have dual-moderation, and
that seems like overkill here).



> Also, when making changes to a model that only changes fields that aren't
> actually in the database (such as the change of help_text), instead of
> making a migration for that it's better to "backpatch" it into the latest
> existing migration. Otherwise we just collect a lot of no-op migrations
> that over time will start costing time and annoyance.
>
> The latest existing migration that includes that field or just the
> latest existing migration?
>

The latest one that includes that field. Doing that makes the "manage.py
makemigration" no longer think there are any changes and stop emitting
anything for that object.


> As for the questions - I think we should absolutely do opt-in, and I do
> think it makes sense to auto-opt-in the current major contributors since
> "it's been like that forever and they haven't complained", but listing the
> others and in a more visible way there are sure to be some who might not
> want it. In particular, maybe we should make the listing of *email* a
> separate opt-in/opt-out? That is you can opt-out of being listed
> completely, or you can say "list me, but not my email"?
>
> So there would be a separate opt-in for being listed on the recognized
> contributor page and for having a public individual contributor
> profile? And we would only auto opt-in major contributors to the
> public individual contributor profiles?
>

When writing that I was not aware that you were intending to have both a
"recognized contributor" and a "self-claimed contributor". That does change
it a bit. If it's a self-claimed contributor then it doesn't make much
sense to opt-out.


And email display would be separately opt-in and if you opt-in it
> displays on both the individual contributor profile page and on the
> main recognized contributor profile page for major contributors and we
> would auto opt-in email display for major contributors?
>

I think Christoph made a good point about this -- once the contributor
profile requires an account, which it does in order to get published here,
they can opt out by just changing their email to being empty. It's only for
contributors that do not have an account connected that *we* need the email
(so we can reach them), but with the discussed plan those wouldn't be
listed anyway  since a username is needed in order to get listed. So that
should leave us without needing a separate opt-out.


>
> > I've thought a bit about it and I do think listing the profiles with the
> username in the URL as you have done is in the end good. I don't think
> people would care about exposing that one -- and it's already exposed on
> for example the commitfest app. But based on that I would go with "does not
> show at all if they don't have an account". They won't be able to edit any
> information on it anyway, and if they care enough to do that they will
> create an account. We will presumably continue to list them in the list of
> contributors, just not provide a details page.
>
> That makes sense. Maybe we should start calling the Contributor
> Profiles page [1] the Recognized Contributors page or Sustained
> Contributors page instead of the Contributor Profiles page to
> disambiguate it from individual contributor profiles. If only to make
> spec'ing out this feature easier.
>

Yeah, I think that makes sense. If we're trying to add a whole new concept,
we shouldn't try to force it in under the same name/umbrella. Let's make it
clear.


> I wonder if we really need a separate "report inappropriate profile"? We
> should be getting notifications for them, but in particular if we are
> seriously worried about this case, we should make the records moderated.
> But these are known contributors - I think we can trust them?
>
> I think we want people who are not recognized significant or major
> contributors to be able to have profiles so that they can build them
> up and eventually make the case for being a recognized significant or
> major contributor. This would significantly increase the number of
> people with profiles so moderation may get tricky.
>

Per above, if it is a field that any user can directly edit and it goes to
publication, it must be moderated. That's a hard requirement. Before we
made the whole "make me an editor" requirement for the wiki, for examples,
we had upwards of 10,000 new accounts signed up every day just to post spam
when it hit the peak.

We might need to add more moderators for it, but it needs to be moderated :)


What does it mean that we would get notifications for them? Is "we"
> the contributors committee?
>

Right now, there's just the one moderation team and address that gets it
for all types of objects. But it would probably be relatively easy to split
that up so there could be multiple moderation-and-notification teams.


I would be okay with having them moderated if we thought the volume
> would be like one a day (with more at the beginning). And maybe if it
> is more, we could add another community member outside the
> contributors committee to help moderate.
>

I don't know what an expected volume would be for "real" contributions, but
even one per day sounds high to me -- obviously once you are past a peak
when it's new. And as long as they don't go direct-to-publish spam
publications tend to be fairly low, but they can certainly reach one or a
couple per day. It's only with direct-to-publish we end up with thousands.

//Magnus


^ permalink  raw  reply  [nested|flat] 5+ messages in thread


end of thread, other threads:[~2025-12-18 12:25 UTC | newest]

Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2025-12-17 00:24 Public contributor profile pages Melanie Plageman <[email protected]>
2025-12-17 14:03 ` Magnus Hagander <[email protected]>
2025-12-17 15:18   ` Melanie Plageman <[email protected]>
2025-12-17 16:07     ` Christoph Berg <[email protected]>
2025-12-18 12:25     ` Magnus Hagander <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox