Add option to display fullname instead of username on UI (#3910)

This commit is contained in:
Naoki Takezoe
2026-01-08 14:21:23 +09:00
committed by GitHub
parent 9a1324a870
commit b334809e3e
12 changed files with 124 additions and 62 deletions

View File

@@ -251,13 +251,22 @@ trait IndexControllerBase extends ControllerBase {
}
}
.map { t =>
Map(
"label" -> s"${avatar(t.userName, 16)}<b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...")
)}</b> ${StringUtil
.escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))}",
"value" -> t.userName
)
if (t.isGroupAccount) {
Map(
"label" -> s"${avatar(t.userName, 16)} <b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...")
)}</b>",
"value" -> t.userName
)
} else {
Map(
"label" -> s"${avatar(t.userName, 16)} <b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...")
)}</b> (${StringUtil
.escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))})",
"value" -> t.userName
)
}
}
)
)

View File

@@ -125,7 +125,8 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
"maxDiffFiles" -> trim(label("Max diff files", number(required))),
"maxDiffLines" -> trim(label("Max diff lines", number(required)))
)(RepositoryViewerSettings.apply),
"defaultBranch" -> trim(label("Default branch", text(required)))
"defaultBranch" -> trim(label("Default branch", text(required))),
"showFullName" -> trim(label("Show full name", boolean()))
)(SystemSettings.apply).verifying { settings =>
Vector(
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {

View File

@@ -93,6 +93,7 @@ trait SystemSettingsService {
props.setProperty(RepositoryViewerMaxDiffFiles, settings.repositoryViewer.maxDiffFiles.toString)
props.setProperty(RepositoryViewerMaxDiffLines, settings.repositoryViewer.maxDiffLines.toString)
props.setProperty(DefaultBranch, settings.defaultBranch)
props.setProperty(ShowFullName, settings.showFullName.toString)
Using.resource(new java.io.FileOutputStream(GitBucketConf)) { out =>
props.store(out, null)
@@ -211,7 +212,8 @@ trait SystemSettingsService {
getValue(props, RepositoryViewerMaxDiffFiles, 100),
getValue(props, RepositoryViewerMaxDiffLines, 1000)
),
getValue(props, DefaultBranch, "main")
getValue(props, DefaultBranch, "main"),
getValue(props, ShowFullName, false)
)
}
}
@@ -238,7 +240,8 @@ object SystemSettingsService {
webHook: WebHook,
upload: Upload,
repositoryViewer: RepositoryViewerSettings,
defaultBranch: String
defaultBranch: String,
showFullName: Boolean
) {
def baseUrl(request: HttpServletRequest): String =
baseUrl.getOrElse(parseBaseUrl(request)).stripSuffix("/")
@@ -457,6 +460,7 @@ object SystemSettingsService {
private val RepositoryViewerMaxDiffFiles = "repository_viewer_max_diff_files"
private val RepositoryViewerMaxDiffLines = "repository_viewer_max_diff_lines"
private val DefaultBranch = "default_branch"
private val ShowFullName = "show_full_name"
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
getConfigValue(key).getOrElse {

View File

@@ -47,18 +47,28 @@ trait AvatarImageProvider { self: RequestCache =>
}
}
val displayName = if (!context.settings.showFullName) {
s"@$userName"
} else {
if (mailAddress.isEmpty) {
getAccountByUserNameFromCache(userName).map(_.fullName).getOrElse(s"@$userName")
} else {
getAccountByMailAddressFromCache(mailAddress).map(_.fullName).getOrElse(s"@$userName")
}
}
if (tooltip) {
Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" }
else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}"
| data-toggle="tooltip" title="${StringUtil.escapeHtml(userName)}" />""".stripMargin
| alt="${StringUtil.escapeHtml(displayName)}"
| data-toggle="tooltip" title="${StringUtil.escapeHtml(displayName)}" />""".stripMargin
)
} else {
Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" }
else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}" />""".stripMargin
| alt="${StringUtil.escapeHtml(displayName)}" />""".stripMargin
)
}
}

View File

@@ -327,12 +327,30 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
*/
def assets(path: String)(implicit context: Context): String = s"${context.path}/assets${path}?${hashQuery}"
def displayUserName(userName: String, mailAddress: String = "")(implicit context: Context): String = {
val displayName = if (!context.settings.showFullName) {
userName
} else {
if (mailAddress.isEmpty) {
getAccountByUserNameFromCache(userName).map(_.fullName).getOrElse(userName)
} else {
getAccountByMailAddressFromCache(mailAddress).map(_.fullName).getOrElse(userName)
}
}
if (userName == displayName) {
userName
} else {
s"$userName ($displayName)"
}
}
/**
* Generates the text link to the account page.
* If user does not exist or disabled, this method returns user name as text without link.
*/
def user(userName: String, mailAddress: String = "", styleClass: String = "")(implicit context: Context): Html =
userWithContent(userName, mailAddress, styleClass)(Html(StringUtil.escapeHtml(userName)))
userWithContent(userName, mailAddress, styleClass)(Html(StringUtil.escapeHtml(displayUserName(userName, mailAddress))))
/**
* Generates the avatar link to the account page.

View File

@@ -56,16 +56,16 @@ $(function(){
$('#addMember').click(function(){
$('#error-members').text('');
var userName = $('#memberName').val();
const userName = $('#memberName').val();
// check empty
if($.trim(userName) == ''){
if($.trim(userName) === ''){
return false;
}
// check duplication
var exists = $('#member-list li').filter(function(){
return $(this).data('name') == userName;
const exists = $('#member-list li').filter(function(){
return $(this).data('name') === userName;
}).length > 0;
if(exists){
$('#error-members').text('User has been already added.');
@@ -75,7 +75,7 @@ $(function(){
// check existence
$.post('@context.path/_user/existence', { 'userName': userName },
function(data, status){
if(data == 'user'){
if(data === 'user'){
addMemberHTML(userName, false);
$('#memberName').val('');
} else {
@@ -90,7 +90,7 @@ $(function(){
// Don't submit form by ENTER key
$('#memberName').keypress(function(e){
return !(e.keyCode == 13);
return !(e.keyCode === 13);
});
$('#delete').click(function(){
@@ -102,11 +102,11 @@ $(function(){
}
function addMemberHTML(userName, isManager){
var memberButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="false" name="' + userName + '">Member</label>');
const memberButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="false" name="' + userName + '">Member</label>');
if(!isManager){
memberButton.addClass('active');
}
var managerButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="true" name="' + userName + '">Manager</label>');
const managerButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="true" name="' + userName + '">Manager</label>');
if(isManager){
managerButton.addClass('active');
}
@@ -123,8 +123,8 @@ $(function(){
}
function updateMembers(){
var members = $('#member-list li').map(function(i, e){
var userName = $(e).data('name');
const members = $('#member-list li').map(function(i, e){
const userName = $(e).data('name');
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
}).get().join(',');
$('#members').val(members);

View File

@@ -253,6 +253,21 @@
<input type="radio" name="showMailAddress" value="false"@if(!context.settings.showMailAddress){ checked}>
<span class="strong">Hide</span> <span class="normal">- Hide mail address in user's profile page.</span>
</label>
</fieldset>
<!--====================================================================-->
<!-- Show full name -->
<!--====================================================================-->
<hr>
<label class="strong">Username display on UI</label>
<fieldset>
<label class="radio">
<input type="radio" name="showFullName" value="false"@if(!context.settings.showFullName){ checked}>
<span class="strong">User name</span> <span class="normal">- Username is primarily displayed on UI.</span>
</label>
<label class="radio">
<input type="radio" name="showFullName" value="true"@if(context.settings.showFullName){ checked}>
<span class="strong">Full name</span> <span class="normal">- Fullname is primarily displayed instead of username on UI.</span>
</label>
</fieldset>
<!--====================================================================-->
<!-- File upload -->

View File

@@ -25,20 +25,23 @@
<a href="@context.path/admin/users/@account.userName/_edituser">Edit</a>
}
</div>
<div class="strong">
@helpers.avatarLink(account.userName, 20)
@if(account.isRemoved){
@account.userName
} else {
<a href="@helpers.url(account.userName)">@account.userName</a>
}
@if(account.isGroupAccount){
(Group)
} else {
@if(account.isAdmin){
(Administrator)
<div>
<span class="strong">
@helpers.avatarLink(account.userName, 20)
@if(account.isRemoved){
@account.userName
} else {
(Normal)
<a href="@helpers.url(account.userName)">@account.userName</a>
}
</span>
@if(account.isGroupAccount){
- Group
} else {
(@account.fullName)
@if(account.isAdmin){
- Administrator
} else {
- Normal
}
}
@if(account.isGroupAccount){
@@ -50,10 +53,10 @@
<div>
<hr>
@if(!account.isGroupAccount){
<i class="octicon octicon-mail"></i> @account.mailAddress
<i class="octicon octicon-mail"></i>@account.mailAddress
}
@account.url.map { url =>
<i class="octicon octicon-home"></i> @url
<i class="octicon octicon-home"></i>@url
}
</div>
<div>

View File

@@ -131,7 +131,7 @@
@collaborators.map { collaborator =>
<li>
<a href="javascript:void(0);" class="toggle-assign" data-name="@collaborator">
@gitbucket.core.helper.html.checkicon(issueAssignees.exists(_.assigneeUserName == collaborator))@helpers.avatar(collaborator, 20) @collaborator
@gitbucket.core.helper.html.checkicon(issueAssignees.exists(_.assigneeUserName == collaborator))@helpers.avatar(collaborator, 20) @helpers.displayUserName(collaborator)
</a>
</li>
}

View File

@@ -48,25 +48,25 @@ $(function(){
});
$('.add').click(function(){
var id = $(this).attr('id') == 'addCollaborator' ? 'collaborator' : 'group';
const id = $(this).attr('id') === 'addCollaborator' ? 'collaborator' : 'group';
$('#error-' + id).text('');
var userName = $('#userName-' + id).val();
const userName = $('#userName-' + id).val();
// check empty
if($.trim(userName) == ''){
if($.trim(userName) === ''){
return false;
}
// check owner
var owner = '@repository.owner' == userName
const owner = '@repository.owner' === userName
if(owner){
$('#error-' + id).text('User is owner of this repository.');
return false;
}
// check duplication
var exists = $('#' + id + '-list li').filter(function(){
return $(this).data('name') == userName;
const exists = $('#' + id + '-list li').filter(function(){
return $(this).data('name') === userName;
}).length > 0;
if(exists){
$('#error-' + id).text('User has been already added.');
@@ -76,7 +76,7 @@ $(function(){
// check existence
$.post('@context.path/_user/existence', { 'userName': userName },
function(data, status){
if(data != ''){
if(data !== ''){
addListHTML(userName, '@Role.ADMIN.name', '#' + id + '-list');
$('#userName-' + id).val('');
} else {
@@ -91,7 +91,7 @@ $(function(){
// Don't submit form by ENTER key
$('#userName-collaborator, #userName-group').keypress(function(e){
return !(e.keyCode == 13);
return !(e.keyCode === 13);
});
@collaborators.map { case (collaborator, isGroup) =>
@@ -99,16 +99,16 @@ $(function(){
}
function addListHTML(userName, role, id){
var adminButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.ADMIN.name" name="' + userName + '">Admin</label>');
if(role == '@Role.ADMIN.name'){
const adminButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.ADMIN.name" name="' + userName + '">Admin</label>');
if(role === '@Role.ADMIN.name'){
adminButton.addClass('active');
}
var writeButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.DEVELOPER.name" name="' + userName + '">Developer</label>');
if(role == '@Role.DEVELOPER.name'){
const writeButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.DEVELOPER.name" name="' + userName + '">Developer</label>');
if(role === '@Role.DEVELOPER.name'){
writeButton.addClass('active');
}
var readButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.GUEST.name" name="' + userName + '">Guest</label>');
if(role == '@Role.GUEST.name'){
const readButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="@Role.GUEST.name" name="' + userName + '">Guest</label>');
if(role === '@Role.GUEST.name'){
readButton.addClass('active');
}
@@ -124,13 +124,13 @@ $(function(){
}
function updateValues(){
var collaborators = $('#collaborator-list li').map(function(i, e){
var userName = $(e).data('name');
const collaborators = $('#collaborator-list li').map(function(i, e){
const userName = $(e).data('name');
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
}).get().join(',');
var groups = $('#group-list li').map(function(i, e){
var userName = $(e).data('name');
const groups = $('#group-list li').map(function(i, e){
const userName = $(e).data('name');
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
}).get().join(',');

View File

@@ -85,7 +85,8 @@ trait ServiceSpecBase {
maxDiffFiles = 100,
maxDiffLines = 1000
),
defaultBranch = "main"
defaultBranch = "main",
showFullName = false
)
def withTestDB[A](action: (Session) => A): A = {

View File

@@ -107,7 +107,7 @@ class AvatarImageProviderSpec extends AnyFunSpec {
provider.toHtml("user", 20, "hoge@hoge.com", true).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@user"
| data-toggle="tooltip" title="user" />""".stripMargin
| data-toggle="tooltip" title="@user" />""".stripMargin
)
}
@@ -125,7 +125,7 @@ class AvatarImageProviderSpec extends AnyFunSpec {
provider.toHtml("""<user>"<name>""", 20, "hoge@hoge.com", true).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@&lt;user&gt;&quot;&lt;name&gt;"
| data-toggle="tooltip" title="&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
| data-toggle="tooltip" title="@&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
)
}
}
@@ -196,7 +196,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
maxDiffFiles = 100,
maxDiffLines = 1000
),
"main"
defaultBranch = "main",
showFullName = false
)
/**