@yuuvis/client-core 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,11 +5,12 @@ import * as i1 from '@angular/common/http';
5
5
  import { HttpErrorResponse, HttpClient, HttpHeaders, HttpRequest, HttpParams, HttpResponse, HttpEventType, provideHttpClient, withInterceptors } from '@angular/common/http';
6
6
  import * as i0 from '@angular/core';
7
7
  import { inject, Injectable, InjectionToken, NgZone, DOCUMENT, Inject, signal, Directive, Pipe, provideEnvironmentInitializer, makeEnvironmentProviders, provideAppInitializer } from '@angular/core';
8
- import { tap, finalize, shareReplay, catchError, filter, map, switchMap, first, scan, delay } from 'rxjs/operators';
9
- import { EMPTY, Subject, of, forkJoin, tap as tap$1, catchError as catchError$1, Observable, ReplaySubject, BehaviorSubject, map as map$1, fromEvent, throwError, switchMap as switchMap$1, merge, filter as filter$1, debounceTime, isObservable } from 'rxjs';
8
+ import { tap, finalize, shareReplay, catchError, map, switchMap, first, filter, scan, delay } from 'rxjs/operators';
9
+ import { EMPTY, Subject, of, forkJoin, Observable, tap as tap$1, catchError as catchError$1, ReplaySubject, BehaviorSubject, map as map$1, throwError, switchMap as switchMap$1, merge, fromEvent, filter as filter$1, debounceTime, isObservable } from 'rxjs';
10
10
  import { StorageMap } from '@ngx-pwa/local-storage';
11
11
  import { __decorate, __param, __metadata } from 'tslib';
12
12
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
13
+ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
13
14
  import { registerLocaleData, DecimalPipe, PercentPipe, CurrencyPipe } from '@angular/common';
14
15
  import localeAr from '@angular/common/locales/ar';
15
16
  import localeBn from '@angular/common/locales/bn';
@@ -59,7 +60,6 @@ import localeTr from '@angular/common/locales/tr';
59
60
  import localeUk from '@angular/common/locales/uk';
60
61
  import localeVi from '@angular/common/locales/vi';
61
62
  import localeZh from '@angular/common/locales/zh';
62
- import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
63
63
  import { DeviceDetectorService } from 'ngx-device-detector';
64
64
  import { FormGroup, FormControl } from '@angular/forms';
65
65
  import { MatTabGroup } from '@angular/material/tabs';
@@ -404,6 +404,28 @@ class YuvUser {
404
404
  }
405
405
  }
406
406
 
407
+ var AuditAction;
408
+ (function (AuditAction) {
409
+ AuditAction["CREATE_METADATA"] = "100";
410
+ AuditAction["CREATE_METADATA_WITH_CONTENT"] = "101";
411
+ AuditAction["CREATE_TAG"] = "110";
412
+ AuditAction["DELETE"] = "200";
413
+ AuditAction["DELETE_CONTENT"] = "201";
414
+ AuditAction["DELETE_MARKED"] = "202";
415
+ AuditAction["DELETE_TAG"] = "210";
416
+ AuditAction["UPDATE_METADATA"] = "300";
417
+ AuditAction["UPDATE_CONTENT"] = "301";
418
+ AuditAction["UPDATE_METADATA_WITH_CONTENT"] = "302";
419
+ AuditAction["UPDATE_MOVE_CONTENT"] = "303";
420
+ AuditAction["UPDATE_TAG"] = "310";
421
+ AuditAction["OBJECT_RESTORED_FROM_VERSION"] = "325";
422
+ AuditAction["GET_CONTENT"] = "400";
423
+ AuditAction["GET_METADATA"] = "401";
424
+ AuditAction["GET_RENDITION_TEXT"] = "402";
425
+ AuditAction["GET_RENDITION_PDF"] = "403";
426
+ AuditAction["GET_RENDITION_THUMBNAIL"] = "404";
427
+ })(AuditAction || (AuditAction = {}));
428
+
407
429
  var Sort;
408
430
  (function (Sort) {
409
431
  Sort["ASC"] = "asc";
@@ -1262,111 +1284,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
1262
1284
  args: [{ providedIn: 'root' }]
1263
1285
  }] });
1264
1286
 
1265
- var Direction;
1266
- (function (Direction) {
1267
- Direction["LTR"] = "ltr";
1268
- Direction["RTL"] = "rtl";
1269
- })(Direction || (Direction = {}));
1270
-
1271
- const CUSTOM_EVENTS = new InjectionToken('CUSTOM_EVENTS', { factory: () => [] });
1272
- const CUSTOM_EVENTS_TRUSTED_ORIGINS = new InjectionToken('CUSTOM_EVENTS_TRUSTED_ORIGINS', { factory: () => [] });
1273
-
1274
- /**
1275
- * Mandatory Custom event prefix for all custom YUV events
1276
- */
1277
- const CUSTOM_YUV_EVENT_PREFIX = 'yuv.';
1287
+ var Operator;
1288
+ (function (Operator) {
1289
+ Operator["EQUAL"] = "eq";
1290
+ Operator["EEQUAL"] = "eeq";
1291
+ Operator["IN"] = "in";
1292
+ Operator["GREATER_THAN"] = "gt";
1293
+ Operator["GREATER_OR_EQUAL"] = "gte";
1294
+ Operator["LESS_THAN"] = "lt";
1295
+ Operator["LESS_OR_EQUAL"] = "lte";
1296
+ Operator["INTERVAL"] = "gtlt";
1297
+ Operator["INTERVAL_INCLUDE_BOTH"] = "gtelte";
1298
+ Operator["INTERVAL_INCLUDE_TO"] = "gtlte";
1299
+ Operator["INTERVAL_INCLUDE_FROM"] = "gtelt";
1300
+ Operator["RANGE"] = "rg";
1301
+ Operator["LIKE"] = "like";
1302
+ Operator["CONTAINS"] = "contains"; // contains
1303
+ })(Operator || (Operator = {}));
1304
+ const OperatorLabel = {
1305
+ /** equal */
1306
+ EQUAL: '=',
1307
+ /** exact equal */
1308
+ EEQUAL: '==',
1309
+ /** match at least one of the provided values (value has to be an array) */
1310
+ IN: '~',
1311
+ /** greater than */
1312
+ GREATER_THAN: '>',
1313
+ /** greater than or equal */
1314
+ GREATER_OR_EQUAL: '≽', //
1315
+ LESS_THAN: '<', // less than
1316
+ LESS_OR_EQUAL: '≼', // less than or equal
1317
+ INTERVAL: '<>', // interval
1318
+ INTERVAL_INCLUDE_BOTH: '-', // interval include left and right
1319
+ INTERVAL_INCLUDE_TO: '>-', // interval include right
1320
+ INTERVAL_INCLUDE_FROM: '-<', // interval include left
1321
+ RANGE: '=', // aggegation ranges
1322
+ LIKE: '~', // like
1323
+ CONTAINS: '~' // contains
1324
+ };
1278
1325
 
1279
1326
  /**
1280
- * Service for providing triggered events
1327
+ * Service for saving or caching data on the users device. It uses the most efficient storage
1328
+ * available (IndexDB, localstorage, ...) on the device. Depending on the type of storage used,
1329
+ * its limitations apply.
1281
1330
  */
1282
- class EventService {
1283
- #customEvents;
1284
- #customEventsTrustedOrigins;
1285
- #ngZone;
1286
- #eventSource;
1287
- constructor() {
1288
- this.#customEvents = inject(CUSTOM_EVENTS);
1289
- this.#customEventsTrustedOrigins = inject(CUSTOM_EVENTS_TRUSTED_ORIGINS);
1290
- this.#ngZone = inject(NgZone);
1291
- this.#eventSource = new Subject();
1292
- this.event$ = this.#eventSource.asObservable();
1293
- this.#listenToWindowEvents();
1294
- }
1295
- #startsWithAllowed(value) {
1296
- return typeof value === 'string' && this.#customEvents.some((prefix) => value.startsWith(prefix));
1297
- }
1298
- #isValidExternalMessage(event) {
1299
- this.#customEvents.push(CUSTOM_YUV_EVENT_PREFIX);
1300
- if (!event.data || typeof event.data !== 'object')
1301
- return false;
1302
- if (!(this.#startsWithAllowed(event.data.source) || this.#startsWithAllowed(event.data.type)))
1303
- return false;
1304
- // Accept messages from trusted origins
1305
- this.#customEventsTrustedOrigins.push(window.location.origin);
1306
- const isFromTrustedOrigin = this.#customEventsTrustedOrigins.includes(event.origin);
1307
- const hasValidStructure = event.data && (event.data.type || event.data.eventType) && typeof event.data === 'object';
1308
- return isFromTrustedOrigin && hasValidStructure;
1331
+ class AppCacheService {
1332
+ #storage = inject(StorageMap);
1333
+ /**
1334
+ * Set item in storage.
1335
+ * @param key The key under which the value is stored.
1336
+ * @param value The value to store.
1337
+ * @returns An Observable that emits true when the operation is successful.
1338
+ */
1339
+ setItem(key, value) {
1340
+ return this.#storage.set(key, value).pipe(map(() => true));
1309
1341
  }
1310
- #listenToWindowEvents() {
1311
- window.addEventListener('message', (event) => {
1312
- this.#ngZone.run(() => this.#isValidExternalMessage(event) && this.trigger(event.data.type, event.data.data));
1313
- });
1342
+ /**
1343
+ * Get item from storage.
1344
+ * @param key The key of the item to retrieve.
1345
+ * @returns An Observable that emits the retrieved value.
1346
+ */
1347
+ getItem(key) {
1348
+ return this.#storage.get(key);
1314
1349
  }
1315
1350
  /**
1316
- * Triggers a postMessage event that will be sent to the yuuvis global event Trigger
1317
- * @param type Type/key of the event
1318
- * @param data Data to be sent along with the event
1351
+ * Remove item from storage.
1352
+ * @param key The key of the item to remove.
1353
+ * @returns An Observable that emits true when the operation is successful.
1319
1354
  */
1320
- triggerPostMessageEvent(type, data) {
1321
- const targetOrigin = window.location.origin;
1322
- const payload = {
1323
- type,
1324
- data,
1325
- timestamp: Date.now()
1326
- };
1327
- window.postMessage(payload, targetOrigin);
1355
+ removeItem(key) {
1356
+ return this.#storage.delete(key).pipe(map(() => true));
1328
1357
  }
1329
1358
  /**
1330
- * Trigger an global event
1331
- * @param type Type/key of the event
1332
- * @param data Data to be send along with the event
1359
+ * Clear storage, optionally filtered by a function.
1360
+ * @param filter A function to filter which keys to clear.
1361
+ * @returns An Observable that emits true when the operation is successful.
1333
1362
  */
1334
- trigger(type, ...args) {
1335
- this.#eventSource.next({ type: type, data: args[0] });
1363
+ clear(filter) {
1364
+ return filter
1365
+ ? this.getStorageKeys().pipe(switchMap((keys) => {
1366
+ const list = keys.filter((k) => filter(k)).map((k) => this.removeItem(k));
1367
+ return list.length ? forkJoin(list).pipe(map(() => true)) : of(true);
1368
+ }))
1369
+ : this.#storage.clear().pipe(map(() => true));
1336
1370
  }
1337
1371
  /**
1338
- * Listen on a triggered event
1339
- * @param types Type/key of the event
1372
+ * Get all storage keys.
1373
+ * @returns An Observable that emits an array of all storage keys.
1340
1374
  */
1341
- on(...types) {
1342
- return this.event$.pipe(filter((event) => event && !!types.find((t) => t === event.type)));
1375
+ getStorageKeys() {
1376
+ return new Observable((observer) => {
1377
+ const keys = [];
1378
+ this.#storage.keys().subscribe({
1379
+ next: (key) => keys.push(key),
1380
+ complete: () => observer.next(keys)
1381
+ });
1382
+ }).pipe(first());
1343
1383
  }
1344
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1345
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EventService, providedIn: 'root' }); }
1384
+ setStorage(options) {
1385
+ return forkJoin(Object.keys(options || {}).map((k) => this.setItem(k, options[k])));
1386
+ }
1387
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AppCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1388
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AppCacheService, providedIn: 'root' }); }
1346
1389
  }
1347
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EventService, decorators: [{
1390
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AppCacheService, decorators: [{
1348
1391
  type: Injectable,
1349
1392
  args: [{
1350
1393
  providedIn: 'root'
1351
1394
  }]
1352
- }], ctorParameters: () => [] });
1353
-
1354
- /**
1355
- * Events emitted by parts of the application
1356
- */
1357
- var YuvEventType;
1358
- (function (YuvEventType) {
1359
- YuvEventType["LOGOUT"] = "yuv.user.logout";
1360
- YuvEventType["CLIENT_LOCALE_CHANGED"] = "yuv.user.locale.client.changed";
1361
- YuvEventType["DMS_OBJECT_LOADED"] = "yuv.dms.object.loaded";
1362
- YuvEventType["DMS_OBJECT_CREATED"] = "yuv.dms.object.created";
1363
- YuvEventType["DMS_OBJECT_DELETED"] = "yuv.dms.object.deleted";
1364
- YuvEventType["DMS_OBJECT_UPDATED"] = "yuv.dms.object.updated";
1365
- YuvEventType["DMS_OBJECT_CONTENT_UPDATED"] = "yuv.dms.object.content.updated";
1366
- YuvEventType["DMS_OBJECTS_MOVED"] = "yuv.dms.objects.moved";
1367
- YuvEventType["RELATIONSHIP_CREATED"] = "yuv.dms.relationship.created";
1368
- YuvEventType["RELATIONSHIP_DELETED"] = "yuv.dms.relationship.deleted";
1369
- })(YuvEventType || (YuvEventType = {}));
1395
+ }] });
1370
1396
 
1371
1397
  /**
1372
1398
  * Service managing localizations. The localizations are fetched
@@ -1397,8 +1423,9 @@ class LocalizationService {
1397
1423
  }));
1398
1424
  }
1399
1425
  getLocalizedResource(key) {
1426
+ // TODO: Fix return type to also include undefined
1400
1427
  const iso = this.#translate.getCurrentLang();
1401
- return { ...this.#i18n, ...this.#extension[iso] }[key] || key;
1428
+ return { ...this.#i18n, ...this.#extension[iso] }[key];
1402
1429
  }
1403
1430
  getLocalizedLabel(id) {
1404
1431
  return this.getLocalizedResource(`${id}_label`);
@@ -1432,77 +1459,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
1432
1459
  }]
1433
1460
  }] });
1434
1461
 
1435
- /**
1436
- * Service for saving or caching data on the users device. It uses the most efficient storage
1437
- * available (IndexDB, localstorage, ...) on the device. Depending on the type of storage used,
1438
- * its limitations apply.
1439
- */
1440
- class AppCacheService {
1441
- #storage = inject(StorageMap);
1442
- /**
1443
- * Set item in storage.
1444
- * @param key The key under which the value is stored.
1445
- * @param value The value to store.
1446
- * @returns An Observable that emits true when the operation is successful.
1447
- */
1448
- setItem(key, value) {
1449
- return this.#storage.set(key, value).pipe(map(() => true));
1450
- }
1451
- /**
1452
- * Get item from storage.
1453
- * @param key The key of the item to retrieve.
1454
- * @returns An Observable that emits the retrieved value.
1455
- */
1456
- getItem(key) {
1457
- return this.#storage.get(key);
1458
- }
1459
- /**
1460
- * Remove item from storage.
1461
- * @param key The key of the item to remove.
1462
- * @returns An Observable that emits true when the operation is successful.
1463
- */
1464
- removeItem(key) {
1465
- return this.#storage.delete(key).pipe(map(() => true));
1466
- }
1467
- /**
1468
- * Clear storage, optionally filtered by a function.
1469
- * @param filter A function to filter which keys to clear.
1470
- * @returns An Observable that emits true when the operation is successful.
1471
- */
1472
- clear(filter) {
1473
- return filter
1474
- ? this.getStorageKeys().pipe(switchMap((keys) => {
1475
- const list = keys.filter((k) => filter(k)).map((k) => this.removeItem(k));
1476
- return list.length ? forkJoin(list).pipe(map(() => true)) : of(true);
1477
- }))
1478
- : this.#storage.clear().pipe(map(() => true));
1479
- }
1480
- /**
1481
- * Get all storage keys.
1482
- * @returns An Observable that emits an array of all storage keys.
1483
- */
1484
- getStorageKeys() {
1485
- return new Observable((observer) => {
1486
- const keys = [];
1487
- this.#storage.keys().subscribe({
1488
- next: (key) => keys.push(key),
1489
- complete: () => observer.next(keys)
1490
- });
1491
- }).pipe(first());
1492
- }
1493
- setStorage(options) {
1494
- return forkJoin(Object.keys(options || {}).map((k) => this.setItem(k, options[k])));
1495
- }
1496
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AppCacheService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1497
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AppCacheService, providedIn: 'root' }); }
1498
- }
1499
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AppCacheService, decorators: [{
1500
- type: Injectable,
1501
- args: [{
1502
- providedIn: 'root'
1503
- }]
1504
- }] });
1505
-
1506
1462
  /**
1507
1463
  * Providing system definitions.
1508
1464
  */
