This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit b247e09a9e06a92b3d304950276834ebba48c0e2
Author: Martin Stockhammer <[email protected]>
AuthorDate: Mon Dec 21 21:38:49 2020 +0100

    Adding user role assignment
---
 .../manage-roles-edit.component.html               | 179 ++++++++++++++++-----
 .../manage-roles-edit.component.ts                 |  65 +++++++-
 .../manage-users-list.component.html               |  10 +-
 .../paginated-entities.component.html              |  10 +-
 .../paginated-entities.component.ts                |  28 ++++
 .../src/app/modules/shared/shared.module.ts        |   9 +-
 .../archiva-web/src/app/services/role.service.ts   |  39 +++++
 .../src/main/archiva-web/src/assets/i18n/en.json   |  12 +-
 8 files changed, 297 insertions(+), 55 deletions(-)

diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html
index b146c72..bf6dd1e 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.html
@@ -18,9 +18,9 @@
 
 <form class="mt-3 mb-3" [formGroup]="userForm" (ngSubmit)="onSubmit()">
     <div class="form-group row col-md-8">
-        <div class="col-md-1" >{{'form.edit' |translate}}&nbsp;<span
+        <div class="col-md-1">{{'form.edit' |translate}}&nbsp;<span
                 class="fas fa-edit"></span></div>
-        <div class="col-md-6" >
+        <div class="col-md-6">
             <input class="form-check-input" type="checkbox" [value]="editMode" 
[checked]="editMode"
                    (change)="editMode=!editMode"
             >
@@ -75,14 +75,14 @@
             </div>
         </div>
         <div class="col-md-2" *ngIf="editMode">
-           <button class="btn btn-primary" type="submit"
-            [disabled]="userForm.invalid || 
!userForm.dirty">{{'form.button.save'|translate}}</button>
+            <button class="btn btn-primary" type="submit"
+                    [disabled]="userForm.invalid || 
!userForm.dirty">{{'form.button.save'|translate}}</button>
         </div>
     </div>
 </form>
 <hr/>
 <ngb-accordion activeIds="parents,children,permissions">
-    <ngb-panel id="parents" >
+    <ngb-panel id="parents">
         <ng-template ngbPanelHeader let-opened="opened">
             <div class="d-flex align-items-center justify-content-between">
                 <button ngbPanelToggle class="btn btn-link text-left 
shadow-none">
@@ -92,27 +92,28 @@
             </div>
         </ng-template>
         <ng-template ngbPanelContent>
-                <ng-container *ngIf="editRole?.parents.length>0">
-                    <ul class="list-group" *ngFor="let parentRole of 
editRole?.parents">
-                        <li class="list-group-item"><a 
routerLink="../{{parentRole?.id}}">{{parentRole?.name}}</a></li>
-                    </ul>
-                </ng-container>
+            <ng-container *ngIf="editRole?.parents.length>0">
+                <ul class="list-group" *ngFor="let parentRole of 
editRole?.parents">
+                    <li class="list-group-item"><a 
routerLink="../{{parentRole?.id}}">{{parentRole?.name}}</a></li>
+                </ul>
+            </ng-container>
         </ng-template>
     </ngb-panel>
     <ngb-panel id="children">
         <ng-template ngbPanelHeader let-opened="opened">
             <div class="d-flex align-items-center justify-content-between">
-            <button ngbPanelToggle class="btn btn-link text-left 
shadow-none"><h3>{{'roles.edit.children'|translate}}</h3></button>
-            <ng-container *ngIf="!opened"><i class="fa 
fa-eye-slash"></i></ng-container>
-            <ng-container *ngIf="opened"><i class="fa 
fa-eye"></i></ng-container>
+                <button ngbPanelToggle class="btn btn-link text-left 
shadow-none">
+                    <h3>{{'roles.edit.children'|translate}}</h3></button>
+                <ng-container *ngIf="!opened"><i class="fa 
fa-eye-slash"></i></ng-container>
+                <ng-container *ngIf="opened"><i class="fa 
fa-eye"></i></ng-container>
             </div>
         </ng-template>
         <ng-template ngbPanelContent>
