Skip to content

Extending eZ Platform UI

Back Office interface

The Back Office interface is produced by the ezplatform-admin-ui Bundle together with ezplatform-admin-ui-modules, which contains React modules that handle specific parts of the application. This interface is accessible in your browser at http://[uri_of_platform]/admin.

The Back Office uses React-based modules that make each part of the UI easily extensible. The interface uses Bootstrap, which facilitates adapting and styling the interface to your needs.

General extensibility

You can extend the Back Office in the following areas:

Back Office menus are based on the KnpMenuBundle and are easily extensible.

Tip

For general information on how to use MenuBuilder, see the official KnpMenuBundle documentation.

Menus are extensible using event subscribers/listeners. You can hook into the following events:

  • ConfigureMenuEvent::MAIN_MENU
  • ConfigureMenuEvent::USER_MENU
  • ConfigureMenuEvent::CONTENT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_SIDEBAR_LEFT
  • ConfigureMenuEvent::TRASH_SIDEBAR_RIGHT
  • ConfigureMenuEvent::SECTION_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::SECTION_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::POLICY_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::POLICY_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::ROLE_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::ROLE_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::USER_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::USER_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::ROLE_ASSIGNMENT_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::LANGUAGE_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::LANGUAGE_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_TYPE_GROUP_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_TYPE_GROUP_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_TYPE_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::CONTENT_TYPE_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::URL_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::USER_PASSWORD_CHANGE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::OBJECT_STATE_GROUP_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::OBJECT_STATE_GROUP_EDIT_SIDEBAR_RIGHT
  • ConfigureMenuEvent::OBJECT_STATE_CREATE_SIDEBAR_RIGHT
  • ConfigureMenuEvent::OBJECT_STATE_EDIT_SIDEBAR_RIGHT

An event subscriber can be implemented as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
namespace EzSystems\EzPlatformAdminUi\EventListener;

use EzSystems\EzPlatformAdminUi\Menu\Event\ConfigureMenuEvent;
use EzSystems\EzPlatformAdminUi\Menu\MainMenuBuilder;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class MenuListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            ConfigureMenuEvent::MAIN_MENU => ['onMenuConfigure', 0],
        ];
    }

    public function onMenuConfigure(ConfigureMenuEvent $event)
    {
        $menu = $event->getMenu();
        $factory = $event->getFactory();
        // options passed from the context (i.e. Content item in Content View)
        $options = $event->getOptions();

        // your customizations
    }
}

After creating a subscriber, add it to app/config/services.yml:

1
2
3
4
services:
    EzSystems\EzPlatformAdminUi\EventListener\MenuListener:
        tags:
           - { name: kernel.event.subscriber } 

Providing the kernel.event.subscriber tag is necessary only if the autoconfigure option is disabled.

Adding menu items

Add a new menu item under "Content" with custom attributes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$menu[MainMenuBuilder::ITEM_CONTENT]->addChild(
    'form_manager',
    [
        'route' => '_ezpublishLocation',
        'routeParameters' => ['locationId' => 2],
        // attributes directly on <a> element
        'linkAttributes' => [
            'class' => 'test_class another_class',
            'data-property' => 'value',
        ],
        // attributes on container <li> element
        'attributes' => [
            'data-property' => 'value',
        ],
    ]
);

Add a top-level menu item with a child

1
2
3
4
5
6
7
8
$menu->addChild(
    'menu_item_1',
    ['label' => 'Menu Item 1', 'extras' => ['icon' => 'file']]
);
$menu['menu_item_1']->addChild(
    '2nd_level_menu_item',
    ['label' => '2nd level menu item', 'uri' => 'http://example.com']
);

Add an item depending on a condition

1
2
3
4
5
6
7
$condition = true;
if ($condition) {
    $menu->addChild(
        'menu_item_2',
        ['label' => 'Menu Item 2', 'extras' => ['icon' => 'article']]
    );
}

Add a top-level menu item with URL redirection

1
2
3
4
5
6
7
8
$menu->addChild(
    'menu_item_3',
    [
        'label' => 'Menu Item 3',
        'uri' => 'http://example.com',
        'extras' => ['icon' => 'article'],
    ]
);

Modifying menu items

Remove the Media menu item from the Content tab

