Skip to content

Configuring Flavors and Features

The yuuvis Shell supports two complementary server-side configuration mechanisms that extend the shell client without requiring code changes or redeployment: object flavor configuration and feature configuration.

Object flavor configuration (yuv:shell:types) makes additional Secondary Object Types (SOTs) available in the Shell UI. SOTs that are not registered by any client app — for example, types from a tenant-specific schema imported directly into the backend — can be registered here so users can apply them to objects.

Feature configuration (yuv:shell:features) controls which apps and extensions are visible and accessible to which users. Instead of building separate client versions for different roles or tenants, a single client can serve all users while the feature configuration determines what each user sees.

Both configurations are stored as JSON files attached to system:item DMS objects. The Shell loads them during bootstrap and applies them throughout the session.

Tenant-Specific Object Types Without Code Enterprise deployments often include tenant-specific document schemas with custom object types. Without flavor configuration, these types would only be accessible if a client app explicitly registers them. Flavor configuration lets administrators make any SOT available in the Shell UI without touching the client code.

Role-Based App Visibility Without Separate Builds Different user groups — accounting, HR, administrators, external partners — typically need different sets of apps. Feature configuration lets a single deployed client show each user only the apps and extensions they are authorized to use, driven entirely by server-side configuration.

Runtime Adaptability Both configurations are loaded fresh at every bootstrap. Administrators can change what types are available or which roles can access which apps without any client release. The change takes effect the next time users open the Shell.

Separation of Administrative Concerns Client developers define the available apps and object types in code. Administrators control access and availability through DMS configuration. Neither side needs to coordinate a deployment to adjust the other’s domain.

Both configurations follow the same storage and retrieval pattern:

  1. A system:item DMS object with the secondary type system:configuration holds a JSON file as its content stream
  2. The object is identified by its system:name property (yuv:shell:types or yuv:shell:features)
  3. During bootstrap, the Shell searches for this object by name and fetches the JSON file
  4. The parsed configuration is applied before the app component renders

Each mechanism is independently opt-in: enable them individually via enableObjectTypeConfig and enableFeatureConfig in the bootstrap options.

Object flavor configuration is stored as a JSON file attached to a dedicated DMS object. When enabled, the Shell fetches this file during bootstrap and registers the declared flavors globally — no code changes or redeployment required.

A flavor is a Secondary Object Type (SOT) that users can apply to a DMS object to enrich it with additional metadata. Flavors appear in the object sidebar as applicable actions and as chips on already-enriched objects. They are normally registered by client apps at development time, but can also be provided through a server-side configuration file.

For a full description of the flavor concept, data model, and programmatic API, see Object Flavors.

Pass enableObjectTypeConfig: true to the bootstrap options in main.ts:

src/main.ts
import { bootstrapShellApplication } from '@yuuvis/client-shell-core';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapShellApplication(
AppComponent,
appConfig,
{
enableObjectTypeConfig: true
}
).catch((err) => console.error(err));

When this option is set, the Shell runs ShellService._initObjectTypeConfig() as an APP_INITIALIZER before rendering. If no yuv:shell:types object exists in the DMS, the initializer completes silently with no effect.

If the configuration object does not yet exist, create it with a POST request to the DMS objects endpoint. The request uses multipart form-data with two parts: data (the object metadata) and a file part containing the JSON configuration.

Create the DMS object:

POST https://<domain>/api/dms/objects
Content-Type: multipart/form-data

The data part:

data (multipart)
{
"objects": [
{
"properties": {
"system:name": { "value": "yuv:shell:types" },
"system:objectTypeId": { "value": "system:item" },
"system:secondaryObjectTypeIds": { "value": ["system:configuration"] },
"system:baseTypeId": { "value": "system:item" }
},
"contentStreams": [
{ "cid": "cid_types_config" }
]
}
]
}

The second part of the multipart body should use cid_types_config as the content ID and contain your JSON configuration file.

Once the object exists, update the configuration file using the DMS Patch API:

PATCH https://<domain>/api/dms/objects/{objectId}/contents/file

To find the object ID at any time:

SELECT * FROM system:item WHERE system:name = 'yuv:shell:types'
PropertyMandatoryTypeDefaultDescription
idyesstringTechnical name of the SOT to register (e.g. tenMyschema:myCase). Used for icon lookup and localization.
baseTypeyesstringThe base object type this SOT applies to: system:object (all), system:folder, or system:document.
instantApplynobooleanfalseIf true, the SOT is applied immediately without showing a metadata input dialog.
applicableToMimeTypesnostring[][""]MIME type patterns that restrict which documents show this flavor. An empty string "" matches all files.

A custom case management SOT that applies to folders only. Users select the flavor from the sidebar; a metadata dialog opens for them to fill in case details before the SOT is applied.

{
"id": "tenMytenantschema:myCase",
"baseType": "system:folder",
"instantApply": false,
"applicableToMimeTypes": [""]
}

A general document SOT that applies to all documents regardless of file type. Useful for classification types that are independent of content format.

{
"id": "tenMytenantschema:classifiedDocument",
"baseType": "system:document",
"instantApply": false,
"applicableToMimeTypes": ["*/*"]
}

An image-specific SOT that is applied immediately when the user selects it — no dialog is shown. Only appears for documents that contain an image file.

{
"id": "appMyapp:idPicture",
"baseType": "system:document",
"instantApply": true,
"applicableToMimeTypes": ["image/*"]
}

A realistic configuration combining multiple types:

yuv:shell:types configuration file
[
{
"id": "tenMytenantschema:myCase",
"baseType": "system:folder",
"instantApply": false,
"applicableToMimeTypes": [""]
},
{
"id": "tenMytenantschema:classifiedDocument",
"baseType": "system:document",
"instantApply": false,
"applicableToMimeTypes": ["*/*"]
},
{
"id": "appMyapp:idPicture",
"baseType": "system:document",
"instantApply": true,
"applicableToMimeTypes": ["image/*"]
}
]

Feature configuration controls which apps and extensions are visible to which users. Like flavor configuration, the rules are stored as a JSON file in the DMS and are applied at bootstrap — allowing administrators to adjust access without any client redeployment.

A feature entry defines the visibility and access rules for a single app or extension in the Shell. Each entry references the app by its technical ID and declares which user roles are allowed or denied access.

Feature configuration is a server-side complement to the Conditional App Activation mechanism. While conditional activation defines requirements and permissions in client code, feature configuration allows administrators to adjust app visibility dynamically — without touching the client application.

Pass enableFeatureConfig: true to the bootstrap options in main.ts:

src/main.ts
import { bootstrapShellApplication } from '@yuuvis/client-shell-core';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapShellApplication(
AppComponent,
appConfig,
{
enableFeatureConfig: true
}
).catch((err) => console.error(err));

Feature configuration is loaded before the app component renders. The Shell queries DMS for the yuv:shell:features object, parses the JSON, and injects the result into FeatureService via provideClientShellFeatures(). If no configuration object exists, all apps are treated as accessible.

Create the DMS object with a POST request if it does not yet exist:

POST https://<domain>/api/dms/objects
Content-Type: multipart/form-data

The data part:

data (multipart)
{
"objects": [
{
"properties": {
"system:name": { "value": "yuv:shell:features" },
"system:objectTypeId": { "value": "system:item" },
"system:secondaryObjectTypeIds": { "value": ["system:configuration"] },
"system:baseTypeId": { "value": "system:item" }
},
"contentStreams": [
{ "cid": "cid_features_config" }
]
}
]
}

Once the object exists, update the configuration file using the DMS Patch API:

PATCH https://<domain>/api/dms/objects/{objectId}/contents/file

To find the object ID:

SELECT * FROM system:item WHERE system:name = 'yuv:shell:features'
PropertyMandatoryTypeDefaultDescription
idyesstringTechnical app or extension ID as registered in the Shell client project.
extensionnobooleanfalseSet to true for extension entries. Regular apps omit this property or set it to false.
allowednostring[][]Role names or user IDs that may use this app. An empty array grants access to all users.
deniednostring[][]Role names or user IDs that are explicitly blocked. denied always overrides allowed.

For each app, FeatureService evaluates the following logic:

isAllowed = allowed.length === 0 OR user has at least one role in allowed
isDenied = user has at least one role in denied
app is accessible = isAllowed AND NOT isDenied

Key rules:

  • An empty allowed array means everyone is allowed — the app is not role-restricted
  • A non-empty allowed array means only users with a matching role can access the app
  • denied is always evaluated last and overrides allowed — a user with both an allowed and a denied role is blocked

The check is applied at three levels:

  1. Navigation — the app does not appear in the sidebar
  2. Route guardcanMatchFeature blocks direct URL access
  3. Extension initialization — extension modules are not initialized at all, saving resources

An empty allowed array combined with an empty denied array makes the app visible to everyone. This is useful to explicitly document that an app is intentionally open, or to add a denied exclusion later without restructuring the entry.

{
"id": "io.yuuvis.app.search",
"allowed": [],
"denied": []
}

Only users with the ACCOUNTING role see this app. All other users do not see it in navigation and cannot access its routes.

{
"id": "io.yuuvis.app.invoice",
"allowed": ["ACCOUNTING"],
"denied": []
}

Users with either YUUVIS_TENANT_ADMIN or SUPPORT can access the app.

{
"id": "io.yuuvis.app.admin",
"allowed": ["YUUVIS_TENANT_ADMIN", "SUPPORT"],
"denied": []
}

The inbox app is open to all users except those with the YUUVIS_NO_BPM role. This pattern is useful when you want to block a specific group without listing every other role in allowed.

{
"id": "io.yuuvis.app.inbox",
"allowed": [],
"denied": ["YUUVIS_NO_BPM"]
}

Extensions must be marked with "extension": true. Otherwise the entry is ignored for extensions.

{
"id": "io.yuuvis.app.invoice.extension",
"extension": true,
"allowed": ["ACCOUNTING"],
"denied": []
}

A realistic configuration covering a mix of apps and extensions:

yuv:shell:features configuration file
[
{
"id": "io.yuuvis.app.search",
"allowed": [],
"denied": []
},
{
"id": "io.yuuvis.app.inbox",
"allowed": [],
"denied": ["YUUVIS_NO_BPM"]
},
{
"id": "io.yuuvis.app.admin",
"allowed": ["YUUVIS_TENANT_ADMIN"],
"denied": []
},
{
"id": "io.yuuvis.app.invoice",
"allowed": ["ACCOUNTING"],
"denied": []
},
{
"id": "io.yuuvis.app.invoice.extension",
"extension": true,
"allowed": ["ACCOUNTING"],
"denied": []
},
{
"id": "io.yuuvis.app.ai.extension",
"extension": true,
"allowed": ["YUUVIS_APP_AI"],
"denied": []
}
]

Both configuration objects can be managed programmatically using ShellConfigService from @yuuvis/shell-core. The service provides a consistent API for creating, reading, updating, and deleting DMS-backed configuration objects.

import { inject } from '@angular/core';
import { ShellConfigService } from '@yuuvis/shell-core';
export class MyAdminComponent {
#configService = inject(ShellConfigService);
// Fetch the current types configuration
loadTypesConfig() {
this.#configService.get('yuv:shell:types').subscribe(config => {
console.log(config.data); // parsed JSON content
});
}
// Update the features configuration
saveFeatures(objectId: string, updatedFeatures: object[]) {
this.#configService.update(objectId, updatedFeatures).subscribe();
}
// Download the configuration as a file
exportConfig(objectId: string) {
this.#configService.download(objectId, 'features-config.json');
}
// Upload a new configuration file
importConfig(objectId: string, file: File) {
this.#configService.upload(objectId, file).subscribe();
}
// Delete the configuration object entirely
deleteConfig(objectId: string) {
this.#configService.delete(objectId).subscribe();
}
}
MethodDescription
get(name)Fetches the configuration object and its file content by system:name
create(name, data)Creates a new configuration object with the given name and JSON data
update(id, data)Replaces the file content of an existing configuration object
delete(id)Deletes the configuration object from DMS
download(id, filename?)Triggers a file download of the configuration
upload(id, file)Uploads a new file to replace the current configuration