-                <ng-container *ngIf="editRole?.children.length>0">
-                    <ul class="list-group" *ngFor="let childRole of 
editRole?.children">
-                        <li class="list-group-item"><a 
routerLink="../{{childRole?.id}}">{{childRole?.name}}</a></li>
-                    </ul>
-                </ng-container>
+            <ng-container *ngIf="editRole?.children.length>0">
+                <ul class="list-group" *ngFor="let childRole of 
editRole?.children">
+                    <li class="list-group-item"><a 
routerLink="../{{childRole?.id}}">{{childRole?.name}}</a></li>
+                </ul>
+            </ng-container>
         </ng-template>
     </ngb-panel>
 
@@ -120,44 +121,138 @@
         <ng-template ngbPanelHeader let-opened="opened">
             <div class="d-flex align-items-center justify-content-between">
 
-            <button ngbPanelToggle class="btn btn-link text-left 
shadow-none"><h3>{{'roles.edit.permissions'|translate}}</h3></button>
-            <ng-container *ngIf="!opened"><i class="fa 
fa-eye-slash"></i></ng-container>
-            <ng-container *ngIf="opened"><i class="fa 
fa-eye"></i></ng-container>
+                <button ngbPanelToggle class="btn btn-link text-left 
shadow-none">
+                    <h3>{{'roles.edit.permissions'|translate}}</h3></button>
+                <ng-container *ngIf="!opened"><i class="fa 
fa-eye-slash"></i></ng-container>
+                <ng-container *ngIf="opened"><i class="fa 
fa-eye"></i></ng-container>
             </div>
         </ng-template>
         <ng-template ngbPanelContent>
-                <ng-container *ngIf="editRole">
-                    <table class="table">
-                        <thead>
-                        <tr>
-                            
<th>{{'permissions.attributes.permission'|translate}}</th>
-                            
<th>{{'permissions.attributes.operation'|translate}}</th>
-                            
<th>{{'permissions.attributes.resource'|translate}}</th>
-                        </tr>
-                        </thead>
-                        <tbody *ngFor="let perm of editRole.permissions">
-                        <tr>
-                            <td>{{perm.name}}</td>
-                            <td>{{perm.operation.name}}</td>
-                            <td>{{perm.resource.identifier}}</td>
-                        </tr>
-                        </tbody>
+            <ng-container *ngIf="editRole">
+                <table class="table">
+                    <thead>
+                    <tr>
+                        
<th>{{'permissions.attributes.permission'|translate}}</th>
+                        
<th>{{'permissions.attributes.operation'|translate}}</th>
+                        
<th>{{'permissions.attributes.resource'|translate}}</th>
+                    </tr>
+                    </thead>
+                    <tbody *ngFor="let perm of editRole.permissions">
+                    <tr>
+                        <td>{{perm.name}}</td>
+                        <td>{{perm.operation.name}}</td>
+                        <td>{{perm.resource.identifier}}</td>
+                    </tr>
+                    </tbody>
 
-                    </table>
-                </ng-container>
+                </table>
+            </ng-container>
         </ng-template>
     </ngb-panel>
 
     <ngb-panel>
         <ng-template ngbPanelHeader let-opened="opened">
             <div class="d-flex align-items-center justify-content-between">
-                <button ngbPanelToggle class="btn btn-link text-left 
shadow-none"><h3>{{'roles.edit.users'|translate}}</h3></button>
+                <button ngbPanelToggle class="btn btn-link text-left 
shadow-none">
+                    <h3>{{'roles.edit.users'|translate}}</h3></button>
                 <ng-container *ngIf="!opened"><i class="fa 
fa-eye-slash"></i></ng-container>
                 <ng-container *ngIf="opened"><i class="fa 
fa-eye"></i></ng-container>
             </div>
         </ng-template>
