concrete/src/Form/Service/Widget/UserSelector.php
public function selectMultipleUsers($fieldName, $users = [])
    {
        $identifier = $this->app->make(Identifier::class)->getString(32);
        $i18n = [
            'username' => t('Username'),
            'emailAddress' => t('Email Address'),
            'chooseUser' => t('Choose User'),
            'noUsers' => t('No users selected.'),
        ];
        $searchLink = $this->app->make(ResolverManagerInterface::class)->resolve(['/ccm/system/dialogs/user/search']);
        $valn = $this->app->make(Numbers::class);
        $userInfoRepository = $this->app->make(UserInfoRepository::class);
        $preselectedUsers = '';
        foreach ($users as $user) {
            if ($valn->integer($user)) {
                $user = $userInfoRepository->getById($user);
            }
            if (is_object($user)) {
                $preselectedUsers .= <<<EOT
<tr data-ccm-user-id="{$user->getUserID()}" class="ccm-list-record">
    <td><input type="hidden" name="{$fieldName}[]" value="{$user->getUserID()}" />{$user->getUserName()}</td>
    <td>{$user->getUserEmail()}</td>
    <td><a href="#" class="ccm-user-list-clear icon-link"><i class="fa fa-minus-circle ccm-user-list-clear-button"></i></a></td>
</tr>
EOT
                ;
            }
        }
        $noUsersStyle = $preselectedUsers === '' ? '' : ' style="display: none"';

        return <<<EOT
<table id="ccmUserSelect-{$identifier}" class="table table-condensed" cellspacing="0" cellpadding="0" border="0">
    <thead>
        <tr>
            <th>{$i18n['username']}</th>
            <th>{$i18n['emailAddress']}</th>
            <th style="width: 1px"><a class="icon-link ccm-user-select-item dialog-launch" dialog-append-buttons="true" dialog-width="90%" dialog-height="70%" dialog-modal="false" dialog-title="{$i18n['chooseUser']}" href="{$searchLink}"><i class="fa fa-plus-circle" /></a></th>
        </tr>
    </thead>
    <tbody>
        {$preselectedUsers}
        <tr class="ccm-user-selected-item-none"{$noUsersStyle}><td colspan="3">{$i18n['noUsers']}</td></tr>
    </tbody>
</table>
<script>
$(function() {
    var container = $('#ccmUserSelect-{$identifier}'),
        noUsersRow = container.find('tr.ccm-user-selected-item-none'),
        updateNoUsers = function() {
            if (container.find('tr[data-ccm-user-id]').length === 0) {
                noUsersRow.show();
            } else {
                noUsersRow.hide();
            }
        },
        userSelectCallback = function(e, data) {
            e.stopPropagation();
            var uID = data.uID,
                uName = data.uName,
                uEmail = data.uEmail;
            if (container.find('tr[data-ccm-user-id=' + uID + ']').length > 0) {
                return;
            }
            noUsersRow.before($('<tr data-ccm-user-id="' + uID + '" class="ccm-list-record" />')
                .append($('<td />')
                    .text(uName)
                    .prepend($('<input type="hidden" name="{$fieldName}[]" />').val(uID))
                )
                .append($('<td />')
                    .text(uEmail)
                )
                .append($('<td><a href="#" class="ccm-user-list-clear icon-link"><i class="fa fa-minus-circle ccm-user-list-clear-button"></i></a></td>'))
            );
            updateNoUsers();
        };
    container.on('click', 'a.ccm-user-list-clear', function(e) {
        e.preventDefault();
        $(this).closest('tr').remove();
        updateNoUsers();
    });
    container.find('.ccm-user-select-item')
        .dialog()
        .on('click', function(e) {
            ConcreteEvent.subscribe('UserSearchDialogSelectUser', userSelectCallback)
        })
    ;
    ConcreteEvent.subscribe('UserSearchDialogAfterSelectUser', function(e) {
        ConcreteEvent.unsubscribe('UserSearchDialogSelectUser');
        jQuery.fn.dialog.closeTop();
    });
});
</script>
EOT
        ;
    }
