File

src/app/core/auth/role-form/role-form.component.ts

Implements

OnInit

Metadata

selector cd-role-form
styleUrls ./role-form.component.scss
templateUrl ./role-form.component.html

Index

Properties
Methods

Constructor

constructor(route: ActivatedRoute, router: Router, roleService: RoleService, scopeService: ScopeService, notificationService: NotificationService, i18n: I18n, actionLabels: ActionLabelsI18n)
Parameters :
Name Type Optional
route ActivatedRoute No
router Router No
roleService RoleService No
scopeService ScopeService No
notificationService NotificationService No
i18n I18n No
actionLabels ActionLabelsI18n No

Methods

createAction
createAction()
Returns : void
createForm
createForm()
Returns : void
editAction
editAction()
Returns : void
getRequest
getRequest()
Returns : RoleFormModel
initCreate
initCreate()
Returns : void
initEdit
initEdit()
Returns : void
isHeaderChecked
isHeaderChecked(property: string)

Checks if the specified header checkbox needs to be rendered as checked.

Parameters :
Name Type Optional Description
property string No

The property/permission (read, create, update, delete) to be checked. If 'scope' is given, all permissions are checked.

Returns : any

Returns true if specified property/permission is selected for all scopes, otherwise false.

isRowChecked
isRowChecked(scope: string)

Checks if the specified row checkbox needs to be rendered as checked.

Parameters :
Name Type Optional Description
scope string No

The scope to be checked, e.g. 'cephfs', 'grafana', 'osd', 'pool' ...

Returns : any

Returns true if all permissions (read, create, update, delete) are checked for the specified scope, otherwise false.

listenToChanges
listenToChanges()
Returns : void
ngOnInit
ngOnInit()
Returns : void
onClickCellCheckbox
onClickCellCheckbox(scope: string, property: string, event: Event)
Parameters :
Name Type Optional Default value
scope string No
property string No
event Event No null
Returns : void
onClickHeaderCheckbox
onClickHeaderCheckbox(property: "scope" | "read" | "create" | "update" | "delete", event: Event)
Parameters :
Name Type Optional
property "scope" | "read" | "create" | "update" | "delete" No
event Event No
Returns : void
submit
submit()
Returns : void

Properties

action
Type : string
Public actionLabels
Type : ActionLabelsI18n
cellPermissionCheckboxTpl
Type : TemplateRef<any>
Decorators :
@ViewChild('cellPermissionCheckboxTpl')
cellScopeCheckboxTpl
Type : TemplateRef<any>
Decorators :
@ViewChild('cellScopeCheckboxTpl')
columns
Type : CdTableColumn[]
headerPermissionCheckboxTpl
Type : TemplateRef<any>
Decorators :
@ViewChild('headerPermissionCheckboxTpl')
modalRef
Type : BsModalRef
mode
Type : RoleFormMode
resource
Type : string
response
Type : RoleFormModel
roleForm
Type : CdFormGroup
roleFormMode
Default value : RoleFormMode
scopes
Type : Array<string>
Default value : []
scopes_permissions
Type : Array<any>
Default value : []
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';

import { I18n } from '@ngx-translate/i18n-polyfill';
import * as _ from 'lodash';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { forkJoin as observableForkJoin } from 'rxjs';

import { RoleService } from '../../../shared/api/role.service';
import { ScopeService } from '../../../shared/api/scope.service';
import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
import { NotificationType } from '../../../shared/enum/notification-type.enum';
import { CdFormGroup } from '../../../shared/forms/cd-form-group';
import { CdValidators } from '../../../shared/forms/cd-validators';
import { CdTableColumn } from '../../../shared/models/cd-table-column';
import { NotificationService } from '../../../shared/services/notification.service';
import { RoleFormMode } from './role-form-mode.enum';
import { RoleFormModel } from './role-form.model';

@Component({
  selector: 'cd-role-form',
  templateUrl: './role-form.component.html',
  styleUrls: ['./role-form.component.scss']
})
export class RoleFormComponent implements OnInit {
  @ViewChild('headerPermissionCheckboxTpl')
  headerPermissionCheckboxTpl: TemplateRef<any>;
  @ViewChild('cellScopeCheckboxTpl')
  cellScopeCheckboxTpl: TemplateRef<any>;
  @ViewChild('cellPermissionCheckboxTpl')
  cellPermissionCheckboxTpl: TemplateRef<any>;