-        <ng-template ngbPanelContent >
-             <h3>There are the users</h3>
+        <ng-template ngbPanelContent>
+            <h4>{{'roles.edit.usersParents'|translate}}</h4>
+            <app-paginated-entities [service]="roleUserParentService" 
pageSize="5" [(sortField)]="userParentSortField"
+                                    [(sortOrder)]="userParentSortOrder" 
[displayControlsIfSinglePage]="false"
+                                    #userParentSection>
+
+                <ng-container *ngIf="userParentSection.items$ |async as 
itemLoader">
+                    <ng-template [ngIf]="itemLoader.loading">
+                        <div class="spinner-border text-primary" role="status">
+                            <span class="sr-only">Loading...</span>
+                        </div>
+                    </ng-template>
+                </ng-container>
+                <ng-container *ngIf="userParentSection.items$ 
|stripLoading|async as userItem">
+                    <table class="table table-striped table-bordered">
+                        <thead class="thead-light">
+                        <tr sorted 
[sortFieldEmitter]="userParentSection.sortFieldChange"
+                            
[sortOrderEmitter]="userParentSection.sortOrderChange"
+                            [toggleObserver]="userParentSection">
+                            <app-th-sorted [fieldArray]="['user_id']"
+                                           
contentText="users.attributes.user_id"></app-th-sorted>
+                            <app-th-sorted [fieldArray]="['full_name']"
+                                           
contentText="users.attributes.full_name"></app-th-sorted>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr *ngFor="let user  of userItem.data">
+                            <td><span data-toggle="tooltip" placement="left"
+                                      
ngbTooltip="{{user.id}}">{{user.user_id}}</span>
+                            </td>
+                            <td>{{user.full_name}}</td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </ng-container>
+            </app-paginated-entities>
+            <hr/>
+            <h4>{{'roles.edit.usersInstance'|translate}}</h4>
+            <app-paginated-entities [service]="roleUserService" pageSize="5" 
[(sortField)]="userSortField"
+                                    [(sortOrder)]="userSortOrder"
+                                    [displayIfEmpty]="false" 
[displayKeyIfEmpty]="'roles.edit.noUsersAssigned'"
+                                    #userSection>
+
+                <ng-container *ngIf="userSection.items$ |async as itemLoader">
+                    <ng-template [ngIf]="itemLoader.loading">
+                        <div class="spinner-border text-primary" role="status">
+                            <span class="sr-only">Loading...</span>
+                        </div>
+                    </ng-template>
+                </ng-container>
+                <ng-container *ngIf="userSection.items$ |stripLoading|async as 
userItem">
+                    <table class="table table-striped table-bordered">
+                        <thead class="thead-light">
+                        <tr sorted 
[sortFieldEmitter]="userSection.sortFieldChange"
+                            [sortOrderEmitter]="userSection.sortOrderChange"
+                            [toggleObserver]="userSection">
+                            <app-th-sorted [fieldArray]="['user_id']"
+                                           
contentText="users.attributes.user_id"></app-th-sorted>
+                            <app-th-sorted [fieldArray]="['full_name']"
+                                           
contentText="users.attributes.full_name"></app-th-sorted>
+                        </tr>
+                        </thead>
+                        <tbody>
+                        <tr *ngFor="let user  of userItem.data">
+                            <td><span data-toggle="tooltip" placement="left"
+                                      
ngbTooltip="{{user.id}}">{{user.user_id}}</span>
+                            </td>
+                            <td>{{user.full_name}}</td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </ng-container>
+            </app-paginated-entities>
+            <hr/>
+            <form class="mt-2">
+                <ng-template #userResultTemplate let-r="result" let-t="term">
+                    <ngb-highlight [result]="r.user_id + '-' + r.full_name" 
[term]="t"></ngb-highlight>
+                </ng-template>
+                <div class="form-group">
+                    <label 
for="typeahead-http">{{'roles.edit.assignUserSearch'|translate}}</label>
+                    <input id="typeahead-http" type="text" class="form-control 
col-md-2"
+                           name="userSearchField"
+                           [class.is-invalid]="userSearchFailed" 
[resultTemplate]="userResultTemplate"
+                           [inputFormatter]="getUserId"
+                           [placement]="'top'"
+                           [(ngModel)]="userSearchModel" 
[ngbTypeahead]="searchUser" placeholder="User Search"/>
+                    <small *ngIf="userSearching"
+                           class="form-text text-muted">{{'form.searching'  
|translate}}</small>
+                    <div class="invalid-feedback"
+                         
*ngIf="userSearchFailed">{{'roles.edit.userSearchFailed'|translate}}</div>
+                </div>
+                <button class="btn btn-primary" 
(click)="assignUserRole()">{{'roles.edit.assignButton'|translate}}</button>
+            </form>
+
         </ng-template>
     </ngb-panel>
 
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts
index b5d033f..71655fe 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/roles/manage-roles-edit/manage-roles-edit.component.ts
@@ -20,12 +20,17 @@ import {AfterContentInit, Component, EventEmitter, OnInit, 
Output} from '@angula
 import {ActivatedRoute} from "@angular/router";
 import {FormBuilder, Validators} from "@angular/forms";
 import {RoleService} from "@app/services/role.service";