public function selectMultipleUsers($fieldName, $users = [])
    {
        $identifier = $this->app->make(Identifier::class)->getString(32);
        $i18n = [
            'username' => t('Username'),
            'emailAddress' => t('Email Address'),
            'chooseUser' => t('Choose User'),
            'noUsers' => t('No users selected.'),
        ];
        $searchLink = $this->app->make(ResolverManagerInterface::class)->resolve(['/ccm/system/dialogs/user/search']);
        $valn = $this->app->make(Numbers::class);
        $userInfoRepository = $this->app->make(UserInfoRepository::class);
        $preselectedUsers = '';
        foreach ($users as $user) {
            if ($valn->integer($user)) {
                $user = $userInfoRepository->getById($user);
            }
            if (is_object($user)) {
                $preselectedUsers .= <<<EOT
<tr data-ccm-user-id="{$user->getUserID()}" class="ccm-list-record">
    <td><input type="hidden" name="{$fieldName}[]" value="{$user->getUserID()}" />{$user->getUserName()}</td>
    <td>{$user->getUserEmail()}</td>
    <td><a href="#" class="ccm-user-list-clear icon-link"><i class="fa fa-minus-circle ccm-user-list-clear-button"></i></a></td>
</tr>
EOT
                ;
            }
        }
        $noUsersStyle = $preselectedUsers === '' ? '' : ' style="display: none"';

        return <<<EOT
<table id="ccmUserSelect-{$identifier}" class="table table-condensed" cellspacing="0" cellpadding="0" border="0">
    <thead>
        <tr>
            <th>{$i18n['username']}</th>
            <th>{$i18n['emailAddress']}</th>
            <th style="width: 1px"><a class="icon-link ccm-user-select-item dialog-launch" dialog-append-buttons="true" dialog-width="90%" dialog-height="70%" dialog-modal="false" dialog-title="{$i18n['chooseUser']}" href="{$searchLink}"><i class="fa fa-plus-circle" /></a></th>
        </tr>
    </thead>
    <tbody>
        {$preselectedUsers}
        <tr class="ccm-user-selected-item-none"{$noUsersStyle}><td colspan="3">{$i18n['noUsers']}</td></tr>
    </tbody>
</table>
<script>
$(function() {
    var container = $('#ccmUserSelect-{$identifier}'),
        noUsersRow = container.find('tr.ccm-user-selected-item-none'),
        updateNoUsers = function() {
            if (container.find('tr[data-ccm-user-id]').length === 0) {
                noUsersRow.show();
            } else {
                noUsersRow.hide();
            }
        },
        userSelectCallback = function(e, data) {
            e.stopPropagation();
            var uID = data.uID,
                uName = data.uName,
                uEmail = data.uEmail;
            if (container.find('tr[data-ccm-user-id=' + uID + ']').length > 0) {
                return;
            }
            noUsersRow.before($('<tr data-ccm-user-id="' + uID + '" class="ccm-list-record" />')
                .append($('<td />')
                    .text(uName)
                    .prepend($('<input type="hidden" name="{$fieldName}[]" />').val(uID))
                )
                .append($('<td />')
                    .text(uEmail)
                )
                .append($('<td><a href="#" class="ccm-user-list-clear icon-link"><i class="fa fa-minus-circle ccm-user-list-clear-button"></i></a></td>'))
            );
            updateNoUsers();
        };
    container.on('click', 'a.ccm-user-list-clear', function(e) {
        e.preventDefault();
        $(this).closest('tr').remove();
        updateNoUsers();
    });
    container.find('.ccm-user-select-item')
        .dialog()
        .on('click', function(e) {
            ConcreteEvent.subscribe('UserSearchDialogSelectUser', userSelectCallback)
        })
    ;
    ConcreteEvent.subscribe('UserSearchDialogAfterSelectUser', function(e) {
        ConcreteEvent.unsubscribe('UserSearchDialogSelectUser');
        jQuery.fn.dialog.closeTop();
    });
});
</script>
EOT
        ;
    }