1
2
3
$menu[MainMenuBuilder::ITEM_CONTENT]->removeChild(
    MainMenuBuilder::ITEM_CONTENT__MEDIA
);

Reorder menu items, i.e. reverse the order

1
2
3
$menu->reorderChildren(
    array_reverse(array_keys($menu->getChildren()))
);

Other menu operations

Pass a parameter to a menu item

You can pass parameters to menu items with template_parameters:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$menu->addChild(
    'menu_item_with_params',
    [
        'extras' => [
            'template' => 'AppBundle::menu_item_template.html.twig',
            'template_parameters' => [
                'custom_parameter' => 'value',
            ],
        ],
    ]
);

You can then use the variable custom_parameter in AppBundle::menu_item_template.html.twig.

Translatable labels

To have translatable labels, use translation.key from the messages domain:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$menu->addChild(
    'menu_item_3',
    [
        'label' => 'translation.key',
        'uri' => 'http://example.com',
        'extras' => [
            'icon' => 'article',
            'translation_domain' => 'messages',
        ],
    ]
);

Custom icons

You can use the extras.icon parameter to select an icon from the built-in set.

To use your custom icon, use the extras.icon_path parameter:

1
2
3
4
5
6
7
8
9
$menu->addChild(
    'menu_item_with_icon',
    [
        'extras' => [
            'icon_path' => '/assets/images/icons/custom.svg',
            'icon_class' => 'my-custom-class',
        ],
    ]
);

The extras.icon_class parameter adds a custom CSS class to the <svg> element.

Dashboard

To extend the Dashboard, make use of an event subscriber.

In the following example, the DashboardEventSubscriber.php reverses the order of sections of the Dashboard (in a default installation this makes the "Everyone" block appear above the "Me" block):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
namespace AppBundle\EventListener;

use EzSystems\EzPlatformAdminUi\Component\Event\RenderGroupEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class DashboardEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            RenderGroupEvent::NAME => ['onRenderGroupEvent', 20],
        ];
    }

    public function onRenderGroupEvent(RenderGroupEvent $event)
    {
        if ($event->getGroupName() !== 'dashboard-blocks') {
            return;
        }

        $components = $event->getComponents();

        $reverseOrder = array_reverse($components);

        $event->setComponents($reverseOrder);

    }
}

Tabs

Many elements of the back-office interface, such as System Info or Location View, are built using tabs.

Tabs in System Information

You can extend existing tab groups with new tabs, or create your own tab groups.

Adding a new tab group

To create a custom tab group with additional logic you need to create it at the level of compiling the Symfony container, using a CompilerPass.

For example, in src/AppBundle/DependencyInjection/Compiler/CustomTabGroupPass.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace AppBundle\DependencyInjection\Compiler;

use EzSystems\EzPlatformAdminUi\Tab\TabGroup;
use EzSystems\EzPlatformAdminUi\Tab\TabRegistry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;

class CustomTabGroupPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition(TabRegistry::class)) {
            return;
        }

        $tabRegistry = $container->getDefinition(TabRegistry::class);
        $tabGroupDefinition = new Definition(
            TabGroup::class,  // or any class that extends TabGroup
            ['custom-tab-group']
        );
        $tabRegistry->addMethodCall('addTabGroup', [$tabGroupDefinition]);
    }
}

A shorter way that you can use when no custom logic is required, is to add your own tab with the new group. If a tab's group is not defined, it will be created automatically.

A new tab group can be rendered using the ez_platform_tabs Twig helper:

1
2
3
<div class="my-tabs">
    {{ ez_platform_tabs('custom-tab-group') }}
</div>

This will render the group and all tabs assigned to it.

Adding a new tab

Before you add a tab to a group you must create the tab's PHP class and define it as a Symfony service with the ezplatform.tab tag:

1
2
3
4
AppBundle\Custom\Tab:
    parent: EzSystems\EzPlatformAdminUi\Tab\AbstractTab
    tags:
        - { name: ezplatform.tab, group: custom-tab-group }

This configuration also assigns the new tab to custom-tab-group.

Tip

If the custom-tab-group does not yet exist, it will be created automatically at this point.

The tab class can look like this (in src/AppBundle/Custom/Tab.php):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace AppBundle\Custom;

use EzSystems\EzPlatformAdminUi\Tab\AbstractTab;