@@ -1714,7 +1670,13 @@ class SystemService {
1714
1670
  * @deprecated use LocalizationService.getLocalizedResource instead
1715
1671
  */
1716
1672
  getLocalizedResource(key) {
1717
- return this.#localization.getLocalizedResource(key);
1673
+ try {
1674
+ // return this.system!.i18n[key];
1675
+ return this.#localization.getLocalizedResource(key);
1676
+ }
1677
+ catch (error) {
1678
+ return key;
1679
+ }
1718
1680
  }
1719
1681
  /**
1720
1682
  * @deprecated use LocalizationService.getLocalizedResource instead
@@ -2139,1268 +2101,1182 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
2139
2101
  }]
2140
2102
  }] });
2141
2103
 
2142
- /**
2143
- * Service providing user account configurations.
2144
- */
2145
- class UserService {
2146
- constructor() {
2147
- this.#backend = inject(BackendService);
2148
- this.#translate = inject(TranslateService);
2149
- this.#logger = inject(Logger);
2150
- this.#system = inject(SystemService);
2151
- this.#localization = inject(LocalizationService);
2152
- this.#eventService = inject(EventService);
2153
- this.#config = inject(ConfigService);
2154
- this.#document = inject(DOCUMENT);
2155
- this.#USERS_SETTINGS = '/users/settings/';
2156
- this.#DEFAULT_SETTINGS = '/users/settings';
2157
- this.#SETTINGS_SECTION_OBJECTCONFIG = 'object-config';
2158
- this.#userSource = new BehaviorSubject(this.#user);
2159
- this.user$ = this.#userSource.asObservable();
2160
- this.globalSettings = new Map();
2161
- this.canCreateObjects = false;
2162
- }
2163
- #backend;
2164
- #translate;
2165
- #logger;
2166
- #system;
2167
- #localization;
2168
- #eventService;
2169
- #config;
2170
- #document;
2171
- #USERS_SETTINGS;
2172
- #DEFAULT_SETTINGS;
2173
- #SETTINGS_SECTION_OBJECTCONFIG;
2174
- #user;
2175
- #userSource;
2104
+ class SearchService {
2105
+ #backend = inject(BackendService);
2106
+ #system = inject(SystemService);
2107
+ static { this.DEFAULT_QUERY_SIZE = 50; }
2176
2108
  /**
2177
- * Set a new current user
2178
- * @param user The user to be set as current user
2109
+ * Execute a search query ans transform the result to a SearchResult object
2110
+ * @param query The search query
2111
+ * @returns Observable of a SearchResult
2179
2112
  */
2180
- setCurrentUser(user) {
2181
- this.#user = user;
2182
- this.changeClientLocale('', false);
2183
- this.#userSource.next(this.#user);
2184
- }
2185
- getCurrentUser() {
2186
- return this.#user;
2187
- }
2188
- get hasAdminRole() {
2189
- return this.#user?.authorities?.includes(AdministrationRoles.ADMIN) || false;
2190
- }
2191
- get hasSystemRole() {
2192
- return this.#user?.authorities?.includes(AdministrationRoles.SYSTEM) || false;
2193
- }
2194
- get hasAdministrationRoles() {
2195
- return this.hasAdminRole || this.hasSystemRole;
2196
- }
2197
- get hasManageSettingsRole() {
2198
- const customRole = this.#config.get('core.permissions.manageSettingsRole');
2199
- const manageSettingsRole = customRole || AdministrationRoles.MANAGE_SETTINGS;
2200
- return this.#user?.authorities?.includes(manageSettingsRole) || false;
2201
- }
2202
- get isAdvancedUser() {
2203
- const customRole = this.#config.get('core.permissions.advancedUserRole');
2204
- const advancedUserRole = customRole || AdministrationRoles.MANAGE_SETTINGS;
2205
- return this.#user?.authorities?.includes(advancedUserRole) || false;
2113
+ search(query) {
2114
+ return this.searchRaw(query).pipe(map((res) => this.toSearchResult(res, query.size || SearchService.DEFAULT_QUERY_SIZE, query.from || 0)));
2206
2115
  }
2207
- get isRetentionManager() {
2208
- const customRole = this.#config.get('core.permissions.retentionManagerRole');
2209
- const retenetionManagerRole = customRole || AdministrationRoles.MANAGE_SETTINGS;
2210
- return this.#user?.authorities?.includes(retenetionManagerRole) || false;
2116
+ /**
2117
+ * Execute a raw search query and return the result as is.
2118
+ * @param query The search query
2119
+ * @returns Observable of the raw search result
2120
+ */
2121
+ searchRaw(query) {
2122
+ if (!query.size)
2123
+ query.size = SearchService.DEFAULT_QUERY_SIZE;
2124
+ return this.#backend.post(`/dms/objects/search`, query);
2211
2125
  }
2212
2126
  /**
2213
- * Change the users client locale
2214
- * @param iso ISO locale string to be set as new client locale
2127
+ * Search for objects in the dms using CMIS like SQL syntax.
2128
+ * @param statement The query statement
2129
+ * @param size The number of items to return
2130
+ * @returns Observable of a SearchResult
2215
2131
  */
2216
- changeClientLocale(iso, persist = true) {
2217
- if (this.#user) {
2218
- const languages = this.#config.getClientLocales().map((lang) => lang.iso);
2219
- iso = iso || this.#user.getClientLocale(this.#config.getDefaultClientLocale());
2220
- if (!languages.includes(iso)) {
2221
- iso = this.#config.getDefaultClientLocale();
2222
- }
2223
- this.#logger.debug("Changed client locale to '" + iso + "'");
2224
- this.#backend.setHeader('Accept-Language', iso);
2225
- this.#user.uiDirection = this.#getUiDirection(iso);
2226
- this.#document.body.dir = this.#user.uiDirection;
2227
- this.#document.documentElement.lang = iso;
2228
- this.#user.userSettings.locale = iso;
2229
- if (this.#translate.getCurrentLang() !== iso || this.#system.authData?.language !== iso) {
2230
- const obs = persist
2231
- ? forkJoin([
2232
- this.#translate.use(iso),
2233
- this.#system
2234
- .updateAuthData({ language: iso })
2235
- .pipe(switchMap(() => this.#localization.fetchLocalizations())),
2236
- this.saveUserSettings(this.#user.userSettings).pipe(tap(() => {
2237
- this.#logger.debug('Loading system definitions i18n resources for new locale.');
2238
- }))
2239
- ])
2240
- : this.#translate.use(iso);
2241
- obs.subscribe(() => this.#eventService.trigger(YuvEventType.CLIENT_LOCALE_CHANGED, iso));
2242
- }
2243
- }
2132
+ searchCmis(statement, size = SearchService.DEFAULT_QUERY_SIZE, includePermissions = false) {
2133
+ return this.#executeCmisSearch(statement, size, 0, includePermissions);
2244
2134
  }
2245
- saveUserSettings(settings) {
2246
- if (this.#user) {
2247
- //console.log(this.#user.userSettings);
2248
- this.#user.userSettings = { ...this.#user.userSettings, ...settings };
2249
- return this.#backend
2250
- .post(this.#DEFAULT_SETTINGS, this.#user.userSettings)
2251
- .pipe(tap(() => this.#userSource.next(this.#user)));
2135
+ #executeCmisSearch(statement, maxItems, skipCount = 0, includePermissions = false) {
2136
+ const payload = {
2137
+ query: {
2138
+ statement,
2139
+ skipCount,
2140
+ maxItems,
2141
+ handleDeletedDocuments: 'DELETED_DOCUMENTS_EXCLUDE'
2142
+ }
2143
+ };
2144
+ if (includePermissions) {
2145
+ payload.query.includePermissions = true;
2252
2146
  }
2253
- else
2254
- return of(null);
2255
- }
2256
- fetchUserSettings() {
2257
- return this.#backend.get('/dms/permissions').pipe(catchError(() => of(undefined)), switchMap((res) => {
2258
- this.#setUserPermissions(res);
2259
- return this.#backend.get(this.#DEFAULT_SETTINGS);
2260
- }));
2147
+ return this.#backend
2148
+ .post('/dms/objects/cmisSearch', payload
2149
+ // Using API-WEB because it enriches the response with additional information (resolved user names, etc.)
2150
+ // ApiBase.core
2151
+ )
2152
+ .pipe(map((res) => this.toSearchResult(res, maxItems, skipCount)));
2261
2153
  }
2262
2154
  /**
2263
- * Search for a user based on a search term
2264
- * @param term Search term
2265
- * @param excludeMe whether or not to exclude myself from the result list
2266
- * @param roles narrow down the search results by certain roles
2155
+ * Fetch aggragations for a given query.
2156
+ * @param q The query
2157
+ * @param aggregations List of aggregations to be fetched (e.g. `enaio:objectTypeId`
2158
+ * to get an aggregation of object types)
2267
2159
  */
2268
- queryUser(term, excludeMe, roles) {
2269
- let params = new HttpParams().set('search', term).set('excludeMe', `${!!excludeMe}`);
2270
- roles?.length && roles.map((r) => (params = params.append(`roles`, r)));
2271
- return this.#backend
2272
- .get(`/idm/users?${params}`)
2273
- .pipe(map((users) => (!users ? [] : users.map((u) => new YuvUser(u)))));
2274
- }
2275
- getUserById(id) {
2276
- return this.#backend.get(`/idm/users/${id}`).pipe(map((user) => new YuvUser(user, this.#user.userSettings)));
2277
- }
2278
- logout(redirRoute) {
2279
- const redir = redirRoute ? `?redir=${redirRoute}` : '';
2280
- window.location.href = `${this.#backend.getApiBase('logout')}${redir}`;
2281
- }
2282
- getSettings(section) {
2283
- return this.#user ? this.#backend.get(this.#USERS_SETTINGS + encodeURIComponent(section)) : of(null);
2284
- }
2285
- saveObjectConfig(objectConfigs) {
2286
- return this.#backend.post(this.#USERS_SETTINGS + encodeURIComponent(this.#SETTINGS_SECTION_OBJECTCONFIG), objectConfigs);
2287
- }
2288
- loadObjectConfig() {
2289
- return this.getSettings(this.#SETTINGS_SECTION_OBJECTCONFIG).pipe(catchError(() => of(undefined)));
2290
- }
2291
- #getUiDirection(iso) {
2292
- // languages that are read right to left
2293
- const rtlLanguages = ['ar', 'arc', 'dv', 'fa', 'ha', 'he', 'khw', 'ks', 'ku', 'ps', 'ur', 'yi'];
2294
- return !rtlLanguages.includes(iso) ? Direction.LTR : Direction.RTL;
2295
- }
2296
- #setUserPermissions(res) {
2297
- this.userPermissions = {
2298
- create: this.#mapPermissions('CREATE', res),
2299
- write: this.#mapPermissions('WRITE', res),
2300
- read: this.#mapPermissions('READ', res),
2301
- delete: this.#mapPermissions('DELETE', res)
2302
- };
2303
- const permissions = {
2304
- createableObjectTypes: [
2305
- ...this.userPermissions.create.folderTypes,
2306
- ...this.userPermissions.create.objectTypes,
2307
- ...this.userPermissions.create.secondaryObjectTypes
2308
- ],
2309
- searchableObjectTypes: [
2310
- ...this.userPermissions.read.folderTypes,
2311
- ...this.userPermissions.read.objectTypes,
2312
- ...this.userPermissions.read.secondaryObjectTypes
2313
- ]
2314
- };
2315
- this.#system.setPermissions(permissions);
2316
- this.canCreateObjects = permissions.createableObjectTypes.length > 0;
2160
+ aggregate(q, aggregations) {
2161
+ q.aggs = aggregations;
2162
+ return this.searchRaw(q).pipe(map((res) => this.#toAggregateResult(res, aggregations)));
2317
2163
  }
2318
- #mapPermissions(section, apiResponse) {
2319
- const res = apiResponse[section] || {};
2164
+ /**
2165
+ * Map search result from the backend to applications AggregateResult object
2166
+ * @param searchResponse The backend response
2167
+ * @param aggregations The aggregations to be fetched
2168
+ */
2169
+ #toAggregateResult(searchResponse, aggregations) {
2170
+ const agg = [];
2171
+ if (aggregations) {
2172
+ aggregations.forEach((a) => {
2173
+ const ag = {
2174
+ aggKey: a,
2175
+ entries: searchResponse.objects.map((o) => ({
2176
+ key: o.properties[a].value,
2177
+ count: o.properties['OBJECT_COUNT'].value
2178
+ }))
2179
+ };
2180
+ agg.push(ag);
2181
+ });
2182
+ }
2320
2183
  return {
2321
- folderTypes: res['folderTypeIds'] || [],
2322
- objectTypes: res['objectTypeIds'] || [],
2323
- secondaryObjectTypes: res['secondaryObjectTypeIds'] || []
2184
+ totalNumItems: searchResponse.totalNumItems,
2185
+ aggregations: agg
2324
2186
  };
2325
2187
  }
2326
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2327
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UserService, providedIn: 'root' }); }
2328
- }
2329
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UserService, decorators: [{
2330
- type: Injectable,
2331
- args: [{
2332
- providedIn: 'root'
2333
- }]
2334
- }] });
2335
-
2336
- /**
2337
- * @ignore
2338
- */
2339
- const CUSTOM_CONFIG = new InjectionToken('CUSTOM_CONFIG', {
2340
- factory: () => ({ main: ['assets/_yuuvis/config/main.json'], translations: ['assets/_yuuvis/i18n/'] })
2341
- });
2342
- const CORE_CONFIG = new InjectionToken('CORE_CONFIG');
2343
-
2344
- /**
2345
- * @ignore
2346
- */
2347
- let CoreConfig = class CoreConfig {
2348
- constructor(__config) {
2349
- this.main = ['assets/_yuuvis/config/main.json'];
2350
- this.translations = ['assets/_yuuvis/i18n/'];
2351
- this.environment = { production: true };
2352
- Object.assign(this, __config);
2353
- }
2354
- };
2355
- CoreConfig = __decorate([
2356
- __param(0, Inject(CUSTOM_CONFIG)),
2357
- __metadata("design:paramtypes", [CoreConfig])
2358
- ], CoreConfig);
2359
-
2360
- /**
2361
- * Service managing the configuarions for object types.
2362
- * Configuration means that you can pick certain fields
2363
- * of an object type and assign them to a config object.
2364
- */
2365
- class ObjectConfigService {
2366
- constructor() {
2367
- this.#system = inject(SystemService);
2368
- this.#user = inject(UserService);
2369
- this.OBJECT_CONFIG_STORAGE_KEY = 'yuv.framework.object-config';
2370
- this.#objectConfigsSource = new ReplaySubject();
2371
- this.#objectConfigs$ = this.#objectConfigsSource.asObservable();
2372
- this.#defaultObjectConfigs = {};
2373
- this.#registeredDefaultObjectConfigs = {
2374
- main: {},
2375
- buckets: []
2376
- };
2377
- this.#objectConfigs = {
2378
- main: {},
2379
- buckets: []
2380
- };
2381
- }
2382
- #system;
2383
- #user;
2384
- #objectConfigsSource;
2385
- #objectConfigs$;
2386
- #defaultObjectConfigs;
2387
- #registeredDefaultObjectConfigs;
2388
- #objectConfigs;
2389
- #isValidObjectConfig(soc) {
2390
- return coerceBooleanProperty(soc && Object.hasOwn(soc, 'main') && Object.hasOwn(soc, 'buckets'));
2391
- }
2392
- // called on core init (auth.service -> initApp)
2393
- init() {
2394
- return this.#user.loadObjectConfig().pipe(tap$1((res) => {
2395
- this.#getDefaultObjectConfig();
2396
- this.#objectConfigs = (this.#isValidObjectConfig(res) && res) || {
2397
- main: {},
2398
- buckets: []
2399
- };
2400
- this.#objectConfigsSource.next(this.#objectConfigs);
2401
- }));
2402
- }
2403
- getObjectConfigs$(bucket, includeDefaults) {
2404
- return this.#objectConfigs$.pipe(map$1((soc) => {
2405
- let ocr = soc.main;
2406
- const defaults = { ...this.#defaultObjectConfigs, ...this.#getRegisteredDefaults(bucket) };
2407
- if (bucket) {
2408
- const tileBucket = soc.buckets.find((b) => b.id === bucket);
2409
- if (tileBucket) {
2410
- ocr = tileBucket.configs;
2411
- if (includeDefaults)
2412
- Object.keys(ocr).forEach((k) => {
2413
- ocr[k] = { ...defaults[k], ...ocr[k] };
2414
- });
2415
- }
2416
- }
2417
- return includeDefaults ? { ...defaults, ...ocr } : { ...ocr };
2418
- }));
2419
- }
2420
2188
  /**
2421
- * Register default object configs. Apps can pre-define how their
2422
- * objects should be rendered.
2423
- * @param bucket Name of a bucket to bind configs to (usually the ID of the app) to
2424
- * separate the configs from other apps. If not provided configs will be saved for
2425
- * the global scope
2189
+ * Go to a page of a search result.
2190
+ * @param query SearchQuery or CMIS query statement
2191
+ * @param page The number of the page to go to
2426
2192
  */