  modalRef: BsModalRef;

  roleForm: CdFormGroup;
  response: RoleFormModel;

  columns: CdTableColumn[];
  scopes: Array<string> = [];
  scopes_permissions: Array<any> = [];

  roleFormMode = RoleFormMode;
  mode: RoleFormMode;

  action: string;
  resource: string;

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private roleService: RoleService,
    private scopeService: ScopeService,
    private notificationService: NotificationService,
    private i18n: I18n,
    public actionLabels: ActionLabelsI18n
  ) {
    this.resource = this.i18n('role');
    this.createForm();
    this.listenToChanges();
  }

  createForm() {
    this.roleForm = new CdFormGroup({
      name: new FormControl('', {
        validators: [Validators.required],
        asyncValidators: [CdValidators.unique(this.roleService.exists, this.roleService)]
      }),
      description: new FormControl(''),
      scopes_permissions: new FormControl({})
    });
  }

  ngOnInit() {
    this.columns = [
      {
        prop: 'scope',
        name: this.i18n('All'),
        flexGrow: 2,
        cellTemplate: this.cellScopeCheckboxTpl,
        headerTemplate: this.headerPermissionCheckboxTpl
      },
      {
        prop: 'read',
        name: this.i18n('Read'),
        flexGrow: 1,
        cellClass: 'text-center',
        cellTemplate: this.cellPermissionCheckboxTpl,
        headerTemplate: this.headerPermissionCheckboxTpl
      },
      {
        prop: 'create',
        name: this.i18n('Create'),
        flexGrow: 1,
        cellClass: 'text-center',
        cellTemplate: this.cellPermissionCheckboxTpl,
        headerTemplate: this.headerPermissionCheckboxTpl
      },
      {
        prop: 'update',
        name: this.i18n('Update'),
        flexGrow: 1,
        cellClass: 'text-center',
        cellTemplate: this.cellPermissionCheckboxTpl,
        headerTemplate: this.headerPermissionCheckboxTpl
      },
      {
        prop: 'delete',
        name: this.i18n('Delete'),
        flexGrow: 1,
        cellClass: 'text-center',
        cellTemplate: this.cellPermissionCheckboxTpl,
        headerTemplate: this.headerPermissionCheckboxTpl
      }
    ];
    if (this.router.url.startsWith('/user-management/roles/edit')) {
      this.mode = this.roleFormMode.editing;
      this.action = this.actionLabels.EDIT;
    } else {
      this.action = this.actionLabels.CREATE;
    }
    if (this.mode === this.roleFormMode.editing) {
      this.initEdit();
    } else {
      this.initCreate();
    }
  }

  initCreate() {
    // Load the scopes and initialize the default scopes/permissions data.
    this.scopeService.list().subscribe((scopes: Array<string>) => {
      this.scopes = scopes;
      this.roleForm.get('scopes_permissions').setValue({});
    });
  }

  initEdit() {
    // Disable the 'Name' input field.
    this.roleForm.get('name').disable();
    // Load the scopes and the role data.
    this.route.params.subscribe((params: { name: string }) => {
      const observables = [];
      observables.push(this.scopeService.list());
      observables.push(this.roleService.get(params.name));
      observableForkJoin(observables).subscribe((resp: any[]) => {
        this.scopes = resp[0];
        ['name', 'description', 'scopes_permissions'].forEach((key) =>
          this.roleForm.get(key).setValue(resp[1][key])
        );
      });
    });
  }

  listenToChanges() {
    // Create/Update the data which is used by the data table to display the
    // scopes/permissions every time the form field value has been changed.
    this.roleForm.get('scopes_permissions').valueChanges.subscribe((value) => {
      const scopes_permissions = [];
      _.each(this.scopes, (scope) => {
        // Set the defaults values.
        const scope_permission = { read: false, create: false, update: false, delete: false };
        scope_permission['scope'] = scope;
        // Apply settings from the given value if they exist.
        if (scope in value) {
          _.each(value[scope], (permission) => {
            scope_permission[permission] = true;
          });
        }
        scopes_permissions.push(scope_permission);
      });
      this.scopes_permissions = scopes_permissions;
    });
  }

  /**
   * Checks if the specified row checkbox needs to be rendered as checked.
   * @param {string} scope The scope to be checked, e.g. 'cephfs', 'grafana',
   *   'osd', 'pool' ...
   * @return Returns true if all permissions (read, create, update, delete)
   *   are checked for the specified scope, otherwise false.
   */
  isRowChecked(scope: string) {
    const scope_permission = _.find(this.scopes_permissions, (o) => {
      return o['scope'] === scope;
    });
    if (_.isUndefined(scope_permission)) {
      return false;
    }
    return (
      scope_permission['read'] &&
      scope_permission['create'] &&
      scope_permission['update'] &&
      scope_permission['delete']
    );
  }

  /**
   * Checks if the specified header checkbox needs to be rendered as checked.
   * @param {string} property The property/permission (read, create,
   *   update, delete) to be checked. If 'scope' is given, all permissions
   *   are checked.
   * @return Returns true if specified property/permission is selected
   *   for all scopes, otherwise false.
   */
  isHeaderChecked(property: string) {
    let permissions = [property];
    if ('scope' === property) {
      permissions = ['read', 'create', 'update', 'delete'];
    }
    return permissions.every((permission) => {
      return this.scopes_permissions.every((scope_permission) => {
        return scope_permission[permission];
      });
    });
  }

  onClickCellCheckbox(scope: string, property: string, event: Event = null) {
    // Use a copy of the form field data to do not trigger the redrawing of the
    // data table with every change.
    const scopes_permissions = _.cloneDeep(this.roleForm.getValue('scopes_permissions'));
    let permissions = [property];
    if ('scope' === property) {
      permissions = ['read', 'create', 'update', 'delete'];
    }
    if (!(scope in scopes_permissions)) {
      scopes_permissions[scope] = [];
    }
    // Add or remove the given permission(s) depending on the click event or if no
    // click event is given then add/remove them if they are absent/exist.
    if (
      (event && event.target['checked']) ||
      !_.isEqual(permissions.sort(), _.intersection(scopes_permissions[scope], permissions).sort())
    ) {
      scopes_permissions[scope] = _.union(scopes_permissions[scope], permissions);
    } else {
      scopes_permissions[scope] = _.difference(scopes_permissions[scope], permissions);
      if (_.isEmpty(scopes_permissions[scope])) {
        _.unset(scopes_permissions, scope);
      }
    }
    this.roleForm.get('scopes_permissions').setValue(scopes_permissions);
  }

  onClickHeaderCheckbox(property: 'scope' | 'read' | 'create' | 'update' | 'delete', event: Event) {
    // Use a copy of the form field data to do not trigger the redrawing of the
    // data table with every change.
    const scopes_permissions = _.cloneDeep(this.roleForm.getValue('scopes_permissions'));
    let permissions = [property];
    if ('scope' === property) {
      permissions = ['read', 'create', 'update', 'delete'];
    }
    _.each(permissions, (permission) => {
      _.each(this.scopes, (scope) => {
        if (event.target['checked']) {
          scopes_permissions[scope] = _.union(scopes_permissions[scope], [permission]);
        } else {
          scopes_permissions[scope] = _.difference(scopes_permissions[scope], [permission]);
          if (_.isEmpty(scopes_permissions[scope])) {
            _.unset(scopes_permissions, scope);
          }
        }
      });
    });
    this.roleForm.get('scopes_permissions').setValue(scopes_permissions);
  }

  getRequest(): RoleFormModel {
    const roleFormModel = new RoleFormModel();
    ['name', 'description', 'scopes_permissions'].forEach(
      (key) => (roleFormModel[key] = this.roleForm.get(key).value)
    );
    return roleFormModel;
  }

  createAction() {
    const roleFormModel = this.getRequest();
    this.roleService.create(roleFormModel).subscribe(
      () => {
        this.notificationService.show(
          NotificationType.success,
          this.i18n(`Created role '{{role_name}}'`, { role_name: roleFormModel.name })
        );
        this.router.navigate(['/user-management/roles']);
      },
      () => {
        this.roleForm.setErrors({ cdSubmitButton: true });
      }
    );
  }

  editAction() {
    const roleFormModel = this.getRequest();
    this.roleService.update(roleFormModel).subscribe(
      () => {
        this.notificationService.show(
          NotificationType.success,
          this.i18n(`Updated role '{{role_name}}'`, { role_name: roleFormModel.name })
        );
        this.router.navigate(['/user-management/roles']);
      },
      () => {
        this.roleForm.setErrors({ cdSubmitButton: true });
      }
    );
  }

  submit() {
    if (this.mode === this.roleFormMode.editing) {
      this.editAction();
    } else {
      this.createAction();
    }
  }
}
<div class="col-sm-12 col-lg-6">
  <form name="roleForm"
        class="form-horizontal"
        #formDir="ngForm"
        [formGroup]="roleForm"
        novalidate>
    <div class="panel panel-default">
      <div class="panel-heading">
        <h3 i18n="form title|Example: Create Pool@@formTitle"
            class="panel-title">{{ action | titlecase }} {{ resource | upperFirst }}</h3>
      </div>
      <div class="panel-body">

        <!-- Name -->
        <div class="form-group"
             [ngClass]="{'has-error': roleForm.showError('name', formDir)}">
          <label class="control-label col-sm-3"
                 for="name">
            <ng-container i18n>Name</ng-container>
            <span class="required"
                  *ngIf="mode !== roleFormMode.editing"></span>
          </label>
          <div class="col-sm-9">
            <input class="form-control"
                   type="text"
                   i18n-placeholder
                   placeholder="Name..."
                   id="name"
                   name="name"
                   formControlName="name"
                   autofocus>
            <span class="help-block"
                  *ngIf="roleForm.showError('name', formDir, 'required')"
                  i18n>This field is required.</span>
            <span class="help-block"
                  *ngIf="roleForm.showError('name', formDir, 'notUnique')"
                  i18n>The chosen name is already in use.</span>
          </div>
        </div>

        <!-- Description -->
        <div class="form-group"
             [ngClass]="{'has-error': roleForm.showError('description', formDir)}">
          <label i18n
                 class="control-label col-sm-3"
                 for="description">Description</label>
          <div class="col-sm-9">
            <input class="form-control"
                   type="text"
                   i18n-placeholder
                   placeholder="Description..."
                   id="description"
                   name="description"
                   formControlName="description">
          </div>
        </div>

        <!-- Permissions -->
        <div class="form-group">
          <label i18n
                 class="control-label col-sm-3">Permissions</label>
          <div class="col-sm-9">
            <cd-table [data]="scopes_permissions"
                      [columns]="columns"
                      columnMode="flex"
                      [toolHeader]="false"
                      [autoReload]="false"
                      [autoSave]="false"
                      [footer]="false"
                      [limit]="0">
            </cd-table>
          </div>
        </div>

      </div>
      <div class="panel-footer">
        <div class="button-group text-right">
          <cd-submit-button
            [form]="formDir" (submitAction)="submit()"
            i18n="form action button|Example: Create Pool@@formActionButton"
            type="button">{{ action | titlecase }} {{ resource | upperFirst }}</cd-submit-button>
          <cd-back-button></cd-back-button>
        </div>
      </div>
    </div>
  </form>
