From c4ece5cef5e718e8dfa84dcd1dcd7e96ce0acd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Sanz=20Garc=C3=ADa?= <esanz-garcia@gitlab.com> Date: Tue, 30 Jul 2024 21:30:36 +0200 Subject: [PATCH 1/3] Add bluesky field to the user profile See https://atproto.com/specs/did Changelog: changed Authored-By: @SlickDomique Co-Authored-By: @eduardosanz --- app/controllers/admin/users_controller.rb | 7 +-- app/controllers/profiles_controller.rb | 23 +++++----- .../user_settings/profiles_controller.rb | 23 +++++----- app/helpers/application_helper.rb | 6 +++ app/helpers/users_helper.rb | 6 +++ app/models/user.rb | 1 + app/models/user_detail.rb | 15 +++++- .../user_settings/profiles/show.html.haml | 3 ++ app/views/users/_profile_sidebar.html.haml | 6 ++- ...40730163326_add_bluesky_to_user_details.rb | 22 +++++++++ db/schema_migrations/20240730163326 | 1 + db/structure.sql | 2 + doc/user/profile/index.md | 1 + locale/gitlab.pot | 3 ++ .../user_settings/profiles_controller_spec.rb | 10 ++++ spec/helpers/application_helper_spec.rb | 15 ++++++ spec/helpers/users_helper_spec.rb | 22 +++++++++ spec/models/user_detail_spec.rb | 46 +++++++++++++++++++ spec/models/user_spec.rb | 3 ++ 19 files changed, 188 insertions(+), 27 deletions(-) create mode 100644 db/migrate/20240730163326_add_bluesky_to_user_details.rb create mode 100644 db/schema_migrations/20240730163326 diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index fe6f06c96ce6a433..bc1114c5aef2dc3a 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -367,9 +367,10 @@ def allowed_user_params :access_level, :avatar, :bio, + :bluesky, :can_create_group, - :color_scheme_id, :color_mode_id, + :color_scheme_id, :discord, :email, :extern_uid, @@ -381,7 +382,9 @@ def allowed_user_params :linkedin, :mastodon, :name, + :note, :password_expires_at, + :private_profile, :projects_limit, :provider, :remember_me, @@ -390,8 +393,6 @@ def allowed_user_params :twitter, :username, :website_url, - :note, - :private_profile, { credit_card_validation_attributes: [:credit_card_validated_at] } ] end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 5fcda8e8be95b1c6..f89ef514ecc0214a 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -76,34 +76,35 @@ def username_param def user_params_attributes [ + :achievements_enabled, :avatar, :bio, + :bluesky, + :commit_email, :discord, :email, - :role, :gitpod_enabled, :hide_no_password, :hide_no_ssh_key, :hide_project_limit, + :include_private_contributions, + :job_title, :linkedin, :location, :mastodon, :name, - :public_email, - :commit_email, - :skype, - :twitter, - :username, - :website_url, :organization, :private_profile, - :include_private_contributions, - :achievements_enabled, - :timezone, - :job_title, :pronouns, :pronunciation, + :public_email, + :role, + :skype, + :timezone, + :twitter, + :username, :validation_password, + :website_url, { status: [:emoji, :message, :availability, :clear_status_after] } ] end diff --git a/app/controllers/user_settings/profiles_controller.rb b/app/controllers/user_settings/profiles_controller.rb index 55d074eddbc2a975..df8da1e8bbea8e05 100644 --- a/app/controllers/user_settings/profiles_controller.rb +++ b/app/controllers/user_settings/profiles_controller.rb @@ -39,34 +39,35 @@ def user def user_params_attributes [ + :achievements_enabled, :avatar, :bio, + :bluesky, + :commit_email, :discord, :email, - :role, :gitpod_enabled, :hide_no_password, :hide_no_ssh_key, :hide_project_limit, + :include_private_contributions, + :job_title, :linkedin, :location, :mastodon, :name, - :public_email, - :commit_email, - :skype, - :twitter, - :username, - :website_url, :organization, :private_profile, - :include_private_contributions, - :achievements_enabled, - :timezone, - :job_title, :pronouns, :pronunciation, + :public_email, + :role, + :skype, + :timezone, + :twitter, + :username, :validation_password, + :website_url, { status: [:emoji, :message, :availability, :clear_status_after] } ] end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 73f515e92eafdc1d..17a80fcde7f8f8e3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -379,6 +379,12 @@ def discord_url(user) "https://discord.com/users/#{user.discord}" end + def bluesky_url(user) + return '' if user.bluesky.blank? + + external_redirect_path(url: "https://bsky.app/profile/#{user.bluesky}") + end + def mastodon_url(user) return '' if user.mastodon.blank? diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index d9ae9368513f5246..70ea4db49dd4481a 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -195,6 +195,12 @@ def admin_user_actions_data_attributes(user) } end + def has_contact_info?(user) + contact_fields = %i[bluesky discord linkedin mastodon skype twitter website_url] + has_contact = contact_fields.any? { |field| user.public_send(field).present? } # rubocop:disable GitlabSecurity/PublicSend + has_contact || display_public_email?(user) + end + def display_public_email?(user) user.public_email.present? end diff --git a/app/models/user.rb b/app/models/user.rb index e39f8c8cf6e78f37..3e9434e11ff9bfc6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -443,6 +443,7 @@ def update_tracked_fields!(request) delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true delegate :pronunciation, :pronunciation=, to: :user_detail, allow_nil: true delegate :registration_objective, :registration_objective=, to: :user_detail, allow_nil: true + delegate :bluesky, :bluesky=, to: :user_detail, allow_nil: true delegate :mastodon, :mastodon=, to: :user_detail, allow_nil: true delegate :linkedin, :linkedin=, to: :user_detail, allow_nil: true delegate :twitter, :twitter=, to: :user_detail, allow_nil: true diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb index 8c2c6ec3bcdab4e8..0bc229d991859c77 100644 --- a/app/models/user_detail.rb +++ b/app/models/user_detail.rb @@ -17,6 +17,14 @@ class UserDetail < MainClusterwide::ApplicationRecord DEFAULT_FIELD_LENGTH = 500 + # specification for bluesky identifier https://web.plc.directory/spec/v0.1/did-plc + BLUESKY_VALIDATION_REGEX = / + \A # beginning of string + did:plc: # beginning of bluesky id + [a-z0-9]{24} # 24 characters of word or digit + \z # end of string + /x + MASTODON_VALIDATION_REGEX = / \A # beginning of string @?\b # optional leading at @@ -33,6 +41,10 @@ class UserDetail < MainClusterwide::ApplicationRecord validate :discord_format validates :linkedin, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true validates :location, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true + validates :bluesky, + allow_blank: true, + format: { with: UserDetail::BLUESKY_VALIDATION_REGEX, + message: proc { s_('Profiles|must contain only a bluesky did:plc identifier.') } } validates :mastodon, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true validate :mastodon_format validates :organization, length: { maximum: DEFAULT_FIELD_LENGTH }, allow_blank: true @@ -47,7 +59,7 @@ class UserDetail < MainClusterwide::ApplicationRecord enum registration_objective: REGISTRATION_OBJECTIVE_PAIRS, _suffix: true def sanitize_attrs - %i[discord linkedin mastodon skype twitter website_url].each do |attr| + %i[bluesky discord linkedin mastodon skype twitter website_url].each do |attr| value = self[attr] self[attr] = Sanitize.clean(value) if value.present? end @@ -60,6 +72,7 @@ def sanitize_attrs private def prevent_nil_fields + self.bluesky = '' if bluesky.nil? self.bio = '' if bio.nil? self.discord = '' if discord.nil? self.linkedin = '' if linkedin.nil? diff --git a/app/views/user_settings/profiles/show.html.haml b/app/views/user_settings/profiles/show.html.haml index e90c5db4ff46ada6..e30c38ae76bb938c 100644 --- a/app/views/user_settings/profiles/show.html.haml +++ b/app/views/user_settings/profiles/show.html.haml @@ -124,6 +124,9 @@ allow_empty: true} %small.form-text.gl-text-secondary = external_accounts_docs_link + .form-group.gl-form-group + = f.label :bluesky + = f.text_field :bluesky, class: 'gl-form-input form-control gl-md-form-input-lg', placeholder: "did:plc:ewvi7nxzyoun6zhxrhs64oiz" .form-group.gl-form-group = f.label :mastodon = f.text_field :mastodon, class: 'gl-form-input form-control gl-md-form-input-lg', placeholder: "@robin@example.com" diff --git a/app/views/users/_profile_sidebar.html.haml b/app/views/users/_profile_sidebar.html.haml index cbee8a5d2318d6da..860c50c59d839edd 100644 --- a/app/views/users/_profile_sidebar.html.haml +++ b/app/views/users/_profile_sidebar.html.haml @@ -47,7 +47,7 @@ = sprite_icon('calendar', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') %span= s_('Member since %{date}') % { date: l(@user.created_at.to_date, format: :long) } - - if @user.website_url.present? || display_public_email?(@user) || @user.skype.present? || @user.linkedin.present? || @user.twitter.present? || @user.mastodon.present? || @user.discord.present? + - if has_contact_info?(@user) .gl-text-gray-900 %h2.gl-font-base.gl-mb-2.gl-mt-4= s_('UserProfile|Contact') - if @user.website_url.present? @@ -70,6 +70,10 @@ .gl-display-flex.gl-gap-2.gl-mb-2 = sprite_icon('x', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.twitter, twitter_url(@user), class: 'gl-text-gray-900', title: _("X (formerly Twitter)"), target: '_blank', rel: 'noopener noreferrer nofollow' + - if @user.bluesky.present? + .gl-display-flex.gl-gap-2.gl-mb-2 + = sprite_icon('at', css_class: 'gl-fill-gray-500 gl-mt-1 flex-shrink-0') + = link_to @user.bluesky, bluesky_url(@user), class: 'gl-text-gray-900', title: "Bluesky", target: '_blank', rel: 'noopener noreferrer nofollow' - if @user.mastodon.present? .gl-display-flex.gl-gap-2.gl-mb-2 = sprite_icon('mastodon', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') diff --git a/db/migrate/20240730163326_add_bluesky_to_user_details.rb b/db/migrate/20240730163326_add_bluesky_to_user_details.rb new file mode 100644 index 0000000000000000..ff5d77b07babe668 --- /dev/null +++ b/db/migrate/20240730163326_add_bluesky_to_user_details.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class AddBlueskyToUserDetails < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + milestone '17.3' + + USER_DETAILS_FIELD_LIMIT = 256 + + def up + with_lock_retries do + add_column :user_details, :bluesky, :text, default: '', null: false, if_not_exists: true + end + + add_text_limit :user_details, :bluesky, USER_DETAILS_FIELD_LIMIT + end + + def down + with_lock_retries do + remove_column :user_details, :bluesky + end + end +end diff --git a/db/schema_migrations/20240730163326 b/db/schema_migrations/20240730163326 new file mode 100644 index 0000000000000000..a0ff08ef2b4bf2ed --- /dev/null +++ b/db/schema_migrations/20240730163326 @@ -0,0 +1 @@ +df9c3f57784f53f2966970e883d56abe6bc816c4cb1fe61886a924271270d6e3 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 023cfb2464f82dc4..e74fb4793f6d8fb4 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -18690,6 +18690,8 @@ CREATE TABLE user_details ( mastodon text DEFAULT ''::text NOT NULL, project_authorizations_recalculated_at timestamp with time zone DEFAULT '2010-01-01 00:00:00+00'::timestamp with time zone NOT NULL, onboarding_status jsonb DEFAULT '{}'::jsonb NOT NULL, + bluesky text DEFAULT ''::text NOT NULL, + CONSTRAINT check_18a53381cd CHECK ((char_length(bluesky) <= 256)), CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)), CONSTRAINT check_444573ee52 CHECK ((char_length(skype) <= 500)), CONSTRAINT check_466a25be35 CHECK ((char_length(twitter) <= 500)), diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index b5fe52a43f9f05da..2c9aa1a9944de051 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -184,6 +184,7 @@ To add links to other accounts: 1. In the **Main settings** section, add your: - Discord [user ID](https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-). - LinkedIn profile name. + - Bluesky [did:plc identifier](https://atproto.com/specs/did). To find your identifier, [resolve your user handle](https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=USER_HANDLE). - Mastodon username. - Skype username. - X (formerly Twitter) @username. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b4ad2eb14e5f13ed..dc12febf032fa510 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -41015,6 +41015,9 @@ msgstr "" msgid "Profiles|https://website.com" msgstr "" +msgid "Profiles|must contain only a bluesky did:plc identifier." +msgstr "" + msgid "Profiles|username" msgstr "" diff --git a/spec/controllers/user_settings/profiles_controller_spec.rb b/spec/controllers/user_settings/profiles_controller_spec.rb index 706612296acbf0f9..ffb1d8a6a6e404f4 100644 --- a/spec/controllers/user_settings/profiles_controller_spec.rb +++ b/spec/controllers/user_settings/profiles_controller_spec.rb @@ -132,6 +132,16 @@ expect(response).to have_gitlab_http_status(:found) end + it 'allows updating user specified bluesky did identifier', :aggregate_failures do + bluesky_did_id = 'did:plc:ewvi7nxzyoun6zhxrhs64oiz' + sign_in(user) + + put :update, params: { user: { bluesky: bluesky_did_id } } + + expect(user.reload.bluesky).to eq(bluesky_did_id) + expect(response).to have_gitlab_http_status(:found) + end + it 'allows updating user specified mastodon username', :aggregate_failures do mastodon_username = '@robin@example.com' sign_in(user) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index d1d04fb5548b979b..49ba36a3e4c79016 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -668,6 +668,21 @@ def stub_controller_method(method_name, value) end end + context 'when bluesky is set' do + let_it_be(:user) { build(:user) } + let(:bluesky) { bluesky_url(user) } + + it 'returns an empty string if bluesky did id is not set' do + expect(bluesky).to eq('') + end + + it 'returns bluesky url when bluesky did id is set' do + user.bluesky = 'did:plc:ewvi7nxzyoun6zhxrhs64oiz' + + expect(bluesky).to eq(external_redirect_path(url: 'https://bsky.app/profile/did:plc:ewvi7nxzyoun6zhxrhs64oiz')) + end + end + context 'when mastodon is set' do let_it_be(:user) { build(:user) } let(:mastodon) { mastodon_url(user) } diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb index fc5a912f4334e05a..5d9d7d52c74b103b 100644 --- a/spec/helpers/users_helper_spec.rb +++ b/spec/helpers/users_helper_spec.rb @@ -11,6 +11,28 @@ def filter_ee_badges(badges) badges.reject { |badge| badge[:text] == 'Is using seat' } end + describe 'has_contact_info?' do + subject { helper.has_contact_info?(user) } + + context 'when user has skype profile' do + let_it_be(:user) { create(:user, bluesky: 'did:plc:ewvi7nxzyoun6zhxrhs64oiz') } + + it { is_expected.to be true } + end + + context 'when user has public email' do + let_it_be(:user) { create(:user, :public_email) } + + it { is_expected.to be true } + end + + context 'when user public email is blank' do + let_it_be(:user) { create(:user, public_email: '') } + + it { is_expected.to be false } + end + end + describe 'display_public_email?' do let_it_be(:user) { create(:user, :public_email) } diff --git a/spec/models/user_detail_spec.rb b/spec/models/user_detail_spec.rb index a8835a0dc2044194..f7f0aef2e4a01b33 100644 --- a/spec/models/user_detail_spec.rb +++ b/spec/models/user_detail_spec.rb @@ -159,6 +159,50 @@ end end + describe '#bluesky' do + context 'when bluesky is set' do + let_it_be(:user_detail) { build(:user_detail) } + + let(:value) { 'did:plc:ewvi7nxzyoun6zhxrhs64oiz' } + + before do + user_detail.bluesky = value + end + + it 'accepts a valid bluesky did id' do + expect(user_detail).to be_valid + end + + shared_examples 'throws an error' do + it do + expect(user_detail).not_to be_valid + expect(user_detail.errors.full_messages) + .to match_array([_('Bluesky must contain only a bluesky did:plc identifier.')]) + end + end + + context 'when bluesky is set to a wrong format' do + context 'when bluesky did:plc is too long' do + let(:value) { 'a' * 33 } + + it_behaves_like 'throws an error' + end + + context 'when bluesky did:plc is wrong' do + let(:value) { 'did:plc:ewvi7nxzyoun6zhxrhs64OIZ' } + + it_behaves_like 'throws an error' + end + + context 'when bluesky other bluesky did: formats are used' do + let(:value) { 'did:web:example.com' } + + it_behaves_like 'throws an error' + end + end + end + end + describe '#mastodon' do it { is_expected.to validate_length_of(:mastodon).is_at_most(500) } @@ -219,6 +263,7 @@ discord: '1234567890123456789', linkedin: 'linkedin', location: 'location', + bluesky: 'did:plc:ewvi7nxzyoun6zhxrhs64oiz', mastodon: '@robin@example.com', organization: 'organization', skype: 'skype', @@ -240,6 +285,7 @@ it_behaves_like 'prevents `nil` value', :discord it_behaves_like 'prevents `nil` value', :linkedin it_behaves_like 'prevents `nil` value', :location + it_behaves_like 'prevents `nil` value', :bluesky it_behaves_like 'prevents `nil` value', :mastodon it_behaves_like 'prevents `nil` value', :organization it_behaves_like 'prevents `nil` value', :skype diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index bcd6208c855f7d2c..fc367ef44a743ad5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -129,6 +129,9 @@ it { is_expected.to delegate_method(:linkedin).to(:user_detail).allow_nil } it { is_expected.to delegate_method(:linkedin=).to(:user_detail).with_arguments(:args).allow_nil } + it { is_expected.to delegate_method(:bluesky).to(:user_detail).allow_nil } + it { is_expected.to delegate_method(:bluesky=).to(:user_detail).with_arguments(:args).allow_nil } + it { is_expected.to delegate_method(:mastodon).to(:user_detail).allow_nil } it { is_expected.to delegate_method(:mastodon=).to(:user_detail).with_arguments(:args).allow_nil } -- GitLab From 2bb01a42d16e835848915bd001a866a07b79b09f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Sanz=20Garc=C3=ADa?= <esanz-garcia@gitlab.com> Date: Wed, 31 Jul 2024 08:33:06 +0200 Subject: [PATCH 2/3] Replace legacy CSS rule for Tailwind equivalent gl-display-flex -> gl-flex --- app/views/users/_profile_sidebar.html.haml | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/views/users/_profile_sidebar.html.haml b/app/views/users/_profile_sidebar.html.haml index 860c50c59d839edd..226edc1daabff1c3 100644 --- a/app/views/users/_profile_sidebar.html.haml +++ b/app/views/users/_profile_sidebar.html.haml @@ -3,11 +3,11 @@ .gl-align-top.gl-text-left.gl-max-w-80.gl-wrap-anywhere .user-info - if @user.confirmed? - .gl-display-flex.gl-gap-4.gl-flex-direction-column + .gl-flex.gl-gap-4.gl-flex-direction-column - if @user.pronouns.present? || @user.pronunciation.present? || @user.bio.present? %div %h2.gl-font-base.gl-mb-2.gl-mt-4= s_('UserProfile|About') - .gl-display-flex.gl-gap-2.gl-flex-direction-column + .gl-flex.gl-gap-2.gl-flex-direction-column - if @user.pronouns.present? || @user.pronunciation.present? .gl-mb-2 - if @user.pronunciation.present? @@ -29,21 +29,21 @@ %div{ itemprop: 'address', itemscope: true, itemtype: 'https://schema.org/PostalAddress' } %h2.gl-font-base.gl-mb-2.gl-mt-4= s_('UserProfile|Info') - if work_information(@user).present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('work', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') %span = work_information(@user, with_schema_markup: true) - if @user.location.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('location', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') %span{ itemprop: 'addressLocality' } = @user.location - if user_local_time.present? - .gl-display-flex.gl-gap-2.gl-mb-2{ data: { testid: 'user-local-time' } } + .gl-flex.gl-gap-2.gl-mb-2{ data: { testid: 'user-local-time' } } = sprite_icon('clock', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') %span = user_local_time - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('calendar', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') %span= s_('Member since %{date}') % { date: l(@user.created_at.to_date, format: :long) } @@ -51,34 +51,34 @@ .gl-text-gray-900 %h2.gl-font-base.gl-mb-2.gl-mt-4= s_('UserProfile|Contact') - if @user.website_url.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('earth', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.short_website_url, @user.full_website_url, class: 'gl-text-gray-900', target: '_blank', rel: 'me noopener noreferrer nofollow', itemprop: 'url' - if display_public_email?(@user) - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('mail', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.public_email, "mailto:#{@user.public_email}", class: 'gl-text-gray-900', itemprop: 'email' - if @user.skype.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('skype', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.skype, "skype:#{@user.skype}", class: 'gl-text-gray-900', title: "Skype" - if @user.linkedin.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('linkedin', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to linkedin_name(@user), linkedin_url(@user), class: 'gl-text-gray-900', title: "LinkedIn", target: '_blank', rel: 'noopener noreferrer nofollow' - if @user.twitter.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('x', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.twitter, twitter_url(@user), class: 'gl-text-gray-900', title: _("X (formerly Twitter)"), target: '_blank', rel: 'noopener noreferrer nofollow' - if @user.bluesky.present? - .gl-display-flex.gl-gap-2.gl-mb-2 - = sprite_icon('at', css_class: 'gl-fill-gray-500 gl-mt-1 flex-shrink-0') + .gl-flex.gl-gap-2.gl-mb-2 + = sprite_icon('at', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.bluesky, bluesky_url(@user), class: 'gl-text-gray-900', title: "Bluesky", target: '_blank', rel: 'noopener noreferrer nofollow' - if @user.mastodon.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('mastodon', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.mastodon, mastodon_url(@user), class: 'gl-text-gray-900', title: "Mastodon", target: '_blank', rel: 'noopener noreferrer nofollow' - if @user.discord.present? - .gl-display-flex.gl-gap-2.gl-mb-2 + .gl-flex.gl-gap-2.gl-mb-2 = sprite_icon('discord', css_class: 'gl-fill-icon-subtle gl-mt-1 flex-shrink-0') = link_to @user.discord, discord_url(@user), class: 'gl-text-gray-900', title: "Discord", target: '_blank', rel: 'noopener noreferrer nofollow' -- GitLab From 3c9e6e3df5339610ced34ccd2090b0a482ab125a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Sanz=20Garc=C3=ADa?= <esanz-garcia@gitlab.com> Date: Wed, 31 Jul 2024 08:37:13 +0200 Subject: [PATCH 3/3] Adding rubocop comment --- app/helpers/users_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 70ea4db49dd4481a..3715dbbc3bc8a9b9 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -197,7 +197,7 @@ def admin_user_actions_data_attributes(user) def has_contact_info?(user) contact_fields = %i[bluesky discord linkedin mastodon skype twitter website_url] - has_contact = contact_fields.any? { |field| user.public_send(field).present? } # rubocop:disable GitlabSecurity/PublicSend + has_contact = contact_fields.any? { |field| user.public_send(field).present? } # rubocop:disable GitlabSecurity/PublicSend -- fields are controlled, it is safe. has_contact || display_public_email?(user) end -- GitLab