2427
- registerDefaults(configs, bucket) {
2428
- if (bucket) {
2429
- // does bucket exist?
2430
- const tileBucketIdx = this.#registeredDefaultObjectConfigs.buckets.findIndex((b) => b.id === bucket);
2431
- if (tileBucketIdx !== -1) {
2432
- this.#registeredDefaultObjectConfigs.buckets[tileBucketIdx].configs = {
2433
- ...this.#registeredDefaultObjectConfigs.buckets[tileBucketIdx].configs,
2434
- ...configs
2435
- };
2436
- }
2437
- else {
2438
- this.#registeredDefaultObjectConfigs.buckets.push({
2439
- id: bucket,
2440
- configs
2441
- });
2442
- }
2443
- }
2444
- else {
2445
- this.#registeredDefaultObjectConfigs.main = { ...this.#registeredDefaultObjectConfigs.main, ...configs };
2446
- }
2447
- }
2448
- unregisterDefaults(configs, bucket) {
2449
- if (bucket) {
2450
- // does bucket exist?
2451
- const tileBucketIdx = this.#registeredDefaultObjectConfigs.buckets.findIndex((b) => b.id === bucket);
2452
- if (tileBucketIdx !== -1) {
2453
- this.#registeredDefaultObjectConfigs.buckets.splice(tileBucketIdx, 1);
2454
- }
2455
- }
2456
- else {
2457
- Object.keys(configs).forEach((key) => {
2458
- delete this.#registeredDefaultObjectConfigs.main[key];
2459
- });
2460
- }
2461
- }
2462
- getRegisteredDefault(objectTypeId, bucket) {
2463
- if (bucket) {
2464
- const b = this.#registeredDefaultObjectConfigs.buckets.find((b) => b.id === bucket);
2465
- return b ? b.configs[objectTypeId] : undefined;
2193
+ getPage(query, page, pageSize = SearchService.DEFAULT_QUERY_SIZE, includePermissions = false) {
2194
+ const isCmis = typeof query === 'string';
2195
+ if (isCmis) {
2196
+ return this.#executeCmisSearch(query, pageSize, (page - 1) * pageSize, includePermissions);
2466
2197
  }
2467
2198
  else {
2468
- return this.#registeredDefaultObjectConfigs.main[objectTypeId];
2469
- }
2470
- }
2471
- #getRegisteredDefaults(bucket) {
2472
- if (bucket) {
2473
- const defaultTileBucket = this.#registeredDefaultObjectConfigs.buckets.find((b) => b.id === bucket);
2474
- return defaultTileBucket?.configs || {};
2199
+ query.from = (page - 1) * (query.size || pageSize);
2200
+ return this.search(query);
2475
2201
  }
2476
- else
2477
- return this.#registeredDefaultObjectConfigs.main;
2478
2202
  }
2479
2203
  /**
2480
- * Get object config for an object type.
2481
- * @param objectTypeId ID of the object type to get the config for
2482
- * @param bucket Optional bucket ID to fetch the config from. Buckets
2483
- * define separated areas to store/receive those configs. This way
2484
- * components/apps can store different object configs than the ones
2485
- * used accross the whole client application.
2204
+ * Map search result from the backend to applications SearchResult object
2205
+ * @param searchResponse The backend response
2486
2206
  */
2487
- getObjectConfig(type, bucket) {
2488
- const b = bucket ? this.#objectConfigs.buckets.find((b) => b.id === bucket) : undefined;
2489
- const otr = b ? b.configs : this.#objectConfigs.main;
2490
- const oc = otr[type.id];
2491
- // return oc || this.#getRegisteredDefaults(bucket)[type.id];
2492
- // override icons by APP_SCHEMA-icons: temporary solution. Todo: remove when handling of custom svg-icons is clear!
2493
- const result = oc || this.#getRegisteredDefaults(bucket)[type.id] || this.#defaultObjectConfigs[type.id];
2494
- if (result?.icon)
2495
- result.icon = type.icon ? { svg: type.icon } : result.icon;
2496
- return result;
2497
- }
2498
- saveObjectConfig(type, config, bucket) {
2499
- return this.saveObjectConfigs({ [type.id]: config }, bucket);
2500
- }
2501
- saveObjectConfigs(configs, bucket) {
2502
- this.#updateObjectConfig(configs, bucket);
2503
- return this.#user.saveObjectConfig(this.#objectConfigs);
2504
- }
2505
- #updateObjectConfig(configs, bucket) {
2506
- if (!this.#objectConfigs) {
2507
- this.#objectConfigs = {
2508
- main: {},
2509
- buckets: []
2510
- };
2511
- }
2512
- if (bucket) {
2513
- // does bucket exist?
2514
- const tileBucketIdx = this.#objectConfigs.buckets.findIndex((b) => b.id === bucket);
2515
- if (tileBucketIdx !== -1) {
2516
- Object.keys(configs).forEach((objectTypeId) => {
2517
- const config = configs[objectTypeId];
2518
- if (config) {
2519
- this.#objectConfigs.buckets[tileBucketIdx].configs[objectTypeId] = config;
2520
- }
2521
- else {
2522
- delete this.#objectConfigs.buckets[tileBucketIdx].configs[objectTypeId];
2523
- }
2524
- });
2525
- }
2526
- else {
2527
- const newBucket = { id: bucket, configs: {} };
2528
- Object.keys(configs).forEach((objectTypeId) => {
2529
- const config = configs[objectTypeId];
2530
- if (config) {
2531
- newBucket.configs[objectTypeId] = config;
2207
+ toSearchResult(searchResponse, pageSize = SearchService.DEFAULT_QUERY_SIZE, skipCount = 0) {
2208
+ const resultListItems = [];
2209
+ const objectTypes = [];
2210
+ searchResponse.objects.forEach((o) => {
2211
+ const fields = new Map();
2212
+ // process properties section of result
2213
+ Object.keys(o.properties).forEach((key) => {
2214
+ let value = o.properties[key].value;
2215
+ if (o.properties[key].clvalue) {
2216
+ // table fields will have a clientValue too ...
2217
+ value = o.properties[key].clvalue;
2218
+ // ... and also may contain values that need to be resolved
2219
+ if (o.properties[key].resolvedValues) {
2220
+ value.forEach((v) => {
2221
+ Object.keys(v).forEach((k) => {
2222
+ const resValue = Array.isArray(v[k]) ? v[k].map((i) => o.properties[key].resolvedValues[i]) : o.properties[key].resolvedValues[v[k]];
2223
+ if (resValue) {
2224
+ v[`${k}_title`] = resValue;
2225
+ }
2226
+ });
2227
+ });
2532
2228
  }
2533
- });
2534
- this.#objectConfigs.buckets.push(newBucket);
2535
- }
2536
- }
2537
- else {
2538
- Object.keys(configs).forEach((objectTypeId) => {
2539
- const config = configs[objectTypeId];
2540
- if (config) {
2541
- this.#objectConfigs.main[objectTypeId] = config;
2542
2229
  }
2543
- else {
2544
- delete this.#objectConfigs.main[objectTypeId];
2230
+ fields.set(key, value);
2231
+ if (o.properties[key].title) {
2232
+ fields.set(key + '_title', o.properties[key].title);
2545
2233
  }
2546
2234
  });
2547
- }
2548
- this.#objectConfigsSource.next(this.#objectConfigs);
2549
- }
2550
- getDefaultConfig(objectTypeID) {
2551
- return this.#defaultObjectConfigs[objectTypeID];
2552
- }
2553
- getResolvedObjectConfig(data, type, bucket, includeDefaults) {
2554
- const defaultCfg = this.#defaultObjectConfigs[data[BaseObjectTypeField.OBJECT_TYPE_ID]];
2555
- let oc = this.getObjectConfig(type, bucket) || defaultCfg;
2556
- if (includeDefaults)
2557
- oc = { ...defaultCfg, ...oc };
2558
- const res = {
2559
- id: data[BaseObjectTypeField.OBJECT_ID],
2560
- objectTypeId: data[BaseObjectTypeField.OBJECT_TYPE_ID],
2561
- actions: oc.actions,
2562
- badges: oc.badges,
2563
- instanceData: data
2564
- };
2565
- if (oc.title)
2566
- res.title = {
2567
- propertyName: oc.title.propertyName,
2568
- value: data[oc.title.propertyName]
2569
- };
2570
- if (oc.description)
2571
- res.description = {
2572
- propertyName: oc.description.propertyName,
2573
- value: data[oc.description.propertyName]
2574
- };
2575
- if (oc.icon)
2576
- res.icon = {
2577
- propertyName: 'custom',
2578
- value: oc.icon.svg
2579
- };
2580
- if (oc.meta)
2581
- res.meta = {
2582
- propertyName: oc.meta.propertyName,
2583
- value: data[oc.meta.propertyName]
2584
- };
2585
- if (oc.aside)
2586
- res.aside = {
2587
- propertyName: oc.aside.propertyName,
2588
- value: data[oc.aside.propertyName]
2589
- };
2590
- return res;
2235
+ // process contentStreams section of result if available.
2236
+ // Objects that don't have files attached won't have this section
2237
+ let content;
2238
+ if (o.contentStreams && o.contentStreams.length > 0) {
2239
+ // we assume that each result object only has ONE file attached, altough
2240
+ // this is an array and there may be more
2241
+ const contentStream = o.contentStreams[0];
2242
+ // also add contentstream related fields to the result fields
2243
+ fields.set(ContentStreamField.LENGTH, contentStream.length);
2244
+ fields.set(ContentStreamField.MIME_TYPE, contentStream.mimeType);
2245
+ fields.set(ContentStreamField.FILENAME, contentStream.fileName);
2246
+ fields.set(ContentStreamField.ID, contentStream.contentStreamId);
2247
+ fields.set(ContentStreamField.RANGE, contentStream.contentStreamRange);
2248
+ fields.set(ContentStreamField.REPOSITORY_ID, contentStream.repositoryId);
2249
+ fields.set(ContentStreamField.DIGEST, contentStream.digest);
2250
+ fields.set(ContentStreamField.ARCHIVE_PATH, contentStream.archivePath);
2251
+ content = {
2252
+ contentStreamId: contentStream.contentStreamId,
2253
+ repositoryId: contentStream.repositoryId,
2254
+ range: contentStream.range,
2255
+ digest: contentStream.digest,
2256
+ archivePath: contentStream.archivePath,
2257
+ fileName: contentStream.fileName,
2258
+ mimeType: contentStream.mimeType,
2259
+ size: contentStream.length
2260
+ };
2261
+ }
2262
+ const objectTypeId = o.properties[BaseObjectTypeField.OBJECT_TYPE_ID] ? o.properties[BaseObjectTypeField.OBJECT_TYPE_ID].value : null;
2263
+ if (objectTypes.indexOf(objectTypeId) === -1) {
2264
+ objectTypes.push(objectTypeId);
2265
+ }
2266
+ resultListItems.push({
2267
+ objectTypeId,
2268
+ content,
2269
+ fields,
2270
+ permissions: o.permissions
2271
+ });
2272
+ });
2273
+ const totalPages = Math.ceil(searchResponse.totalNumItems / pageSize);
2274
+ const page = (!skipCount ? 0 : skipCount / pageSize) + 1;
2275
+ const result = {
2276
+ hasMoreItems: searchResponse.hasMoreItems,
2277
+ totalNumItems: searchResponse.totalNumItems,
2278
+ items: resultListItems,
2279
+ objectTypes: objectTypes,
2280
+ paging: totalPages > 1 ? { page, totalPages } : undefined
2281
+ };
2282
+ return result;
2591
2283
  }
2592
2284
  /**
2593
- * Load default/fallbac configs based on the object types and
2594
- * their first properties
2285
+ * Maps data extracted from a search form to search filters. Every key of the form data object
2286
+ * will be mapped to a search filter.
2287
+ * @param formData form data
2288
+ * @returns Array of search filters
2595
2289
  */
2596
- #getDefaultObjectConfig() {
2597
- this.#system.getObjectTypes(true).forEach((ot) => {
2598
- this.#defaultObjectConfigs[ot.id] = {
2599
- objectTypeId: ot.id,
2600
- title: this.#toObjectProperty(ot.fields[0]),
2601
- description: this.#toObjectProperty(ot.fields[1]),
2602
- meta: this.#toObjectProperty(ot.fields[2]),
2603
- aside: this.#toObjectProperty(ot.fields[3])
2604
- };
2290
+ formDataToSearchFilter(formData) {
2291
+ const isRangeValue = (v) => {
2292
+ return typeof v === 'object' && v !== null && 'firstValue' in v && 'operator' in v;
2293
+ };
2294
+ const filters = [];
2295
+ Object.keys(formData).forEach((key) => {
2296
+ const value = formData[key];
2297
+ if (isRangeValue(value)) {
2298
+ const f = this.rangeValueToSearchFilter(value, key);
2299
+ if (f)
2300
+ filters.push(f);
2301
+ }
2302
+ else {
2303
+ filters.push({
2304
+ f: key,
2305
+ o: Array.isArray(value) ? Operator.IN : Operator.EQUAL,
2306
+ v1: value
2307
+ });
2308
+ }
2605
2309
  });
2310
+ return filters;
2606
2311
  }
2607
- #toObjectProperty(field) {
2608
- return field
2609
- ? {
2610
- label: this.#system.getLocalizedLabel(field.id),
2611
- propertyName: field.id
2312
+ rangeValueToSearchFilter(value, property) {
2313
+ return this.#system.getObjectTypeField(property)?.propertyType === 'datetime'
2314
+ ? this.#dateRangeValueToSearchFilter(value, property)
2315
+ : {
2316
+ f: property,
2317
+ o: value.operator,
2318
+ v1: value.firstValue,
2319
+ v2: value.secondValue
2320
+ };
2321
+ }
2322
+ #dateRangeValueToSearchFilter(rv, property) {
2323
+ const v1 = rv.firstValue;
2324
+ const v2 = rv.secondValue;
2325
+ switch (rv.operator) {
2326
+ case Operator.EQUAL: {
2327
+ return {
2328
+ f: property,
2329
+ o: Operator.INTERVAL_INCLUDE_BOTH,
2330
+ v1: this.#dateToISOString(v1, 'start'),
2331
+ v2: this.#dateToISOString(v1, 'end')
2332
+ };
2612
2333
  }
2613
- : undefined;
2334
+ case Operator.GREATER_OR_EQUAL: {
2335
+ return {
2336
+ f: property,
2337
+ o: rv.operator,
2338
+ v1: this.#dateToISOString(v1)
2339
+ };
2340
+ }
2341
+ case Operator.LESS_OR_EQUAL: {
2342
+ return {
2343
+ f: property,
2344
+ o: rv.operator,
2345
+ v1: this.#dateToISOString(v1, 'end')
2346
+ };
2347
+ }
2348
+ case Operator.INTERVAL_INCLUDE_BOTH: {
2349
+ return {
2350
+ f: property,
2351
+ o: rv.operator,
2352
+ v1: this.#dateToISOString(v1, 'start'),
2353
+ v2: v2 ? this.#dateToISOString(v2, 'end') : ''
2354
+ };
2355
+ }
2356
+ default:
2357
+ return undefined;
2358
+ }
2614
2359
  }
2615
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2616
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectConfigService, providedIn: 'root' }); }
2360
+ /**
2361
+ * Checks if value is a date and retrieves an ISOString representation. Optionally you can determine, if it should be the beginning or the end of a day.
2362
+ *
2363
+ * @param value
2364
+ * @param range
2365
+ * @private
2366
+ */
2367
+ #dateToISOString(value, range = 'start') {
2368
+ const date = typeof value === 'string' ? new Date(value) : value;
2369
+ const offset = Utils.DEFAULT_TIME_OFFSET_V2; // T+23:59:59:999
2370
+ if (!Utils.isValidDate(date))
2371
+ return '';
2372
+ const isoDateString = date.toISOString();
2373
+ // const isoDateStringWithOffset = new Date(date.getTime() + offset).toISOString();
2374
+ switch (range) {
2375
+ case 'start':
2376
+ return isoDateString;
2377
+ case 'end':
2378
+ return isoDateString;
2379
+ default:
2380
+ return isoDateString;
2381
+ }
2382
+ }
2383
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SearchService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2384
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SearchService, providedIn: 'root' }); }
2617
2385
  }
2618
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectConfigService, decorators: [{
2386
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SearchService, decorators: [{
2619
2387
  type: Injectable,
2620
2388
  args: [{
2621
2389
  providedIn: 'root'
2622
2390
  }]
2623
2391
  }] });
2624
2392
 
2625
- const YUV_USER = new InjectionToken('Currently logged in user', {
2626
- factory: () => undefined
2627
- });
2628
- const provideUser = (user) => {
2629
- return { provide: YUV_USER, useValue: user };
2630
- };
2393
+ var Direction;
2394
+ (function (Direction) {
2395
+ Direction["LTR"] = "ltr";
2396
+ Direction["RTL"] = "rtl";
2397
+ })(Direction || (Direction = {}));
2398
+
2399
+ const CUSTOM_EVENTS = new InjectionToken('CUSTOM_EVENTS', { factory: () => [] });
2400
+ const CUSTOM_EVENTS_TRUSTED_ORIGINS = new InjectionToken('CUSTOM_EVENTS_TRUSTED_ORIGINS', { factory: () => [] });
2631
2401
 
2632
2402
  /**
2633
- * Service handling authentication related issues.
2403
+ * Mandatory Custom event prefix for all custom YUV events
2634
2404
  */