-import {catchError, filter, map, switchMap, tap} from "rxjs/operators";
+import {catchError, debounceTime, distinctUntilChanged, filter, map, 
switchMap, tap} from "rxjs/operators";
 import {Role} from '@app/model/role';
 import {ErrorResult} from "@app/model/error-result";
 import {EditBaseComponent} from "@app/modules/shared/edit-base.component";
-import {forkJoin, iif, Observable, of, pipe, zip} from 'rxjs';
+import {forkJoin, Observable, of, zip} from 'rxjs';
 import {RoleUpdate} from "@app/model/role-update";
+import {EntityService} from "@app/model/entity-service";
+import {User} from '@app/model/user';
+import {PagedResult} from "@app/model/paged-result";
+import {UserService} from "@app/services/user.service";
+import {UserInfo} from '@app/model/user-info';
 
 @Component({
     selector: 'app-manage-roles-edit',
@@ -38,12 +43,23 @@ export class ManageRolesEditComponent extends 
EditBaseComponent<Role> implements
     editProperties = ['id', 'name', 'description', 'template_instance', 
'resource', 'assignable'];
     originRole;
     roleCache: Map<string, Role> = new Map<string, Role>();
+    roleUserService: EntityService<User>
+    roleUserParentService: EntityService<User>;
+    userSortField = ["id"];
+    userSortOrder = "asc";
+    userParentSortField = ["id"];
+    userParentSortOrder = "asc";
+
+    userSearching:boolean=false;
+    userSearchFailed:boolean=false;
+
+    public userSearchModel:any;
 
 
     @Output()
     roleIdEvent: EventEmitter<string> = new EventEmitter<string>(true);
 
-    constructor(private route: ActivatedRoute, private roleService: 
RoleService, public fb: FormBuilder) {
+    constructor(private route: ActivatedRoute, public roleService: 
RoleService, private userService: UserService, public fb: FormBuilder) {
         super(fb);
         super.init(fb.group({
             id: [''],
@@ -53,6 +69,7 @@ export class ManageRolesEditComponent extends 
EditBaseComponent<Role> implements
             template_instance: [''],
             assignable: ['']
         }, {}));
+
     }
 
     createEntity(): Role {
@@ -75,6 +92,14 @@ export class ManageRolesEditComponent extends 
EditBaseComponent<Role> implements
             this.editRole = role;
             this.originRole = role;
             this.copyToForm(this.editProperties, this.editRole);
+            const fRoleService = this.roleService;
+            const roleId = role.id;
+            this.roleUserService = function (searchTerm: string, offset: 
number, limit: number, orderBy: string[], order: string): 
Observable<PagedResult<User>> {
+                return fRoleService.queryAssignedUsers(roleId, searchTerm, 
offset, limit, orderBy, order);
+            };
+            this.roleUserParentService = function (searchTerm: string, offset: 
number, limit: number, orderBy: string[], order: string): 
Observable<PagedResult<User>> {
+                return fRoleService.queryAssignedParentUsers(roleId, 
searchTerm, offset, limit, orderBy, order, true);
+            };
         }, error => {
             this.editRole = new Role();
         });
@@ -165,5 +190,39 @@ export class ManageRolesEditComponent extends 
EditBaseComponent<Role> implements
         }
     }
 
