mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[AC-2086] Limit admin access - Collection Modal (#8335)
* feat: add view collection string, update button text, refs AC-2086 * feat: remove canEdit from Restricted Collection Access component, refs AC-2086 * feat: add view collection clicked flow, refs AC-2086 * fix: revert accidental svg icon changes, refs AC-2086 * feat: add input for access selector to hide multi select, refs AC-2086 * feat: apply readonly/disabled changes to access dialog, refs AC-2086 * fix: messages file conflict, refs AC-2086 * feat: apply disabled state to access selector, refs AC-2086 * fix: formatting, refs AC-2086 * fix: permission mode read only relocate, refs AC-2086 * fix: conform readonly casing, refs AC-2086
This commit is contained in:
@@ -22,7 +22,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
<bit-form-field class="tw-grow">
|
<bit-form-field class="tw-grow" *ngIf="!disabled">
|
||||||
<bit-label>{{ selectorLabelText }}</bit-label>
|
<bit-label>{{ selectorLabelText }}</bit-label>
|
||||||
<bit-multi-select
|
<bit-multi-select
|
||||||
class="tw-w-full"
|
class="tw-w-full"
|
||||||
@@ -120,7 +120,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
*ngIf="item.readonly"
|
*ngIf="item.readonly || disabled"
|
||||||
class="tw-max-w-40 tw-overflow-hidden tw-overflow-ellipsis tw-whitespace-nowrap tw-font-bold tw-text-muted"
|
class="tw-max-w-40 tw-overflow-hidden tw-overflow-ellipsis tw-whitespace-nowrap tw-font-bold tw-text-muted"
|
||||||
[title]="permissionLabelId(item.readonlyPermission) | i18n"
|
[title]="permissionLabelId(item.readonlyPermission) | i18n"
|
||||||
>
|
>
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
|
|
||||||
<td bitCell class="tw-text-right">
|
<td bitCell class="tw-text-right">
|
||||||
<button
|
<button
|
||||||
*ngIf="!item.readonly"
|
*ngIf="!disabled && !item.readonly"
|
||||||
type="button"
|
type="button"
|
||||||
bitIconButton="bwi-close"
|
bitIconButton="bwi-close"
|
||||||
buttonType="muted"
|
buttonType="muted"
|
||||||
|
|||||||
@@ -121,6 +121,13 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
|||||||
protected permissionList: Permission[];
|
protected permissionList: Permission[];
|
||||||
protected initialPermission = CollectionPermission.View;
|
protected initialPermission = CollectionPermission.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When disabled, the access selector will make the assumption that a readonly state is desired.
|
||||||
|
* The PermissionMode will be set to Readonly
|
||||||
|
* The Multi-Select control will be hidden
|
||||||
|
* The delete action on each row item will be hidden
|
||||||
|
* The readonly permission label/property needs to configured on the access item views being passed into the component
|
||||||
|
*/
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -225,6 +232,7 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
|||||||
|
|
||||||
// Keep the internal FormGroup in sync
|
// Keep the internal FormGroup in sync
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
|
this.permissionMode = PermissionMode.Readonly;
|
||||||
this.formGroup.disable();
|
this.formGroup.disable();
|
||||||
} else {
|
} else {
|
||||||
this.formGroup.enable();
|
this.formGroup.enable();
|
||||||
|
|||||||
@@ -65,17 +65,22 @@
|
|||||||
</bit-tab>
|
</bit-tab>
|
||||||
<bit-tab label="{{ 'access' | i18n }}">
|
<bit-tab label="{{ 'access' | i18n }}">
|
||||||
<div class="tw-mb-3" *ngIf="organization.flexibleCollections">
|
<div class="tw-mb-3" *ngIf="organization.flexibleCollections">
|
||||||
<span *ngIf="organization.useGroups">{{ "grantCollectionAccess" | i18n }}</span>
|
<ng-container *ngIf="dialogReadonly">
|
||||||
<span *ngIf="!organization.useGroups">{{
|
<span>{{ "readOnlyCollectionAccess" | i18n }}</span>
|
||||||
"grantCollectionAccessMembersOnly" | i18n
|
</ng-container>
|
||||||
}}</span>
|
<ng-container *ngIf="!dialogReadonly">
|
||||||
<span
|
<span *ngIf="organization.useGroups">{{ "grantCollectionAccess" | i18n }}</span>
|
||||||
*ngIf="
|
<span *ngIf="!organization.useGroups">{{
|
||||||
(flexibleCollectionsV1Enabled$ | async) &&
|
"grantCollectionAccessMembersOnly" | i18n
|
||||||
organization.allowAdminAccessToAllCollectionItems
|
}}</span>
|
||||||
"
|
<span
|
||||||
>{{ " " + ("adminCollectionAccess" | i18n) }}</span
|
*ngIf="
|
||||||
>
|
(flexibleCollectionsV1Enabled$ | async) &&
|
||||||
|
organization.allowAdminAccessToAllCollectionItems
|
||||||
|
"
|
||||||
|
>{{ " " + ("adminCollectionAccess" | i18n) }}</span
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="tw-mb-3 tw-text-danger"
|
class="tw-mb-3 tw-text-danger"
|
||||||
@@ -85,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<bit-access-selector
|
<bit-access-selector
|
||||||
*ngIf="organization.useGroups"
|
*ngIf="organization.useGroups"
|
||||||
[permissionMode]="PermissionMode.Edit"
|
[permissionMode]="dialogReadonly ? PermissionMode.Readonly : PermissionMode.Edit"
|
||||||
formControlName="access"
|
formControlName="access"
|
||||||
[items]="accessItems"
|
[items]="accessItems"
|
||||||
[columnHeader]="'groupSlashMemberColumnHeader' | i18n"
|
[columnHeader]="'groupSlashMemberColumnHeader' | i18n"
|
||||||
@@ -96,7 +101,7 @@
|
|||||||
></bit-access-selector>
|
></bit-access-selector>
|
||||||
<bit-access-selector
|
<bit-access-selector
|
||||||
*ngIf="!organization.useGroups"
|
*ngIf="!organization.useGroups"
|
||||||
[permissionMode]="PermissionMode.Edit"
|
[permissionMode]="dialogReadonly ? PermissionMode.Readonly : PermissionMode.Edit"
|
||||||
formControlName="access"
|
formControlName="access"
|
||||||
[items]="accessItems"
|
[items]="accessItems"
|
||||||
[columnHeader]="'memberColumnHeader' | i18n"
|
[columnHeader]="'memberColumnHeader' | i18n"
|
||||||
@@ -108,7 +113,13 @@
|
|||||||
</bit-tab-group>
|
</bit-tab-group>
|
||||||
</div>
|
</div>
|
||||||
<ng-container bitDialogFooter>
|
<ng-container bitDialogFooter>
|
||||||
<button type="submit" bitButton bitFormButton buttonType="primary" [disabled]="loading">
|
<button
|
||||||
|
type="submit"
|
||||||
|
bitButton
|
||||||
|
bitFormButton
|
||||||
|
buttonType="primary"
|
||||||
|
[disabled]="loading || dialogReadonly"
|
||||||
|
>
|
||||||
{{ "save" | i18n }}
|
{{ "save" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { first } from "rxjs/operators";
|
|||||||
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||||
|
import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses/organization-user.response";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
@@ -27,7 +28,11 @@ import { CollectionResponse } from "@bitwarden/common/vault/models/response/coll
|
|||||||
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
|
||||||
import { BitValidators, DialogService } from "@bitwarden/components";
|
import { BitValidators, DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
import { GroupService, GroupView } from "../../../admin-console/organizations/core";
|
import {
|
||||||
|
GroupService,
|
||||||
|
GroupView,
|
||||||
|
CollectionAccessSelectionView,
|
||||||
|
} from "../../../admin-console/organizations/core";
|
||||||
import { PermissionMode } from "../../../admin-console/organizations/shared/components/access-selector/access-selector.component";
|
import { PermissionMode } from "../../../admin-console/organizations/shared/components/access-selector/access-selector.component";
|
||||||
import {
|
import {
|
||||||
AccessItemType,
|
AccessItemType,
|
||||||
@@ -36,8 +41,6 @@ import {
|
|||||||
CollectionPermission,
|
CollectionPermission,
|
||||||
convertToPermission,
|
convertToPermission,
|
||||||
convertToSelectionView,
|
convertToSelectionView,
|
||||||
mapGroupToAccessItemView,
|
|
||||||
mapUserToAccessItemView,
|
|
||||||
} from "../../../admin-console/organizations/shared/components/access-selector/access-selector.models";
|
} from "../../../admin-console/organizations/shared/components/access-selector/access-selector.models";
|
||||||
import { CollectionAdminService } from "../../core/collection-admin.service";
|
import { CollectionAdminService } from "../../core/collection-admin.service";
|
||||||
import { CollectionAdminView } from "../../core/views/collection-admin.view";
|
import { CollectionAdminView } from "../../core/views/collection-admin.view";
|
||||||
@@ -54,6 +57,7 @@ export interface CollectionDialogParams {
|
|||||||
parentCollectionId?: string;
|
parentCollectionId?: string;
|
||||||
showOrgSelector?: boolean;
|
showOrgSelector?: boolean;
|
||||||
collectionIds?: string[];
|
collectionIds?: string[];
|
||||||
|
readonly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollectionDialogResult {
|
export interface CollectionDialogResult {
|
||||||
@@ -158,7 +162,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
? from(this.collectionAdminService.get(orgId, this.params.collectionId))
|
? from(this.collectionAdminService.get(orgId, this.params.collectionId))
|
||||||
: of(null),
|
: of(null),
|
||||||
groups: groups$,
|
groups: groups$,
|
||||||
users: this.organizationUserService.getAllUsers(orgId),
|
// Collection(s) needed to map readonlypermission for (potential) access selector disabled state
|
||||||
|
users: this.organizationUserService.getAllUsers(orgId, { includeCollections: true }),
|
||||||
collection: this.params.collectionId
|
collection: this.params.collectionId
|
||||||
? this.collectionService.get(this.params.collectionId)
|
? this.collectionService.get(this.params.collectionId)
|
||||||
: of(null),
|
: of(null),
|
||||||
@@ -177,8 +182,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
}) => {
|
}) => {
|
||||||
this.organization = organization;
|
this.organization = organization;
|
||||||
this.accessItems = [].concat(
|
this.accessItems = [].concat(
|
||||||
groups.map(mapGroupToAccessItemView),
|
groups.map((group) => mapGroupToAccessItemView(group, this.collectionId)),
|
||||||
users.data.map(mapUserToAccessItemView),
|
users.data.map((user) => mapUserToAccessItemView(user, this.collectionId)),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Force change detection to update the access selector's items
|
// Force change detection to update the access selector's items
|
||||||
@@ -209,7 +214,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
access: accessSelections,
|
access: accessSelections,
|
||||||
});
|
});
|
||||||
this.collection.manage = collection?.manage ?? false; // Get manage flag from sync data collection
|
this.collection.manage = collection?.manage ?? false; // Get manage flag from sync data collection
|
||||||
this.showDeleteButton = this.collection.canDelete(organization);
|
this.showDeleteButton = !this.dialogReadonly && this.collection.canDelete(organization);
|
||||||
} else {
|
} else {
|
||||||
this.nestOptions = collections;
|
this.nestOptions = collections;
|
||||||
const parent = collections.find((c) => c.id === this.params.parentCollectionId);
|
const parent = collections.find((c) => c.id === this.params.parentCollectionId);
|
||||||
@@ -244,6 +249,8 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.formGroup.controls.access.updateValueAndValidity();
|
this.formGroup.controls.access.updateValueAndValidity();
|
||||||
|
|
||||||
|
this.handleFormGroupReadonly(this.dialogReadonly);
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -257,11 +264,20 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
return this.params.collectionId != undefined;
|
return this.params.collectionId != undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected get dialogReadonly() {
|
||||||
|
return this.params.readonly === true;
|
||||||
|
}
|
||||||
|
|
||||||
protected async cancel() {
|
protected async cancel() {
|
||||||
this.close(CollectionDialogAction.Canceled);
|
this.close(CollectionDialogAction.Canceled);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected submit = async () => {
|
protected submit = async () => {
|
||||||
|
// Saving a collection is prohibited while in read only mode
|
||||||
|
if (this.dialogReadonly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.formGroup.markAllAsTouched();
|
this.formGroup.markAllAsTouched();
|
||||||
|
|
||||||
if (this.formGroup.invalid) {
|
if (this.formGroup.invalid) {
|
||||||
@@ -316,6 +332,11 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected delete = async () => {
|
protected delete = async () => {
|
||||||
|
// Deleting a collection is prohibited while in read only mode
|
||||||
|
if (this.dialogReadonly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
title: this.collection?.name,
|
title: this.collection?.name,
|
||||||
content: { key: "deleteCollectionConfirmation" },
|
content: { key: "deleteCollectionConfirmation" },
|
||||||
@@ -342,6 +363,20 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
|||||||
this.destroy$.complete();
|
this.destroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleFormGroupReadonly(readonly: boolean) {
|
||||||
|
if (readonly) {
|
||||||
|
this.formGroup.controls.name.disable();
|
||||||
|
this.formGroup.controls.externalId.disable();
|
||||||
|
this.formGroup.controls.parent.disable();
|
||||||
|
this.formGroup.controls.access.disable();
|
||||||
|
} else {
|
||||||
|
this.formGroup.controls.name.enable();
|
||||||
|
this.formGroup.controls.externalId.enable();
|
||||||
|
this.formGroup.controls.parent.enable();
|
||||||
|
this.formGroup.controls.access.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private close(action: CollectionDialogAction, collection?: CollectionResponse | CollectionView) {
|
private close(action: CollectionDialogAction, collection?: CollectionResponse | CollectionView) {
|
||||||
this.dialogRef.close({ action, collection } as CollectionDialogResult);
|
this.dialogRef.close({ action, collection } as CollectionDialogResult);
|
||||||
}
|
}
|
||||||
@@ -383,6 +418,50 @@ function validateCanManagePermission(control: AbstractControl) {
|
|||||||
return hasManagePermission ? null : { managePermissionRequired: true };
|
return hasManagePermission ? null : { managePermissionRequired: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param group Current group being used to translate object into AccessItemView
|
||||||
|
* @param collectionId Current collection being viewed/edited
|
||||||
|
* @returns AccessItemView customized to set a readonlyPermission to be displayed if the access selector is in a disabled state
|
||||||
|
*/
|
||||||
|
function mapGroupToAccessItemView(group: GroupView, collectionId: string): AccessItemView {
|
||||||
|
return {
|
||||||
|
id: group.id,
|
||||||
|
type: AccessItemType.Group,
|
||||||
|
listName: group.name,
|
||||||
|
labelName: group.name,
|
||||||
|
accessAllItems: group.accessAll,
|
||||||
|
readonly: group.accessAll,
|
||||||
|
readonlyPermission: convertToPermission(group.collections.find((gc) => gc.id == collectionId)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param user Current user being used to translate object into AccessItemView
|
||||||
|
* @param collectionId Current collection being viewed/edited
|
||||||
|
* @returns AccessItemView customized to set a readonlyPermission to be displayed if the access selector is in a disabled state
|
||||||
|
*/
|
||||||
|
function mapUserToAccessItemView(
|
||||||
|
user: OrganizationUserUserDetailsResponse,
|
||||||
|
collectionId: string,
|
||||||
|
): AccessItemView {
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
type: AccessItemType.Member,
|
||||||
|
email: user.email,
|
||||||
|
role: user.type,
|
||||||
|
listName: user.name?.length > 0 ? `${user.name} (${user.email})` : user.email,
|
||||||
|
labelName: user.name ?? user.email,
|
||||||
|
status: user.status,
|
||||||
|
accessAllItems: user.accessAll,
|
||||||
|
readonly: user.accessAll,
|
||||||
|
readonlyPermission: convertToPermission(
|
||||||
|
new CollectionAccessSelectionView(user.collections.find((uc) => uc.id == collectionId)),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strongly typed helper to open a CollectionDialog
|
* Strongly typed helper to open a CollectionDialog
|
||||||
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
* @param dialogService Instance of the dialog service that will be used to open the dialog
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, EventEmitter, Output } from "@angular/core";
|
||||||
|
|
||||||
import { ButtonModule, NoItemsModule, svgIcon } from "@bitwarden/components";
|
import { ButtonModule, NoItemsModule, svgIcon } from "@bitwarden/components";
|
||||||
|
|
||||||
@@ -16,21 +16,18 @@ const icon = svgIcon`<svg xmlns="http://www.w3.org/2000/svg" width="120" height=
|
|||||||
template: `<bit-no-items [icon]="icon" class="tw-mt-2 tw-block">
|
template: `<bit-no-items [icon]="icon" class="tw-mt-2 tw-block">
|
||||||
<span slot="title" class="tw-mt-4 tw-block">{{ "collectionAccessRestricted" | i18n }}</span>
|
<span slot="title" class="tw-mt-4 tw-block">{{ "collectionAccessRestricted" | i18n }}</span>
|
||||||
<button
|
<button
|
||||||
*ngIf="canEdit"
|
|
||||||
slot="button"
|
slot="button"
|
||||||
bitButton
|
bitButton
|
||||||
(click)="editInfoClicked.emit()"
|
(click)="viewCollectionClicked.emit()"
|
||||||
buttonType="secondary"
|
buttonType="secondary"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<i aria-hidden="true" class="bwi bwi-pencil-square"></i> {{ "editInfo" | i18n }}
|
<i aria-hidden="true" class="bwi bwi-pencil-square"></i> {{ "viewCollection" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
</bit-no-items>`,
|
</bit-no-items>`,
|
||||||
})
|
})
|
||||||
export class CollectionAccessRestrictedComponent {
|
export class CollectionAccessRestrictedComponent {
|
||||||
protected icon = icon;
|
protected icon = icon;
|
||||||
|
|
||||||
@Input() canEdit = false;
|
@Output() viewCollectionClicked = new EventEmitter<void>();
|
||||||
|
|
||||||
@Output() editInfoClicked = new EventEmitter<void>();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,11 +99,9 @@
|
|||||||
</bit-no-items>
|
</bit-no-items>
|
||||||
<collection-access-restricted
|
<collection-access-restricted
|
||||||
*ngIf="showCollectionAccessRestricted"
|
*ngIf="showCollectionAccessRestricted"
|
||||||
[canEdit]="
|
(viewCollectionClicked)="
|
||||||
selectedCollection != null &&
|
editCollection(selectedCollection.node, CollectionDialogTabType.Info, true)
|
||||||
selectedCollection.node.canEdit(organization, flexibleCollectionsV1Enabled)
|
|
||||||
"
|
"
|
||||||
(editInfoClicked)="editCollection(selectedCollection.node, CollectionDialogTabType.Info)"
|
|
||||||
>
|
>
|
||||||
</collection-access-restricted>
|
</collection-access-restricted>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -1058,9 +1058,18 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async editCollection(c: CollectionView, tab: CollectionDialogTabType): Promise<void> {
|
async editCollection(
|
||||||
|
c: CollectionView,
|
||||||
|
tab: CollectionDialogTabType,
|
||||||
|
readonly: boolean = false,
|
||||||
|
): Promise<void> {
|
||||||
const dialog = openCollectionDialog(this.dialogService, {
|
const dialog = openCollectionDialog(this.dialogService, {
|
||||||
data: { collectionId: c?.id, organizationId: this.organization?.id, initialTab: tab },
|
data: {
|
||||||
|
collectionId: c?.id,
|
||||||
|
organizationId: this.organization?.id,
|
||||||
|
initialTab: tab,
|
||||||
|
readonly: readonly,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await lastValueFrom(dialog.closed);
|
const result = await lastValueFrom(dialog.closed);
|
||||||
|
|||||||
@@ -7501,6 +7501,9 @@
|
|||||||
"collectionAccessRestricted": {
|
"collectionAccessRestricted": {
|
||||||
"message": "Collection access is restricted"
|
"message": "Collection access is restricted"
|
||||||
},
|
},
|
||||||
|
"readOnlyCollectionAccess": {
|
||||||
|
"message": "You do not have access to manage this collection."
|
||||||
|
},
|
||||||
"grantCollectionAccess": {
|
"grantCollectionAccess": {
|
||||||
"message": "Grant groups or members access to this collection."
|
"message": "Grant groups or members access to this collection."
|
||||||
},
|
},
|
||||||
@@ -7603,6 +7606,9 @@
|
|||||||
"providerPortal": {
|
"providerPortal": {
|
||||||
"message": "Provider Portal"
|
"message": "Provider Portal"
|
||||||
},
|
},
|
||||||
|
"viewCollection": {
|
||||||
|
"message": "View collection"
|
||||||
|
},
|
||||||
"restrictedGroupAccess": {
|
"restrictedGroupAccess": {
|
||||||
"message": "You cannot add yourself to groups."
|
"message": "You cannot add yourself to groups."
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user