2635
- class AuthService {
2636
- // user may have been fetched already in bootstrapping process
2637
- #userToken;
2638
- #eventService;
2639
- #userService;
2640
- #objectConfigService;
2641
- #appCache;
2642
- #systemService;
2643
- #localizationService;
2644
- #backend;
2645
- #INITIAL_REQUEST_STORAGE_KEY;
2646
- #USER_FETCH_URI;
2647
- #authenticated;
2648
- #authSource;
2649
- #authData;
2650
- constructor(coreConfig) {
2651
- this.coreConfig = coreConfig;
2652
- // user may have been fetched already in bootstrapping process
2653
- this.#userToken = inject(YUV_USER);
2654
- this.#eventService = inject(EventService);
2655
- this.#userService = inject(UserService);
2656
- this.#objectConfigService = inject(ObjectConfigService);
2657
- this.#appCache = inject(AppCacheService);
2658
- this.#systemService = inject(SystemService);
2659
- this.#localizationService = inject(LocalizationService);
2660
- this.#backend = inject(BackendService);
2661
- this.#INITIAL_REQUEST_STORAGE_KEY = 'yuv.core.auth.initialrequest';
2662
- this.#USER_FETCH_URI = '/idm/whoami';
2663
- this.#authenticated = false;
2664
- this.#authSource = new BehaviorSubject(false);
2665
- this.authenticated$ = this.#authSource.asObservable();
2666
- }
2667
- isLoggedIn() {
2668
- return this.#authenticated;
2669
- }
2670
- /**
2671
- * Called while app/core is initialized (APP_INITIALIZER)
2672
- * @ignore
2673
- */
2674
- initUser() {
2675
- return this.fetchUser();
2676
- }
2677
- /**
2678
- * Get the current tenant or the previous one persisted locally
2679
- */
2680
- getTenant() {
2681
- return this.#authData?.tenant;
2405
+ const CUSTOM_YUV_EVENT_PREFIX = 'yuv.';
2406
+
2407
+ /**
2408
+ * Service for providing triggered events
2409
+ */
2410
+ class EventService {
2411
+ #customEvents;
2412
+ #customEventsTrustedOrigins;
2413
+ #ngZone;
2414
+ #eventSource;
2415
+ constructor() {
2416
+ this.#customEvents = inject(CUSTOM_EVENTS);
2417
+ this.#customEventsTrustedOrigins = inject(CUSTOM_EVENTS_TRUSTED_ORIGINS);
2418
+ this.#ngZone = inject(NgZone);
2419
+ this.#eventSource = new Subject();
2420
+ this.event$ = this.#eventSource.asObservable();
2421
+ this.#listenToWindowEvents();
2682
2422
  }
2683
- /**
2684
- * Fetch information about the user currently logged in
2685
- */
2686
- fetchUser() {
2687
- return (this.#userToken ? of(this.#userToken) : this.#backend.get(this.#USER_FETCH_URI)).pipe(tap(() => {
2688
- this.#authenticated = true;
2689
- this.#authSource.next(this.#authenticated);
2690
- }), switchMap((userJson) => this.#initApp(userJson)));
2423
+ #startsWithAllowed(value) {
2424
+ return typeof value === 'string' && this.#customEvents.some((prefix) => value.startsWith(prefix));
2691
2425
  }
2692
- /**
2693
- * Logs out the current user.
2694
- */
2695
- logout() {
2696
- this.#authenticated = false;
2697
- this.#authSource.next(this.#authenticated);
2698
- this.#eventService.trigger(YuvEventType.LOGOUT);
2426
+ #isValidExternalMessage(event) {
2427
+ this.#customEvents.push(CUSTOM_YUV_EVENT_PREFIX);
2428
+ if (!event.data || typeof event.data !== 'object')
2429
+ return false;
2430
+ if (!(this.#startsWithAllowed(event.data.source) || this.#startsWithAllowed(event.data.type)))
2431
+ return false;
2432
+ // Accept messages from trusted origins
2433
+ this.#customEventsTrustedOrigins.push(window.location.origin);
2434
+ const isFromTrustedOrigin = this.#customEventsTrustedOrigins.includes(event.origin);
2435
+ const hasValidStructure = event.data && (event.data.type || event.data.eventType) && typeof event.data === 'object';
2436
+ return isFromTrustedOrigin && hasValidStructure;
2699
2437
  }
2700
- /**
2701
- * Persists the initial request URI. This is nessesary to be able to
2702
- * redirect the user to the requested page after authentication.
2703
- * This is done on app initialization (core-init).
2704
- */
2705
- setInitialRequestUri() {
2706
- const ignore = ['/', '/index.html'];
2707
- const baseHref = Utils.getBaseHref();
2708
- // Check only the pathname against the ignore list so that auth callback
2709
- // query params (e.g. ?session_state=…) don't bypass the check.
2710
- let path = location.pathname.replace(baseHref, '');
2711
- path = !path.startsWith('/') ? `/${path}` : path;
2712
- if (!ignore.includes(path)) {
2713
- let uri = `${location.pathname}${location.search}`.replace(baseHref, '');
2714
- uri = !uri.startsWith('/') ? `/${uri}` : uri;
2715
- return this.#appCache.setItem(this.#INITIAL_REQUEST_STORAGE_KEY, {
2716
- uri: uri,
2717
- timestamp: Date.now()
2718
- });
2719
- }
2720
- return of(false);
2438
+ #listenToWindowEvents() {
2439
+ window.addEventListener('message', (event) => {
2440
+ this.#ngZone.run(() => this.#isValidExternalMessage(event) && this.trigger(event.data.type, event.data.data));
2441
+ });
2721
2442
  }
2722
2443
  /**
2723
- * Get the URL that entered the app. May be a deep link that could then be
2724
- * picked up again after user has been authenticated.
2444
+ * Triggers a postMessage event that will be sent to the yuuvis global event Trigger
2445
+ * @param type Type/key of the event
2446
+ * @param data Data to be sent along with the event
2725
2447
  */
2726
- getInitialRequestUri() {
2727
- return this.#appCache.getItem(this.#INITIAL_REQUEST_STORAGE_KEY);
2448
+ triggerPostMessageEvent(type, data) {
2449
+ const targetOrigin = window.location.origin;
2450
+ const payload = {
2451
+ type,
2452
+ data,
2453
+ timestamp: Date.now()
2454
+ };
2455
+ window.postMessage(payload, targetOrigin);
2728
2456
  }
2729
- resetInitialRequestUri() {
2730
- return this.#appCache.removeItem(this.#INITIAL_REQUEST_STORAGE_KEY);
2457
+ /**
2458
+ * Trigger an global event
2459
+ * @param type Type/key of the event
2460
+ * @param data Data to be send along with the event
2461
+ */
2462
+ trigger(type, ...args) {
2463
+ this.#eventSource.next({ type: type, data: args[0] });
2731
2464
  }
2732
2465
  /**
2733
- * Initialize/setup the application for a given user. This involves fetching
2734
- * settings and schema information.
2735
- * @param userJson Data retrieved from the backend
2466
+ * Listen on a triggered event
2467
+ * @param types Type/key of the event
2736
2468
  */
2737
- #initApp(userJson) {
2738
- return this.#userService.fetchUserSettings().pipe(switchMap((userSettings) => {
2739
- const currentUser = new YuvUser(userJson, userSettings);
2740
- this.#userService.setCurrentUser(currentUser);
2741
- this.#authData = {
2742
- tenant: currentUser.tenant,
2743
- language: currentUser.getClientLocale()
2744
- };
2745
- return forkJoin([
2746
- this.#localizationService.fetchLocalizations(),
2747
- this.#systemService.getSystemDefinition(this.#authData)
2748
- ]).pipe(switchMap(() => this.#objectConfigService.init()), map(() => currentUser));
2749
- }));
2469
+ on(...types) {
2470
+ return this.event$.pipe(filter((event) => event && !!types.find((t) => t === event.type)));
2750
2471
  }
2751
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuthService, deps: [{ token: CORE_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
2752
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuthService, providedIn: 'root' }); }
2472
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2473
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EventService, providedIn: 'root' }); }
2753
2474
  }
2754
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuthService, decorators: [{
2475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EventService, decorators: [{
2755
2476
  type: Injectable,
2756
2477
  args: [{
2757
2478
  providedIn: 'root'
2758
2479
  }]
2759
- }], ctorParameters: () => [{ type: CoreConfig, decorators: [{
2760
- type: Inject,
2761
- args: [CORE_CONFIG]
2762
- }] }] });
2480
+ }], ctorParameters: () => [] });
2763
2481
 
2764
2482
  /**
2765
- * Prevent app from running into 401 issues related to gateway timeouts.
2483
+ * Events emitted by parts of the application
2766
2484
  */
2767
- function AuthInterceptorFnc(req, next) {
2768
- const userService = inject(UserService);
2769
- const auth = inject(AuthService);
2770
- return next(req).pipe(tap({
2771
- next: (event) => {
2772
- if (event instanceof HttpResponse) {
2773
- // do stuff with response if you want
2774
- }
2775
- },
2776
- error: (error) => {
2777
- if (error instanceof HttpErrorResponse || error.isHttpErrorResponse) {
2778
- if (error.status === 401) {
2779
- auth.logout();
2780
- userService.logout();
2781
- }
2782
- }
2783
- }
2784
- }));
2785
- }
2786
-
2787
- var LoginStateName;
2788
- (function (LoginStateName) {
2789
- LoginStateName["STATE_LOGIN_URI"] = "login.uri";
2790
- LoginStateName["STATE_DONE"] = "login.done";
2791
- LoginStateName["STATE_CANCELED"] = "login.canceled";
2792
- })(LoginStateName || (LoginStateName = {}));
2485
+ var YuvEventType;
2486
+ (function (YuvEventType) {
2487
+ YuvEventType["LOGOUT"] = "yuv.user.logout";
2488
+ YuvEventType["CLIENT_LOCALE_CHANGED"] = "yuv.user.locale.client.changed";
2489
+ YuvEventType["DMS_OBJECT_LOADED"] = "yuv.dms.object.loaded";
2490
+ YuvEventType["DMS_OBJECT_CREATED"] = "yuv.dms.object.created";
2491
+ YuvEventType["DMS_OBJECT_DELETED"] = "yuv.dms.object.deleted";
2492
+ YuvEventType["DMS_OBJECT_UPDATED"] = "yuv.dms.object.updated";
2493
+ YuvEventType["DMS_OBJECT_CONTENT_UPDATED"] = "yuv.dms.object.content.updated";
2494
+ YuvEventType["DMS_OBJECTS_MOVED"] = "yuv.dms.objects.moved";
2495
+ YuvEventType["RELATIONSHIP_CREATED"] = "yuv.dms.relationship.created";
2496
+ YuvEventType["RELATIONSHIP_DELETED"] = "yuv.dms.relationship.deleted";
2497
+ })(YuvEventType || (YuvEventType = {}));
2793
2498
 
2794
2499
  /**
2795
- * Service to monitor the online/offline state of the application.
2796
- * It listens to the browser's online and offline events and provides an observable
2797
- * to track the current connection state.
2798
- *
2799
- * An observable `connection$` is provided which emits the current connection state
2800
- * whenever the online or offline state changes. The initial state is determined by
2801
- * the `window.navigator.onLine` property.
2500
+ * Service providing user account configurations.
2802
2501
  */
2803
- class ConnectionService {
2502
+ class UserService {
2503
+ constructor() {
2504
+ this.#backend = inject(BackendService);
2505
+ this.#translate = inject(TranslateService);
2506
+ this.#logger = inject(Logger);
2507
+ this.#system = inject(SystemService);
2508
+ this.#localization = inject(LocalizationService);
2509
+ this.#eventService = inject(EventService);
2510
+ this.#config = inject(ConfigService);
2511
+ this.#document = inject(DOCUMENT);
2512
+ this.#USERS_SETTINGS = '/users/settings/';
2513
+ this.#DEFAULT_SETTINGS = '/users/settings';
2514
+ this.#SETTINGS_SECTION_OBJECTCONFIG = 'object-config';
2515
+ this.#userSource = new BehaviorSubject(this.#user);
2516
+ this.user$ = this.#userSource.asObservable();
2517
+ this.globalSettings = new Map();
2518
+ this.canCreateObjects = false;
2519
+ }
2520
+ #backend;
2521
+ #translate;
2522
+ #logger;
2523
+ #system;
2524
+ #localization;
2525
+ #eventService;
2526
+ #config;
2527
+ #document;
2528
+ #USERS_SETTINGS;
2529
+ #DEFAULT_SETTINGS;
2530
+ #SETTINGS_SECTION_OBJECTCONFIG;
2531
+ #user;
2532
+ #userSource;
2804
2533
  /**
2805
- * @ignore
2534
+ * Set a new current user
2535
+ * @param user The user to be set as current user
2806
2536
  */
2807
- constructor() {
2808
- this.currentState = {
2809
- isOnline: window.navigator.onLine
2537
+ setCurrentUser(user) {
2538
+ this.#user = user;
2539
+ this.changeClientLocale('', false);
2540
+ this.#userSource.next(this.#user);
2541
+ }
2542
+ getCurrentUser() {
2543
+ return this.#user;
2544
+ }
2545
+ get hasAdminRole() {
2546
+ return this.#user?.authorities?.includes(AdministrationRoles.ADMIN) || false;
2547
+ }
2548
+ get hasSystemRole() {
2549
+ return this.#user?.authorities?.includes(AdministrationRoles.SYSTEM) || false;
2550
+ }
2551
+ get hasAdministrationRoles() {
2552
+ return this.hasAdminRole || this.hasSystemRole;
2553
+ }
2554
+ get hasManageSettingsRole() {
2555
+ const customRole = this.#config.get('core.permissions.manageSettingsRole');
2556
+ const manageSettingsRole = customRole || AdministrationRoles.MANAGE_SETTINGS;
2557
+ return this.#user?.authorities?.includes(manageSettingsRole) || false;
2558
+ }
2559
+ get isAdvancedUser() {
2560
+ const customRole = this.#config.get('core.permissions.advancedUserRole');
2561
+ const advancedUserRole = customRole || AdministrationRoles.MANAGE_SETTINGS;
2562
+ return this.#user?.authorities?.includes(advancedUserRole) || false;
2563
+ }
2564
+ get isRetentionManager() {
2565
+ const customRole = this.#config.get('core.permissions.retentionManagerRole');
2566
+ const retenetionManagerRole = customRole || AdministrationRoles.MANAGE_SETTINGS;
2567
+ return this.#user?.authorities?.includes(retenetionManagerRole) || false;
2568
+ }
2569
+ /**
2570
+ * Change the users client locale
2571
+ * @param iso ISO locale string to be set as new client locale
2572
+ */
2573
+ changeClientLocale(iso, persist = true) {
2574
+ if (this.#user) {
2575
+ const languages = this.#config.getClientLocales().map((lang) => lang.iso);
2576
+ iso = iso || this.#user.getClientLocale(this.#config.getDefaultClientLocale());
2577
+ if (!languages.includes(iso)) {
2578
+ iso = this.#config.getDefaultClientLocale();
2579
+ }
2580
+ this.#logger.debug("Changed client locale to '" + iso + "'");
2581
+ this.#backend.setHeader('Accept-Language', iso);
2582
+ this.#user.uiDirection = this.#getUiDirection(iso);
2583
+ this.#document.body.dir = this.#user.uiDirection;
2584
+ this.#document.documentElement.lang = iso;
2585
+ this.#user.userSettings.locale = iso;
2586
+ if (this.#translate.getCurrentLang() !== iso || this.#system.authData?.language !== iso) {
2587
+ const obs = persist
2588
+ ? forkJoin([
2589
+ this.#translate.use(iso),
2590
+ this.#system
2591
+ .updateAuthData({ language: iso })
2592
+ .pipe(switchMap(() => this.#localization.fetchLocalizations())),
2593
+ this.saveUserSettings(this.#user.userSettings).pipe(tap(() => {
2594
+ this.#logger.debug('Loading system definitions i18n resources for new locale.');
2595
+ }))
2596
+ ])
2597
+ : this.#translate.use(iso);
2598
+ obs.subscribe(() => this.#eventService.trigger(YuvEventType.CLIENT_LOCALE_CHANGED, iso));
2599
+ }
2600
+ }
2601
+ }
2602
+ saveUserSettings(settings) {
2603
+ if (this.#user) {
2604
+ //console.log(this.#user.userSettings);
2605
+ this.#user.userSettings = { ...this.#user.userSettings, ...settings };
2606
+ return this.#backend
2607
+ .post(this.#DEFAULT_SETTINGS, this.#user.userSettings)
2608
+ .pipe(tap(() => this.#userSource.next(this.#user)));
2609
+ }
2610
+ else
2611
+ return of(null);
2612
+ }
2613
+ fetchUserSettings() {
2614
+ return this.#backend.get('/dms/permissions').pipe(catchError(() => of(undefined)), switchMap((res) => {
2615
+ this.#setUserPermissions(res);
2616
+ return this.#backend.get(this.#DEFAULT_SETTINGS);
2617
+ }));
2618
+ }
2619
+ /**
2620
+ * Search for a user based on a search term
2621
+ * @param term Search term
2622
+ * @param excludeMe whether or not to exclude myself from the result list
2623
+ * @param roles narrow down the search results by certain roles
2624
+ */
2625
+ queryUser(term, excludeMe, roles) {
2626
+ let params = new HttpParams().set('search', term).set('excludeMe', `${!!excludeMe}`);
2627
+ roles?.length && roles.map((r) => (params = params.append(`roles`, r)));
2628
+ return this.#backend
2629
+ .get(`/idm/users?${params}`)
2630
+ .pipe(map((users) => (!users ? [] : users.map((u) => new YuvUser(u)))));
2631
+ }
2632
+ getUserById(id) {
2633
+ return this.#backend.get(`/idm/users/${id}`).pipe(map((user) => new YuvUser(user, this.#user.userSettings)));
2634
+ }
2635
+ logout(redirRoute) {
2636
+ const redir = redirRoute ? `?redir=${redirRoute}` : '';
2637
+ window.location.href = `${this.#backend.getApiBase('logout')}${redir}`;
2638
+ }
2639
+ getSettings(section) {
2640
+ return this.#user ? this.#backend.get(this.#USERS_SETTINGS + encodeURIComponent(section)) : of(null);
2641
+ }
2642
+ saveObjectConfig(objectConfigs) {
2643
+ return this.#backend.post(this.#USERS_SETTINGS + encodeURIComponent(this.#SETTINGS_SECTION_OBJECTCONFIG), objectConfigs);
2644
+ }
2645
+ loadObjectConfig() {
2646
+ return this.getSettings(this.#SETTINGS_SECTION_OBJECTCONFIG).pipe(catchError(() => of(undefined)));
2647
+ }
2648
+ #getUiDirection(iso) {
2649
+ // languages that are read right to left
2650
+ const rtlLanguages = ['ar', 'arc', 'dv', 'fa', 'ha', 'he', 'khw', 'ks', 'ku', 'ps', 'ur', 'yi'];
2651
+ return !rtlLanguages.includes(iso) ? Direction.LTR : Direction.RTL;
2652
+ }
2653
+ #setUserPermissions(res) {
2654
+ this.userPermissions = {
2655
+ create: this.#mapPermissions('CREATE', res),
2656
+ write: this.#mapPermissions('WRITE', res),
2657
+ read: this.#mapPermissions('READ', res),
2658
+ delete: this.#mapPermissions('DELETE', res)
2810
2659
  };
2811
- this.connectionStateSource = new ReplaySubject();
2812
- this.connection$ = this.connectionStateSource.asObservable();
2813
- this.connectionStateSource.next(this.currentState);
2814
- fromEvent(window, 'online').subscribe(() => {
2815
- this.currentState.isOnline = true;
2816
- this.connectionStateSource.next(this.currentState);
2817
- });
2818
- fromEvent(window, 'offline').subscribe(() => {
2819
- this.currentState.isOnline = false;
2820
- this.connectionStateSource.next(this.currentState);
2821
- });
2660
+ const permissions = {
2661
+ createableObjectTypes: [
2662
+ ...this.userPermissions.create.folderTypes,
2663
+ ...this.userPermissions.create.objectTypes,
2664
+ ...this.userPermissions.create.secondaryObjectTypes
2665
+ ],
2666
+ searchableObjectTypes: [
2667
+ ...this.userPermissions.read.folderTypes,
2668
+ ...this.userPermissions.read.objectTypes,
2669
+ ...this.userPermissions.read.secondaryObjectTypes
2670
+ ]
2671
+ };
2672
+ this.#system.setPermissions(permissions);
2673
+ this.canCreateObjects = permissions.createableObjectTypes.length > 0;
2822
2674
  }
2823
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ConnectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2824
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ConnectionService, providedIn: 'root' }); }
2675
+ #mapPermissions(section, apiResponse) {
2676
+ const res = apiResponse[section] || {};
2677
+ return {
2678
+ folderTypes: res['folderTypeIds'] || [],
2679
+ objectTypes: res['objectTypeIds'] || [],
2680
+ secondaryObjectTypes: res['secondaryObjectTypeIds'] || []
2681
+ };
2682
+ }
2683
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2684
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UserService, providedIn: 'root' }); }
2825
2685
  }