+    searchUser = (text$: Observable<string>) =>
+        text$.pipe(
+            debounceTime(300),
+            distinctUntilChanged(),
+            tap(() => this.userSearching = true),
+            switchMap(term =>
+                this.userService.query(term, 0, 10).pipe(
+                    tap(() => this.userSearchFailed = false),
+                    map(pagedResult=>
+                    pagedResult.data),
+                    catchError(() => {
+                        this.userSearchFailed = true;
+                        return of([]);
+                    }))
+            ),
+            tap(() => this.userSearching = false)
+        )
+
+    getUserId(item:UserInfo) : string {
+        return item.user_id;
+    }
+
+    assignUserRole() {
+        let userId;
+        if (typeof(this.userSearchModel)=='string') {
+            userId=this.userSearchModel;
+        } else {
+            if (this.userSearchModel.user_id) {
+                userId = this.userSearchModel.user_id;
+            }
+        }
+        console.log("Assigning user " + userId)
+    }
+
 }
 
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-list/manage-users-list.component.html
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-list/manage-users-list.component.html
index d87fc57..f72b3e7 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-list/manage-users-list.component.html
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/security/users/manage-users-list/manage-users-list.component.html
@@ -19,14 +19,14 @@
 <app-paginated-entities [service]="service" pageSize="10" 
[(sortField)]="sortField" [(sortOrder)]="sortOrder"
                         #parent>
 
-    <ng-container *ngIf="parent.items$ |async as roleItemLoader">
-        <ng-template [ngIf]="roleItemLoader.loading">
+    <ng-container *ngIf="parent.items$ |async as itemLoader">
+        <ng-template [ngIf]="itemLoader.loading">
             <div class="spinner-border text-primary" role="status">
                 <span class="sr-only">Loading...</span>
             </div>
         </ng-template>
     </ng-container>
-    <ng-container *ngIf="parent.items$ |stripLoading|async as roleItem">
+    <ng-container *ngIf="parent.items$ |stripLoading|async as userItem">
 
             <table class="table table-striped table-bordered">
                 <thead class="thead-light">
@@ -54,11 +54,11 @@
                     <app-th-sorted [fieldArray]="['created']" 
contentText="users.attributes.created"></app-th-sorted>
                     <app-th-sorted [fieldArray]="['last_password_change']"
                                    
contentText="users.attributes.last_password_change"></app-th-sorted>
-                    <th>Action</th>
+                    <th>{{'headers.action'|translate}}</th>
                 </tr>
                 </thead>
                 <tbody>
-                <tr *ngFor="let user  of roleItem.data"
+                <tr *ngFor="let user  of userItem.data"
                     
[ngClass]="(user.permanent||user.readOnly)?'table-secondary':''">
                     <td><span data-toggle="tooltip" placement="left" 
ngbTooltip="{{user.id}}">{{user.user_id}}</span>
                     </td>
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.html
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.html
index ce0a6a5..879ff98 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.html
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.html
@@ -16,8 +16,9 @@
   ~ under the License.
   -->
 
+<ng-template [ngIf]="(displayIfEmpty || (total$|async)>0)" 
[ngIfElse]="noContent" >
 <form class="mt-3 mb-3">
