Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions app/views/decidim/devise/shared/omniauth_buttons/_default.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<% link_classes = "login__omniauth-button button--#{normalize_provider_name(provider)}" %>
<% display_name = current_organization.enabled_omniauth_providers.dig(provider.to_sym, :display_name) %>
<% provider_name = display_name.present? ? display_name : normalize_provider_name(provider).titleize %>
<% provider_name = I18n.t("decidim.devise.shared.links.log_in_with_provider") if custom_publik_translation?(provider) %>
<%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: link_classes, method: :post, title: t("devise.shared.links.log_in_with_provider", provider: provider_name) do %>
<%= oauth_icon provider %>
<span>
<%= provider_name %>
</span>
<% provider_name = omniauth_provider_label(provider) %>
<% if full_image_button?(provider) %>
<% link_classes = "login__omniauth-image-button image-button--#{normalize_provider_name(provider)} group/oauth-icon" %>
<%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: link_classes, method: :post, title: t("devise.shared.links.log_in_with_provider", provider: provider_name) do %>
<%= oauth_icon_with_hover provider %>
<% end %>
<% else %>
<% link_classes = "login__omniauth-button button--#{normalize_provider_name(provider)}" %>
<%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: link_classes, method: :post, title: t("devise.shared.links.log_in_with_provider", provider: provider_name) do %>
<%= oauth_icon provider %>
<span>
<%= provider_name %>
</span>
<% end %>
<% end %>
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
<% link_classes = "login__omniauth-image-button image-button--#{normalize_provider_name(provider)} group/oauth-icon" %>
<% display_name = current_organization.enabled_omniauth_providers.dig(provider.to_sym, :display_name) %>
<% provider_name = display_name.present? ? display_name : normalize_provider_name(provider).titleize %>
<div class="max-w-md flex flex-col items-center justify-center">
<p class="font-normal text-center p-3">
<%= t("decidim.devise.omniauth.france_connect.introduction") %>
</p>
<%= link_to decidim.send("user_#{provider}_omniauth_authorize_path"), class: link_classes, method: :post, title: t("devise.shared.links.log_in_with_provider", provider: provider_name) do %>
<%= oauth_icon_with_hover provider %>
<% end %>
<%= render partial: "decidim/devise/shared/omniauth_buttons/default", locals: { provider: provider } %>
<p class="font-normal text-center p-3">
<%= link_to "https://franceconnect.gouv.fr/", class: "text-secondary hover:underline", target: "_blank", title: t("decidim.devise.omniauth.france_connect.help_link") do %>
<%= t("decidim.devise.omniauth.france_connect.help_link") %>
Expand Down
1 change: 1 addition & 0 deletions config/i18n-tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,4 @@ ignore_unused:
- decidim.searches.filters.jump_to
- decidim.components.proposals.settings.global.enable_iframe
- decidim.components.proposals.settings.global.iframe_url
- decidim.devise.shared.links.(log_in_with_provider|provider_display_name)
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ en:
shared:
links:
log_in_with_provider: Log in with %{provider}
provider_display_name: "%{provider}"
events:
proposals:
author_confirmation_proposal_event:
Expand Down
1 change: 1 addition & 0 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ fr:
shared:
links:
log_in_with_provider: Connectez-vous avec %{provider}
provider_display_name: "%{provider}"
events:
proposals:
author_confirmation_proposal_event:
Expand Down
99 changes: 82 additions & 17 deletions lib/extends/helpers/decidim/omniauth_helper_extends.rb
Original file line number Diff line number Diff line change
@@ -1,36 +1,101 @@
# frozen_string_literal: true

module OmniauthHelperExtends
# Public: icon for omniauth buttons
def oauth_icon_with_hover(provider)
info = current_organization.enabled_omniauth_providers[provider.to_sym]
FRANCE_CONNECT_DEFAULT_ICONS = {
icon_path: "media/images/franceconnect-btn-principal.svg",
icon_hover_path: "media/images/franceconnect-btn-principal-hover.svg"
}.freeze

icon_path = info&.dig(:icon_path)
icon_hover_path = info&.dig(:icon_hover_path)
FALLBACK_ICON_NAME = "login-box-line"

if provider.to_sym == :france_connect
icon_path = "media/images/franceconnect-btn-principal.svg" if icon_path.blank?
icon_hover_path = "media/images/franceconnect-btn-principal-hover.svg" if icon_hover_path.blank?
end
def full_image_button?(provider)
icons = provider_icon_settings(provider)
icons[:icon_path].present? && icons[:icon_hover_path].present?
end

return oauth_icon(provider) unless icon_path.present? && icon_hover_path.present?
def oauth_icon_with_hover(provider)
icons = provider_icon_settings(provider)
return oauth_icon(provider) unless icons[:icon_path].present? && icons[:icon_hover_path].present?