2826
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ConnectionService, decorators: [{
2686
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: UserService, decorators: [{
2827
2687
  type: Injectable,
2828
2688
  args: [{
2829
2689
  providedIn: 'root'
2830
2690
  }]
2831
- }], ctorParameters: () => [] });
2832
-
2833
- /**
2834
- * Http Offline interceptor trys to serving offline content for method/url
2835
- */
2836
- function OfflineInterceptorFnc(req, next) {
2837
- return next(req);
2838
- }
2839
-
2840
- /**
2841
- * Providing functions,that are are injected at application startup and executed during app initialization.
2842
- */
2843
- const init_moduleFnc = () => {
2844
- const coreConfig = inject(CORE_CONFIG);
2845
- const logger = inject(Logger);
2846
- const http = inject(HttpClient);
2847
- const configService = inject(ConfigService);
2848
- const authService = inject(AuthService);
2849
- return authService.setInitialRequestUri().pipe(switchMap(() => coreConfig.main
2850
- ? !Array.isArray(coreConfig.main)
2851
- ? of([coreConfig.main])
2852
- : forkJoin([...coreConfig.main].map((uri) => http.get(`${Utils.getBaseHref()}${uri}`).pipe(catchError((e) => {
2853
- logger.error('failed to catch config file', e);
2854
- return of({});
2855
- }), map((res) => res)))).pipe(
2856
- // switchMap((configs: YuvConfig[]) => configService.extendConfig(configs)),
2857
- tap((configs) => configService.extendConfig(configs)), switchMap(() => authService.initUser().pipe(catchError((e) => {
2858
- authService.initError = {
2859
- status: e.status,
2860
- key: e.error.error
2861
- };
2862
- return of(true);
2863
- }))))
2864
- : of(false)));
2865
- };
2866
-
2867
- /**
2868
- * Handles missing translations
2869
- * @ignore
2870
- */
2871
- class EoxMissingTranslationHandler {
2872
- handle(params) {
2873
- return '!missing key: ' + params.key;
2874
- }
2875
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxMissingTranslationHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2876
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxMissingTranslationHandler }); }
2877
- }
2878
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxMissingTranslationHandler, decorators: [{
2879
- type: Injectable
2880
2691
  }] });
2881
2692
 
2882
2693
  /**
2883
- * i18n packages
2884
- */
2885
- /**
2886
- * Loader that fetches translations based on the configured locations
2887
- * @ignore
2694
+ * Service providing access to the systems audit entries. Audits can be seen as the history of
2695
+ * an object. Actions perormed on an object (eg. read, write, delete, ...) will be recorded during
2696
+ * the objects lifecycle. Audits are provided based on a users permissions. Beside the audit entries
2697
+ * visible to regular users there are more technical ones that will only be shown to users that
2698
+ * have administrative role.
2888
2699
  */
2889
- class EoxTranslateJsonLoader {
2890
- constructor(http, config) {
2891
- this.http = http;
2892
- this.config = config;
2893
- registerLocaleData(localeDe, 'de', localeExtraDe); // German
2894
- registerLocaleData(localeAr, 'ar', localeExtraAr); // Arabic
2895
- registerLocaleData(localeEs, 'es', localeExtraEs); // Spanish
2896
- registerLocaleData(localePt, 'pt', localeExtraPt); // Portuguese
2897
- registerLocaleData(localeFr, 'fr', localeExtraFr); // French
2898
- registerLocaleData(localeZh, 'zh', localeExtraZh); // Chinese
2899
- registerLocaleData(localeLv, 'lv', localeExtraLv); // Latvian
2900
- registerLocaleData(localeRu, 'ru', localeExtraRu); // Russian
2901
- registerLocaleData(localeIt, 'it', localeExtraIt); // Italian
2902
- registerLocaleData(localeSk, 'sk', localeExtraSk); // Slovak
2903
- registerLocaleData(localePl, 'pl', localeExtraPl); // Polish
2904
- registerLocaleData(localeUk, 'uk', localeExtraUk); // Ukrainian
2905
- registerLocaleData(localeJa, 'ja', localeExtraJa); // Japanese
2906
- registerLocaleData(localeKo, 'ko', localeExtraKo); // Korean
2907
- registerLocaleData(localeHi, 'hi', localeExtraHi); // Hindi
2908
- registerLocaleData(localeBn, 'bn', localeExtraBn); // Bengalese
2909
- registerLocaleData(localeVi, 'vi', localeExtraVi); // Vietnamese
2910
- registerLocaleData(localeTr, 'tr', localeExtraTr); // Turkish
2911
- registerLocaleData(localeNl, 'nl', localeExtraNl); // Dutch
2912
- registerLocaleData(localeNb, 'nb', localeExtraNb); // Norwegian
2913
- registerLocaleData(localeTh, 'th', localeExtraTh); // Thai
2914
- registerLocaleData(localeFi, 'fi', localeExtraFi); // Finnish
2915
- registerLocaleData(localeSv, 'sv', localeExtraSv); // Swedish
2916
- registerLocaleData(localeDeCh, 'de-CH', localeExtraDeCh); // German Swiss
2917
- }
2918
- /**
2919
- *
2920
- * @param string lang
2921
- * @returns Observable<Object>
2922
- */
2923
- getTranslation(lang) {
2924
- const t = (this.config.translations || []).map((path) => this.loadTranslationFile(path, lang));
2925
- return forkJoin(t).pipe(map((res) => res.reduce((acc, x) => Object.assign(acc, x), {})));
2926
- }
2927
- loadTranslationFile(path, lang) {
2928
- const version = document.body.dataset['bt'] ?? document.body.dataset['version'];
2929
- const cacheBuster = version ? `?v=${version}` : '';
2930
- return this.http.get(`${Utils.getBaseHref()}${path}${lang}.json${cacheBuster}`).pipe(catchError(() => {
2931
- // ISO codes with more than 2 characters are sub-languages like de-CH.
2932
- // If there is no translation file for that sub-language we'll try to load
2933
- // the file for the base language (in this case de).
2934
- return lang.length > 2 ? this.loadTranslationFile(path, lang.substring(0, 2)) : of({});
2935
- }));
2700
+ class AuditService {
2701
+ constructor() {
2702
+ this.#searchService = inject(SearchService);
2703
+ this.#userService = inject(UserService);
2704
+ // default number of items to be fetched
2705
+ this.DEFAULT_RES_SIZE = 20;
2706
+ // audit action codes that should be visible to regular users
2707
+ this.userAuditActions = [
2708
+ 100, // metadata created
2709
+ 101, // metadata created (with content)
2710
+ 110, // tag created
2711
+ 201, // content deleted
2712
+ 210, // tag deleted
2713
+ 300, // metadata updated
2714
+ 301, // content updated
2715
+ 302, // metadata and content updated
2716
+ 303, // content moved
2717
+ 310, // tag updated
2718
+ 325, // object restored form version
2719
+ 340, // object moved
2720
+ 10000 // custom audit entries
2721
+ ];
2722
+ // audit action codes that should be visible to admin users
2723
+ this.adminAuditActions = [
2724
+ 202, // marked for delete
2725
+ 220, // version deleted
2726
+ 400, // content read
2727
+ 401, // metadata read
2728
+ 402, // rendition read (text)
2729
+ 403, // rendition read (pdf)
2730
+ 404 // rendition read (thumbnail)
2731
+ ];
2936
2732
  }
2937
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxTranslateJsonLoader, deps: [{ token: i1.HttpClient }, { token: CORE_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
2938
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxTranslateJsonLoader }); }
2939
- }
2940
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxTranslateJsonLoader, decorators: [{
2941
- type: Injectable
2942
- }], ctorParameters: () => [{ type: i1.HttpClient }, { type: CoreConfig, decorators: [{
2943
- type: Inject,
2944
- args: [CORE_CONFIG]
2945
- }] }] });
2946
-
2947
- var Operator;
2948
- (function (Operator) {
2949
- Operator["EQUAL"] = "eq";
2950
- Operator["EEQUAL"] = "eeq";
2951
- Operator["IN"] = "in";
2952
- Operator["GREATER_THAN"] = "gt";
2953
- Operator["GREATER_OR_EQUAL"] = "gte";
2954
- Operator["LESS_THAN"] = "lt";
2955
- Operator["LESS_OR_EQUAL"] = "lte";
2956
- Operator["INTERVAL"] = "gtlt";
2957
- Operator["INTERVAL_INCLUDE_BOTH"] = "gtelte";
2958
- Operator["INTERVAL_INCLUDE_TO"] = "gtlte";
2959
- Operator["INTERVAL_INCLUDE_FROM"] = "gtelt";
2960
- Operator["RANGE"] = "rg";
2961
- Operator["LIKE"] = "like";
2962
- Operator["CONTAINS"] = "contains"; // contains
2963
- })(Operator || (Operator = {}));
2964
- const OperatorLabel = {
2965
- /** equal */
2966
- EQUAL: '=',
2967
- /** exact equal */
2968
- EEQUAL: '==',
2969
- /** match at least one of the provided values (value has to be an array) */
2970
- IN: '~',
2971
- /** greater than */
2972
- GREATER_THAN: '>',
2973
- /** greater than or equal */
2974
- GREATER_OR_EQUAL: '≽', //
2975
- LESS_THAN: '<', // less than
2976
- LESS_OR_EQUAL: '≼', // less than or equal
2977
- INTERVAL: '<>', // interval
2978
- INTERVAL_INCLUDE_BOTH: '-', // interval include left and right
2979
- INTERVAL_INCLUDE_TO: '>-', // interval include right
2980
- INTERVAL_INCLUDE_FROM: '-<', // interval include left
2981
- RANGE: '=', // aggegation ranges
2982
- LIKE: '~', // like
2983
- CONTAINS: '~' // contains
2984
- };
2985
-
2986
- class SearchService {
2987
- #backend = inject(BackendService);
2988
- #system = inject(SystemService);
2989
- static { this.DEFAULT_QUERY_SIZE = 50; }
2733
+ #searchService;
2734
+ #userService;
2990
2735
  /**
2991
- * Execute a search query ans transform the result to a SearchResult object
2992
- * @param query The search query
2993
- * @returns Observable of a SearchResult
2736
+ * Get audit entries of a dms object
2737
+ * @param id The id of the object to get the audit entries for
2738
+ * @param options Options
2994
2739
  */
2995
- search(query) {
2996
- return this.searchRaw(query).pipe(map((res) => this.toSearchResult(res, query.size || SearchService.DEFAULT_QUERY_SIZE, query.from || 0)));
2740
+ getAuditEntries(id, options = {}) {
2741
+ const auditActions = this.getAuditActions(!!options.allActions, options?.skipActions);
2742
+ const q = {
2743
+ size: this.DEFAULT_RES_SIZE,
2744
+ types: [SystemType.AUDIT],
2745
+ filters: [
2746
+ {
2747
+ f: AuditField.REFERRED_OBJECT_ID,
2748
+ o: Operator.EQUAL,
2749
+ v1: id
2750
+ },
2751
+ {
2752
+ f: AuditField.ACTION,
2753
+ o: Operator.IN,
2754
+ v1: auditActions
2755
+ }
2756
+ ],
2757
+ sort: [
2758
+ {
2759
+ field: AuditField.CREATION_DATE,
2760
+ order: 'desc'
2761
+ }
2762
+ ]
2763
+ };
2764
+ return this.#fetchAudits(q);
2997
2765
  }
2998
2766
  /**
2999
- * Execute a raw search query and return the result as is.
3000
- * @param query The search query
3001
- * @returns Observable of the raw search result
2767
+ * Get an array of action codes that are provided by the service. Based on
2768
+ * whether or not the user has admin permissions you'll get a different
2769
+ * set of actions.
2770
+ * @param skipActions codes of actions that should not be fetched
3002
2771
  */
3003
- searchRaw(query) {
3004
- if (!query.size)
3005
- query.size = SearchService.DEFAULT_QUERY_SIZE;
3006
- return this.#backend.post(`/dms/objects/search`, query);
2772
+ getAuditActions(allActions, skipActions) {
2773
+ const actions = allActions || this.#userService.isAdvancedUser ? [...this.userAuditActions, ...this.adminAuditActions] : this.userAuditActions;
2774
+ return actions.filter((a) => !skipActions || !skipActions.includes(a));
3007
2775
  }
3008
2776
  /**
3009
- * Search for objects in the dms using CMIS like SQL syntax.
3010
- * @param statement The query statement
3011
- * @param size The number of items to return
3012
- * @returns Observable of a SearchResult
2777
+ * Get a certain page for a former audits query.
2778
+ * @param auditsResult The result object of a former audits query
2779
+ * @param page The page to load
3013
2780
  */
3014
- searchCmis(statement, size = SearchService.DEFAULT_QUERY_SIZE, includePermissions = false) {
3015
- return this.#executeCmisSearch(statement, size, 0, includePermissions);
2781
+ getPage(auditsResult, page) {
2782
+ const q = auditsResult.query;
2783
+ q.from = (page - 1) * (q.size || this.DEFAULT_RES_SIZE);
2784
+ return this.#fetchAudits(q);
3016
2785
  }
3017
- #executeCmisSearch(statement, maxItems, skipCount = 0, includePermissions = false) {
3018
- const payload = {
3019
- query: {
3020
- statement,
3021
- skipCount,
3022
- maxItems,
3023
- handleDeletedDocuments: 'DELETED_DOCUMENTS_EXCLUDE'
3024
- }
3025
- };
3026
- if (includePermissions) {
3027
- payload.query.includePermissions = true;
2786
+ #fetchAudits(q) {
2787
+ return this.#searchService.searchRaw(q).pipe(map((res) => ({
2788
+ query: q,
2789
+ items: res.objects.map((o) => ({
2790
+ action: o.properties[AuditField.ACTION].value,
2791
+ actionGroup: this.#getActionGroup(o.properties[AuditField.ACTION].value),
2792
+ detail: o.properties[AuditField.DETAIL].value,
2793
+ subaction: o.properties[AuditField.SUBACTION] ? o.properties[AuditField.SUBACTION].value : null,
2794
+ version: o.properties[AuditField.VERSION].value,
2795
+ creationDate: o.properties[AuditField.CREATION_DATE].value,
2796
+ createdBy: {
2797
+ id: o.properties[AuditField.CREATED_BY].value,
2798
+ title: o.properties[AuditField.CREATED_BY].title
2799
+ }
2800
+ })),
2801
+ hasMoreItems: res.hasMoreItems,
2802
+ page: !q.from ? 1 : q.from / q.size + 1
2803
+ })));
2804
+ }
2805
+ #getActionGroup(action) {
2806
+ try {
2807
+ return parseInt(`${action}`.substr(0, 1));
2808
+ }
2809
+ catch {
2810
+ return -1;
3028
2811
  }
3029
- return this.#backend
3030
- .post('/dms/objects/cmisSearch', payload
3031
- // Using API-WEB because it enriches the response with additional information (resolved user names, etc.)
3032
- // ApiBase.core
3033
- )
3034
- .pipe(map((res) => this.toSearchResult(res, maxItems, skipCount)));
3035
2812
  }
