2018-12-05 23:21:45 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
module UsersHelper
|
2021-02-22 17:27:13 +05:30
|
|
|
def admin_users_data_attributes(users)
|
|
|
|
{
|
2021-03-08 18:12:59 +05:30
|
|
|
users: Admin::UserSerializer.new.represent(users, { current_user: current_user }).to_json,
|
2021-02-22 17:27:13 +05:30
|
|
|
paths: admin_users_paths.to_json
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
def user_clear_status_at(user)
|
|
|
|
# The user.status can be nil when the user has no status, so we need to protect against that case.
|
|
|
|
# iso8601 is the official RFC supported format for frontend parsing of date:
|
|
|
|
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
|
|
|
|
user.status&.clear_status_at&.to_s(:iso8601)
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
def user_link(user)
|
2023-05-27 22:25:52 +05:30
|
|
|
link_to(user.name, user_path(user), title: user.email, class: 'has-tooltip commit-committer-link')
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
|
|
|
def user_email_help_text(user)
|
2022-08-27 11:52:29 +05:30
|
|
|
return _('We also use email for avatar detection if no avatar is uploaded.') unless user.unconfirmed_email.present?
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
confirmation_link = link_to _('Resend confirmation e-mail'), user_confirmation_path(user: { email: user.unconfirmed_email }), method: :post
|
2022-10-11 01:57:18 +05:30
|
|
|
h(_('Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}.')) % {
|
|
|
|
html_tag_strong_start: '<strong>'.html_safe,
|
|
|
|
html_tag_strong_end: '</strong>'.html_safe,
|
|
|
|
email: user.unconfirmed_email
|
|
|
|
} + content_tag(:p) { confirmation_link }
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
2018-03-27 19:54:05 +05:30
|
|
|
|
|
|
|
def profile_tabs
|
|
|
|
@profile_tabs ||= get_profile_tabs
|
|
|
|
end
|
|
|
|
|
|
|
|
def profile_tab?(tab)
|
|
|
|
profile_tabs.include?(tab)
|
|
|
|
end
|
|
|
|
|
2018-11-20 20:47:30 +05:30
|
|
|
def user_internal_regex_data
|
|
|
|
settings = Gitlab::CurrentSettings.current_application_settings
|
|
|
|
|
|
|
|
pattern, options = if settings.user_default_internal_regex_enabled?
|
|
|
|
regex = settings.user_default_internal_regex_instance
|
|
|
|
JsRegex.new(regex).to_h.slice(:source, :options).values
|
|
|
|
end
|
|
|
|
|
|
|
|
{ user_internal_regex_pattern: pattern, user_internal_regex_options: options }
|
|
|
|
end
|
|
|
|
|
2018-10-15 14:42:47 +05:30
|
|
|
def current_user_menu_items
|
|
|
|
@current_user_menu_items ||= get_current_user_menu_items
|
|
|
|
end
|
|
|
|
|
|
|
|
def current_user_menu?(item)
|
|
|
|
current_user_menu_items.include?(item)
|
|
|
|
end
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
# Used to preload when you are rendering many projects and checking access
|
|
|
|
def load_max_project_member_accesses(projects)
|
2023-06-20 00:43:36 +05:30
|
|
|
# There are two different request store paradigms for max member access and
|
|
|
|
# we need to preload both of them. One is keyed User the other is keyed by
|
|
|
|
# Project. See https://gitlab.com/gitlab-org/gitlab/-/issues/396822
|
|
|
|
|
|
|
|
# rubocop: disable CodeReuse/ActiveRecord: `projects` can be array which also responds to pluck
|
|
|
|
project_ids = projects.pluck(:id)
|
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
|
|
|
|
Preloaders::UserMaxAccessLevelInProjectsPreloader
|
|
|
|
.new(project_ids, current_user)
|
|
|
|
.execute
|
|
|
|
|
|
|
|
current_user&.max_member_access_for_project_ids(project_ids)
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
2018-11-08 19:23:39 +05:30
|
|
|
def max_project_member_access(project)
|
|
|
|
current_user&.max_member_access_for_project(project.id) || Gitlab::Access::NO_ACCESS
|
|
|
|
end
|
|
|
|
|
|
|
|
def max_project_member_access_cache_key(project)
|
|
|
|
"access:#{max_project_member_access(project)}"
|
|
|
|
end
|
|
|
|
|
2018-11-18 11:00:15 +05:30
|
|
|
def user_status(user)
|
|
|
|
return unless user
|
|
|
|
|
|
|
|
unless user.association(:status).loaded?
|
|
|
|
exception = RuntimeError.new("Status was not preloaded")
|
2020-01-01 13:55:28 +05:30
|
|
|
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(exception, user: user.inspect)
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
return unless user.status
|
|
|
|
|
|
|
|
content_tag :span,
|
2023-05-27 22:25:52 +05:30
|
|
|
class: 'user-status-emoji has-tooltip',
|
|
|
|
title: user.status.message_html,
|
|
|
|
data: { html: true, placement: 'top' } do
|
2018-11-18 11:00:15 +05:30
|
|
|
emoji_icon user.status.emoji
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-15 15:39:39 +05:30
|
|
|
def impersonation_enabled?
|
|
|
|
Gitlab.config.gitlab.impersonation_enabled
|
|
|
|
end
|
|
|
|
|
2019-03-02 22:35:43 +05:30
|
|
|
def user_badges_in_admin_section(user)
|
|
|
|
[].tap do |badges|
|
2021-01-03 14:25:43 +05:30
|
|
|
badges << blocked_user_badge(user) if user.blocked?
|
2023-03-17 16:20:25 +05:30
|
|
|
badges << { text: s_('AdminUsers|Admin'), variant: 'success' } if user.admin? # rubocop:disable Cop/UserAdmin
|
2022-10-11 01:57:18 +05:30
|
|
|
badges << { text: s_('AdminUsers|Bot'), variant: 'muted' } if user.bot?
|
2019-03-02 22:35:43 +05:30
|
|
|
badges << { text: s_('AdminUsers|External'), variant: 'secondary' } if user.external?
|
2021-06-08 01:23:25 +05:30
|
|
|
badges << { text: s_("AdminUsers|It's you!"), variant: 'muted' } if current_user == user
|
2022-04-04 11:22:00 +05:30
|
|
|
badges << { text: s_("AdminUsers|Locked"), variant: 'warning' } if user.access_locked?
|
2019-03-02 22:35:43 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-29 00:20:46 +05:30
|
|
|
def work_information(user, with_schema_markup: false)
|
2020-04-08 14:13:33 +05:30
|
|
|
return unless user
|
|
|
|
|
|
|
|
organization = user.organization
|
|
|
|
job_title = user.job_title
|
|
|
|
|
|
|
|
if organization.present? && job_title.present?
|
2021-01-29 00:20:46 +05:30
|
|
|
render_job_title_and_organization(job_title, organization, with_schema_markup: with_schema_markup)
|
2020-04-08 14:13:33 +05:30
|
|
|
elsif job_title.present?
|
2021-01-29 00:20:46 +05:30
|
|
|
render_job_title(job_title, with_schema_markup: with_schema_markup)
|
2020-04-08 14:13:33 +05:30
|
|
|
elsif organization.present?
|
2021-01-29 00:20:46 +05:30
|
|
|
render_organization(organization, with_schema_markup: with_schema_markup)
|
2020-04-08 14:13:33 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
def can_force_email_confirmation?(user)
|
|
|
|
!user.confirmed?
|
|
|
|
end
|
|
|
|
|
2021-09-30 23:02:18 +05:30
|
|
|
def confirm_user_data(user)
|
|
|
|
message = if user.unconfirmed_email.present?
|
2023-07-09 08:55:56 +05:30
|
|
|
safe_format(_('This user has an unconfirmed email address (%{email}). You may force a confirmation.'), email: user.unconfirmed_email)
|
2021-09-30 23:02:18 +05:30
|
|
|
else
|
|
|
|
_('This user has an unconfirmed email address. You may force a confirmation.')
|
|
|
|
end
|
|
|
|
|
|
|
|
modal_attributes = Gitlab::Json.dump({
|
|
|
|
title: s_('AdminUsers|Confirm user %{username}?') % { username: sanitize_name(user.name) },
|
|
|
|
messageHtml: message,
|
|
|
|
actionPrimary: {
|
|
|
|
text: s_('AdminUsers|Confirm user'),
|
2022-07-23 23:45:48 +05:30
|
|
|
attributes: [{ variant: 'confirm', 'data-qa-selector': 'confirm_user_confirm_button' }]
|
2021-09-30 23:02:18 +05:30
|
|
|
},
|
|
|
|
actionSecondary: {
|
|
|
|
text: _('Cancel'),
|
|
|
|
attributes: [{ variant: 'default' }]
|
|
|
|
}
|
|
|
|
})
|
2021-02-22 17:27:13 +05:30
|
|
|
|
|
|
|
{
|
2021-09-30 23:02:18 +05:30
|
|
|
path: confirm_admin_user_path(user),
|
2021-02-22 17:27:13 +05:30
|
|
|
method: 'put',
|
2021-09-30 23:02:18 +05:30
|
|
|
modal_attributes: modal_attributes,
|
|
|
|
qa_selector: 'confirm_user_button'
|
2021-02-22 17:27:13 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def user_display_name(user)
|
|
|
|
return s_('UserProfile|Blocked user') if user.blocked?
|
|
|
|
|
|
|
|
can_read_profile = can?(current_user, :read_user_profile, user)
|
|
|
|
return s_('UserProfile|Unconfirmed user') unless user.confirmed? || can_read_profile
|
|
|
|
|
|
|
|
user.name
|
|
|
|
end
|
|
|
|
|
2021-09-30 23:02:18 +05:30
|
|
|
def admin_user_actions_data_attributes(user)
|
|
|
|
{
|
|
|
|
user: Admin::UserEntity.represent(user, { current_user: current_user }).to_json,
|
|
|
|
paths: admin_users_paths.to_json
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2022-04-04 11:22:00 +05:30
|
|
|
def display_public_email?(user)
|
|
|
|
user.public_email.present?
|
|
|
|
end
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
def user_profile_tabs_app_data(user)
|
|
|
|
{
|
|
|
|
followees: user.followees.count,
|
|
|
|
followers: user.followers.count,
|
|
|
|
user_calendar_path: user_calendar_path(user, :json),
|
2023-06-20 00:43:36 +05:30
|
|
|
utc_offset: local_timezone_instance(user.timezone).now.utc_offset,
|
|
|
|
user_id: user.id
|
2023-05-27 22:25:52 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
private
|
|
|
|
|
2021-02-22 17:27:13 +05:30
|
|
|
def admin_users_paths
|
|
|
|
{
|
|
|
|
edit: edit_admin_user_path(:id),
|
|
|
|
approve: approve_admin_user_path(:id),
|
|
|
|
reject: reject_admin_user_path(:id),
|
|
|
|
unblock: unblock_admin_user_path(:id),
|
|
|
|
block: block_admin_user_path(:id),
|
|
|
|
deactivate: deactivate_admin_user_path(:id),
|
|
|
|
activate: activate_admin_user_path(:id),
|
|
|
|
unlock: unlock_admin_user_path(:id),
|
|
|
|
delete: admin_user_path(:id),
|
2021-10-27 15:23:28 +05:30
|
|
|
delete_with_contributions: admin_user_path(:id, hard_delete: true),
|
2021-09-30 23:02:18 +05:30
|
|
|
admin_user: admin_user_path(:id),
|
|
|
|
ban: ban_admin_user_path(:id),
|
|
|
|
unban: unban_admin_user_path(:id)
|
2021-02-22 17:27:13 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
def blocked_user_badge(user)
|
|
|
|
pending_approval_badge = { text: s_('AdminUsers|Pending approval'), variant: 'info' }
|
|
|
|
return pending_approval_badge if user.blocked_pending_approval?
|
|
|
|
|
2021-06-08 01:23:25 +05:30
|
|
|
banned_badge = { text: s_('AdminUsers|Banned'), variant: 'danger' }
|
|
|
|
return banned_badge if user.banned?
|
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
ldap_blocked_badge = { text: s_('AdminUsers|LDAP Blocked'), variant: 'danger' }
|
|
|
|
return ldap_blocked_badge if user.ldap_blocked?
|
|
|
|
|
2021-01-03 14:25:43 +05:30
|
|
|
{ text: s_('AdminUsers|Blocked'), variant: 'danger' }
|
|
|
|
end
|
|
|
|
|
2018-03-27 19:54:05 +05:30
|
|
|
def get_profile_tabs
|
2018-11-18 11:00:15 +05:30
|
|
|
tabs = []
|
|
|
|
|
|
|
|
if can?(current_user, :read_user_profile, @user)
|
2021-03-11 19:13:27 +05:30
|
|
|
tabs += [:overview, :activity, :groups, :contributed, :projects, :starred, :snippets, :followers, :following]
|
2018-11-18 11:00:15 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
tabs
|
2018-03-27 19:54:05 +05:30
|
|
|
end
|
2018-10-15 14:42:47 +05:30
|
|
|
|
|
|
|
def get_current_user_menu_items
|
|
|
|
items = []
|
|
|
|
|
|
|
|
items << :sign_out if current_user
|
|
|
|
|
|
|
|
return items if current_user&.required_terms_not_accepted?
|
|
|
|
|
|
|
|
items << :help
|
|
|
|
items << :profile if can?(current_user, :read_user, current_user)
|
|
|
|
items << :settings if can?(current_user, :update_user, current_user)
|
|
|
|
|
|
|
|
items
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
|
|
|
|
def render_job_title(job_title, with_schema_markup: false)
|
|
|
|
if with_schema_markup
|
|
|
|
content_tag :span, itemprop: 'jobTitle' do
|
|
|
|
job_title
|
|
|
|
end
|
|
|
|
else
|
|
|
|
job_title
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_organization(organization, with_schema_markup: false)
|
|
|
|
if with_schema_markup
|
|
|
|
content_tag :span, itemprop: 'worksFor' do
|
|
|
|
organization
|
|
|
|
end
|
|
|
|
else
|
|
|
|
organization
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_job_title_and_organization(job_title, organization, with_schema_markup: false)
|
|
|
|
if with_schema_markup
|
|
|
|
job_title = '<span itemprop="jobTitle">'.html_safe + job_title + "</span>".html_safe
|
|
|
|
organization = '<span itemprop="worksFor">'.html_safe + organization + "</span>".html_safe
|
|
|
|
|
2023-04-23 21:23:45 +05:30
|
|
|
html_escape(s_('Profile|%{job_title} at %{organization}')) % { job_title: job_title, organization: organization }
|
|
|
|
else
|
|
|
|
s_('Profile|%{job_title} at %{organization}') % { job_title: job_title, organization: organization }
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
end
|
2021-03-11 19:13:27 +05:30
|
|
|
|
|
|
|
def user_table_headers
|
|
|
|
[
|
|
|
|
{
|
|
|
|
section_class_name: 'section-40',
|
|
|
|
header_text: _('Name')
|
|
|
|
},
|
|
|
|
{
|
|
|
|
section_class_name: 'section-10',
|
|
|
|
header_text: _('Projects')
|
|
|
|
},
|
|
|
|
{
|
|
|
|
section_class_name: 'section-15',
|
|
|
|
header_text: _('Created on')
|
|
|
|
},
|
|
|
|
{
|
|
|
|
section_class_name: 'section-15',
|
|
|
|
header_text: _('Last activity')
|
|
|
|
}
|
|
|
|
]
|
|
|
|
end
|
2022-11-25 23:54:43 +05:30
|
|
|
|
|
|
|
# the keys should match the user model defined roles in app/models/user.rb
|
|
|
|
def localized_user_roles
|
|
|
|
{
|
|
|
|
software_developer: s_('User|Software Developer'),
|
|
|
|
development_team_lead: s_('User|Development Team Lead'),
|
|
|
|
devops_engineer: s_('User|Devops Engineer'),
|
|
|
|
systems_administrator: s_('User|Systems Administrator'),
|
|
|
|
security_analyst: s_('User|Security Analyst'),
|
|
|
|
data_analyst: s_('User|Data Analyst'),
|
|
|
|
product_manager: s_('User|Product Manager'),
|
|
|
|
product_designer: s_('User|Product Designer'),
|
|
|
|
other: s_('User|Other')
|
|
|
|
}.with_indifferent_access.freeze
|
|
|
|
end
|
2023-04-23 21:23:45 +05:30
|
|
|
|
|
|
|
def saved_replies_enabled?
|
|
|
|
Feature.enabled?(:saved_replies, current_user)
|
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
2019-12-04 20:38:33 +05:30
|
|
|
|
2021-06-08 01:23:25 +05:30
|
|
|
UsersHelper.prepend_mod_with('UsersHelper')
|