class Tab extends AbstractTab
{
    public function getIdentifier(): string
    {
        return 'custom-tab';
    }

    public function getName(): string
    {
        return /** @Desc("Custom Tab") */
            $this->translator->trans('custom.tab.name', [], 'some_translation_domain');
    }

    public function renderView(array $parameters): string
    {
        // Do rendering here

        return $this->twig->render('AppBundle:my_tabs/custom.html.twig', [
            'foo' => 'Bar!',
        ]);
    }
}

Beyond the AbstractTab used in the example above you can use two specialized tab types:

  • AbstractControllerBasedTab enables you to embed the results of a controller action in the tab.
  • AbstractRouteBasedTab embeds the results of the selected routing, passing applicable parameters.

Modifying tab display

You can order the tabs by making the tab implement OrderedTabInterface. The order will then depend on the numerical value returned by the getOrder method.

1
2
3
4
5
6
7
class Tab extends AbstractTab implements OrderedTabInterface
{
    public function getOrder(): int
    {
        return 100;
    }
}

The tabs will be displayed according to this value in ascending order.

Tip

It is good practice to reserve some distance between these values, for example to stagger them by step of 10. It may come useful if you later need to place something between the existing tabs.

You can also influence tab display (e.g. order tabs, remove or modify them, etc.) by using Event Listeners:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class TabEvents
{
    /**
     * Happens just before rendering a tab group.
     */
    const TAB_GROUP_PRE_RENDER = 'ezplatform.tab.group.pre_render';

    /**
     * Happens just before rendering a tab.
     */
    const TAB_PRE_RENDER = 'ezplatform.tab.pre_render';
}

As an example, see how OrderedTabInterface is implemented:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
namespace EzSystems\EzPlatformAdminUi\Tab\Event\Subscriber;

use EzSystems\EzPlatformAdminUi\Tab\Event\TabEvents;
use EzSystems\EzPlatformAdminUi\Tab\Event\TabGroupEvent;
use EzSystems\EzPlatformAdminUi\Tab\OrderedTabInterface;
use EzSystems\EzPlatformAdminUi\Tab\TabInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Reorders tabs according to their Order value (Tabs implementing OrderedTabInterface).
 * Tabs without order specified are pushed to the end of the group.
 *
 * @see OrderedTabInterface
 */
class OrderedTabSubscriber implements EventSubscriberInterface
{
    /**
     * @return array
     */
    public static function getSubscribedEvents(): array
    {
        return [
            TabEvents::TAB_GROUP_PRE_RENDER => ['onTabGroupPreRender'],
        ];
    }

    /**
     * @param TabGroupEvent $tabGroupEvent
     */
    public function onTabGroupPreRender(TabGroupEvent $tabGroupEvent)
    {
        $tabGroup = $tabGroupEvent->getData();
        $tabs = $tabGroup->getTabs();

        $tabs = $this->reorderTabs($tabs);

        $tabGroup->setTabs($tabs);
        $tabGroupEvent->setData($tabGroup);
    }

    /**
     * @param TabInterface[] $tabs
     *
     * @return array
     */
    private function reorderTabs($tabs): array
    {
        $orderedTabs = [];
        foreach ($tabs as $tab) {
            if ($tab instanceof OrderedTabInterface) {
                $orderedTabs[$tab->getIdentifier()] = $tab;
                unset($tabs[$tab->getIdentifier()]);
            }
        }

        uasort($orderedTabs, [$this, 'sortTabs']);

        return array_merge($orderedTabs, $tabs);
    }

    /**
     * @param OrderedTabInterface $tab1
     * @param OrderedTabInterface $tab2
     *
     * @return int
     */
    private function sortTabs(OrderedTabInterface $tab1, OrderedTabInterface $tab2): int
    {
        return $tab1->getOrder() <=> $tab2->getOrder();
    }
}

Custom Content Type icons

To add custom icons for existing Content Types or custom Content Types in eZ Platform follow the instructions below.

Configuration

A configuration of the default icon for Content Type is possible via default-config key. For example:

1
2
3
4
5
6
ezpublish:
    system:
       default:
           content_type:
              default-config:
                 thumbnail: /assets/images/mydefaulticon.svg

To configure a custom icon you just need to replace the default-config key with a Content Type identifier. For example:

1
2
3
4
5
6
ezpublish:
    system:
       default:
           content_type:
              article:
                 thumbnail: /assets/images/customicon.svg

Icons format

All icons should be in SVG format so they can display properly in Back Office.

Custom icons in Twig templates

Content Type icons are accessible in Twig templates via ez_content_type_icon function. It requires Content Type identifier as an argument. The function returns the path to a Content Type icon.

1
2
3
<svg class="ez-icon ez-icon-{{ contentType.identifier }}">
    <use xlink:href="{{ ez_content_type_icon(contentType.identifier) }}"></use>
</svg>

If the icon for given Content Type is not specified in the configuration the default icon will be returned.

Custom icons in JavaScript

Content Types icons configuration is stored in a global object: eZ.adminUiConfig.contentTypes.

You can easily retrieve icon URL with a helper function getContentTypeIcon, set on a global object eZ.helpers.contentType. It takes Content Type identifier as an argument and returns:

  • URL of given Content Type's icon or
  • null if there is no Content Type with given identifier

Example with getContentTypeIcon:

1
2
3
4
5
6
const contentTypeIconUrl = eZ.helpers.contentType.getContentTypeIconUrl(contentTypeIdentifier);
return (
   <svg className="ez-icon">
       <use xlinkHref={contentTypeIconUrl} />
   </svg>
)

Workflow event timeline

Workflow event timeline is used out of the box to display workflow transitions.

You can also use it to render custom entries in the timeline, for example system alerts on workflows.

Adding custom entry type

To add a custom entry type, create a custom class extending \EzSystems\EzPlatformWorkflow\WorkflowTimeline\Value\AbstractEntry. Use an \EzSystems\EzPlatformWorkflow\Event\TimelineEvents::COLLECT_ENTRIES event to add your entries to the timeline..

Providing custom templates

To provide custom templates for new event timeline entries, use the following configuration:

1
2
3
4
5
6
7
ezpublish:
    system:
        default:
            workflows_config:
                # Workflow Timeline
                timeline_entry_templates:
                    - { template: '@EzPlatformWorkflow/ezplatform_workflow/timeline/entries.html.twig', priority: 10 }

The template has to provide a block named ez_workflow_timeline_entry_{ENTRY_IDENTIFIER}.

Injecting custom components

The Back Office has designated places where you can use your own components.

Components enable you to inject widgets (e.g. Dashboard blocks) and HTML code (e.g. a tag for loading JS or CSS files). A component is any class that implements the Renderable interface. It must be tagged as a service:

1
2
3
AppBundle\Component\MyNewComponent:
    tags:
        - { name: ezplatform.admin_ui.component, group: content-edit-form-before }

group indicates where the widget will be displayed. The available groups are:

Base component classes

If you only need to inject a short element (e.g. a Twig template or a CSS link) without writing a class, you can make use of the following base classes:

  • TwigComponent renders a Twig template.
  • LinkComponent renders the HTML <link> tag.
  • ScriptComponent renders the HTML <script> tag.

In this case all you have to do is add a service definition (with proper parameters), for example:

1
2
3
4
5
6
7
8
9
appbundle.components.my_twig_component:
    parent: EzSystems\EzPlatformAdminUi\Component\TwigComponent
    arguments:
        $template: path/to/file.html.twig
        $parameters:
            first_param: first_value
            second_param: second_value
    tags:
        - { name: ezplatform.admin_ui.component, group: dashboard-blocks }

This renders the path/to/file.html.twig template with first_param and second_param as parameters.

With LinkComponent and ScriptComponent you provide $href and $src as arguments, respectively:

1
2
3
4
5
6
appbundle.components.my_link_component:
    parent: EzSystems\EzPlatformAdminUi\Component\LinkComponent
    arguments:
        $href: 'http://address.of/file.css'
    tags:
        - { name: ezplatform.admin_ui.component, group: stylesheet-head }
1
2
3
4
5
6
appbundle.components.my_script_component:
    parent: EzSystems\EzPlatformAdminUi\Component\ScriptComponent
    arguments:
        $src: 'http://address.of/file.js'
    tags:
        - { name: ezplatform.admin_ui.component, group: script-body }

Format date and time

Apart from changing the date and time formats, you can use Twig filters:

  • ez_short_datetime
  • ez_short_date
  • ez_short_time
  • ez_full_datetime
  • ez_full_date
  • ez_full_time