3036
- /**
3037
- * Fetch aggragations for a given query.
3038
- * @param q The query
3039
- * @param aggregations List of aggregations to be fetched (e.g. `enaio:objectTypeId`
3040
- * to get an aggregation of object types)
3041
- */
3042
- aggregate(q, aggregations) {
3043
- q.aggs = aggregations;
3044
- return this.searchRaw(q).pipe(map((res) => this.#toAggregateResult(res, aggregations)));
2813
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuditService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2814
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuditService, providedIn: 'root' }); }
2815
+ }
2816
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuditService, decorators: [{
2817
+ type: Injectable,
2818
+ args: [{
2819
+ providedIn: 'root'
2820
+ }]
2821
+ }] });
2822
+
2823
+ /**
2824
+ * @ignore
2825
+ */
2826
+ const CUSTOM_CONFIG = new InjectionToken('CUSTOM_CONFIG', {
2827
+ factory: () => ({ main: ['assets/_yuuvis/config/main.json'], translations: ['assets/_yuuvis/i18n/'] })
2828
+ });
2829
+ const CORE_CONFIG = new InjectionToken('CORE_CONFIG');
2830
+
2831
+ /**
2832
+ * @ignore
2833
+ */
2834
+ let CoreConfig = class CoreConfig {
2835
+ constructor(__config) {
2836
+ this.main = ['assets/_yuuvis/config/main.json'];
2837
+ this.translations = ['assets/_yuuvis/i18n/'];
2838
+ this.environment = { production: true };
2839
+ Object.assign(this, __config);
2840
+ }
2841
+ };
2842
+ CoreConfig = __decorate([
2843
+ __param(0, Inject(CUSTOM_CONFIG)),
2844
+ __metadata("design:paramtypes", [CoreConfig])
2845
+ ], CoreConfig);
2846
+
2847
+ /**
2848
+ * Service managing the configuarions for object types.
2849
+ * Configuration means that you can pick certain fields
2850
+ * of an object type and assign them to a config object.
2851
+ */
2852
+ class ObjectConfigService {
2853
+ constructor() {
2854
+ this.#system = inject(SystemService);
2855
+ this.#user = inject(UserService);
2856
+ this.OBJECT_CONFIG_STORAGE_KEY = 'yuv.framework.object-config';
2857
+ this.#objectConfigsSource = new ReplaySubject();
2858
+ this.#objectConfigs$ = this.#objectConfigsSource.asObservable();
2859
+ this.#defaultObjectConfigs = {};
2860
+ this.#registeredDefaultObjectConfigs = {
2861
+ main: {},
2862
+ buckets: []
2863
+ };
2864
+ this.#objectConfigs = {
2865
+ main: {},
2866
+ buckets: []
2867
+ };
2868
+ }
2869
+ #system;
2870
+ #user;
2871
+ #objectConfigsSource;
2872
+ #objectConfigs$;
2873
+ #defaultObjectConfigs;
2874
+ #registeredDefaultObjectConfigs;
2875
+ #objectConfigs;
2876
+ #isValidObjectConfig(soc) {
2877
+ return coerceBooleanProperty(soc && Object.hasOwn(soc, 'main') && Object.hasOwn(soc, 'buckets'));
2878
+ }
2879
+ // called on core init (auth.service -> initApp)
2880
+ init() {
2881
+ return this.#user.loadObjectConfig().pipe(tap$1((res) => {
2882
+ this.#getDefaultObjectConfig();
2883
+ this.#objectConfigs = (this.#isValidObjectConfig(res) && res) || {
2884
+ main: {},
2885
+ buckets: []
2886
+ };
2887
+ this.#objectConfigsSource.next(this.#objectConfigs);
2888
+ }));
2889
+ }
2890
+ getObjectConfigs$(bucket, includeDefaults) {
2891
+ return this.#objectConfigs$.pipe(map$1((soc) => {
2892
+ let ocr = soc.main;
2893
+ const defaults = { ...this.#defaultObjectConfigs, ...this.#getRegisteredDefaults(bucket) };
2894
+ if (bucket) {
2895
+ const tileBucket = soc.buckets.find((b) => b.id === bucket);
2896
+ if (tileBucket) {
2897
+ ocr = tileBucket.configs;
2898
+ if (includeDefaults)
2899
+ Object.keys(ocr).forEach((k) => {
2900
+ ocr[k] = { ...defaults[k], ...ocr[k] };
2901
+ });
2902
+ }
2903
+ }
2904
+ return includeDefaults ? { ...defaults, ...ocr } : { ...ocr };
2905
+ }));
3045
2906
  }
3046
2907
  /**
3047
- * Map search result from the backend to applications AggregateResult object
3048
- * @param searchResponse The backend response
3049
- * @param aggregations The aggregations to be fetched
2908
+ * Register default object configs. Apps can pre-define how their
2909
+ * objects should be rendered.
2910
+ * @param bucket Name of a bucket to bind configs to (usually the ID of the app) to
2911
+ * separate the configs from other apps. If not provided configs will be saved for
2912
+ * the global scope
3050
2913
  */
3051
- #toAggregateResult(searchResponse, aggregations) {
3052
- const agg = [];
3053
- if (aggregations) {
3054
- aggregations.forEach((a) => {
3055
- const ag = {
3056
- aggKey: a,
3057
- entries: searchResponse.objects.map((o) => ({
3058
- key: o.properties[a].value,
3059
- count: o.properties['OBJECT_COUNT'].value
3060
- }))
2914
+ registerDefaults(configs, bucket) {
2915
+ if (bucket) {
2916
+ // does bucket exist?
2917
+ const tileBucketIdx = this.#registeredDefaultObjectConfigs.buckets.findIndex((b) => b.id === bucket);
2918
+ if (tileBucketIdx !== -1) {
2919
+ this.#registeredDefaultObjectConfigs.buckets[tileBucketIdx].configs = {
2920
+ ...this.#registeredDefaultObjectConfigs.buckets[tileBucketIdx].configs,
2921
+ ...configs
3061
2922
  };
3062
- agg.push(ag);
2923
+ }
2924
+ else {
2925
+ this.#registeredDefaultObjectConfigs.buckets.push({
2926
+ id: bucket,
2927
+ configs
2928
+ });
2929
+ }
2930
+ }
2931
+ else {
2932
+ this.#registeredDefaultObjectConfigs.main = { ...this.#registeredDefaultObjectConfigs.main, ...configs };
2933
+ }
2934
+ }
2935
+ unregisterDefaults(configs, bucket) {
2936
+ if (bucket) {
2937
+ // does bucket exist?
2938
+ const tileBucketIdx = this.#registeredDefaultObjectConfigs.buckets.findIndex((b) => b.id === bucket);
2939
+ if (tileBucketIdx !== -1) {
2940
+ this.#registeredDefaultObjectConfigs.buckets.splice(tileBucketIdx, 1);
2941
+ }
2942
+ }
2943
+ else {
2944
+ Object.keys(configs).forEach((key) => {
2945
+ delete this.#registeredDefaultObjectConfigs.main[key];
3063
2946
  });
3064
2947
  }
3065
- return {
3066
- totalNumItems: searchResponse.totalNumItems,
3067
- aggregations: agg
3068
- };
3069
2948
  }
3070
- /**
3071
- * Go to a page of a search result.
3072
- * @param query SearchQuery or CMIS query statement
3073
- * @param page The number of the page to go to
3074
- */
3075
- getPage(query, page, pageSize = SearchService.DEFAULT_QUERY_SIZE, includePermissions = false) {
3076
- const isCmis = typeof query === 'string';
3077
- if (isCmis) {
3078
- return this.#executeCmisSearch(query, pageSize, (page - 1) * pageSize, includePermissions);
2949
+ getRegisteredDefault(objectTypeId, bucket) {
2950
+ if (bucket) {
2951
+ const b = this.#registeredDefaultObjectConfigs.buckets.find((b) => b.id === bucket);
2952
+ return b ? b.configs[objectTypeId] : undefined;
3079
2953
  }
3080
2954
  else {
3081
- query.from = (page - 1) * (query.size || pageSize);
3082
- return this.search(query);
2955
+ return this.#registeredDefaultObjectConfigs.main[objectTypeId];
2956
+ }
2957
+ }
2958
+ #getRegisteredDefaults(bucket) {
2959
+ if (bucket) {
2960
+ const defaultTileBucket = this.#registeredDefaultObjectConfigs.buckets.find((b) => b.id === bucket);
2961
+ return defaultTileBucket?.configs || {};
3083
2962
  }
2963
+ else
2964
+ return this.#registeredDefaultObjectConfigs.main;
3084
2965
  }
3085
2966
  /**
3086
- * Map search result from the backend to applications SearchResult object
3087
- * @param searchResponse The backend response
2967
+ * Get object config for an object type.
2968
+ * @param objectTypeId ID of the object type to get the config for
2969
+ * @param bucket Optional bucket ID to fetch the config from. Buckets
2970
+ * define separated areas to store/receive those configs. This way
2971
+ * components/apps can store different object configs than the ones
2972
+ * used accross the whole client application.
3088
2973
  */
3089
- toSearchResult(searchResponse, pageSize = SearchService.DEFAULT_QUERY_SIZE, skipCount = 0) {
3090
- const resultListItems = [];
3091
- const objectTypes = [];
3092
- searchResponse.objects.forEach((o) => {
3093
- const fields = new Map();
3094
- // process properties section of result
3095
- Object.keys(o.properties).forEach((key) => {
3096
- let value = o.properties[key].value;
3097
- if (o.properties[key].clvalue) {
3098
- // table fields will have a clientValue too ...
3099
- value = o.properties[key].clvalue;
3100
- // ... and also may contain values that need to be resolved
3101
- if (o.properties[key].resolvedValues) {
3102
- value.forEach((v) => {
3103
- Object.keys(v).forEach((k) => {
3104
- const resValue = Array.isArray(v[k]) ? v[k].map((i) => o.properties[key].resolvedValues[i]) : o.properties[key].resolvedValues[v[k]];
3105
- if (resValue) {
3106
- v[`${k}_title`] = resValue;
3107
- }
3108
- });
3109
- });
3110
- }
3111
- }
3112
- fields.set(key, value);
3113
- if (o.properties[key].title) {
3114
- fields.set(key + '_title', o.properties[key].title);
3115
- }
3116
- });
3117
- // process contentStreams section of result if available.
3118
- // Objects that don't have files attached won't have this section
3119
- let content;
3120
- if (o.contentStreams && o.contentStreams.length > 0) {
3121
- // we assume that each result object only has ONE file attached, altough
3122
- // this is an array and there may be more
3123
- const contentStream = o.contentStreams[0];
3124
- // also add contentstream related fields to the result fields
3125
- fields.set(ContentStreamField.LENGTH, contentStream.length);
3126
- fields.set(ContentStreamField.MIME_TYPE, contentStream.mimeType);
3127
- fields.set(ContentStreamField.FILENAME, contentStream.fileName);
3128
- fields.set(ContentStreamField.ID, contentStream.contentStreamId);
3129
- fields.set(ContentStreamField.RANGE, contentStream.contentStreamRange);
3130
- fields.set(ContentStreamField.REPOSITORY_ID, contentStream.repositoryId);
3131
- fields.set(ContentStreamField.DIGEST, contentStream.digest);
3132
- fields.set(ContentStreamField.ARCHIVE_PATH, contentStream.archivePath);
3133
- content = {
3134
- contentStreamId: contentStream.contentStreamId,
3135
- repositoryId: contentStream.repositoryId,
3136
- range: contentStream.range,
3137
- digest: contentStream.digest,
3138
- archivePath: contentStream.archivePath,
3139
- fileName: contentStream.fileName,
3140
- mimeType: contentStream.mimeType,
3141
- size: contentStream.length
3142
- };
3143
- }
3144
- const objectTypeId = o.properties[BaseObjectTypeField.OBJECT_TYPE_ID] ? o.properties[BaseObjectTypeField.OBJECT_TYPE_ID].value : null;
3145
- if (objectTypes.indexOf(objectTypeId) === -1) {
3146
- objectTypes.push(objectTypeId);
3147
- }
3148
- resultListItems.push({
3149
- objectTypeId,
3150
- content,
3151
- fields,
3152
- permissions: o.permissions
3153
- });
3154
- });
3155
- const totalPages = Math.ceil(searchResponse.totalNumItems / pageSize);
3156
- const page = (!skipCount ? 0 : skipCount / pageSize) + 1;
3157
- const result = {
3158
- hasMoreItems: searchResponse.hasMoreItems,
3159
- totalNumItems: searchResponse.totalNumItems,
3160
- items: resultListItems,
3161
- objectTypes: objectTypes,
3162
- paging: totalPages > 1 ? { page, totalPages } : undefined
3163
- };
2974
+ getObjectConfig(type, bucket) {
2975
+ const b = bucket ? this.#objectConfigs.buckets.find((b) => b.id === bucket) : undefined;
2976
+ const otr = b ? b.configs : this.#objectConfigs.main;
2977
+ const oc = otr[type.id];
2978
+ // return oc || this.#getRegisteredDefaults(bucket)[type.id];
2979
+ // override icons by APP_SCHEMA-icons: temporary solution. Todo: remove when handling of custom svg-icons is clear!
2980
+ const result = oc || this.#getRegisteredDefaults(bucket)[type.id] || this.#defaultObjectConfigs[type.id];
2981
+ if (result?.icon)
2982
+ result.icon = type.icon ? { svg: type.icon } : result.icon;
3164
2983
  return result;
3165
2984
  }
3166
- /**
3167
- * Maps data extracted from a search form to search filters. Every key of the form data object
3168
- * will be mapped to a search filter.
3169
- * @param formData form data
3170
- * @returns Array of search filters
3171
- */
3172
- formDataToSearchFilter(formData) {
3173
- const isRangeValue = (v) => {
3174
- return typeof v === 'object' && v !== null && 'firstValue' in v && 'operator' in v;
3175
- };
3176
- const filters = [];
3177
- Object.keys(formData).forEach((key) => {
3178
- const value = formData[key];
3179
- if (isRangeValue(value)) {
3180
- const f = this.rangeValueToSearchFilter(value, key);
3181
- if (f)
3182
- filters.push(f);
2985
+ saveObjectConfig(type, config, bucket) {
2986
+ return this.saveObjectConfigs({ [type.id]: config }, bucket);
2987
+ }
2988
+ saveObjectConfigs(configs, bucket) {
2989
+ this.#updateObjectConfig(configs, bucket);
2990
+ return this.#user.saveObjectConfig(this.#objectConfigs);
2991
+ }
2992
+ #updateObjectConfig(configs, bucket) {
2993
+ if (!this.#objectConfigs) {
2994
+ this.#objectConfigs = {
2995
+ main: {},
2996
+ buckets: []
2997
+ };
2998
+ }
2999
+ if (bucket) {
3000
+ // does bucket exist?
3001
+ const tileBucketIdx = this.#objectConfigs.buckets.findIndex((b) => b.id === bucket);
3002
+ if (tileBucketIdx !== -1) {
3003
+ Object.keys(configs).forEach((objectTypeId) => {
3004
+ const config = configs[objectTypeId];
3005
+ if (config) {
3006
+ this.#objectConfigs.buckets[tileBucketIdx].configs[objectTypeId] = config;
3007
+ }
3008
+ else {
3009
+ delete this.#objectConfigs.buckets[tileBucketIdx].configs[objectTypeId];
3010
+ }
3011
+ });
3183
3012
  }
3184
3013
  else {
3185
- filters.push({
3186
- f: key,
3187
- o: Array.isArray(value) ? Operator.IN : Operator.EQUAL,
3188
- v1: value
3014
+ const newBucket = { id: bucket, configs: {} };
3015
+ Object.keys(configs).forEach((objectTypeId) => {
3016
+ const config = configs[objectTypeId];
3017
+ if (config) {
3018
+ newBucket.configs[objectTypeId] = config;
3019
+ }
3189
3020
  });
3021
+ this.#objectConfigs.buckets.push(newBucket);
3190
3022
  }
3191
- });
3192
- return filters;
3023
+ }
3024
+ else {
3025
+ Object.keys(configs).forEach((objectTypeId) => {
3026
+ const config = configs[objectTypeId];
3027
+ if (config) {
3028
+ this.#objectConfigs.main[objectTypeId] = config;
3029
+ }
3030
+ else {
3031
+ delete this.#objectConfigs.main[objectTypeId];
3032
+ }
3033
+ });
3034
+ }
3035
+ this.#objectConfigsSource.next(this.#objectConfigs);
3193
3036
  }
3194
- rangeValueToSearchFilter(value, property) {
3195
- return this.#system.getObjectTypeField(property)?.propertyType === 'datetime'
3196
- ? this.#dateRangeValueToSearchFilter(value, property)
3197
- : {
3198
- f: property,
3199
- o: value.operator,
3200
- v1: value.firstValue,
3201
- v2: value.secondValue
3202
- };
3037
+ getDefaultConfig(objectTypeID) {
3038
+ return this.#defaultObjectConfigs[objectTypeID];
3203
3039
  }
3204
- #dateRangeValueToSearchFilter(rv, property) {
3205
- const v1 = rv.firstValue;
3206
- const v2 = rv.secondValue;
3207
- switch (rv.operator) {
3208
- case Operator.EQUAL: {
3209
- return {
3210
- f: property,
3211
- o: Operator.INTERVAL_INCLUDE_BOTH,
3212
- v1: this.#dateToISOString(v1, 'start'),
3213
- v2: this.#dateToISOString(v1, 'end')
3214
- };
3215
- }
3216
- case Operator.GREATER_OR_EQUAL: {
3217
- return {
3218
- f: property,
3219
- o: rv.operator,
3220
- v1: this.#dateToISOString(v1)
3221
- };
3222
- }
3223
- case Operator.LESS_OR_EQUAL: {
3224
- return {
3225
- f: property,
3226
- o: rv.operator,
3227
- v1: this.#dateToISOString(v1, 'end')
3228
- };
3229
- }
3230
- case Operator.INTERVAL_INCLUDE_BOTH: {
3231
- return {
3232
- f: property,
3233
- o: rv.operator,
3234
- v1: this.#dateToISOString(v1, 'start'),
3235
- v2: v2 ? this.#dateToISOString(v2, 'end') : ''
3236
- };
3237
- }
3238
- default:
3239
- return undefined;
3240
- }
3040
+ getResolvedObjectConfig(data, type, bucket, includeDefaults) {
3041
+ const defaultCfg = this.#defaultObjectConfigs[data[BaseObjectTypeField.OBJECT_TYPE_ID]];
3042
+ let oc = this.getObjectConfig(type, bucket) || defaultCfg;
3043
+ if (includeDefaults)
3044
+ oc = { ...defaultCfg, ...oc };
3045
+ const res = {
3046
+ id: data[BaseObjectTypeField.OBJECT_ID],
3047
+ objectTypeId: data[BaseObjectTypeField.OBJECT_TYPE_ID],
3048
+ actions: oc.actions,
3049
+ badges: oc.badges,
3050
+ instanceData: data
3051
+ };
3052
+ if (oc.title)
3053
+ res.title = {
3054
+ propertyName: oc.title.propertyName,
3055
+ value: data[oc.title.propertyName]
3056
+ };
3057
+ if (oc.description)
3058
+ res.description = {
3059
+ propertyName: oc.description.propertyName,
3060
+ value: data[oc.description.propertyName]
3061
+ };
3062
+ if (oc.icon)
3063
+ res.icon = {
3064
+ propertyName: 'custom',
3065
+ value: oc.icon.svg
3066
+ };
3067
+ if (oc.meta)
3068
+ res.meta = {
3069
+ propertyName: oc.meta.propertyName,
3070
+ value: data[oc.meta.propertyName]
3071
+ };
3072
+ if (oc.aside)
3073
+ res.aside = {
3074
+ propertyName: oc.aside.propertyName,
3075
+ value: data[oc.aside.propertyName]
3076
+ };
3077
+ return res;
3241
3078
  }
3242
3079
  /**
3243
- * Checks if value is a date and retrieves an ISOString representation. Optionally you can determine, if it should be the beginning or the end of a day.
3244
- *
3245
- * @param value
3246
- * @param range
3247
- * @private
3080
+ * Load default/fallbac configs based on the object types and
3081
+ * their first properties
3248
3082
  */
3249
- #dateToISOString(value, range = 'start') {
3250
- const date = typeof value === 'string' ? new Date(value) : value;
3251
- const offset = Utils.DEFAULT_TIME_OFFSET_V2; // T+23:59:59:999
3252
- if (!Utils.isValidDate(date))
3253
- return '';
3254
- const isoDateString = date.toISOString();
3255
- // const isoDateStringWithOffset = new Date(date.getTime() + offset).toISOString();
3256
- switch (range) {
3257
- case 'start':
3258
- return isoDateString;
3259
- case 'end':
3260
- return isoDateString;
3261
- default:
3262
- return isoDateString;
3263
- }
3083
+ #getDefaultObjectConfig() {
3084
+ this.#system.getObjectTypes(true).forEach((ot) => {
3085
+ this.#defaultObjectConfigs[ot.id] = {
3086
+ objectTypeId: ot.id,
3087
+ title: this.#toObjectProperty(ot.fields[0]),
3088
+ description: this.#toObjectProperty(ot.fields[1]),
3089
+ meta: this.#toObjectProperty(ot.fields[2]),
3090
+ aside: this.#toObjectProperty(ot.fields[3])
3091
+ };
3092
+ });
3264
3093
  }
3265
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SearchService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3266
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SearchService, providedIn: 'root' }); }
3094
+ #toObjectProperty(field) {
3095
+ return field
3096
+ ? {
3097
+ label: this.#system.getLocalizedLabel(field.id),
3098
+ propertyName: field.id
3099
+ }
3100
+ : undefined;
3101
+ }
3102
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3103
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectConfigService, providedIn: 'root' }); }
3267
3104
  }