# parent html element needs to have the "group/oauth-icon" tailwind class
icon_html = external_icon(icon_path, class: "block group-hover/oauth-icon:hidden")
icon_html += external_icon(icon_hover_path, class: "hidden group-hover/oauth-icon:block")
icon_html = external_icon(icons[:icon_path], class: "block group-hover/oauth-icon:hidden")
icon_html += external_icon(icons[:icon_hover_path], class: "hidden group-hover/oauth-icon:block")
icon_html.html_safe
end

def oauth_icon(provider)
icons = provider_icon_settings(provider)
return external_icon(icons[:icon_path]) if icons[:icon_path].present?

icon(safe_icon_name(provider, icons[:icon]))
end

def omniauth_provider_label(provider)
custom_provider_display_name(provider) ||
current_organization.enabled_omniauth_providers.dig(provider.to_sym, :display_name).presence ||
humanized_provider_name(provider)
end

def normalize_provider_name(provider)
return "x" if provider == :twitter
# customize the name of the omniauth btn login with publik
return I18n.t("decidim.devise.shared.links.log_in_with_provider") if custom_publik_translation?(provider)

provider.to_s.split("_").first
end

def custom_publik_translation?(provider)
provider == :publik && Decidim::TermCustomizer::Translation.where(key: "decidim.devise.shared.links.log_in_with_provider").present?
private

def custom_provider_display_name(provider)
scoped_key = "decidim.devise.shared.links.provider_display_name_#{provider}"
return I18n.t(scoped_key, provider: humanized_provider_name(provider)) if term_customizer_override?(scoped_key)

shared_key = "decidim.devise.shared.links.provider_display_name"
return I18n.t(shared_key, provider: humanized_provider_name(provider)) if term_customizer_override?(shared_key)

nil
end

def term_customizer_override?(key)
defined?(Decidim::TermCustomizer::Translation) &&
Decidim::TermCustomizer::Translation.exists?(key:)
end

def humanized_provider_name(provider)
return "X" if provider.to_sym == :twitter
return "Google" if provider.to_sym == :google_oauth2

provider.to_s.tr("_", " ").titleize
end

def provider_icon_settings(provider)
db_info = current_organization.enabled_omniauth_providers[provider.to_sym] || {}
global_info = global_omniauth_provider_config(provider)

settings = {
icon_path: db_info[:icon_path].presence || global_info[:icon_path].presence,
icon_hover_path: db_info[:icon_hover_path].presence || global_info[:icon_hover_path].presence,
icon: db_info[:icon].presence || global_info[:icon].presence
}

settings.merge!(FRANCE_CONNECT_DEFAULT_ICONS) if provider.to_sym == :france_connect && settings[:icon_path].blank?
settings
end

def global_omniauth_provider_config(provider)
return {} unless Decidim.respond_to?(:omniauth_providers)

Decidim.omniauth_providers[provider.to_sym] || {}
rescue StandardError
{}
end

def safe_icon_name(provider, configured_icon)
candidates = [configured_icon, "#{normalize_provider_name(provider)}-fill", FALLBACK_ICON_NAME].compact
candidates.find { |name| icon_registered?(name) } || FALLBACK_ICON_NAME
end

def icon_registered?(name)
Decidim.icons.all.has_key?(name.to_s)
rescue StandardError
false
end
end

Expand Down
160 changes: 141 additions & 19 deletions spec/helpers/decidim/omniauth_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@

module Decidim
describe OmniauthHelper do
let(:facebook_enabled) { true }
let(:twitter_enabled) { true }
let(:publik_enabled) { true }
let(:secrets) do
{
omniauth: {
facebook: { enabled: facebook_enabled },
twitter: { enabled: twitter_enabled },
publik: { enabled: publik_enabled }
}
}
end
before { Rails.cache.clear }

shared_context "with a stubbed organization" do
let(:organization) { create(:organization, host: "test-#{SecureRandom.hex(4)}.org") }
let(:enabled_providers) { {} }

before do
allow(Rails.application).to receive(:secrets).and_return(secrets)
before do
allow(helper).to receive(:current_organization).and_return(organization)
allow(organization).to receive(:enabled_omniauth_providers).and_return(enabled_providers)
end
end

describe "#normalize_provider_name" do
Expand All @@ -28,16 +23,143 @@ module Decidim
end
end

context "when provider is publik" do
context "when provider is twitter" do
it "returns x" do
expect(helper.normalize_provider_name(:twitter)).to eq("x")
end
end

context "when provider is a composed name" do
it "truncates to the first segment (used for CSS classes only, never shown to users)" do
expect(helper.normalize_provider_name(:cultuur_connect)).to eq("cultuur")
expect(helper.normalize_provider_name(:france_connect)).to eq("france")
end
end
end

