Skip to content

Commit 2207905

Browse files
agjohnsonstsewd
andauthored
Allauth: tune login/connect sorting, naming, and add GitHub App/Oauth modal (#609)
- Requires readthedocs/readthedocs.org#12242 - Requires readthedocs/readthedocs-corporate#2007 - Fixes #608 - Fixes #379 - Fixes #588 ---- Work left: - [x] Test with SAML providers - [x] **On or before release, all database SocialApps need settings in JSON field for `{"hidden": true}` and testing on actual auth.** - Instead of relying on `hidden`, SAML providers were filtered from `get_providers` ### Examples #### Business with Google provider and GitHub modal button ![image](https://github.com/user-attachments/assets/c3f7ac6f-5979-4940-af29-96508c253ace) #### Business when no modal is needed ![image](https://github.com/user-attachments/assets/0168c6bf-be63-440b-bda2-c96d10848071) #### GitHub modal ![image](https://github.com/user-attachments/assets/c076c87d-8ccf-41ae-bdeb-a3d0f1080fa9) #### Social account list ![image](https://github.com/user-attachments/assets/d1a53a83-3a9e-49fe-94e8-880486d05c0f) --------- Co-authored-by: Santos Gallegos <[email protected]>
1 parent 53f518b commit 2207905

File tree

4 files changed

+233
-44
lines changed

4 files changed

+233
-44
lines changed

readthedocsext/theme/templates/socialaccount/partials/social_account_list.html

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
{% extends "includes/crud/table_list.html" %}
22

3-
{% load i18n %}
4-
{% load gravatar %}
3+
{% load blocktrans trans from i18n %}
54
{% load can_be_disconnected from readthedocs.socialaccounts %}
65

7-
{% load core_tags %}
8-
{% load privacy_tags %}
9-
{% load projects_tags %}
10-
{% load ext_theme_tags %}
6+
{% load get_account_username from ext_theme_tags %}
117

128
{% block top_menu %}
13-
{% if form.non_field_errors %}<div class="ui error message">{{ form.non_field_errors }}</div>{% endif %}
9+
{% if form.non_field_errors %}
10+
<div class="ui error message">{{ form.non_field_errors }}</div>
11+
{% endif %}
1412
{{ block.super }}
1513
{% endblock top_menu %}
1614
{% block top_left_menu_items %}
1715
{% endblock top_left_menu_items %}
1816

1917
{% block create_button %}
2018
<div class="ui green button"
21-
data-bind="click: $root.show_modal('socialaccount-connections')">{% trans "Add new connection" %}</div>
19+
data-bind="click: $root.show_modal('socialaccount-connections')">
20+
{% trans "Add new connection" %}
21+
</div>
2222
{% endblock create_button %}
2323

2424
{% block list_placeholder_icon_class %}
@@ -56,11 +56,19 @@
5656
{% endblock list_item_right_menu %}
5757

5858
{% block list_item_image %}
59-
<img class="ui rounded image"
60-
src="{{ object.get_avatar_url }}"
61-
height="28"
62-
alt="{% trans "Social account avatar" %}"
63-
width="28" />
59+
{% with avatar_url=object.get_avatar_url %}
60+
{% if avatar_url %}
61+
<img class="ui rounded image"
62+
src="{{ avatar_url }}"
63+
height="28"
64+
alt="{% trans "Social account avatar" %}"
65+
width="28" />
66+
{% else %}
67+
<div class="ui center aligned image">
68+
<i class="fad fa-user icon"></i>
69+
</div>
70+
{% endif %}
71+
{% endwith %}
6472
{% endblock list_item_image %}
6573

6674
{% block list_item_header %}
@@ -73,12 +81,19 @@
7381
{% endblock list_item_header %}
7482

7583
{% block list_item_meta_items %}
76-
{% with object.get_provider_account as provider %}
77-
<span class="ui label">
78-
{# get_brand.name|lower here because we can't use `id`, `Bitbucket2Provider.id == 'bitbucket_oauth2'` #}
79-
<i class="fa-brands fa-{{ provider.get_brand.name|lower }} icon"></i>
80-
{{ provider.get_brand.name }}
81-
</span>
84+
{% with provider=object.get_provider %}
85+
{% if provider.app.pk and provider.id == "saml" %}
86+
<span class="ui label">
87+
<i class="fas fa-users icon"></i>
88+
{% trans "SAML" %}
89+
</span>
90+
{% else %}
91+
{# Provider is not a database provider #}
92+
<span class="ui label">
93+
<i class="fa-brands fa-{{ provider.name|lower }} icon"></i>
94+
{{ provider.app.name|default:provider.name }}
95+
</span>
96+
{% endif %}
8297
{% endwith %}
8398
{% endblock list_item_meta_items %}
8499

Lines changed: 137 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,142 @@
1-
{% load get_providers provider_login_url from socialaccount %}
21
{% load trans blocktrans from i18n %}
2+
{% load get_github_providers get_providers from ext_theme_tags %}
33

4-
{% get_providers as socialaccount_providers %}
4+
{% comment %}
5+
``get_providers`` filters out providers marked as hidden in our settings file.
6+
``get_github_providers`` is just used for the modal and can be removed eventually.
7+
{% endcomment %}
8+
{% get_providers process=process as providers %}
9+
{% get_github_providers process=process as providers_github %}
510

6-
{% for provider in socialaccount_providers %}
7-
{% comment %}
8-
- OpenID is not implemented.
9-
- SAML is handled in another view, we don't want to list all SAML integrations here.
10-
- GitHub App is not exposed to users yet.
11-
{% endcomment %}
12-
{% if provider.id != 'saml' and provider.id != 'githubapp' %}
13-
{% if allowed_providers and provider.id in allowed_providers or not allowed_providers %}
14-
<li class="item">
15-
<form class="ui form"
16-
method="post"
17-
action="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}">
18-
{% csrf_token %}
19-
<button class="ui button" type="submit" title="{{ provider.name }}">
20-
<i class="fa-brands fa-{{ provider.name|lower }} icon"></i>
21-
{% blocktrans trimmed with provider_name=provider.name verbiage=verbiage|default:'Connect to' %}
22-
{{ verbiage }} {{ provider_name }}
23-
{% endblocktrans %}
24-
</button>
25-
</form>
26-
</li>
27-
{% endif %}
11+
{% comment %}
12+
If both GitHub providers are configured, and they are not ``hidden=True`` or
13+
``hidden_on_{process}=True`` in settings, we'll show these providers in a modal.
14+
{% endcomment %}
15+
{% get_providers process=process as providers %}
16+
{% if providers_github|length == 2 %}
17+
{% trans "GitHub" as provider_name %}
18+
<li class="item">
19+
<a class="ui button"
20+
data-bind="click: $root.show_modal('github-select')"
21+
title="{{ provider_name }}">
22+
<i class="fa-brands fa-github icon"></i>
23+
{% blocktrans trimmed with provider_name=provider_name verbiage=verbiage|default:'Connect to' %}
24+
{{ verbiage }} {{ provider_name }}
25+
{% endblocktrans %}
26+
</a>
27+
</li>
28+
<div class="ui tiny modal" data-modal-id="github-select">
29+
<div class="header">
30+
{% blocktrans trimmed with verbiage=verbiage|default:'Connect to' provider_name=provider_name %}
31+
{{ verbiage }} {{ provider_name }}
32+
{% endblocktrans %}
33+
</div>
34+
<div class="content">
35+
<p>
36+
{% blocktrans trimmed %}
37+
Read the Docs now supports two methods for connecting your GitHub account:
38+
{% endblocktrans %}
39+
</p>
40+
<div class="ui segment">
41+
<div class="ui small divided items">
42+
{% for provider in providers_github %}
43+
<div class="item">
44+
<div class="content">
45+
<div class="header">
46+
{% if provider.id == "github" %}
47+
{% blocktrans trimmed %}
48+
Our GitHub OAuth app
49+
{% endblocktrans %}
50+
<span class="ui compact small basic label">Default</span>
51+
{% elif provider.id == "githubapp" %}
52+
{% blocktrans trimmed %}
53+
Our GitHub App
54+
{% endblocktrans %}
55+
<span class="ui violet compact small basic label">Beta</span>
56+
{% endif %}
57+
</div>
58+
<div class="description">
59+
<p>
60+
{# As we progress in a beta test period, the connect text should be tuned to make GHA sound like less of a beta feature. #}
61+
{% with is_early_beta=False %}
62+
{% if provider.id == "github" %}
63+
{% if process == "connect" and is_early_beta %}
64+
{% blocktrans trimmed %}
65+
For new users and users reconnecting a previously connected GitHub account.
66+
{% endblocktrans %}
67+
{% elif process == "connect" and not is_early_beta %}
68+
{% blocktrans trimmed %}
69+
For users reconnecting a previously connected GitHub account and users unable to switch to our new app.
70+
{% endblocktrans %}
71+
<i>
72+
{% blocktrans trimmed %}
73+
We recommend new users connect their account using our GitHub App.
74+
{% endblocktrans %}
75+
</i>
76+
{% else %}
77+
{% blocktrans trimmed %}
78+
For users that connected their GitHub accounts before June 24, 2025.
79+
{% endblocktrans %}
80+
{% endif %}
81+
{% elif provider.id == "githubapp" %}
82+
{% if process == "connect" and is_early_beta %}
83+
{% blocktrans trimmed %}
84+
For users that would like early access to our new app and would like to test new features.
85+
{% endblocktrans %}
86+
<i>
87+
{% blocktrans trimmed %}
88+
We recommend only advanced users use this method.
89+
{% endblocktrans %}
90+
</i>
91+
{% elif process == "connect" and not is_early_beta %}
92+
{% blocktrans trimmed %}
93+
For users that want granular control of permissions to repositories.
94+
{% endblocktrans %}
95+
{% else %}
96+
{% blocktrans trimmed %}
97+
For users that have switched their connected GitHub accounts already.
98+
{% endblocktrans %}
99+
{% endif %}
100+
{% endif %}
101+
{% endwith %}
102+
</p>
103+
</div>
104+
<div class="extra">
105+
{% with button_classes=forloop.first|yesno:"right floated primary,right floated basic" %}
106+
{% include "socialaccount/snippets/provider_list_item.html" with process=process verbiage=text_log_in provider=provider button_classes=button_classes %}
107+
{% endwith %}
108+
</div>
109+
</div>
110+
</div>
111+
{% endfor %}
112+
</div>
113+
</div>
114+
115+
<div class="ui info message">
116+
<i class="fas fa-info-circle icon"></i>
117+
To learn more about our new GitHub App integration,
118+
<a href="https://about.readthedocs.com/blog/2025/06/announcing-our-github-app-beta/">read our beta announcement</a>
119+
</div>
120+
</div>
121+
<div class="actions">
122+
<div class="ui cancel button">{% trans "Cancel" %}</div>
123+
</div>
124+
</div>
125+
{% endif %}
126+
127+
{# This is the basic list but with sorting now. If there is a modal above, we skip the GitHub providers here #}
128+
{% for provider in providers %}
129+
{% if providers_github|length == 2 and "github" in provider.id %}
130+
{% comment %}
131+
TODO remove this after we are mostly using GHA and don't need a modal
132+
133+
Skip any provider that is in the modal above if the modal is in use. This
134+
adjusts for when one provider is shown on connect but the modal is used
135+
on login.
136+
{% endcomment %}
137+
{% else %}
138+
<li class="item">
139+
{% include "socialaccount/snippets/provider_list_item.html" with process=process verbiage=text_log_in provider=provider %}
140+
</li>
28141
{% endif %}
29142
{% endfor %}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{% load provider_login_url from socialaccount %}
2+
{% load blocktrans trans from i18n %}
3+
4+
{% if allowed_providers and provider.id in allowed_providers or not allowed_providers %}
5+
<form class="ui form"
6+
method="post"
7+
action="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}">
8+
{% csrf_token %}
9+
10+
<button class="ui {{ button_classes }} button"
11+
type="submit"
12+
title="{{ provider.name }}">
13+
<i class="fa-brands fa-{{ provider.name|lower }} icon"></i>
14+
{% blocktrans trimmed with provider_name=provider.app.name|default:provider.name verbiage=verbiage|default:'Connect to' %}
15+
{{ verbiage }} {{ provider_name }}
16+
{% endblocktrans %}
17+
</button>
18+
19+
</form>
20+
{% endif %}

readthedocsext/theme/templatetags/ext_theme_tags.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
)
1111
from django.templatetags.static import StaticNode, PrefixNode
1212
from django.forms import boundfield
13-
13+
from allauth.socialaccount.templatetags.socialaccount import (
14+
get_providers as base_get_providers,
15+
)
1416

1517
log = logging.getLogger(__name__)
1618
register = template.Library()
@@ -134,6 +136,45 @@ def get_spam_score(project):
134136
return spam_score(project)
135137

136138

139+
@register.simple_tag(takes_context=True)
140+
def get_providers(context, process="login"):
141+
"""
142+
Adds additional app setting support and sorting to the Allauth provider list tag.
143+
144+
There are multiple app settings checked to determine if we show the
145+
provider to the user and which order:
146+
147+
``hidden`` (bool)
148+
This comes from the base Allauth tag
149+
150+
``hidden_on_login``/``hidden_on_connect``/``hidden_on_{process}`` (bool)
151+
Hide the provider from the Allauth process view
152+
153+
``priority``
154+
Priority order value for list of providers, higher values are lower in priority on the list.
155+
156+
Additionally, filter out providers from the database -- applications that
157+
have a ``pk`` -- these are per-user applications like SAML.
158+
"""
159+
# The base Allauth ``get_providers`` tag filters out providers marked as hidden in our settings file.
160+
providers = [
161+
provider
162+
for provider in base_get_providers(context)
163+
if not provider.app.settings.get(f"hidden_on_{process}", False)
164+
and not provider.app.pk
165+
]
166+
return sorted(
167+
providers, key=lambda provider: provider.app.settings.get("priority", 100)
168+
)
169+
170+
171+
# TODO remove this after we don't need to separate the providers with a modal
172+
@register.simple_tag(takes_context=True)
173+
def get_github_providers(context, process="login"):
174+
providers = get_providers(context, process)
175+
return list(filter(lambda provider: "github" in provider.id, providers))
176+
177+
137178
# Simple solution to not supported "zh" language code.
138179
#
139180
# When `project.language="zh"` the Django `language_name_local` raises an exception and returns 500.

0 commit comments

Comments
 (0)