3268
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: SearchService, decorators: [{
3105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ObjectConfigService, decorators: [{
3269
3106
  type: Injectable,
3270
3107
  args: [{
3271
3108
  providedIn: 'root'
3272
3109
  }]
3273
3110
  }] });
3274
3111
 
3112
+ const YUV_USER = new InjectionToken('Currently logged in user', {
3113
+ factory: () => undefined
3114
+ });
3115
+ const provideUser = (user) => {
3116
+ return { provide: YUV_USER, useValue: user };
3117
+ };
3118
+
3275
3119
  /**
3276
- * Service providing access to the systems audit entries. Audits can be seen as the history of
3277
- * an object. Actions perormed on an object (eg. read, write, delete, ...) will be recorded during
3278
- * the objects lifecycle. Audits are provided based on a users permissions. Beside the audit entries
3279
- * visible to regular users there are more technical ones that will only be shown to users that
3280
- * have administrative role.
3120
+ * Service handling authentication related issues.
3281
3121
  */
3282
- class AuditService {
3283
- constructor() {
3284
- this.#searchService = inject(SearchService);
3122
+ class AuthService {
3123
+ // user may have been fetched already in bootstrapping process
3124
+ #userToken;
3125
+ #eventService;
3126
+ #userService;
3127
+ #objectConfigService;
3128
+ #appCache;
3129
+ #systemService;
3130
+ #localizationService;
3131
+ #backend;
3132
+ #INITIAL_REQUEST_STORAGE_KEY;
3133
+ #USER_FETCH_URI;
3134
+ #authenticated;
3135
+ #authSource;
3136
+ #authData;
3137
+ constructor(coreConfig) {
3138
+ this.coreConfig = coreConfig;
3139
+ // user may have been fetched already in bootstrapping process
3140
+ this.#userToken = inject(YUV_USER);
3141
+ this.#eventService = inject(EventService);
3285
3142
  this.#userService = inject(UserService);
3286
- // default number of items to be fetched
3287
- this.DEFAULT_RES_SIZE = 20;
3288
- // audit action codes that should be visible to regular users
3289
- this.userAuditActions = [
3290
- 100, // metadata created
3291
- 101, // metadata created (with content)
3292
- 110, // tag created
3293
- 201, // content deleted
3294
- 210, // tag deleted
3295
- 300, // metadata updated
3296
- 301, // content updated
3297
- 302, // metadata and content updated
3298
- 303, // content moved
3299
- 310, // tag updated
3300
- 325, // object restored form version
3301
- 340, // object moved
3302
- 10000 // custom audit entries
3303
- ];
3304
- // audit action codes that should be visible to admin users
3305
- this.adminAuditActions = [
3306
- 202, // marked for delete
3307
- 220, // version deleted
3308
- 400, // content read
3309
- 401, // metadata read
3310
- 402, // rendition read (text)
3311
- 403, // rendition read (pdf)
3312
- 404 // rendition read (thumbnail)
3313
- ];
3143
+ this.#objectConfigService = inject(ObjectConfigService);
3144
+ this.#appCache = inject(AppCacheService);
3145
+ this.#systemService = inject(SystemService);
3146
+ this.#localizationService = inject(LocalizationService);
3147
+ this.#backend = inject(BackendService);
3148
+ this.#INITIAL_REQUEST_STORAGE_KEY = 'yuv.core.auth.initialrequest';
3149
+ this.#USER_FETCH_URI = '/idm/whoami';
3150
+ this.#authenticated = false;
3151
+ this.#authSource = new BehaviorSubject(false);
3152
+ this.authenticated$ = this.#authSource.asObservable();
3153
+ }
3154
+ isLoggedIn() {
3155
+ return this.#authenticated;
3314
3156
  }
3315
- #searchService;
3316
- #userService;
3317
3157
  /**
3318
- * Get audit entries of a dms object
3319
- * @param id The id of the object to get the audit entries for
3320
- * @param options Options
3158
+ * Called while app/core is initialized (APP_INITIALIZER)
3159
+ * @ignore
3321
3160
  */
3322
- getAuditEntries(id, options = {}) {
3323
- const auditActions = this.getAuditActions(!!options.allActions, options?.skipActions);
3324
- const q = {
3325
- size: this.DEFAULT_RES_SIZE,
3326
- types: [SystemType.AUDIT],
3327
- filters: [
3328
- {
3329
- f: AuditField.REFERRED_OBJECT_ID,
3330
- o: Operator.EQUAL,
3331
- v1: id
3332
- },
3333
- {
3334
- f: AuditField.ACTION,
3335
- o: Operator.IN,
3336
- v1: auditActions
3337
- }
3338
- ],
3339
- sort: [
3340
- {
3341
- field: AuditField.CREATION_DATE,
3342
- order: 'desc'
3343
- }
3344
- ]
3345
- };
3346
- return this.#fetchAudits(q);
3161
+ initUser() {
3162
+ return this.fetchUser();
3347
3163
  }
3348
3164
  /**
3349
- * Get an array of action codes that are provided by the service. Based on
3350
- * whether or not the user has admin permissions you'll get a different
3351
- * set of actions.
3352
- * @param skipActions codes of actions that should not be fetched
3165
+ * Get the current tenant or the previous one persisted locally
3353
3166
  */
3354
- getAuditActions(allActions, skipActions) {
3355
- const actions = allActions || this.#userService.isAdvancedUser ? [...this.userAuditActions, ...this.adminAuditActions] : this.userAuditActions;
3356
- return actions.filter((a) => !skipActions || !skipActions.includes(a));
3167
+ getTenant() {
3168
+ return this.#authData?.tenant;
3357
3169
  }
3358
3170
  /**
3359
- * Get a certain page for a former audits query.
3360
- * @param auditsResult The result object of a former audits query
3361
- * @param page The page to load
3171
+ * Fetch information about the user currently logged in
3362
3172
  */
3363
- getPage(auditsResult, page) {
3364
- const q = auditsResult.query;
3365
- q.from = (page - 1) * (q.size || this.DEFAULT_RES_SIZE);
3366
- return this.#fetchAudits(q);
3173
+ fetchUser() {
3174
+ return (this.#userToken ? of(this.#userToken) : this.#backend.get(this.#USER_FETCH_URI)).pipe(tap(() => {
3175
+ this.#authenticated = true;
3176
+ this.#authSource.next(this.#authenticated);
3177
+ }), switchMap((userJson) => this.#initApp(userJson)));
3367
3178
  }
3368
- #fetchAudits(q) {
3369
- return this.#searchService.searchRaw(q).pipe(map((res) => ({
3370
- query: q,
3371
- items: res.objects.map((o) => ({
3372
- action: o.properties[AuditField.ACTION].value,
3373
- actionGroup: this.#getActionGroup(o.properties[AuditField.ACTION].value),
3374
- detail: o.properties[AuditField.DETAIL].value,
3375
- subaction: o.properties[AuditField.SUBACTION] ? o.properties[AuditField.SUBACTION].value : null,
3376
- version: o.properties[AuditField.VERSION].value,
3377
- creationDate: o.properties[AuditField.CREATION_DATE].value,
3378
- createdBy: {
3379
- id: o.properties[AuditField.CREATED_BY].value,
3380
- title: o.properties[AuditField.CREATED_BY].title
3381
- }
3382
- })),
3383
- hasMoreItems: res.hasMoreItems,
3384
- page: !q.from ? 1 : q.from / q.size + 1
3385
- })));
3179
+ /**
3180
+ * Logs out the current user.
3181
+ */
3182
+ logout() {
3183
+ this.#authenticated = false;
3184
+ this.#authSource.next(this.#authenticated);
3185
+ this.#eventService.trigger(YuvEventType.LOGOUT);
3386
3186
  }
3387
- #getActionGroup(action) {
3388
- try {
3389
- return parseInt(`${action}`.substr(0, 1));
3390
- }
3391
- catch {
3392
- return -1;
3187
+ /**
3188
+ * Persists the initial request URI. This is nessesary to be able to
3189
+ * redirect the user to the requested page after authentication.
3190
+ * This is done on app initialization (core-init).
3191
+ */
3192
+ setInitialRequestUri() {
3193
+ const ignore = ['/', '/index.html'];
3194
+ const baseHref = Utils.getBaseHref();
3195
+ // Check only the pathname against the ignore list so that auth callback
3196
+ // query params (e.g. ?session_state=…) don't bypass the check.
3197
+ let path = location.pathname.replace(baseHref, '');
3198
+ path = !path.startsWith('/') ? `/${path}` : path;
3199
+ if (!ignore.includes(path)) {
3200
+ let uri = `${location.pathname}${location.search}`.replace(baseHref, '');
3201
+ uri = !uri.startsWith('/') ? `/${uri}` : uri;
3202
+ return this.#appCache.setItem(this.#INITIAL_REQUEST_STORAGE_KEY, {
3203
+ uri: uri,
3204
+ timestamp: Date.now()
3205
+ });
3393
3206
  }
3207
+ return of(false);
3208
+ }
3209
+ /**
3210
+ * Get the URL that entered the app. May be a deep link that could then be
3211
+ * picked up again after user has been authenticated.
3212
+ */
3213
+ getInitialRequestUri() {
3214
+ return this.#appCache.getItem(this.#INITIAL_REQUEST_STORAGE_KEY);
3215
+ }
3216
+ resetInitialRequestUri() {
3217
+ return this.#appCache.removeItem(this.#INITIAL_REQUEST_STORAGE_KEY);
3218
+ }
3219
+ /**
3220
+ * Initialize/setup the application for a given user. This involves fetching
3221
+ * settings and schema information.
3222
+ * @param userJson Data retrieved from the backend
3223
+ */
3224
+ #initApp(userJson) {
3225
+ return this.#userService.fetchUserSettings().pipe(switchMap((userSettings) => {
3226
+ const currentUser = new YuvUser(userJson, userSettings);
3227
+ this.#userService.setCurrentUser(currentUser);
3228
+ this.#authData = {
3229
+ tenant: currentUser.tenant,
3230
+ language: currentUser.getClientLocale()
3231
+ };
3232
+ return forkJoin([
3233
+ this.#localizationService.fetchLocalizations(),
3234
+ this.#systemService.getSystemDefinition(this.#authData)
3235
+ ]).pipe(switchMap(() => this.#objectConfigService.init()), map(() => currentUser));
3236
+ }));
3394
3237
  }
3395
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuditService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3396
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuditService, providedIn: 'root' }); }
3238
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuthService, deps: [{ token: CORE_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
3239
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuthService, providedIn: 'root' }); }
3397
3240
  }
3398
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuditService, decorators: [{
3241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: AuthService, decorators: [{
3399
3242
  type: Injectable,
3400
3243
  args: [{
3401
3244
  providedIn: 'root'
3402
3245
  }]
3403
- }] });
3246
+ }], ctorParameters: () => [{ type: CoreConfig, decorators: [{
3247
+ type: Inject,
3248
+ args: [CORE_CONFIG]
3249
+ }] }] });
3250
+
3251
+ /**
3252
+ * Prevent app from running into 401 issues related to gateway timeouts.
3253
+ */
3254
+ function AuthInterceptorFnc(req, next) {
3255
+ const userService = inject(UserService);
3256
+ const auth = inject(AuthService);
3257
+ return next(req).pipe(tap({
3258
+ next: (event) => {
3259
+ if (event instanceof HttpResponse) {
3260
+ // do stuff with response if you want
3261
+ }
3262
+ },
3263
+ error: (error) => {
3264
+ if (error instanceof HttpErrorResponse || error.isHttpErrorResponse) {
3265
+ if (error.status === 401) {
3266
+ auth.logout();
3267
+ userService.logout();
3268
+ }
3269
+ }
3270
+ }
3271
+ }));
3272
+ }
3273
+
3274
+ var LoginStateName;
3275
+ (function (LoginStateName) {
3276
+ LoginStateName["STATE_LOGIN_URI"] = "login.uri";
3277
+ LoginStateName["STATE_DONE"] = "login.done";
3278
+ LoginStateName["STATE_CANCELED"] = "login.canceled";
3279
+ })(LoginStateName || (LoginStateName = {}));
3404
3280
 
3405
3281
  const ProcessAction = {
3406
3282
  complete: 'complete',
@@ -3453,6 +3329,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
3453
3329
  }]
3454
3330
  }] });
3455
3331
 
3332
+ const LOCALIZATION_COLUMNS = ['locale', 'label', 'description'];
3333
+
3456
3334
  // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
3457
3335
  const transformResponse = () => map((res) => (res.body ? res.body.objects.map((val) => val) : null));
3458
3336
  /**
@@ -4352,8 +4230,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
4352
4230
  }]
4353
4231
  }] });
4354
4232
 
4355
- const LOCALIZATION_COLUMNS = ['locale', 'label', 'description'];
4356
-
4357
4233
  class CatalogService {
4358
4234
  constructor() {
4359
4235
  this.#backend = inject(BackendService);
@@ -4476,7 +4352,11 @@ class CatalogService {
4476
4352
  #getCatalogs() {
4477
4353
  return this.#search.searchCmis(`SELECT * FROM ${SystemType.CATALOG}`, 1000).pipe(map$1((res) => res.items.map((item) => ({
4478
4354
  objectId: item.fields.get(BaseObjectTypeField.OBJECT_ID),
4479
- name: item.fields.get(CatalogTypeField.NATIVE_ID)
4355
+ name: item.fields.get(CatalogTypeField.NATIVE_ID),
4356
+ raw: Array.from(item.fields).reduce((acc, [key, wrapper]) => {
4357
+ acc[key] = wrapper;
4358
+ return acc;
4359
+ }, {})
4480
4360
  }))));
4481
4361
  }
4482
4362
  /** Loads the catalog list, sorts by name and publishes to the `catalogs` signal. */