-    <div class="form-row align-items-center">
+    <div class="form-row align-items-center" 
*ngIf="displayControlsIfSinglePage||(multiplePages$|async)">
         <div class="col-lg-4 col-md-2 col-sm-1">
             <label class="sr-only" for="searchQuery">{{'search.label' 
|translate}}</label>
             <input type="text" class="form-control" id="searchQuery" 
placeholder="{{'search.input'|translate}}" #searchTerm
@@ -33,6 +34,11 @@
 
 <ng-content></ng-content>
 
-<ngb-pagination [collectionSize]="total$|async" [pageSize]="pageSize" 
[maxSize]="pagination.maxSize" [rotate]="pagination.rotate"
+<ngb-pagination *ngIf="displayControlsIfSinglePage||(multiplePages$|async)"
+                [collectionSize]="total$|async" [pageSize]="pageSize" 
[maxSize]="pagination.maxSize" [rotate]="pagination.rotate"
                 [boundaryLinks]="pagination.boundaryLinks" 
[ellipses]="pagination.ellipses"
                 [(page)]="page" (pageChange)="changePage($event)" 
aria-label="Pagination"></ngb-pagination>
+</ng-template>
+<ng-template #noContent>
+    {{displayKeyIfEmpty|translate}}
+</ng-template>
\ No newline at end of file
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.ts
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.ts
index cc62ac6..d2157d8 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.ts
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/paginated-entities/paginated-entities.component.ts
@@ -94,6 +94,26 @@ export class PaginatedEntitiesComponent<T> implements 
OnInit, FieldToggle, After
     }
 
     /**
+     * If true, all controls are displayed, if the total count is 0
+     */
+    @Input()
+    displayIfEmpty:boolean=true;
+    /**
+     * Sets the translation key, for the text to display, if 
displayIfEmpty=false and the total count is 0.
+     */
+    @Input()
+    displayKeyIfEmpty:string='form.emptyContent';
+
+    /**
+     * If set to true, all controls are displayed, even if there is only one 
page to display.
+     * Otherwise the controls are not displayed, if there is only a single 
page of results.
+     */
+    @Input()
+    displayControlsIfSinglePage:boolean=true;
+
+
+
+    /**
      * The current page that is selected
      */
     page = 1;
@@ -124,6 +144,13 @@ export class PaginatedEntitiesComponent<T> implements 
OnInit, FieldToggle, After
      */
     items$: Observable<LoadingValue<PagedResult<T>>>;
 
+    /**
+     * true, if the current page result value represents a result with 
multiple pages,
+     * otherwise false.
+     */
+    multiplePages$:Observable<boolean>;
+
+
     private pageStream: Subject<number> = new Subject<number>();
     private searchTermStream: Subject<string> = new Subject<string>();
 
@@ -153,6 +180,7 @@ export class PaginatedEntitiesComponent<T> implements 
OnInit, FieldToggle, After
             ), share());
         this.total$ = 
source.pipe(filter(val=>val.hasValue()),map(val=>val.value),pluck('pagination', 
'total_count'));
         this.items$ = source;
