mirror of
https://github.com/discourse/discourse.git
synced 2025-06-23 01:32:49 +08:00
FEATURE: add link to "associated accounts" providers (#33275)
This adds a link for each authentication providers that are listed in /my/preferences/account in the "Associated Accounts" section. This is particularly useful when Discourse is being used in the PWA or in the DiscourseMobile app where there's no browser bar available and the only way to visit the provider's website is to open a browser window. That way, they can _just_ click the provider's name. Internal ref - t/156255 --- **BEFORE**  **AFTER** 
This commit is contained in:
@ -87,16 +87,12 @@ export default class AccountController extends Controller {
|
||||
|
||||
@discourseComputed("model.associated_accounts.[]")
|
||||
authProviders(accounts) {
|
||||
const allMethods = findAll();
|
||||
|
||||
const result = allMethods.map((method) => {
|
||||
return {
|
||||
return findAll()
|
||||
.map((method) => ({
|
||||
method,
|
||||
account: accounts.find((account) => account.name === method.name), // Will be undefined if no account
|
||||
};
|
||||
});
|
||||
|
||||
return result.filter((value) => value.account || value.method.can_connect);
|
||||
account: accounts.find(({ name }) => name === method.name),
|
||||
}))
|
||||
.filter((value) => value.account || value.method.can_connect);
|
||||
}
|
||||
|
||||
@discourseComputed(
|
||||
|
@ -168,7 +168,17 @@ export default RouteTemplate(
|
||||
</td>
|
||||
<td>
|
||||
<div class="associated-account__name">
|
||||
{{authProvider.method.prettyName}}
|
||||
{{#if authProvider.method.provider_url}}
|
||||
<a
|
||||
href={{authProvider.method.provider_url}}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{{authProvider.method.prettyName}}
|
||||
</a>
|
||||
{{else}}
|
||||
{{authProvider.method.prettyName}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="associated-account__description">
|
||||
{{authProvider.account.description}}
|
||||
@ -206,7 +216,17 @@ export default RouteTemplate(
|
||||
</td>
|
||||
<td>
|
||||
<div class="associated-account__name">
|
||||
{{authProvider.method.prettyName}}
|
||||
{{#if authProvider.method.provider_url}}
|
||||
<a
|
||||
href={{authProvider.method.provider_url}}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
{{authProvider.method.prettyName}}
|
||||
</a>
|
||||
{{else}}
|
||||
{{authProvider.method.prettyName}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="associated-account__description">
|
||||
{{authProvider.account.description}}
|
||||
|
@ -1,28 +1,27 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AuthProviderSerializer < ApplicationSerializer
|
||||
attributes :name,
|
||||
:custom_url,
|
||||
:pretty_name_override,
|
||||
:title_override,
|
||||
:frame_width,
|
||||
:frame_height,
|
||||
:can_connect,
|
||||
attributes :can_connect,
|
||||
:can_revoke,
|
||||
:icon
|
||||
:custom_url,
|
||||
:frame_height,
|
||||
:frame_width,
|
||||
:icon,
|
||||
:name,
|
||||
:pretty_name_override,
|
||||
:provider_url,
|
||||
:title_override
|
||||
|
||||
def title_override
|
||||
return SiteSetting.get(object.title_setting) if object.title_setting
|
||||
object.title
|
||||
# ensures that the "/custom" route doesn't trigger the magic custom_url helper in ActionDispatch
|
||||
def custom_url
|
||||
object.custom_url
|
||||
end
|
||||
|
||||
def pretty_name_override
|
||||
return SiteSetting.get(object.pretty_name_setting) if object.pretty_name_setting
|
||||
object.pretty_name
|
||||
object.pretty_name_setting ? SiteSetting.get(object.pretty_name_setting) : object.pretty_name
|
||||
end
|
||||
|
||||
def custom_url
|
||||
# ensures that the "/custom" route doesn't trigger the magic custom_url helper in ActionDispatch
|
||||
object.custom_url
|
||||
def title_override
|
||||
object.title_setting ? SiteSetting.get(object.title_setting) : object.title
|
||||
end
|
||||
end
|
||||
|
@ -10,23 +10,19 @@ class Auth::AuthProvider
|
||||
def self.auth_attributes
|
||||
%i[
|
||||
authenticator
|
||||
pretty_name
|
||||
title
|
||||
frame_width
|
||||
frame_height
|
||||
pretty_name_setting
|
||||
title_setting
|
||||
custom_url
|
||||
frame_height
|
||||
frame_width
|
||||
icon
|
||||
pretty_name
|
||||
pretty_name_setting
|
||||
title
|
||||
title_setting
|
||||
]
|
||||
end
|
||||
|
||||
attr_accessor(*auth_attributes)
|
||||
|
||||
def name
|
||||
authenticator.name
|
||||
end
|
||||
|
||||
def can_connect
|
||||
authenticator.can_connect_existing_user?
|
||||
end
|
||||
@ -34,4 +30,12 @@ class Auth::AuthProvider
|
||||
def can_revoke
|
||||
authenticator.can_revoke?
|
||||
end
|
||||
|
||||
def name
|
||||
authenticator.name
|
||||
end
|
||||
|
||||
def provider_url
|
||||
authenticator.provider_url
|
||||
end
|
||||
end
|
||||
|
@ -13,6 +13,11 @@ class Auth::Authenticator
|
||||
name
|
||||
end
|
||||
|
||||
# Used in /my/preferences/account to link to the provider's website
|
||||
def provider_url
|
||||
nil
|
||||
end
|
||||
|
||||
def enabled?
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
@ -46,6 +46,10 @@ class Auth::DiscordAuthenticator < Auth::ManagedAuthenticator
|
||||
"Discord"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
"https://discord.com"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_discord_logins?
|
||||
end
|
||||
|
@ -33,6 +33,10 @@ class Auth::DiscourseIdAuthenticator < Auth::ManagedAuthenticator
|
||||
"Discourse ID"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
site
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_discourse_id && SiteSetting.discourse_id_client_id.present? &&
|
||||
SiteSetting.discourse_id_client_secret.present?
|
||||
|
@ -11,6 +11,10 @@ class Auth::FacebookAuthenticator < Auth::ManagedAuthenticator
|
||||
"Facebook"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
"https://www.facebook.com"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_facebook_logins
|
||||
end
|
||||
|
@ -11,6 +11,10 @@ class Auth::GithubAuthenticator < Auth::ManagedAuthenticator
|
||||
"GitHub"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
"https://github.com"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_github_logins
|
||||
end
|
||||
|
@ -14,6 +14,10 @@ class Auth::GoogleOAuth2Authenticator < Auth::ManagedAuthenticator
|
||||
"Google"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
"https://accounts.google.com"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_google_oauth2_logins
|
||||
end
|
||||
|
@ -49,6 +49,10 @@ class Auth::LinkedInOidcAuthenticator < Auth::ManagedAuthenticator
|
||||
"LinkedIn"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
"https://www.linkedin.com"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_linkedin_oidc_logins
|
||||
end
|
||||
|
@ -9,6 +9,10 @@ class Auth::TwitterAuthenticator < Auth::ManagedAuthenticator
|
||||
"X / Twitter"
|
||||
end
|
||||
|
||||
def provider_url
|
||||
"https://x.com"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
SiteSetting.enable_twitter_logins
|
||||
end
|
||||
|
@ -1,9 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe "User preferences | Avatar", type: :system do
|
||||
describe "User preferences | Account", type: :system do
|
||||
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
||||
let(:user_account_preferences_page) { PageObjects::Pages::UserPreferencesAccount.new }
|
||||
let(:avatar_selector_modal) { PageObjects::Modals::AvatarSelector.new }
|
||||
|
||||
before { sign_in(user) }
|
||||
|
||||
describe "avatar-selector modal" do
|
||||
@ -33,4 +34,64 @@ describe "User preferences | Avatar", type: :system do
|
||||
expect(avatar_selector_modal).to have_no_avatar_upload_button
|
||||
end
|
||||
end
|
||||
|
||||
describe "external login provider URLs" do
|
||||
it "shows provider URLs as links when available" do
|
||||
SiteSetting.enable_discord_logins = true
|
||||
SiteSetting.enable_facebook_logins = true
|
||||
SiteSetting.enable_github_logins = true
|
||||
SiteSetting.enable_google_oauth2_logins = true
|
||||
|
||||
# Let's connect at least 1 external account
|
||||
UserAssociatedAccount.create!(
|
||||
user:,
|
||||
provider_name: "google_oauth2",
|
||||
provider_uid: "123456",
|
||||
info: {
|
||||
"email" => user.email,
|
||||
},
|
||||
)
|
||||
|
||||
user_account_preferences_page.visit(user)
|
||||
|
||||
name = find(".pref-associated-accounts table tr.discord .associated-account__name")
|
||||
expect(name).to have_link("Discord", href: "https://discord.com")
|
||||
|
||||
name = find(".pref-associated-accounts table tr.facebook .associated-account__name")
|
||||
expect(name).to have_link("Facebook", href: "https://www.facebook.com")
|
||||
|
||||
name = find(".pref-associated-accounts table tr.github .associated-account__name")
|
||||
expect(name).to have_link("GitHub", href: "https://github.com")
|
||||
|
||||
name = find(".pref-associated-accounts table tr.google-oauth2 .associated-account__name")
|
||||
expect(name).to have_link("Google", href: "https://accounts.google.com")
|
||||
end
|
||||
|
||||
it "shows provider names without links when provider_url is not implemented" do
|
||||
begin
|
||||
authenticator =
|
||||
Class
|
||||
.new(Auth::ManagedAuthenticator) do
|
||||
def name
|
||||
"test_no_url"
|
||||
end
|
||||
|
||||
def enabled?
|
||||
true
|
||||
end
|
||||
end
|
||||
.new
|
||||
|
||||
provider = Auth::AuthProvider.new(authenticator:, icon: "flash")
|
||||
DiscoursePluginRegistry.register_auth_provider(provider)
|
||||
|
||||
user_account_preferences_page.visit(user)
|
||||
|
||||
name = find(".pref-associated-accounts table tr.test-no-url .associated-account__name")
|
||||
expect(name).not_to have_css("a")
|
||||
ensure
|
||||
DiscoursePluginRegistry.reset!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user