The following are examples of using the filters:

1
2
3
4
5
6
7
<div>
    // Date formatted in the preferred time zone and short datetime format:
    {{ content.versionInfo.creationDate|ez_short_datetime }}

    // Date formatted in UTC and preferred short datetime format:
    {{ content.versionInfo.creationDate|ez_short_datetime('UTC') }}
</div>

The filters accept an optional timezone parameter for displaying date and time in a chosen time zone. The default time zone is set in the User settings menu.

For details, see reference materials on the full format filters and short format filters.

You can also format date and time by using the following services:

  • @ezplatform.user.settings.short_datetime_format.formatter
  • @ezplatform.user.settings.short_datet_format.formatter
  • @ezplatform.user.settings.short_time_format.formatter
  • @ezplatform.user.settings.full_datetime_format.formatter
  • @ezplatform.user.settings.full_date_format.formatter
  • @ezplatform.user.settings.full_time_format.formatter

To use them, create an src\AppBundle\Service\MyService.php file containing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

namespace AppBundle\Service;

use EzSystems\EzPlatformUser\UserSetting\DateTimeFormat\FormatterInterface;

class MyService
{
    /** @var \EzSystems\EzPlatformUser\UserSetting\DateTimeFormat\FormatterInterface */
    private $shortDateTimeFormatter;

    public function __construct(FormatterInterface $shortDateTimeFormatter)
    {
        // your code

        $this->shortDateTimeFormatter = $shortDateTimeFormatter;

        // your code
    }

    public function foo()
    {
        // your code

        $now = new \DateTimeImmutable();
        $date = $this->shortDateTimeFormatter->format($now);
        $utc = $this->shortDateTimeFormatter->format($now, 'UTC');

        // your code
    }
}

Then, add the following to app/config/services.yml:

1
2
3
4
services:    
    AppBundle\Service\MyService:
        arguments:
            $shortDateTimeFormatter: '@ezplatform.user.settings.short_datetime_format.formatter'

User settings

You can add new preferences to the User settings menu in the Back Office.

To do so, create a setting class implementing two interfaces: ValueDefinitionInterface and FormMapperInterface.

In this example the class is located in AppBundle/Setting/Unit.php and enables the user to select their preference for metric or imperial unit systems.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
declare(strict_types=1);

namespace AppBundle\Setting;

use EzSystems\EzPlatformUser\UserSetting\FormMapperInterface;
use EzSystems\EzPlatformUser\UserSetting\ValueDefinitionInterface;
use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use EzSystems\EzPlatformAdminUi\UserSetting as AdminUiUserSettings;

class Unit implements ValueDefinitionInterface, FormMapperInterface
{
    public const METRIC_OPTION = 'metric';
    public const IMPERIAL_OPTION = 'imperial';

    public function getName(): string
    {
        return 'Unit';
    }

    public function getDescription(): string
    {
        return 'Choose between metric and imperial unit systems';
    }

    public function getDisplayValue(string $storageValue): string
    {
        switch($storageValue) {
            case self::METRIC_OPTION:
                return 'Metric';
            case self::IMPERIAL_OPTION:
                return 'Imperial';
            default:
                throw new InvalidArgumentException(
                    '$storageValue',
                    sprintf('There is no \'%s\' option', $storageValue)
                );
        }
    }

    public function getDefaultValue(): string
    {
        return 'metric';
    }

    public function mapFieldForm(FormBuilderInterface $formBuilder, AdminUiUserSettings\ValueDefinitionInterface $value): FormBuilderInterface
    {
        $choices = [
            'Metric' => self::METRIC_OPTION,
            'Imperial' => self::IMPERIAL_OPTION,
        ];

        return $formBuilder->create(
            'value',
            ChoiceType::class,
            [
                'multiple' => false,
                'required' => true,
                'label' => $this->getDescription(),
                'choices' => $choices,
            ]
        );
    }
}

Register the class as a service:

1
2
3
4
AppBundle\Setting\Unit:
    tags:
        - { name: ezplatform.admin_ui.user_setting.value, identifier: unit, priority: 50 }
        - { name: ezplatform.admin_ui.user_setting.form_mapper, identifier: unit }

You can order the settings in the User menu by setting their priority.

The value of the setting is accessible with ez_user_settings['unit'].

Read the Docs