</div>

<ng-template #cellScopeCheckboxTpl
             let-column="column"
             let-row="row"
             let-value="value">
  <div class="checkbox checkbox-primary">
    <input id="scope_{{ row.scope }}"
           type="checkbox"
           [checked]="isRowChecked(row.scope)"
           (change)="onClickCellCheckbox(row.scope, column.prop, $event)">
    <label class="datatable-permissions-scope-cell-label"
           for="scope_{{ row.scope }}">{{ value }}</label>
  </div>
</ng-template>

<ng-template #cellPermissionCheckboxTpl
             let-column="column"
             let-row="row"
             let-value="value">
  <div class="checkbox checkbox-primary">
    <input type="checkbox"
           [checked]="value"
           (change)="onClickCellCheckbox(row.scope, column.prop, $event)">
    <label></label>
  </div>
</ng-template>

<ng-template #headerPermissionCheckboxTpl
             let-column="column">
  <div class="checkbox checkbox-primary">
    <input id="header_{{ column.prop }}"
           type="checkbox"
           [checked]="isHeaderChecked(column.prop)"
           (change)="onClickHeaderCheckbox(column.prop, $event)">
    <label class="datatable-permissions-header-cell-label"
           for="header_{{ column.prop }}">{{ column.name }}</label>
  </div>
</ng-template>

./role-form.component.scss

.datatable-permissions-header-cell-label,
.datatable-permissions-scope-cell-label {
  font-weight: bold;
}
Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""