Pre-PR Discussion: Configurable user name order – "Lastname Firstname" (fully implemented)

Hi everyone,

I was already annoyed by the “Firstname Lastname” order, so I finally implemented it myself.

There have been several requests for this in the past:

Important: Nothing changes for anyone who leaves the new option at the default (false).

I only had to touch 4 files and made sure the change is UI-only (email recipient lines always stay “Firstname Lastname” – this is intentional and follows common email conventions).

1. New setting (db/seeds/settings.rb)

New option as string gives us the opportunity in the future to expand it for a format like “Smith, John”.

#Insert after the block pretty_date_format
Setting.create_or_update(
  title:       __('User Name Format'),
  name:        'ui_user_name_format',
  area:        'System::Branding',
  description: __('Defines how user names are displayed in dropdowns, overviews and selection fields.'),
  options:     {
    form: [
      {
        display: '',
        null:    false,
        name:    'ui_user_name_format',
        tag:     'select',
        options: {
          first_last: __('John Smith'),
          last_first: __('Smith John'),
        },
		translate: true,
      },
    ],
  },
  preferences: {
    render:     true,
    prio:       11,
    permission: ['admin.branding'],
  },
  state:        'first_last',
  frontend:     true
)

2. app/models/user.rb

Used in the new desktop view and for formatting of the e-mail recipient. However, the usual format “First Name Last Name” is retained when sending emails.

Change this line:

#Old code
    name = "#{firstname} #{lastname}".strip

to this:

#New code
    format = Setting.get('ui_user_name_format')
	
    if recipient_line
      # Email sending: always first name last name (regardless of setting)
      name = "#{firstname} #{lastname}".strip
    elsif format == 'last_first'
      # UI display: Lastname Firstname
      name = "#{lastname} #{firstname}".strip
    else
	  # UI display: Firstname Lastname
      name = "#{firstname} #{lastname}".strip
    end

3. app/assets/javascripts/app/models/user.coffee

This code is used in the current desktop view. I replaced the following block, which is primarily for the dropdown menu and general name displays…

#Old code
  displayName: ->
    if !_.isEmpty(@firstname)
      name = @firstname
    if !_.isEmpty(@lastname)
      if _.isEmpty(name)
        name = ''
      else
        name = name + ' '
      name = name + @lastname
    return name if !_.isEmpty(name)
    if @email
      return @email
    if @phone
      return @phone
    if @mobile
      return @mobile
    if @login
      return @login
    return '-'

to this:

#New code
  displayName: ->
    format = App.Config.get('ui_user_name_format') or 'first_last'
    parts =
      if format == 'last_first'
        [@lastname, @firstname]
      else
        [@firstname, @lastname]
    name = _.compact(parts).join(' ')
    return name   if name
    return @email if @email
    return @phone if @phone
    return @mobile if @mobile
    return @login if @login
    '-'

The next section is primarily used for overviews. I replaced the following block…

#Old code
  displayNameLong: ->
    if !_.isEmpty(@firstname)
      name = @firstname
    if !_.isEmpty(@lastname)
      if _.isEmpty(name)
        name = ''
      else
        name = name + ' '
      name = name + @lastname
    if !_.isEmpty(name)
      if !_.isEmpty(@organization)
        if typeof @organization is 'object'
          name = "#{name} (#{@organization.name})"
        else
          name = "#{name} (#{@organization})"
      else if !_.isEmpty(@department)
        name = "#{name} (#{@department})"
    return name if !_.isEmpty(name)
    if @email
      return @email
    if @phone
      return @phone
    if @mobile
      return @mobile
    if @login
      return @login
    return '-'

with this:

#New code
  displayName: ->
    format = App.Config.get('ui_user_name_format') or 'first_last'
    parts =
      if format == 'last_first'
        [@lastname, @firstname]
      else
        [@firstname, @lastname]
    name = _.compact(parts).join(' ')
    return name   if name
    return @email if @email
    return @phone if @phone
    return @mobile if @mobile
    return @login if @login
    '-'

4. app/assets/javascripts/app/controllers/_ui_element/_application_action.coffee

This is for the list of e-mail recipients, a.e., in triggers. Here I changed it to use the function instead, which also resolves the problem of displaying “null” if firstname or lastname is null.

After the change:

Changed this line

#Old code
      columnSelectRecipientUserOptions.push({ value: key, name: "#{user.firstname} #{user.lastname}", selected: selected })

to this

#New code
      u = if user instanceof App.User then user else new App.User(user)
      columnSelectRecipientUserOptions.push
        value: key
        name: u.displayName()
        selected: selected
    columnSelectRecipientUserOptions = _.sortBy(columnSelectRecipientUserOptions, 'name')

After making these changes, I executed the following commands:

sudo rm -rf /opt/zammad/tmp/cache/webpacker
sudo zammad run rake assets:precompile
sudo systemctl restart zammad
sudo zammad run rake db:seed

Go to Settings, Settings, Branding and change the setting to “Smith John”:

Now the owner dropdown looks like this:

Question to the team:
Is the approach OK (one setting + explicit email exception in fullname)? Or would you prefer a second setting for email recipient lines?
Would love to hear your thoughts before I open a PR.

PS for the devs of the new desktop view: In the current desktop view, the “Organization” or “Department” is displayed next to the name in the overviews. This is not yet present in the new desktop view.