+        this.multiplePages$ = source.pipe(filter(val => val.hasValue()), 
map(val => val.value.pagination.total_count >= val.value.pagination.limit));
     }
 
     search(terms: string) {
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts
index 0420576..76bd984 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/modules/shared/shared.module.ts
@@ -21,7 +21,13 @@ import {CommonModule} from '@angular/common';
 import {PaginatedEntitiesComponent} from 
"./paginated-entities/paginated-entities.component";
 import {SortedTableHeaderComponent} from 
"./sorted-table-header/sorted-table-header.component";
 import {SortedTableHeaderRowComponent} from 
"./sorted-table-header-row/sorted-table-header-row.component";
-import {NgbAccordionModule, NgbModalModule, NgbPaginationModule, 
NgbTooltipModule} from "@ng-bootstrap/ng-bootstrap";
+import {
+    NgbAccordionModule,
+    NgbModalModule,
+    NgbPaginationModule,
+    NgbTooltipModule,
+    NgbTypeaheadModule
+} from "@ng-bootstrap/ng-bootstrap";
 import {TranslateCompiler, TranslateLoader, TranslateModule} from 
"@ngx-translate/core";
 import {TranslateMessageFormatCompiler} from 
"ngx-translate-messageformat-compiler";
 import {HttpClient} from "@angular/common/http";
@@ -49,6 +55,7 @@ export { PageQuery } from './model/page-query';
         NgbTooltipModule,
         NgbAccordionModule,
         NgbModalModule,
+        NgbTypeaheadModule,
         PaginatedEntitiesComponent,
         SortedTableHeaderComponent,
         SortedTableHeaderRowComponent,
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts
index 99424a0..aeabc33 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/app/services/role.service.ts
@@ -25,6 +25,7 @@ import {HttpResponse} from "@angular/common/http";
 import {PagedResult} from "@app/model/paged-result";
 import {UserInfo} from "@app/model/user-info";
 import {RoleUpdate} from "@app/model/role-update";
+import { User } from '@app/model/user';
 
 @Injectable({
   providedIn: 'root'
@@ -61,6 +62,44 @@ export class RoleService {
     });
   }
 
+  public queryAssignedUsers(roleId: string,
+                            searchTerm: string, offset: number = 0, limit: 
number = 5,
+                            orderBy: string[] = ['id'], order: string = 
'asc'): Observable<PagedResult<User>> {
+    if (searchTerm == null) {
+      searchTerm = ""
+    }
+    if (orderBy == null || orderBy.length == 0) {
+      orderBy = ['id'];
+    }
+    return this.rest.executeRestCall<PagedResult<User>>("get", "redback", 
"roles/" + roleId + "/user", {
+      'q': searchTerm,
+      'offset': offset,
+      'limit': limit,
+      'orderBy': orderBy,
+      'order': order
+    });
+  }
+
+  public queryAssignedParentUsers(roleId: string,
+                            searchTerm: string, offset: number = 0, limit: 
number = 5,
+                            orderBy: string[] = ['id'], order: string = 'asc', 
parentsOnly:boolean=true): Observable<PagedResult<User>> {
+    if (searchTerm == null) {
+      searchTerm = ""
+    }
+    if (orderBy == null || orderBy.length == 0) {
+      orderBy = ['id'];
+    }
+    const recurseFlag = parentsOnly ? 'parentsOnly' : 'true';
+    return this.rest.executeRestCall<PagedResult<User>>("get", "redback", 
"roles/" + roleId + "/user", {
+      'recurse':recurseFlag,
+      'q': searchTerm,
+      'offset': offset,
+      'limit': limit,
+      'orderBy': orderBy,
+      'order': order
+    });
+  }
+
   public getRole(roleId:string) : Observable<Role> {
     return this.rest.executeRestCall("get", "redback", "roles/" + roleId, 
null);
   }
diff --git 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
index d094e39..297c9f2 100644
--- 
a/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
+++ 
b/archiva-modules/archiva-web/archiva-webapp/src/main/archiva-web/src/assets/i18n/en.json
@@ -136,7 +136,13 @@
       "parents": "Parents",
       "children": "Children",
       "permissions": "Permissions",
-      "users": "Users"
+      "users": "Users",
+      "usersInstance": "Users Assigned to this Role",
+      "usersParents": "Users Assigned to Parent Roles",
+      "noUsersAssigned": "There are no users assigned to this role",
+      "assignUserSearch": "Search and assign users to this role",
+      "userSearchFailed": "Sorry, could not load users",
+      "assignButton": "Assign"
     },
     "attributes": {
       "id": "Identifier",
@@ -172,7 +178,9 @@
       "no": "No",
       "save": "Save Changes"
     },
-    "edit": "Edit"
+    "edit": "Edit",
+    "emptyContent": "No values",
+    "searching": "Searching ..."
   },
   "headers": {
     "action": "Action"

Reply via email to