How to remove or disable the components under left drawer(side menu) in custom cockpit application

Product/components used and version/fix level are you on:

Cumulocity Production

Detailed explanation of the problem:

We have created a cockpit application using c8ycli to create our own custom widgets. I would like to disable or remove some of the predefined components such as Data explorer, Reports and Configuration is which is rendering as part of the application under left drawer(side menu) which is not required to show in our use case. do we have any configuration under c8y object in package.json?
“c8y”: {
“application”: {

}, cli:{}
}

Regards
Mohan

Hi Mohan,

The cockpit application you’ve created consists of certain Angular (app.module.ts) and AngularJS (ng1.ts) modules.
By removing/commenting certain modules, you remove specific parts of the application.

I would always recommend to comment these lines out, as it might make the upgrade process to a newer WebSDK version a lot easier, as you do not have to struggle with finding out which modules were removed by you or added by the WebSDK.

To remove the Data explorer you would e.g. comment out the import '@c8y/ng1-modules/cockpit-dataPointExplorerUI/cumulocity.json'; line in the ng1.ts file.
While for the Reports, you would comment out the ReportDashboardModule from the app.module.ts file.

The Configuration section of the navigation consists of multiple modules, depending on the version you are using these are part of the angular or angularJS modules.
It is usually pretty simple to identify these modules by going through the imports of both files.

Regards,
Tristan

Thanks Tristan, That is working. Can we able to manage those components by user role ? For eg: For ADMIN_ROLE need to enable for READER_ROLE need to disable. will it be possible ? instead of removing entirely from the application.

Regards
Mohan

Hi Mohan,

you can keep the modules and instead just filter out the nodes in the navigation.
Note that a users who know the right URL may still be able to access these views.

You can override the NavigatorService with a custom implementation, by defining the following service:

import { Injectable, Injector } from '@angular/core';
import { Router } from '@angular/router';
import { AppStateService, NavigatorService, Permissions } from '@c8y/ngx-components';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class CustomNaivgationService extends NavigatorService {

    constructor(rootInjector: Injector, router: Router, appState: AppStateService, permissions: Permissions) {
        super(rootInjector, router);
        this.items$ = combineLatest([this.items$, appState.currentUser]).pipe(map(([items, user]) => {
            if (!user) {
                return [];
            }

            if (permissions.hasRole('ROLE_TENANT_ADMIN')) {
                return items;
            }

            const filteredItems = items.filter(item => item.label !== 'Data explorer');
            return filteredItems;
        }))
    }

}

And provide it in your AppModule in the providers array:

@NgModule({
  imports: [
    // Upgrade module must be the first
    UpgradeModule,
    BrowserAnimationsModule,
    RouterModule.forRoot(),
    NgRouterModule.forRoot([...UPGRADE_ROUTES], { enableTracing: false, useHash: true }),
    CoreModule.forRoot(),
    ReportsModule,
    NgUpgradeModule,
    AssetsNavigatorModule,
    DashboardUpgradeModule,
    CockpitDashboardModule,
    SensorPhoneModule,
    ReportDashboardModule,
    BinaryFileDownloadModule,
    SearchModule,
    SubAssetsModule,
    ChildDevicesModule,
    CockpitConfigModule,
    DatapointLibraryModule.forRoot(),
    WidgetsModule,
  ],
  providers: [
    {
      provide: NavigatorService,
      useClass: CustomNaivgationService
    }
  ]
})
export class AppModule extends HybridAppModule {
  constructor(protected upgrade: NgUpgradeModule) {
    super();
  }
}

This sample CustomNaivgationService filters out the Data explorer navigator node in case the user does not have the ROLE_TENANT_ADMIN permission.

Regards,
Tristan

1 Like

Tristan,

Here where are we checking user roles ? below condition will get satisfy all the times. because we are checking application has this role or not, not the user roles right ? because of that it will not go to filteredItems line.

 if (permissions.hasRole('ROLE_TENANT_ADMIN')) 
    {
                return items;
    }

For this I just modified the condition as below. I am checking user role as well here, If both the conditions get satisfy then I am returning items.

  if (permissions.hasRole('ROLE_TENANT_ADMIN') && user.roles.references.map((val: any) => val.role.name).includes('ROLE_TENANT_ADMIN'))
      {
                return items;
      }

