mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
[PM-25481] Update copy in Admin-Console export-page (#16594)
* add support for export-scope-callout.component to conditionally render organizational export message • use config service to capture feature flag status • use platform service and routing to determine admin console context
This commit is contained in:
@@ -3198,6 +3198,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Error"
|
"message": "Error"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2654,6 +2654,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"message": "Locked"
|
"message": "Locked"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7038,6 +7038,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": {
|
||||||
|
"message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"accessDenied": {
|
"accessDenied": {
|
||||||
"message": "Access denied. You do not have permission to view this page."
|
"message": "Access denied. You do not have permission to view this page."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ import { Component, effect, input } from "@angular/core";
|
|||||||
import { firstValueFrom, map } from "rxjs";
|
import { firstValueFrom, map } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import {
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
getOrganizationById,
|
|
||||||
OrganizationService,
|
|
||||||
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
|
import { getById } from "@bitwarden/common/platform/misc/rxjs-operators";
|
||||||
import { CalloutModule } from "@bitwarden/components";
|
import { CalloutModule } from "@bitwarden/components";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -30,6 +28,8 @@ export class ExportScopeCalloutComponent {
|
|||||||
readonly organizationId = input<string>();
|
readonly organizationId = input<string>();
|
||||||
/* Optional export format, determines which individual export description to display */
|
/* Optional export format, determines which individual export description to display */
|
||||||
readonly exportFormat = input<string>();
|
readonly exportFormat = input<string>();
|
||||||
|
/* The description key to use for organizational exports */
|
||||||
|
readonly orgExportDescription = input<string>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected organizationService: OrganizationService,
|
protected organizationService: OrganizationService,
|
||||||
@@ -37,35 +37,45 @@ export class ExportScopeCalloutComponent {
|
|||||||
) {
|
) {
|
||||||
effect(async () => {
|
effect(async () => {
|
||||||
this.show = false;
|
this.show = false;
|
||||||
await this.getScopeMessage(this.organizationId(), this.exportFormat());
|
await this.getScopeMessage(
|
||||||
|
this.organizationId(),
|
||||||
|
this.exportFormat(),
|
||||||
|
this.orgExportDescription(),
|
||||||
|
);
|
||||||
this.show = true;
|
this.show = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getScopeMessage(organizationId: string, exportFormat: string): Promise<void> {
|
private async getScopeMessage(
|
||||||
|
organizationId: string,
|
||||||
|
exportFormat: string,
|
||||||
|
orgExportDescription: string,
|
||||||
|
): Promise<void> {
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
this.scopeConfig =
|
|
||||||
organizationId != null
|
if (organizationId != null) {
|
||||||
? {
|
// exporting from organizational vault
|
||||||
title: "exportingOrganizationVaultTitle",
|
const org = await firstValueFrom(
|
||||||
description: "exportingOrganizationVaultDesc",
|
this.organizationService.organizations$(userId).pipe(getById(organizationId)),
|
||||||
scopeIdentifier: (
|
);
|
||||||
await firstValueFrom(
|
|
||||||
this.organizationService
|
this.scopeConfig = {
|
||||||
.organizations$(userId)
|
title: "exportingOrganizationVaultTitle",
|
||||||
.pipe(getOrganizationById(organizationId)),
|
description: orgExportDescription,
|
||||||
)
|
scopeIdentifier: org?.name ?? "",
|
||||||
).name,
|
};
|
||||||
}
|
} else {
|
||||||
: {
|
this.scopeConfig = {
|
||||||
title: "exportingPersonalVaultTitle",
|
// exporting from individual vault
|
||||||
description:
|
title: "exportingPersonalVaultTitle",
|
||||||
exportFormat == "zip"
|
description:
|
||||||
? "exportingIndividualVaultWithAttachmentsDescription"
|
exportFormat === "zip"
|
||||||
: "exportingIndividualVaultDescription",
|
? "exportingIndividualVaultWithAttachmentsDescription"
|
||||||
scopeIdentifier: await firstValueFrom(
|
: "exportingIndividualVaultDescription",
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.email)),
|
scopeIdentifier:
|
||||||
),
|
(await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.email)))) ??
|
||||||
};
|
"",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<tools-export-scope-callout
|
<tools-export-scope-callout
|
||||||
[organizationId]="organizationId"
|
[organizationId]="organizationId"
|
||||||
[exportFormat]="format"
|
[exportFormat]="format"
|
||||||
|
[orgExportDescription]="orgExportDescription"
|
||||||
></tools-export-scope-callout>
|
></tools-export-scope-callout>
|
||||||
|
|
||||||
<form [formGroup]="exportForm" [bitSubmit]="submit" id="export_form_exportForm">
|
<form [formGroup]="exportForm" [bitSubmit]="submit" id="export_form_exportForm">
|
||||||
@@ -19,10 +20,10 @@
|
|||||||
[label]="'myVault' | i18n"
|
[label]="'myVault' | i18n"
|
||||||
value="myVault"
|
value="myVault"
|
||||||
icon="bwi-user"
|
icon="bwi-user"
|
||||||
*ngIf="!(organizationDataOwnershipPolicy$ | async)"
|
*ngIf="!(organizationDataOwnershipPolicyAppliesToUser$ | async)"
|
||||||
/>
|
/>
|
||||||
<bit-option
|
<bit-option
|
||||||
*ngFor="let o of organizations$ | async"
|
*ngFor="let o of organizations"
|
||||||
[value]="o.id"
|
[value]="o.id"
|
||||||
[label]="o.name"
|
[label]="o.name"
|
||||||
icon="bwi-business"
|
icon="bwi-business"
|
||||||
|
|||||||
@@ -10,14 +10,20 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
|
Optional,
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from "@angular/forms";
|
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from "@angular/forms";
|
||||||
|
import { Router } from "@angular/router";
|
||||||
import {
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
combineLatest,
|
combineLatest,
|
||||||
firstValueFrom,
|
firstValueFrom,
|
||||||
|
from,
|
||||||
map,
|
map,
|
||||||
merge,
|
merge,
|
||||||
Observable,
|
Observable,
|
||||||
|
of,
|
||||||
|
shareReplay,
|
||||||
startWith,
|
startWith,
|
||||||
Subject,
|
Subject,
|
||||||
switchMap,
|
switchMap,
|
||||||
@@ -36,10 +42,13 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
|||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { EventType } from "@bitwarden/common/enums";
|
import { ClientType, EventType } from "@bitwarden/common/enums";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
import { getById } from "@bitwarden/common/platform/misc";
|
import { getById } from "@bitwarden/common/platform/misc";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { pin } from "@bitwarden/common/tools/rx";
|
import { pin } from "@bitwarden/common/tools/rx";
|
||||||
@@ -84,11 +93,9 @@ import { ExportScopeCalloutComponent } from "./export-scope-callout.component";
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
private _organizationId: OrganizationId | undefined;
|
private _organizationId$ = new BehaviorSubject<OrganizationId | undefined>(undefined);
|
||||||
|
private createDefaultLocationFlagEnabled$: Observable<boolean>;
|
||||||
get organizationId(): OrganizationId | undefined {
|
private _showExcludeMyItems = false;
|
||||||
return this._organizationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the hosting control to pass in an organizationId
|
* Enables the hosting control to pass in an organizationId
|
||||||
@@ -96,29 +103,57 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
*/
|
*/
|
||||||
@Input() set organizationId(value: OrganizationId | string | undefined) {
|
@Input() set organizationId(value: OrganizationId | string | undefined) {
|
||||||
if (Utils.isNullOrEmpty(value)) {
|
if (Utils.isNullOrEmpty(value)) {
|
||||||
this._organizationId = undefined;
|
this._organizationId$.next(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isId<OrganizationId>(value)) {
|
if (!isId<OrganizationId>(value)) {
|
||||||
this._organizationId = undefined;
|
this._organizationId$.next(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._organizationId = value;
|
this._organizationId$.next(value);
|
||||||
|
|
||||||
getUserId(this.accountService.activeAccount$)
|
getUserId(this.accountService.activeAccount$)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((userId) =>
|
switchMap((userId) => this.organizationService.organizations$(userId).pipe(getById(value))),
|
||||||
this.organizationService.organizations$(userId).pipe(getById(this._organizationId)),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((organization) => {
|
.subscribe((organization) => {
|
||||||
this._organizationId = organization?.id;
|
this._organizationId$.next(organization?.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get organizationId(): OrganizationId | undefined {
|
||||||
|
return this._organizationId$.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get showExcludeMyItems(): boolean {
|
||||||
|
return this._showExcludeMyItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
get orgExportDescription(): string {
|
||||||
|
if (!this._showExcludeMyItems) {
|
||||||
|
return "exportingOrganizationVaultDesc";
|
||||||
|
}
|
||||||
|
return this.isAdminConsoleContext
|
||||||
|
? "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc"
|
||||||
|
: "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc";
|
||||||
|
}
|
||||||
|
|
||||||
|
private get isAdminConsoleContext(): boolean {
|
||||||
|
const isWeb = this.platformUtilsService.getClientType?.() === ClientType.Web;
|
||||||
|
if (!isWeb || !this.router) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const url = this.router.url ?? "";
|
||||||
|
return url.includes("/organizations/");
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hosting control also needs a bitSubmitDirective (on the Submit button) which calls this components {@link submit}-method.
|
* The hosting control also needs a bitSubmitDirective (on the Submit button) which calls this components {@link submit}-method.
|
||||||
* This components formState (loading/disabled) is emitted back up to the hosting component so for example the Submit button can be enabled/disabled and show loading state.
|
* This components formState (loading/disabled) is emitted back up to the hosting component so for example the Submit button can be enabled/disabled and show loading state.
|
||||||
@@ -143,7 +178,7 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
/**
|
/**
|
||||||
* Emits when the creation and download of the export-file have succeeded
|
* Emits when the creation and download of the export-file have succeeded
|
||||||
* - Emits an undefined when exporting from an individual vault
|
* - Emits an undefined when exporting from an individual vault
|
||||||
* - Emits the organizationId when exporting from an organizationl vault
|
* - Emits the organizationId when exporting from an organizational vault
|
||||||
* */
|
* */
|
||||||
@Output()
|
@Output()
|
||||||
onSuccessfulExport = new EventEmitter<OrganizationId | undefined>();
|
onSuccessfulExport = new EventEmitter<OrganizationId | undefined>();
|
||||||
@@ -162,7 +197,10 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disablePersonalVaultExportPolicy$: Observable<boolean>;
|
disablePersonalVaultExportPolicy$: Observable<boolean>;
|
||||||
organizationDataOwnershipPolicy$: Observable<boolean>;
|
// detects if policy is enabled and applies to the user, admins are exempted
|
||||||
|
organizationDataOwnershipPolicyAppliesToUser$: Observable<boolean>;
|
||||||
|
// detects if policy is enabled regardless of admin exemption
|
||||||
|
organizationDataOwnershipPolicyEnabledForOrg$: Observable<boolean>;
|
||||||
|
|
||||||
exportForm = this.formBuilder.group({
|
exportForm = this.formBuilder.group({
|
||||||
vaultSelector: [
|
vaultSelector: [
|
||||||
@@ -203,14 +241,46 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
protected organizationService: OrganizationService,
|
protected organizationService: OrganizationService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
@Optional() private router?: Router,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
// Setup subscription to emit when this form is enabled/disabled
|
this.observeFeatureFlags();
|
||||||
|
this.observeFormState();
|
||||||
|
this.observePolicyStatus();
|
||||||
|
this.observeFormSelections();
|
||||||
|
|
||||||
|
// order is important below this line
|
||||||
|
this.observeMyItemsExclusionCriteria();
|
||||||
|
this.observeValidatorAdjustments();
|
||||||
|
this.setupPasswordGeneration();
|
||||||
|
|
||||||
|
if (this.organizationId) {
|
||||||
|
// organization vault export
|
||||||
|
this.initOrganizationOnly();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// individual vault export
|
||||||
|
this.initIndividual();
|
||||||
|
this.setupPolicyBasedFormState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeFeatureFlags(): void {
|
||||||
|
this.createDefaultLocationFlagEnabled$ = from(
|
||||||
|
this.configService.getFeatureFlag(FeatureFlag.CreateDefaultLocation),
|
||||||
|
).pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeFormState(): void {
|
||||||
this.exportForm.statusChanges.pipe(takeUntil(this.destroy$)).subscribe((c) => {
|
this.exportForm.statusChanges.pipe(takeUntil(this.destroy$)).subscribe((c) => {
|
||||||
this.formDisabled.emit(c === "DISABLED");
|
this.formDisabled.emit(c === "DISABLED");
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private observePolicyStatus(): void {
|
||||||
this.disablePersonalVaultExportPolicy$ = this.accountService.activeAccount$.pipe(
|
this.disablePersonalVaultExportPolicy$ = this.accountService.activeAccount$.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
@@ -218,13 +288,42 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.organizationDataOwnershipPolicy$ = this.accountService.activeAccount$.pipe(
|
// when true, html template will hide "My Vault" option in vault selector drop down
|
||||||
|
this.organizationDataOwnershipPolicyAppliesToUser$ = this.accountService.activeAccount$.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
|
this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Determines how organization exports are described in the callout.
|
||||||
|
Admins are exempted from organization data ownership policy,
|
||||||
|
and so this needs to determine if the policy is enabled for the org, not if it applies to the user.
|
||||||
|
*/
|
||||||
|
this.organizationDataOwnershipPolicyEnabledForOrg$ = combineLatest([
|
||||||
|
this.accountService.activeAccount$.pipe(getUserId),
|
||||||
|
this._organizationId$,
|
||||||
|
]).pipe(
|
||||||
|
switchMap(([userId, organizationId]) => {
|
||||||
|
if (!organizationId || !userId) {
|
||||||
|
return of(false);
|
||||||
|
}
|
||||||
|
return this.policyService.policies$(userId).pipe(
|
||||||
|
map((policies) => {
|
||||||
|
const policy = policies?.find(
|
||||||
|
(p) =>
|
||||||
|
p.type === PolicyType.OrganizationDataOwnership &&
|
||||||
|
p.organizationId === organizationId,
|
||||||
|
);
|
||||||
|
return policy?.enabled ?? false;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private observeFormSelections(): void {
|
||||||
this.exportForm.controls.vaultSelector.valueChanges
|
this.exportForm.controls.vaultSelector.valueChanges
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((value) => {
|
.subscribe((value) => {
|
||||||
@@ -236,15 +335,50 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
this.formatOptions.push({ name: ".zip (with attachments)", value: "zip" });
|
this.formatOptions.push({ name: ".zip (with attachments)", value: "zip" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine value of showExcludeMyItems. Returns true when:
|
||||||
|
* CreateDefaultLocation feature flag is on
|
||||||
|
* AND organizationDataOwnershipPolicy is enabled for the selected organization
|
||||||
|
* AND a valid OrganizationId is present (not exporting from individual vault)
|
||||||
|
*/
|
||||||
|
private observeMyItemsExclusionCriteria(): void {
|
||||||
|
combineLatest({
|
||||||
|
createDefaultLocationFlagEnabled: this.createDefaultLocationFlagEnabled$,
|
||||||
|
organizationDataOwnershipPolicyEnabledForOrg:
|
||||||
|
this.organizationDataOwnershipPolicyEnabledForOrg$,
|
||||||
|
organizationId: this._organizationId$,
|
||||||
|
})
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(
|
||||||
|
({
|
||||||
|
createDefaultLocationFlagEnabled,
|
||||||
|
organizationDataOwnershipPolicyEnabledForOrg,
|
||||||
|
organizationId,
|
||||||
|
}) => {
|
||||||
|
if (!createDefaultLocationFlagEnabled || !organizationId) {
|
||||||
|
this._showExcludeMyItems = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._showExcludeMyItems = organizationDataOwnershipPolicyEnabledForOrg;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup validator adjustments based on format and encryption type changes
|
||||||
|
private observeValidatorAdjustments(): void {
|
||||||
merge(
|
merge(
|
||||||
this.exportForm.get("format").valueChanges,
|
this.exportForm.get("format").valueChanges,
|
||||||
this.exportForm.get("fileEncryptionType").valueChanges,
|
this.exportForm.get("fileEncryptionType").valueChanges,
|
||||||
)
|
)
|
||||||
.pipe(startWith(0), takeUntil(this.destroy$))
|
.pipe(startWith(0), takeUntil(this.destroy$))
|
||||||
.subscribe(() => this.adjustValidators());
|
.subscribe(() => this.adjustValidators());
|
||||||
|
}
|
||||||
|
|
||||||
// Wire up the password generation for the password-protected export
|
// Wire up the password generation for password-protected exports
|
||||||
|
private setupPasswordGeneration(): void {
|
||||||
const account$ = this.accountService.activeAccount$.pipe(
|
const account$ = this.accountService.activeAccount$.pipe(
|
||||||
pin({
|
pin({
|
||||||
name() {
|
name() {
|
||||||
@@ -255,6 +389,7 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.generatorService
|
this.generatorService
|
||||||
.generate$({ on$: this.onGenerate$, account$ })
|
.generate$({ on$: this.onGenerate$, account$ })
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
@@ -264,23 +399,29 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
confirmFilePassword: generated.credential,
|
confirmFilePassword: generated.credential,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (this.organizationId) {
|
/*
|
||||||
this.organizations$ = this.accountService.activeAccount$.pipe(
|
Initialize component for organization only export
|
||||||
getUserId,
|
Hides "My Vault" option by returning immediately
|
||||||
switchMap((userId) =>
|
*/
|
||||||
this.organizationService
|
private initOrganizationOnly(): void {
|
||||||
.memberOrganizations$(userId)
|
this.organizations$ = this.accountService.activeAccount$.pipe(
|
||||||
.pipe(map((orgs) => orgs.filter((org) => org.id == this.organizationId))),
|
getUserId,
|
||||||
),
|
switchMap((userId) =>
|
||||||
);
|
this.organizationService
|
||||||
this.exportForm.controls.vaultSelector.patchValue(this.organizationId);
|
.memberOrganizations$(userId)
|
||||||
this.exportForm.controls.vaultSelector.disable();
|
.pipe(map((orgs) => orgs.filter((org) => org.id == this.organizationId))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this.exportForm.controls.vaultSelector.patchValue(this.organizationId);
|
||||||
|
this.exportForm.controls.vaultSelector.disable();
|
||||||
|
|
||||||
this.onlyManagedCollections = false;
|
this.onlyManagedCollections = false;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Initialize component to support individual and organizational exports
|
||||||
|
private initIndividual(): void {
|
||||||
this.organizations$ = this.accountService.activeAccount$
|
this.organizations$ = this.accountService.activeAccount$
|
||||||
.pipe(
|
.pipe(
|
||||||
getUserId,
|
getUserId,
|
||||||
@@ -296,18 +437,18 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||||||
const managedCollectionsOrgIds = new Set(
|
const managedCollectionsOrgIds = new Set(
|
||||||
collections.filter((c) => c.manage).map((c) => c.organizationId),
|
collections.filter((c) => c.manage).map((c) => c.organizationId),
|
||||||
);
|
);
|
||||||
// Filter organizations that exist in managedCollectionsOrgIds
|
|
||||||
const filteredOrgs = memberOrganizations.filter((org) =>
|
const filteredOrgs = memberOrganizations.filter((org) =>
|
||||||
managedCollectionsOrgIds.has(org.id),
|
managedCollectionsOrgIds.has(org.id),
|
||||||
);
|
);
|
||||||
// Sort the filtered organizations based on the name
|
|
||||||
return filteredOrgs.sort(Utils.getSortFunction(this.i18nService, "name"));
|
return filteredOrgs.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupPolicyBasedFormState(): void {
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.disablePersonalVaultExportPolicy$,
|
this.disablePersonalVaultExportPolicy$,
|
||||||
this.organizationDataOwnershipPolicy$,
|
this.organizationDataOwnershipPolicyAppliesToUser$,
|
||||||
this.organizations$,
|
this.organizations$,
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
|
|||||||
Reference in New Issue
Block a user