public function selectMultipleUsers ( $ fieldName , $ users = [ ] ) { $ identifier = $ this -> app -> make ( Identifier :: class ) -> getString ( 32 ) ; $ i18n = [ 'username' => t ( 'Username' ) , 'emailAddress' => t ( 'Email Address' ) , 'chooseUser' => t ( 'Choose User' ) , 'noUsers' => t ( 'No users selected.' ) , ] ; $ searchLink = $ this -> app -> make ( ResolverManagerInterface :: class ) -> resolve ( [ '/ccm/system/dialogs/user/search' ] ) ; $ valn = $ this -> app -> make ( Numbers :: class ) ; $ userInfoRepository = $ this -> app -> make ( UserInfoRepository :: class ) ; $ preselectedUsers = '' ; foreach ( $ users as $ user ) { if ( $ valn -> integer ( $ user ) ) { $ user = $ userInfoRepository -> getById ( $ user ) ; } if ( is_object ( $ user ) ) { $ preselectedUsers .=  <<<EOT
<tr data-ccm-user-id="{$user->getUserID()}" class="ccm-list-record">
    <td><input type="hidden" name="{$fieldName}[]" value="{$user->getUserID()}" />{$user->getUserName()}</td>
    <td>{$user->getUserEmail()}</td>
    <td><a href="#" class="ccm-user-list-clear icon-link"><i class="fa fa-minus-circle ccm-user-list-clear-button"></i></a></td>
</tr>
EOT ; } } $ noUsersStyle = $ preselectedUsers === '' ? '' : ' style="display: none"' ; return  <<<EOT
<table id="ccmUserSelect-{$identifier}" class="table table-condensed" cellspacing="0" cellpadding="0" border="0">
    <thead>
        <tr>
            <th>{$i18n['username']}</th>
            <th>{$i18n['emailAddress']}</th>
            <th style="width: 1px"><a class="icon-link ccm-user-select-item dialog-launch" dialog-append-buttons="true" dialog-width="90%" dialog-height="70%" dialog-modal="false" dialog-title="{$i18n['chooseUser']}" href="{$searchLink}"><i class="fa fa-plus-circle" /></a></th>
        </tr>
    </thead>
    <tbody>
        {$preselectedUsers}
        <tr class="ccm-user-selected-item-none"{$noUsersStyle}><td colspan="3">{$i18n['noUsers']}</td></tr>
    </tbody>
</table>
<script>
$(function() {
    var container = $('#ccmUserSelect-{$identifier}'),
        noUsersRow = container.find('tr.ccm-user-selected-item-none'),
        updateNoUsers = function() {
            if (container.find('tr[data-ccm-user-id]').length === 0) {
                noUsersRow.show();
            } else {
                noUsersRow.hide();
            }
        },
        userSelectCallback = function(e, data) {
            e.stopPropagation();
            var uID = data.uID,
                uName = data.uName,
                uEmail = data.uEmail;
            if (container.find('tr[data-ccm-user-id=' + uID + ']').length > 0) {
                return;
            }
            noUsersRow.before($('<tr data-ccm-user-id="' + uID + '" class="ccm-list-record" />')
                .append($('<td />')
                    .text(uName)
                    .prepend($('<input type="hidden" name="{$fieldName}[]" />').val(uID))
                )
                .append($('<td />')
                    .text(uEmail)
                )
                .append($('<td><a href="#" class="ccm-user-list-clear icon-link"><i class="fa fa-minus-circle ccm-user-list-clear-button"></i></a></td>'))
            );
            updateNoUsers();
        };
    container.on('click', 'a.ccm-user-list-clear', function(e) {
        e.preventDefault();
        $(this).closest('tr').remove();
        updateNoUsers();
    });
    container.find('.ccm-user-select-item')
        .dialog()
        .on('click', function(e) {
            ConcreteEvent.subscribe('UserSearchDialogSelectUser', userSelectCallback)
        })
    ;
    ConcreteEvent.subscribe('UserSearchDialogAfterSelectUser', function(e) {
        ConcreteEvent.unsubscribe('UserSearchDialogSelectUser');
        jQuery.fn.dialog.closeTop();
    });
});
</script>
EOT ; }
Build the HTML to be placed in a page to choose multiple users using a popup dialog.

@param string $fieldName the name of the field
@param \Concrete\Core\Entity\User\User[]|\Concrete\Core\User\UserInfo[]|int[]\Traversable $users The users to be initially selected

@return string
Build the HTML to be placed in a page to choose multiple users using a popup dialog .