But for user this ROLE_TENANT_ADMIN role is not coming as part of user response even though he has admins role as global role. Always coming this below two roles

ROLE_USER_MANAGEMENT_OWN_READ ROLE_USER_MANAGEMENT_OWN_ADMIN

Do I need to specifically assign any other role to get ROLE_TENANT_ADMIN as part of user response ?

Regards
Mohan

Hi Mohan,

The ROLE_TENANT_ADMIN was just a sample. This specific role is just assigned to the admin of your tenant and you can not assign it to a user or group manually as far as I know.

The permissions.hasRole(...) will check the assigned roles of the user. Roles can either be directly assigned to the user (user.roles attribute) or via the groups the user belongs to (user.groups).
The permissions.hasRole(...) part will check both places.

Regards,
Tristan

1 Like

Okay ! I was looking roles of the user under global role level. I should check under groups of the user object to check which role he/she has. Thanks Tristan it worked.

One last question I have about side menu we are configuring the devices under groups. I would like to show my own custom icon before to the device and heading section of the device. For testing I tested like below with c8y-icon the same icon coming as app icon and device icon.

"icon": {
        "class": "c8y-icon-sub-tenants"
      }, 

image

image

Regards
Mohan

You can customize the icons for devices within the navigation by overriding the AssetNodeService.
This customized AssetNodeService (CustomAssetNodeService) will then create custom AssetNodes (CustomAssetNode).
The CustomAssetNode will set the iconComponent attribute to a different custom component (CustomAssetNodeIconComponent).
Within this component you can then define which icon you would like to display.

Here is some sample code which should work with version 1016 of the WebSDK (didn’t test newer or older versions, but it should work in a similar way):

import { Component, Inject, Injectable, Input, Optional } from "@angular/core";
import { IManagedObject, InventoryService, UserService } from "@c8y/client";
import {
  AlertService,
  AppStateService,
  BreadcrumbService,
  ModalService,
  NavigatorNodeData,
  OptionsService,
} from "@c8y/ngx-components";
import { ApiService } from "@c8y/ngx-components/api";
import {
  ASSET_NAVIGATOR_CONFIG,
  AssetNavigatorConfig,
  AssetNode,
  AssetNodeService,
  DeviceGroupService,
} from "@c8y/ngx-components/assets-navigator";

@Component({
  selector: 'custom-asset-node-icon',
  template: '<i class="icon" c8yIcon="c8y-atom"></i>',
})
export class CustomAssetNodeIconComponent {
    // in case you want to adjust the icon depending on the device..
    device: IManagedObject;
    @Input('mo') set node(value: AssetNode) {
        this.device = value.mo;
    }
}

class CustomAssetNode extends AssetNode {
    constructor(service: AssetNodeService, config?: NavigatorNodeData) {
        super(service, config);
        if (this.iconComponent !== undefined) {
            this.iconComponent = CustomAssetNodeIconComponent;
        }
    }
}

@Injectable()
export class CustomAssetNodeService extends AssetNodeService {
  constructor(
    inventory: InventoryService,
    apiService: ApiService,
    modal: ModalService,
    alert: AlertService,
    breadcrumbService: BreadcrumbService,
    user: UserService,
    appState: AppStateService,
    optionsService: OptionsService,
    @Optional() @Inject(ASSET_NAVIGATOR_CONFIG) moduleConfig: AssetNavigatorConfig,
    deviceGroupService: DeviceGroupService
  ) {
    super(
      inventory,
      apiService,
      modal,
      alert,
      breadcrumbService,
      user,
      appState,
      optionsService,
      moduleConfig,
      deviceGroupService
    );
  }

  createAssetNode(config: Partial<AssetNode>): AssetNode {
    return new CustomAssetNode(this, config);
  }
}

You also need to add the CustomAssetNodeIconComponent and CustomAssetNodeService to e.g. your AppModule like this:

declarations: [
    CustomAssetNodeIconComponent
  ],
  providers: [
    {
      provide: AssetNodeService,
      useClass: CustomAssetNodeService
    }
  ]

Regards,
Tristan

1 Like