@@ -4530,16 +4410,35 @@ class CatalogService {
4530
4410
  totalNumItems: response.totalNumItems
4531
4411
  };
4532
4412
  }
4413
+ #getEntryState(properties) {
4414
+ const validFrom = this.#prop(properties, CatalogTypeField.DATE_VALID_FROM);
4415
+ const validUntil = this.#prop(properties, CatalogTypeField.DATE_VALID_UNTIL);
4416
+ const now = new Date().toISOString();
4417
+ if (validFrom && now < validFrom) {
4418
+ return 'upcoming';
4419
+ }
4420
+ if (validUntil && now > validUntil) {
4421
+ return 'expired';
4422
+ }
4423
+ return 'active';
4424
+ }
4533
4425
  #mapEntry(apiObject) {
4534
4426
  const props = apiObject.properties;
4535
4427
  return {
4428
+ _state: this.#getEntryState(props),
4536
4429
  objectId: this.#prop(props, BaseObjectTypeField.OBJECT_ID),
4537
4430
  name: this.#prop(props, CatalogTypeField.NATIVE_ID),
4538
4431
  catalogName: this.#prop(props, CatalogTypeField.CATALOG_NATIVE_ID),
4539
4432
  localizations: this.#parseLocalization(props[CatalogTypeField.LOCALIZATION]),
4540
4433
  validFrom: this.#prop(props, CatalogTypeField.DATE_VALID_FROM),
4541
4434
  validUntil: this.#prop(props, CatalogTypeField.DATE_VALID_UNTIL),
4542
- properties: this.#extractCustomProperties(props)
4435
+ properties: this.#extractCustomProperties(props),
4436
+ raw: Object.entries(props).reduce((acc, [key, wrapper]) => {
4437
+ if (wrapper && typeof wrapper === 'object' && 'value' in wrapper) {
4438
+ acc[key] = wrapper.value;
4439
+ }
4440
+ return acc;
4441
+ }, {})
4543
4442
  };
4544
4443
  }
4545
4444
  #prop(properties, key) {
@@ -4743,6 +4642,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
4743
4642
  }]
4744
4643
  }] });
4745
4644
 
4645
+ /**
4646
+ * Service to monitor the online/offline state of the application.
4647
+ * It listens to the browser's online and offline events and provides an observable
4648
+ * to track the current connection state.
4649
+ *
4650
+ * An observable `connection$` is provided which emits the current connection state
4651
+ * whenever the online or offline state changes. The initial state is determined by
4652
+ * the `window.navigator.onLine` property.
4653
+ */
4654
+ class ConnectionService {
4655
+ /**
4656
+ * @ignore
4657
+ */
4658
+ constructor() {
4659
+ this.currentState = {
4660
+ isOnline: window.navigator.onLine
4661
+ };
4662
+ this.connectionStateSource = new ReplaySubject();
4663
+ this.connection$ = this.connectionStateSource.asObservable();
4664
+ this.connectionStateSource.next(this.currentState);
4665
+ fromEvent(window, 'online').subscribe(() => {
4666
+ this.currentState.isOnline = true;
4667
+ this.connectionStateSource.next(this.currentState);
4668
+ });
4669
+ fromEvent(window, 'offline').subscribe(() => {
4670
+ this.currentState.isOnline = false;
4671
+ this.connectionStateSource.next(this.currentState);
4672
+ });
4673
+ }
4674
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ConnectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4675
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ConnectionService, providedIn: 'root' }); }
4676
+ }
4677
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: ConnectionService, decorators: [{
4678
+ type: Injectable,
4679
+ args: [{
4680
+ providedIn: 'root'
4681
+ }]
4682
+ }], ctorParameters: () => [] });
4683
+
4684
+ /**
4685
+ * Http Offline interceptor trys to serving offline content for method/url
4686
+ */
4687
+ function OfflineInterceptorFnc(req, next) {
4688
+ return next(req);
4689
+ }
4690
+
4691
+ /**
4692
+ * Providing functions,that are are injected at application startup and executed during app initialization.
4693
+ */
4694
+ const init_moduleFnc = () => {
4695
+ const coreConfig = inject(CORE_CONFIG);
4696
+ const logger = inject(Logger);
4697
+ const http = inject(HttpClient);
4698
+ const configService = inject(ConfigService);
4699
+ const authService = inject(AuthService);
4700
+ return authService.setInitialRequestUri().pipe(switchMap(() => coreConfig.main
4701
+ ? !Array.isArray(coreConfig.main)
4702
+ ? of([coreConfig.main])
4703
+ : forkJoin([...coreConfig.main].map((uri) => http.get(`${Utils.getBaseHref()}${uri}`).pipe(catchError((e) => {
4704
+ logger.error('failed to catch config file', e);
4705
+ return of({});
4706
+ }), map((res) => res)))).pipe(
4707
+ // switchMap((configs: YuvConfig[]) => configService.extendConfig(configs)),
4708
+ tap((configs) => configService.extendConfig(configs)), switchMap(() => authService.initUser().pipe(catchError((e) => {
4709
+ authService.initError = {
4710
+ status: e.status,
4711
+ key: e.error.error
4712
+ };
4713
+ return of(true);
4714
+ }))))
4715
+ : of(false)));
4716
+ };
4717
+
4718
+ /**
4719
+ * Handles missing translations
4720
+ * @ignore
4721
+ */
4722
+ class EoxMissingTranslationHandler {
4723
+ handle(params) {
4724
+ return '!missing key: ' + params.key;
4725
+ }
4726
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxMissingTranslationHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4727
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxMissingTranslationHandler }); }
4728
+ }
4729
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxMissingTranslationHandler, decorators: [{
4730
+ type: Injectable
4731
+ }] });
4732
+
4733
+ /**
4734
+ * i18n packages
4735
+ */
4736
+ /**
4737
+ * Loader that fetches translations based on the configured locations
4738
+ * @ignore
4739
+ */
4740
+ class EoxTranslateJsonLoader {
4741
+ constructor(http, config) {
4742
+ this.http = http;
4743
+ this.config = config;
4744
+ registerLocaleData(localeDe, 'de', localeExtraDe); // German
4745
+ registerLocaleData(localeAr, 'ar', localeExtraAr); // Arabic
4746
+ registerLocaleData(localeEs, 'es', localeExtraEs); // Spanish
4747
+ registerLocaleData(localePt, 'pt', localeExtraPt); // Portuguese
4748
+ registerLocaleData(localeFr, 'fr', localeExtraFr); // French
4749
+ registerLocaleData(localeZh, 'zh', localeExtraZh); // Chinese
4750
+ registerLocaleData(localeLv, 'lv', localeExtraLv); // Latvian
4751
+ registerLocaleData(localeRu, 'ru', localeExtraRu); // Russian
4752
+ registerLocaleData(localeIt, 'it', localeExtraIt); // Italian
4753
+ registerLocaleData(localeSk, 'sk', localeExtraSk); // Slovak
4754
+ registerLocaleData(localePl, 'pl', localeExtraPl); // Polish
4755
+ registerLocaleData(localeUk, 'uk', localeExtraUk); // Ukrainian
4756
+ registerLocaleData(localeJa, 'ja', localeExtraJa); // Japanese
4757
+ registerLocaleData(localeKo, 'ko', localeExtraKo); // Korean
4758
+ registerLocaleData(localeHi, 'hi', localeExtraHi); // Hindi
4759
+ registerLocaleData(localeBn, 'bn', localeExtraBn); // Bengalese
4760
+ registerLocaleData(localeVi, 'vi', localeExtraVi); // Vietnamese
4761
+ registerLocaleData(localeTr, 'tr', localeExtraTr); // Turkish
4762
+ registerLocaleData(localeNl, 'nl', localeExtraNl); // Dutch
4763
+ registerLocaleData(localeNb, 'nb', localeExtraNb); // Norwegian
4764
+ registerLocaleData(localeTh, 'th', localeExtraTh); // Thai
4765
+ registerLocaleData(localeFi, 'fi', localeExtraFi); // Finnish
4766
+ registerLocaleData(localeSv, 'sv', localeExtraSv); // Swedish
4767
+ registerLocaleData(localeDeCh, 'de-CH', localeExtraDeCh); // German Swiss
4768
+ }
4769
+ /**
4770
+ *
4771
+ * @param string lang
4772
+ * @returns Observable<Object>
4773
+ */
4774
+ getTranslation(lang) {
4775
+ const t = (this.config.translations || []).map((path) => this.loadTranslationFile(path, lang));
4776
+ return forkJoin(t).pipe(map((res) => res.reduce((acc, x) => Object.assign(acc, x), {})));
4777
+ }
4778
+ loadTranslationFile(path, lang) {
4779
+ const version = document.body.dataset['bt'] ?? document.body.dataset['version'];
4780
+ const cacheBuster = version ? `?v=${version}` : '';
4781
+ return this.http.get(`${Utils.getBaseHref()}${path}${lang}.json${cacheBuster}`).pipe(catchError(() => {
4782
+ // ISO codes with more than 2 characters are sub-languages like de-CH.
4783
+ // If there is no translation file for that sub-language we'll try to load
4784
+ // the file for the base language (in this case de).
4785
+ return lang.length > 2 ? this.loadTranslationFile(path, lang.substring(0, 2)) : of({});
4786
+ }));
4787
+ }
4788
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxTranslateJsonLoader, deps: [{ token: i1.HttpClient }, { token: CORE_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
4789
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxTranslateJsonLoader }); }
4790
+ }
4791
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: EoxTranslateJsonLoader, decorators: [{
4792
+ type: Injectable
4793
+ }], ctorParameters: () => [{ type: i1.HttpClient }, { type: CoreConfig, decorators: [{
4794
+ type: Inject,
4795
+ args: [CORE_CONFIG]
4796
+ }] }] });
4797
+
4746
4798
  var DeviceScreenOrientation;
4747
4799
  (function (DeviceScreenOrientation) {
4748
4800
  DeviceScreenOrientation["PORTRAIT"] = "portrait";
@@ -5508,6 +5560,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
5508
5560
  }]
5509
5561
  }] });
5510
5562
 
5563
+ /**
5564
+ * Interface to be implemented by all components (state components) that are aware of pending changes
5565
+ */
5566
+
5511
5567
  /**
5512
5568
  * EditingObserver service is used to track changes made inside the application, that should prevent
5513
5569
  * doing something or going somewhere before the changes are persisted or ignored. For example when
@@ -5627,27 +5683,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
5627
5683
  }]
5628
5684
  }] });
5629
5685
 
5630
- /**
5631
- * Providing a `PendingChangesComponent`.
5632
- */
5633
- class PendingChangesGuard {
5634
- constructor(pendingChanges) {
5635
- this.pendingChanges = pendingChanges;
5636
- }
5637
- canDeactivate(component) {
5638
- // if there are no pending changes, just allow deactivation; else confirm first
5639
- return !this.pendingChanges.check(component);
5640
- }
5641
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PendingChangesGuard, deps: [{ token: PendingChangesService }], target: i0.ɵɵFactoryTarget.Injectable }); }
5642
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PendingChangesGuard, providedIn: 'root' }); }
5643
- }
5644
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PendingChangesGuard, decorators: [{
5645
- type: Injectable,
5646
- args: [{
5647
- providedIn: 'root'
5648
- }]
5649
- }], ctorParameters: () => [{ type: PendingChangesService }] });
5650
-
5651
5686
  /**
5652
5687
  * Service that provides guards for Material Dialog close operations.
5653
5688
  *
@@ -5803,6 +5838,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImpo
5803
5838
  type: Injectable
5804
5839
  }] });
5805
5840
 
5841
+ /**
5842
+ * Providing a `PendingChangesComponent`.
5843
+ */
5844
+ class PendingChangesGuard {
5845
+ constructor(pendingChanges) {
5846
+ this.pendingChanges = pendingChanges;
5847
+ }
5848
+ canDeactivate(component) {
5849
+ // if there are no pending changes, just allow deactivation; else confirm first
5850
+ return !this.pendingChanges.check(component);
5851
+ }
5852
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PendingChangesGuard, deps: [{ token: PendingChangesService }], target: i0.ɵɵFactoryTarget.Injectable }); }
5853
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PendingChangesGuard, providedIn: 'root' }); }
5854
+ }
5855
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.12", ngImport: i0, type: PendingChangesGuard, decorators: [{
5856
+ type: Injectable,
5857
+ args: [{
5858
+ providedIn: 'root'
5859
+ }]
5860
+ }], ctorParameters: () => [{ type: PendingChangesService }] });
5861
+
5806
5862
  class TabGuardDirective {
5807
5863
  #tabGroup = inject(MatTabGroup);
5808
5864
  #pending = inject(PendingChangesService);
@@ -6556,5 +6612,5 @@ const provideYuvClientCore = (options = { translations: [] }, customEvents, cust
6556
6612
  * Generated bundle index. Do not edit.
6557
6613
  */
6558
6614
 
6559
- export { AFO_STATE, AVAILABLE_BACKEND_APPS, AdministrationRoles, ApiBase, AppCacheService, AuditField, AuditService, AuthInterceptorFnc, AuthService, BackendService, BaseObjectTypeField, BpmService, CLIENT_APP_REQUIREMENTS, CORE_CONFIG, CUSTOM_CONFIG, CUSTOM_YUV_EVENT_PREFIX, CatalogService, CatalogTypeField, Classification, ClassificationPrefix, ClientCacheService, ClientDefaultsObjectTypeField, ClipboardService, ColumnConfigSkipFields, ConfigService, ConnectionService, ContentStreamAllowed, ContentStreamField, CoreConfig, DEFAULT_LOCK_OPTIONS, DeviceScreenOrientation, DeviceService, DialogCloseGuard, Direction, DmsObject, DmsService, EoxMissingTranslationHandler, EoxTranslateJsonLoader, EventService, FileSizePipe, IdmService, InternalFieldType, KeysPipe, LOCALIZATION_COLUMNS, LOCK_TAG_OWNER_INDEX, LocaleCurrencyPipe, LocaleDatePipe, LocaleDecimalPipe, LocaleNumberPipe, LocalePercentPipe, LocalizationService, LockField, Logger, LoginStateName, NativeNotificationService, NotificationService, ObjectConfigService, ObjectFormControl, ObjectFormControlWrapper, ObjectFormGroup, ObjectLockingService, ObjectTag, ObjectTypeClassification, ObjectTypePropertyClassification, OfflineInterceptorFnc, Operator, OperatorLabel, ParentField, PendingChangesGuard, PendingChangesService, PredictionService, ProcessAction, RelationshipTypeField, RetentionField, RetentionService, SafeHtmlPipe, SafeUrlPipe, SearchService, SearchUtils, SecondaryObjectTypeClassification, SessionStorageService, Situation, Sort, SystemResult, SystemSOT, SystemService, SystemType, TENANT_HEADER, TabGuardDirective, ToastService, UploadService, UserRoles, UserService, UserStorageService, Utils, YUV_USER, YuvError, YuvEventType, YuvUser, findLockTag, init_moduleFnc, provideAvailabilityManagement, provideBeforeUnloadProtection, provideNavigationProtection, providePopstateDialogProtection, provideRequirements, provideUser, provideYuvClientCore };
6615
+ export { AFO_STATE, AVAILABLE_BACKEND_APPS, AdministrationRoles, ApiBase, AppCacheService, AuditAction, AuditField, AuditService, AuthInterceptorFnc, AuthService, BackendService, BaseObjectTypeField, BpmService, CLIENT_APP_REQUIREMENTS, CORE_CONFIG, CUSTOM_CONFIG, CUSTOM_EVENTS, CUSTOM_EVENTS_TRUSTED_ORIGINS, CUSTOM_YUV_EVENT_PREFIX, CatalogService, CatalogTypeField, Classification, ClassificationPrefix, ClientCacheService, ClientDefaultsObjectTypeField, ClipboardService, ColumnConfigSkipFields, ConfigService, ConnectionService, ContentStreamAllowed, ContentStreamField, CoreConfig, DEFAULT_LOCK_OPTIONS, DeviceScreenOrientation, DeviceService, DialogCloseGuard, Direction, DmsObject, DmsService, EoxMissingTranslationHandler, EoxTranslateJsonLoader, EventService, FileSizePipe, IdmService, InternalFieldType, KeysPipe, LOCALIZATION_COLUMNS, LOCK_TAG_OWNER_INDEX, LocaleCurrencyPipe, LocaleDatePipe, LocaleDecimalPipe, LocaleNumberPipe, LocalePercentPipe, LocalizationService, LockField, Logger, LoginStateName, NativeNotificationService, NotificationService, ObjectConfigService, ObjectFormControl, ObjectFormControlWrapper, ObjectFormGroup, ObjectLockingService, ObjectTag, ObjectTypeClassification, ObjectTypePropertyClassification, OfflineInterceptorFnc, Operator, OperatorLabel, ParentField, PendingChangesGuard, PendingChangesService, PredictionService, ProcessAction, RelationshipTypeField, RetentionField, RetentionService, SafeHtmlPipe, SafeUrlPipe, SearchService, SearchUtils, SecondaryObjectTypeClassification, SessionStorageService, Situation, Sort, SystemResult, SystemSOT, SystemService, SystemType, TENANT_HEADER, TabGuardDirective, ToastService, UploadService, UserRoles, UserService, UserStorageService, Utils, YUV_USER, YuvError, YuvEventType, YuvToastStyles, YuvUser, findLockTag, init_moduleFnc, provideAvailabilityManagement, provideBeforeUnloadProtection, provideNavigationProtection, providePopstateDialogProtection, provideRequirements, provideUser, provideYuvClientCore };
6560
6616
  //# sourceMappingURL=yuuvis-client-core.mjs.map