describe "#omniauth_provider_label" do
include_context "with a stubbed organization"

context "when a Term Customizer override exists in the database" do
let(:enabled_providers) { { publik: {} } }
let(:key) { "decidim.devise.shared.links.provider_display_name_publik" }
let(:translation_set) { create(:translation_set) }
let!(:translation) { create(:translation, key: "decidim.devise.shared.links.log_in_with_provider", value: "Login with MyPublik") }
let!(:translation) { create(:translation, translation_set:, key:, value: "Login with MyPublik") }

it "detects the override exists" do
expect(helper.send(:term_customizer_override?, key)).to be(true)
end
end

context "when I18n resolves a Term Customizer override for the provider" do
let(:enabled_providers) { { publik: {} } }
let(:key) { "decidim.devise.shared.links.provider_display_name_publik" }

before do
allow(helper).to receive(:term_customizer_override?).with(key).and_return(true)
allow(I18n).to receive(:t).with(key, provider: "Publik").and_return("Login with MyPublik")
end

it "returns that translation, taking priority over display_name and the humanized fallback" do
expect(helper.omniauth_provider_label(:publik)).to eq("Login with MyPublik")
end
end

context "when no Term Customizer translation exists but display_name is configured" do
let(:enabled_providers) { { cultuur_connect: { display_name: "Mon SSO" } } }

it "returns the configured display_name" do
expect(helper.omniauth_provider_label(:cultuur_connect)).to eq("Mon SSO")
end
end

context "when nothing is configured" do
let(:enabled_providers) { { cultuur_connect: {} } }

it "returns the full humanized provider name, not truncated" do
expect(helper.omniauth_provider_label(:cultuur_connect)).to eq("Cultuur Connect")
end
end

context "when provider is twitter with nothing configured" do
let(:enabled_providers) { { twitter: {} } }

it "returns X" do
expect(helper.omniauth_provider_label(:twitter)).to eq("X")
end
end

context "when provider is google_oauth2 with nothing configured" do
let(:enabled_providers) { { google_oauth2: {} } }

it "returns Google, not Google Oauth2" do
expect(helper.omniauth_provider_label(:google_oauth2)).to eq("Google")
end
end
end

describe "#full_image_button?" do
include_context "with a stubbed organization"

context "when both icon_path and icon_hover_path are configured" do
let(:enabled_providers) { { openid_connect: { icon_path: "a.svg", icon_hover_path: "b.svg" } } }

it "returns true" do
expect(helper.full_image_button?(:openid_connect)).to be(true)
end
end

context "when only icon_path is configured" do
let(:enabled_providers) { { openid_connect: { icon_path: "a.svg" } } }

it "returns false" do
expect(helper.full_image_button?(:openid_connect)).to be(false)
end
end

context "when provider is france_connect with no icon configured at all" do
let(:enabled_providers) { { france_connect: {} } }

it "returns true, using the hardcoded default icons" do
expect(helper.full_image_button?(:france_connect)).to be(true)
end
end
end

describe "#oauth_icon" do
include_context "with a stubbed organization"

context "when Decidim.omniauth_providers is not defined on this Decidim version" do
let(:enabled_providers) { { cultuur_connect: {} } }

before do
allow(Decidim).to receive(:respond_to?).with(:omniauth_providers).and_return(false)
end

it "does not raise and falls back to the generic icon" do
expect { helper.oauth_icon(:cultuur_connect) }.not_to raise_error
end
end

context "when the provider has an icon_path configured" do
let(:enabled_providers) { { facebook: { icon_path: "facebook.svg" } } }

it "renders the external icon" do
expect(helper).to receive(:external_icon).with("facebook.svg")
helper.oauth_icon(:facebook)
end
end

context "when nothing is configured and the guessed icon name isn't registered" do
let(:enabled_providers) { { openid_connect: {} } }

before do
allow(I18n).to receive(:t).with("decidim.devise.shared.links.log_in_with_provider").and_return("Login with MyPublik")
allow(Decidim.icons).to receive(:all).and_return({})
end

it "returns the specific translation key" do
expect(helper.normalize_provider_name(:publik)).to eq("Login with MyPublik")
it "falls back to the hardcoded fallback icon name" do
expect(helper).to receive(:icon).with(OmniauthHelperExtends::FALLBACK_ICON_NAME)
helper.oauth_icon(:openid_connect)
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions spec/system/registration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ def fill_registration_form(
end
end
end

describe "omniauth buttons", :caching do
it "renders every configured provider without raising an error" do
expect(page).to have_no_content("We're sorry, but something went wrong")
expect(page).to have_css(".login__omniauth-button, .login__omniauth-image-button", minimum: 1)
end
end
end

context "when newsletter checkbox is unchecked" do
Expand Down
Loading