-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Link IndividualMember to user profile page #2277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 13 commits
bb10b9c
2c29158
093ed87
8b918c0
6e90cd5
6a2322d
a55a0f5
9a180f1
a352eda
36b3f90
b7ad21f
da706a4
d4d8873
b918122
e46a504
f1a4781
20117b6
84c0826
bc0467e
7365e8b
46cb8cd
a69e9e1
85c211d
65950f7
9fc7fd1
bb963f6
2588d94
94ba067
81d7137
db9a7dd
aa9d1cb
f37e3f8
ef54d5b
b956bfb
f922710
a0f2d47
b91a2a0
26eed00
c9cc83e
a8984d5
4725cfa
5b67032
a42b481
58ed3bc
29c77ed
e13bd18
f314007
86d44c4
c3ae853
38f3ca3
ea1ee73
6b0d2c3
dd4e5d8
511c922
32a44ed
e77cf23
c50371e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Generated by Django 5.2.7 on 2025-10-19 01:39 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("accounts", "0002_migrate_sha1_passwords"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name="profile", | ||
| name="bio", | ||
| field=models.TextField(blank=True), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ <h2 id="aside-header"> | |
|
|
||
| <p>{% translate "Need to edit something? Here's how:" %}</p> | ||
| <ul> | ||
| <li><a href="{% url 'edit_profile' %}">{% translate "Edit your name and email here." %}</a></li> | ||
| <li><a href="{% url 'edit_profile' %}">{% translate "Edit your name, email and bio here." %}</a></li> | ||
| <li>{% blocktranslate trimmed %} | ||
| The image is the <a href="https://en.gravatar.com/">Gravatar</a> linked | ||
| to the email address you signed up with. You can change the image over | ||
|
|
@@ -37,10 +37,16 @@ <h2 id="aside-header"> | |
| <img class='avatar' width='150' height='150' | ||
| src="https://secure.gravatar.com/avatar/{{ email_hash }}?s=150&d=https%3A%2F%2Frobohash.org%2F{{ email_hash }}%3Fset%3Dset3%26size%3D150x150"> | ||
|
|
||
| <h1> | ||
| <h1 class="name"> | ||
| {% firstof user_obj.profile.name user_obj.username %} | ||
| </h1> | ||
|
|
||
| {% if user_obj.profile.bio %} | ||
| <p class="bio"> | ||
| {{ user_obj.profile.bio }} | ||
|
||
| </p> | ||
| {% endif %} | ||
|
|
||
| {% if stats %} | ||
| <h2>{% translate "Statistics on Django core contributions:" %}</h2> | ||
| <ul> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| {% spaceless %} | ||
| Hello {{ name }}, | ||
|
|
||
| We're updating the Django Individual Members list so each member's name can link to their djangoproject.com profile. On your profile, you can share a short bio, highlight your contributions to Django or the community, and provide ways for others to get in touch. | ||
|
|
||
| To have your name linked, please create an account (if you don't already have one) at: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if it's worth mentioning that their account name should match their github username if they use their github account for Trac. This let's us show stats about their contributions in their profile page
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, thank you! That's something I wasn't aware of. I'll work on it. Additionally, I've checked the code and I'm thinking that we could provide another optional input to let user override the parameter passed to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure as I guess folks could try to claim the stats of others as their own 🤔
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, displaying the username on the page wouldn't be enough. What if we put the input to the admin only instead, and mention in the email that if they have a mismatch they can request a fix?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure that's an idea 👍
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've done the following:
Additional screenshots:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've refactored the profile view to prevent rendering false Trac stats in certain situations. Here's the step-by-step details:
Since Please feel free to let me know if anything should be handled differently. |
||
|
|
||
| {% url 'registration_register' %} | ||
|
|
||
| Once your account is ready, let us know your username so we can connect it to the members list. | ||
|
|
||
| This small step helps the community grow more connected and makes it easier for everyone to explore and engage with it. | ||
|
|
||
| Thank you for being part of the Django community. | ||
|
|
||
| Django Software Foundation | ||
| {% endspaceless %} | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,12 +1,20 @@ | ||||||
| from datetime import date, timedelta | ||||||
|
|
||||||
| from django.contrib import admin | ||||||
| from django.contrib import admin, messages | ||||||
| from django.contrib.auth import get_permission_codename | ||||||
| from django.db import transaction | ||||||
| from django.templatetags.static import static | ||||||
| from django.utils.formats import localize | ||||||
| from django.utils.html import format_html | ||||||
| from django.utils.translation import gettext_lazy as _ | ||||||
| from django.utils.translation import gettext_lazy as _, ngettext | ||||||
|
|
||||||
| from members.models import CorporateMember, IndividualMember, Invoice, Team | ||||||
| from members.models import ( | ||||||
| CorporateMember, | ||||||
| IndividualMember, | ||||||
| IndividualMemberAccountInviteSendMailStatus, | ||||||
| Invoice, | ||||||
| Team, | ||||||
| ) | ||||||
|
|
||||||
|
|
||||||
| @admin.register(IndividualMember) | ||||||
|
|
@@ -17,8 +25,77 @@ class IndividualMemberAdmin(admin.ModelAdmin): | |||||
| "is_active", | ||||||
| "member_since", | ||||||
| "member_until", | ||||||
| "account_invite_mail_sent_at", | ||||||
| ] | ||||||
| search_fields = ["name"] | ||||||
| list_filter = ["member_until", "account_invite_mail_sent_at"] | ||||||
| autocomplete_fields = ["user"] | ||||||
| actions = ["send_account_invite_mail"] | ||||||
|
|
||||||
| @admin.action( | ||||||
| description=_("Send account invite mail to selected individual members"), | ||||||
| permissions=["send_account_invite_mail"], | ||||||
| ) | ||||||
| def send_account_invite_mail(self, request, queryset): | ||||||
| with transaction.atomic(): | ||||||
| results = IndividualMember.send_account_invite_mails(queryset) | ||||||
| sent_count = results.get( | ||||||
| IndividualMemberAccountInviteSendMailStatus.SENT, | ||||||
| 0, | ||||||
| ) | ||||||
| failed_count = results.get( | ||||||
| IndividualMemberAccountInviteSendMailStatus.FAILED, | ||||||
| 0, | ||||||
| ) | ||||||
| skipped_count = results.get( | ||||||
| IndividualMemberAccountInviteSendMailStatus.SKIPPED, | ||||||
| 0, | ||||||
| ) | ||||||
| if sent_count > 0: | ||||||
| self.message_user( | ||||||
| request, | ||||||
| ngettext( | ||||||
| "Sent account invite mail to 1 individual member.", | ||||||
|
||||||
| "Sent account invite mail to 1 individual member.", | |
| "Sent account invite mail to %(count)d individual member.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right! I missed that detail, and I'll fix it accordingly. Thanks so much for catching it and for the kind words. 💚
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK! I've fixed it.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Generated by Django 5.2.7 on 2025-10-19 18:16 | ||
|
|
||
| import django.db.models.deletion | ||
| from django.conf import settings | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("members", "0009_alter_individualmember_add_reason_help_text"), | ||
| migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name="individualmember", | ||
| name="user", | ||
| field=models.OneToOneField( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As an idea, we could have a custom migration which links existing
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's done too. I also made it reusable to handle occasional stale states by adding a management command. |
||
| blank=True, | ||
| null=True, | ||
| on_delete=django.db.models.deletion.SET_NULL, | ||
| to=settings.AUTH_USER_MODEL, | ||
| ), | ||
| ), | ||
| ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # Generated by Django 5.2.7 on 2025-10-20 19:04 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ("members", "0010_individualmember_user"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterModelOptions( | ||
| name="individualmember", | ||
| options={ | ||
| "ordering": ["name"], | ||
| "permissions": [ | ||
| ( | ||
| "send_account_invite_mail", | ||
| "Can send account invite mail to an individual member", | ||
| ) | ||
| ], | ||
| }, | ||
| ), | ||
| migrations.AddField( | ||
| model_name="individualmember", | ||
| name="account_invite_mail_sent_at", | ||
| field=models.DateTimeField(blank=True, db_index=True, null=True), | ||
| ), | ||
| migrations.AlterField( | ||
| model_name="individualmember", | ||
| name="member_until", | ||
| field=models.DateField(blank=True, db_index=True, null=True), | ||
| ), | ||
| ] |


Uh oh!
There was an error while loading. Please reload this page.