mirror of
https://github.com/bitwarden/browser
synced 2025-12-22 03:03:43 +00:00
Merge master into merge/feature/org-admin-refresh (using imerge)
This commit is contained in:
@@ -21,4 +21,4 @@
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer to the [Web vault section](https://contributing.bitwarden.com/clients/web-vault/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||
Please refer to the [Web vault section](https://contributing.bitwarden.com/getting-started/clients/web-vault/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
"proxyEvents": "https://events.bitwarden.com"
|
||||
},
|
||||
"flags": {
|
||||
"showTrial": true,
|
||||
"showPasswordless": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"proxyNotifications": "http://localhost:61840"
|
||||
},
|
||||
"flags": {
|
||||
"showTrial": true,
|
||||
"secretsManager": true,
|
||||
"showPasswordless": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,5 @@
|
||||
"proxyNotifications": "http://localhost:61841",
|
||||
"port": 8081
|
||||
},
|
||||
"flags": {
|
||||
"showTrial": false
|
||||
}
|
||||
"flags": {}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"proxyEvents": "https://events.qa.bitwarden.pw"
|
||||
},
|
||||
"flags": {
|
||||
"showTrial": true,
|
||||
"secretsManager": false,
|
||||
"showPasswordless": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
"port": 8081
|
||||
},
|
||||
"flags": {
|
||||
"showTrial": false,
|
||||
"showPasswordless": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bitwarden/web-vault",
|
||||
"version": "2022.10.2",
|
||||
"version": "2022.11.2",
|
||||
"scripts": {
|
||||
"build:oss": "webpack",
|
||||
"build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js",
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<input
|
||||
id="login_input_master-password"
|
||||
bitInput
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
[type]="showPassword ? 'text' : 'password'"
|
||||
formControlName="masterPassword"
|
||||
appAutofocus
|
||||
/>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<input
|
||||
id="register-form_input_master-password"
|
||||
bitInput
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
[type]="showPassword ? 'text' : 'password'"
|
||||
formControlName="masterPassword"
|
||||
/>
|
||||
<button type="button" bitSuffix bitButton (click)="togglePassword()">
|
||||
@@ -65,7 +65,7 @@
|
||||
<input
|
||||
id="register-form_input_confirm-master-password"
|
||||
bitInput
|
||||
type="{{ showPassword ? 'text' : 'password' }}"
|
||||
[type]="showPassword ? 'text' : 'password'"
|
||||
formControlName="confirmMasterPassword"
|
||||
/>
|
||||
<button type="button" bitSuffix bitButton (click)="togglePassword()">
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
<div class="layout" [ngClass]="['layout', layout]">
|
||||
<!-- TEAMS 1 Header -->
|
||||
<header
|
||||
class="header"
|
||||
*ngIf="
|
||||
layout === 'default' ||
|
||||
layout === 'teams' ||
|
||||
layout === 'teams1' ||
|
||||
layout === 'teams2' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'enterprise2' ||
|
||||
layout === 'cnetcmpgnent' ||
|
||||
layout === 'cnetcmpgnteams' ||
|
||||
layout === 'cnetcmpgnind'
|
||||
"
|
||||
>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-7">
|
||||
<img
|
||||
alt="Bitwarden"
|
||||
class="logo mb-2"
|
||||
src="../../images/register-layout/logo-horizontal-white.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-7" *ngIf="layout">
|
||||
<div class="mt-5">
|
||||
<!-- Default Body -->
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'teams' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'default'
|
||||
"
|
||||
>
|
||||
<h1>The Bitwarden Password Manager</h1>
|
||||
<h2>
|
||||
Trusted by millions of individuals, teams, and organizations worldwide for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Store logins, secure notes, and more</p>
|
||||
<p>Collaborate and share securely</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
|
||||
<!-- Teams & Enterprise Body -->
|
||||
<div *ngIf="layout === 'teams1' || layout === 'teams2' || layout === 'enterprise2'">
|
||||
<h1>
|
||||
Start Your <span *ngIf="layout === 'teams1' || layout === 'teams1'">Teams<br /></span
|
||||
><span *ngIf="layout === 'enterprise2'">Enterprise</span> Free Trial Now
|
||||
</h1>
|
||||
<h2>
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Collaborate and share securely</p>
|
||||
<p>Deploy and manage quickly and easily</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
|
||||
<!-- CNET Campaign Teams & Enterprise Body -->
|
||||
<div *ngIf="layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnent'">
|
||||
<h1>
|
||||
Start Your <span *ngIf="layout === 'cnetcmpgnteams'">Teams<br /></span
|
||||
><span *ngIf="layout === 'cnetcmpgnent'">Enterprise</span> Free Trial Now
|
||||
</h1>
|
||||
<h2>
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Collaborate and share securely</p>
|
||||
<p>Deploy and manage quickly and easily</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
|
||||
<!-- CNET Campaign Premium Body -->
|
||||
<div *ngIf="layout === 'cnetcmpgnind'">
|
||||
<h1>Start Your Premium Account Now</h1>
|
||||
<h2>
|
||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
||||
password storage and sharing.
|
||||
</h2>
|
||||
<p>Store logins, secure notes, and more</p>
|
||||
<p>Secure your account with advanced two-step login</p>
|
||||
<p>Access anywhere on any device</p>
|
||||
<p>Create your account to get started</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div [ngClass]="{ 'col-5': layout, 'col-12': !layout }">
|
||||
<div class="row justify-content-md-center mt-5">
|
||||
<div [ngClass]="{ 'col-5': !layout, 'col-12': layout }">
|
||||
<h1 class="lead text-center mb-4" *ngIf="!layout">{{ "createAccount" | i18n }}</h1>
|
||||
<div class="card d-block">
|
||||
<div class="card-body">
|
||||
<app-callout
|
||||
title="{{ 'createOrganizationStep1' | i18n }}"
|
||||
type="info"
|
||||
icon="bwi bwi-thumb-tack"
|
||||
*ngIf="showCreateOrgMessage"
|
||||
>
|
||||
{{ "createOrganizationCreatePersonalAccount" | i18n }}
|
||||
</app-callout>
|
||||
<app-register-form
|
||||
[queryParamEmail]="email"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
[referenceDataValue]="referenceData"
|
||||
></app-register-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-7 d-flex align-items-center">
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
|
||||
"
|
||||
>
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/cnet-logo.svg"
|
||||
class="w-25 d-block mx-auto"
|
||||
alt="cnet logo"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote class="mx-auto text-center px-4">
|
||||
"No more excuses; start using Bitwarden today. The identity you save could be your
|
||||
own. The money definitely will be."
|
||||
</blockquote>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'teams' ||
|
||||
layout === 'teams1' ||
|
||||
layout === 'teams2' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'enterprise2' ||
|
||||
layout === 'default'
|
||||
"
|
||||
>
|
||||
<figure>
|
||||
<figcaption>
|
||||
<cite>
|
||||
<img
|
||||
src="../../images/register-layout/forbes-logo.svg"
|
||||
class="w-25 d-block mx-auto"
|
||||
alt="Forbes Logo"
|
||||
/>
|
||||
</cite>
|
||||
</figcaption>
|
||||
<blockquote class="mx-auto text-center px-4">
|
||||
“Bitwarden boasts the backing of some of the world's best security experts and an
|
||||
attractive, easy-to-use interface”
|
||||
</blockquote>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
|
||||
"
|
||||
class="col-5 d-flex align-items-center justify-content-center"
|
||||
>
|
||||
<img
|
||||
src="../../images/register-layout/usnews-360-badge.svg"
|
||||
class="w-50 d-block"
|
||||
alt="US News 360 Reviews Best Password Manager"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="
|
||||
layout === 'teams' ||
|
||||
layout === 'teams1' ||
|
||||
layout === 'teams2' ||
|
||||
layout === 'enterprise' ||
|
||||
layout === 'enterprise1' ||
|
||||
layout === 'enterprise2' ||
|
||||
layout === 'default'
|
||||
"
|
||||
class="col-5 d-flex align-items-center justify-content-center"
|
||||
>
|
||||
<img
|
||||
src="../../images/register-layout/usnews-360-badge.svg"
|
||||
class="w-50 d-block"
|
||||
alt="US News 360 Reviews Best Password Manager"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,149 +0,0 @@
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { UntypedFormBuilder } from "@angular/forms";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
import { first } from "rxjs/operators";
|
||||
|
||||
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
||||
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { PolicyData } from "@bitwarden/common/models/data/policy.data";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/master-password-policy-options";
|
||||
import { Policy } from "@bitwarden/common/models/domain/policy";
|
||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request";
|
||||
|
||||
import { RouterService } from "../core";
|
||||
|
||||
@Component({
|
||||
selector: "app-register",
|
||||
templateUrl: "register.component.html",
|
||||
})
|
||||
export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy {
|
||||
email = "";
|
||||
showCreateOrgMessage = false;
|
||||
layout = "";
|
||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||
|
||||
private policies: Policy[];
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
formValidationErrorService: FormValidationErrorsService,
|
||||
formBuilder: UntypedFormBuilder,
|
||||
authService: AuthService,
|
||||
router: Router,
|
||||
i18nService: I18nService,
|
||||
cryptoService: CryptoService,
|
||||
apiService: ApiService,
|
||||
private route: ActivatedRoute,
|
||||
stateService: StateService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
private policyApiService: PolicyApiServiceAbstraction,
|
||||
private policyService: PolicyService,
|
||||
environmentService: EnvironmentService,
|
||||
logService: LogService,
|
||||
private routerService: RouterService
|
||||
) {
|
||||
super(
|
||||
formValidationErrorService,
|
||||
formBuilder,
|
||||
authService,
|
||||
router,
|
||||
i18nService,
|
||||
cryptoService,
|
||||
apiService,
|
||||
stateService,
|
||||
platformUtilsService,
|
||||
passwordGenerationService,
|
||||
environmentService,
|
||||
logService
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
this.route.queryParams.pipe(first()).subscribe((qParams) => {
|
||||
this.referenceData = new ReferenceEventRequest();
|
||||
if (qParams.email != null && qParams.email.indexOf("@") > -1) {
|
||||
this.email = qParams.email;
|
||||
}
|
||||
if (qParams.premium != null) {
|
||||
this.routerService.setPreviousUrl("/settings/premium");
|
||||
} else if (qParams.org != null) {
|
||||
this.showCreateOrgMessage = true;
|
||||
this.referenceData.flow = qParams.org;
|
||||
const route = this.router.createUrlTree(["create-organization"], {
|
||||
queryParams: { plan: qParams.org },
|
||||
});
|
||||
this.routerService.setPreviousUrl(route.toString());
|
||||
}
|
||||
if (qParams.layout != null) {
|
||||
this.layout = this.referenceData.layout = qParams.layout;
|
||||
}
|
||||
if (qParams.reference != null) {
|
||||
this.referenceData.id = qParams.reference;
|
||||
} else {
|
||||
this.referenceData.id = ("; " + document.cookie)
|
||||
.split("; reference=")
|
||||
.pop()
|
||||
.split(";")
|
||||
.shift();
|
||||
}
|
||||
// Are they coming from an email for sponsoring a families organization
|
||||
if (qParams.sponsorshipToken != null) {
|
||||
// After logging in redirect them to setup the families sponsorship
|
||||
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
|
||||
queryParams: { plan: qParams.sponsorshipToken },
|
||||
});
|
||||
this.routerService.setPreviousUrl(route.toString());
|
||||
}
|
||||
if (this.referenceData.id === "") {
|
||||
this.referenceData.id = null;
|
||||
}
|
||||
});
|
||||
const invite = await this.stateService.getOrganizationInvitation();
|
||||
if (invite != null) {
|
||||
try {
|
||||
const policies = await this.policyApiService.getPoliciesByToken(
|
||||
invite.organizationId,
|
||||
invite.token,
|
||||
invite.email,
|
||||
invite.organizationUserId
|
||||
);
|
||||
if (policies.data != null) {
|
||||
const policiesData = policies.data.map((p) => new PolicyData(p));
|
||||
this.policies = policiesData.map((p) => new Policy(p));
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.policies != null) {
|
||||
this.policyService
|
||||
.masterPasswordPolicyOptions$(this.policies)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe((enforcedPasswordPolicyOptions) => {
|
||||
this.enforcedPolicyOptions = enforcedPasswordPolicyOptions;
|
||||
});
|
||||
}
|
||||
|
||||
await super.ngOnInit();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
@@ -23,26 +23,39 @@
|
||||
|
||||
<div class="tw-pt-12">
|
||||
<!-- Layout params are used by marketing to determine left-hand content -->
|
||||
<app-default-content *ngIf="layout === 'default'"></app-default-content>
|
||||
<app-teams-content *ngIf="layout === 'teams'"></app-teams-content>
|
||||
<app-teams1-content *ngIf="layout === 'teams1'"></app-teams1-content>
|
||||
<app-teams2-content *ngIf="layout === 'teams2'"></app-teams2-content>
|
||||
<app-enterprise-content *ngIf="layout === 'enterprise'"></app-enterprise-content>
|
||||
<app-enterprise1-content *ngIf="layout === 'enterprise1'"></app-enterprise1-content>
|
||||
<app-enterprise2-content *ngIf="layout === 'enterprise2'"></app-enterprise2-content>
|
||||
<app-default-content *ngIf="layout === layouts.default"></app-default-content>
|
||||
<app-teams-content *ngIf="layout === layouts.teams"></app-teams-content>
|
||||
<app-teams1-content *ngIf="layout === layouts.teams1"></app-teams1-content>
|
||||
<app-teams2-content *ngIf="layout === layouts.teams2"></app-teams2-content>
|
||||
<app-enterprise-content *ngIf="layout === layouts.enterprise"></app-enterprise-content>
|
||||
<app-enterprise1-content *ngIf="layout === layouts.enterprise1"></app-enterprise1-content>
|
||||
<app-enterprise2-content *ngIf="layout === layouts.enterprise2"></app-enterprise2-content>
|
||||
<app-cnet-enterprise-content
|
||||
*ngIf="layout === 'cnetcmpgnent'"
|
||||
*ngIf="layout === layouts.cnetcmpgnent"
|
||||
></app-cnet-enterprise-content>
|
||||
<app-cnet-individual-content
|
||||
*ngIf="layout === 'cnetcmpgnind'"
|
||||
*ngIf="layout === layouts.cnetcmpgnind"
|
||||
></app-cnet-individual-content>
|
||||
<app-cnet-teams-content *ngIf="layout === 'cnetcmpgnteams'"></app-cnet-teams-content>
|
||||
<app-abm-enterprise-content *ngIf="layout === 'abmenterprise'"></app-abm-enterprise-content>
|
||||
<app-abm-teams-content *ngIf="layout === 'abmteams'"></app-abm-teams-content>
|
||||
<app-cnet-teams-content *ngIf="layout === layouts.cnetcmpgnteams"></app-cnet-teams-content>
|
||||
<app-abm-enterprise-content
|
||||
*ngIf="layout === layouts.abmenterprise"
|
||||
></app-abm-enterprise-content>
|
||||
<app-abm-teams-content *ngIf="layout === layouts.abmteams"></app-abm-teams-content>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-w-1/2">
|
||||
<div class="tw-pt-56">
|
||||
<div *ngIf="!useTrialStepper">
|
||||
<div
|
||||
class="tw-min-w-xl tw-m-auto tw-mt-28 tw-max-w-xl tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-8"
|
||||
>
|
||||
<app-register-form
|
||||
[queryParamEmail]="email"
|
||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||
[referenceDataValue]="referenceData"
|
||||
></app-register-form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tw-pt-44" *ngIf="useTrialStepper">
|
||||
<div class="tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background">
|
||||
<div class="tw-flex tw-h-12 tw-w-full tw-items-center tw-rounded-t tw-bg-secondary-100">
|
||||
<h2 class="tw-mb-0 tw-pl-4 tw-text-base tw-font-bold tw-uppercase">
|
||||
@@ -62,7 +75,7 @@
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
[disabled]="orgInfoFormGroup.get('name').hasError('required')"
|
||||
[disabled]="orgInfoFormGroup.get('name').invalid"
|
||||
cdkStepperNext
|
||||
>
|
||||
Next
|
||||
|
||||
@@ -168,8 +168,9 @@ describe("TrialInitiationComponent", () => {
|
||||
it("should set org variable to be enterprise and plan to EnterpriseAnnually if org param is enterprise", fakeAsync(() => {
|
||||
mockQueryParams.next({ org: "enterprise" });
|
||||
tick(); // wait for resolution
|
||||
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
component.ngOnInit();
|
||||
expect(component.org).toBe("enterprise");
|
||||
expect(component.plan).toBe(PlanType.EnterpriseAnnually);
|
||||
}));
|
||||
@@ -182,13 +183,33 @@ describe("TrialInitiationComponent", () => {
|
||||
expect(component.org).toBe("");
|
||||
expect(component.accountCreateOnly).toBe(true);
|
||||
}));
|
||||
it("should set the org to be families and plan to FamiliesAnnually if org param is invalid ", fakeAsync(async () => {
|
||||
it("should not set the org if org param is invalid ", fakeAsync(async () => {
|
||||
mockQueryParams.next({ org: "hahahaha" });
|
||||
tick(); // wait for resolution
|
||||
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
expect(component.org).toBe("");
|
||||
expect(component.accountCreateOnly).toBe(true);
|
||||
}));
|
||||
it("should set the layout variable if layout param is valid ", fakeAsync(async () => {
|
||||
mockQueryParams.next({ layout: "teams1" });
|
||||
tick(); // wait for resolution
|
||||
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
expect(component.layout).toBe("teams1");
|
||||
expect(component.accountCreateOnly).toBe(false);
|
||||
}));
|
||||
it("should not set the layout variable and leave as 'default' if layout param is invalid ", fakeAsync(async () => {
|
||||
mockQueryParams.next({ layout: "asdfasdf" });
|
||||
tick(); // wait for resolution
|
||||
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
component.ngOnInit();
|
||||
expect(component.org).toBe("families");
|
||||
expect(component.plan).toBe(PlanType.FamiliesAnnually);
|
||||
expect(component.layout).toBe("default");
|
||||
expect(component.accountCreateOnly).toBe(true);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,30 @@ import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenc
|
||||
import { RouterService } from "./../../core/router.service";
|
||||
import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component";
|
||||
|
||||
enum ValidOrgParams {
|
||||
families = "families",
|
||||
enterprise = "enterprise",
|
||||
teams = "teams",
|
||||
individual = "individual",
|
||||
premium = "premium",
|
||||
free = "free",
|
||||
}
|
||||
|
||||
enum ValidLayoutParams {
|
||||
default = "default",
|
||||
teams = "teams",
|
||||
teams1 = "teams1",
|
||||
teams2 = "teams2",
|
||||
enterprise = "enterprise",
|
||||
enterprise1 = "enterprise1",
|
||||
enterprise2 = "enterprise2",
|
||||
cnetcmpgnent = "cnetcmpgnent",
|
||||
cnetcmpgnind = "cnetcmpgnind",
|
||||
cnetcmpgnteams = "cnetcmpgnteams",
|
||||
abmenterprise = "abmenterprise",
|
||||
abmteams = "abmteams",
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: "app-trial",
|
||||
templateUrl: "trial-initiation.component.html",
|
||||
@@ -35,14 +59,25 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
|
||||
plan: PlanType;
|
||||
product: ProductType;
|
||||
accountCreateOnly = true;
|
||||
useTrialStepper = false;
|
||||
policies: Policy[];
|
||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||
validOrgs: string[] = ["teams", "enterprise", "families"];
|
||||
trialFlowOrgs: string[] = [
|
||||
ValidOrgParams.teams,
|
||||
ValidOrgParams.enterprise,
|
||||
ValidOrgParams.families,
|
||||
];
|
||||
routeFlowOrgs: string[] = [
|
||||
ValidOrgParams.free,
|
||||
ValidOrgParams.premium,
|
||||
ValidOrgParams.individual,
|
||||
];
|
||||
layouts = ValidLayoutParams;
|
||||
referenceData: ReferenceEventRequest;
|
||||
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
||||
|
||||
orgInfoFormGroup = this.formBuilder.group({
|
||||
name: ["", [Validators.required]],
|
||||
name: ["", { validators: [Validators.required, Validators.maxLength(50)], updateOn: "change" }],
|
||||
email: [""],
|
||||
});
|
||||
|
||||
@@ -87,39 +122,38 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.referenceDataId = qParams.reference;
|
||||
|
||||
if (!qParams.org) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (qParams.layout) {
|
||||
if (Object.values(ValidLayoutParams).includes(qParams.layout)) {
|
||||
this.layout = qParams.layout;
|
||||
this.accountCreateOnly = false;
|
||||
}
|
||||
|
||||
if (this.validOrgs.includes(qParams.org)) {
|
||||
if (this.trialFlowOrgs.includes(qParams.org)) {
|
||||
this.org = qParams.org;
|
||||
} else {
|
||||
this.org = "families";
|
||||
}
|
||||
this.orgLabel = this.titleCasePipe.transform(this.org);
|
||||
this.useTrialStepper = true;
|
||||
this.referenceData.flow = qParams.org;
|
||||
|
||||
this.referenceData.flow = qParams.org;
|
||||
if (this.org === ValidOrgParams.families) {
|
||||
this.plan = PlanType.FamiliesAnnually;
|
||||
this.product = ProductType.Families;
|
||||
} else if (this.org === ValidOrgParams.teams) {
|
||||
this.plan = PlanType.TeamsAnnually;
|
||||
this.product = ProductType.Teams;
|
||||
} else if (this.org === ValidOrgParams.enterprise) {
|
||||
this.plan = PlanType.EnterpriseAnnually;
|
||||
this.product = ProductType.Enterprise;
|
||||
}
|
||||
} else if (this.routeFlowOrgs.includes(qParams.org)) {
|
||||
this.referenceData.flow = qParams.org;
|
||||
const route = this.router.createUrlTree(["create-organization"], {
|
||||
queryParams: { plan: qParams.org },
|
||||
});
|
||||
this.routerService.setPreviousUrl(route.toString());
|
||||
}
|
||||
|
||||
// Are they coming from an email for sponsoring a families organization
|
||||
// After logging in redirect them to setup the families sponsorship
|
||||
this.setupFamilySponsorship(qParams.sponsorshipToken);
|
||||
|
||||
this.orgLabel = this.titleCasePipe.transform(this.org);
|
||||
this.accountCreateOnly = false;
|
||||
|
||||
if (this.org === "families") {
|
||||
this.plan = PlanType.FamiliesAnnually;
|
||||
this.product = ProductType.Families;
|
||||
} else if (this.org === "teams") {
|
||||
this.plan = PlanType.TeamsAnnually;
|
||||
this.product = ProductType.Teams;
|
||||
} else if (this.org === "enterprise") {
|
||||
this.plan = PlanType.EnterpriseAnnually;
|
||||
this.product = ProductType.Enterprise;
|
||||
}
|
||||
});
|
||||
|
||||
const invite = await this.stateService.getOrganizationInvitation();
|
||||
@@ -148,6 +182,12 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
|
||||
this.enforcedPolicyOptions = enforcedPasswordPolicyOptions;
|
||||
});
|
||||
}
|
||||
|
||||
this.orgInfoFormGroup.controls.name.valueChanges
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => {
|
||||
this.orgInfoFormGroup.controls.name.markAsTouched();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { CdkStepper } from "@angular/cdk/stepper";
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Component, Input, QueryList } from "@angular/core";
|
||||
|
||||
import { VerticalStep } from "./vertical-step.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-vertical-stepper",
|
||||
@@ -7,6 +9,8 @@ import { Component, Input } from "@angular/core";
|
||||
providers: [{ provide: CdkStepper, useExisting: VerticalStepperComponent }],
|
||||
})
|
||||
export class VerticalStepperComponent extends CdkStepper {
|
||||
readonly steps: QueryList<VerticalStep>;
|
||||
|
||||
@Input()
|
||||
activeClass = "active";
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</ng-container>
|
||||
<ng-container *ngIf="selectedProviderType === providerType.WebAuthn">
|
||||
<div id="web-authn-frame" class="mb-3">
|
||||
<iframe id="webauthn_iframe" [allow]="webAuthnAllow"></iframe>
|
||||
<iframe id="webauthn_iframe" [attr.allow]="webAuthnAllow"></iframe>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container
|
||||
|
||||
@@ -12,7 +12,7 @@ import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.s
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||
import { InternalFolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service";
|
||||
@@ -74,7 +74,7 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
private notificationsService: NotificationsService,
|
||||
private routerService: RouterService,
|
||||
private stateService: StateService,
|
||||
private eventService: EventService,
|
||||
private eventUploadService: EventUploadService,
|
||||
private policyService: InternalPolicyService,
|
||||
protected policyListService: PolicyListService,
|
||||
private keyConnectorService: KeyConnectorService
|
||||
@@ -221,10 +221,9 @@ export class AppComponent implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
private async logOut(expired: boolean) {
|
||||
await this.eventService.uploadEvents();
|
||||
await this.eventUploadService.uploadEvents();
|
||||
const userId = await this.stateService.getUserId();
|
||||
await Promise.all([
|
||||
this.eventService.clearEvents(),
|
||||
this.syncService.setLastSync(new Date(0)),
|
||||
this.cryptoService.clearKeys(),
|
||||
this.settingsService.clear(userId),
|
||||
|
||||
@@ -4,6 +4,7 @@ import { map, Observable } from "rxjs";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import {
|
||||
canAccessAdmin,
|
||||
isNotProviderUser,
|
||||
OrganizationService,
|
||||
} from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { Utils } from "@bitwarden/common/misc/utils";
|
||||
@@ -23,6 +24,7 @@ export class OrganizationSwitcherComponent implements OnInit {
|
||||
|
||||
async ngOnInit() {
|
||||
this.organizations$ = this.organizationService.organizations$.pipe(
|
||||
map((orgs) => orgs.filter(isNotProviderUser)),
|
||||
canAccessAdmin(this.i18nService),
|
||||
map((orgs) => orgs.sort(Utils.getSortFunction(this.i18nService, "name")))
|
||||
);
|
||||
|
||||
46
apps/web/src/app/components/user-verification.component.html
Normal file
46
apps/web/src/app/components/user-verification.component.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<ng-container *ngIf="!usesKeyConnector">
|
||||
<label for="masterPassword">{{ "masterPass" | i18n }}</label>
|
||||
<input
|
||||
id="masterPassword"
|
||||
type="password"
|
||||
name="MasterPasswordHash"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="usesKeyConnector">
|
||||
<div class="form-group">
|
||||
<label class="d-block">{{ "sendVerificationCode" | i18n }}</label>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="requestOTP()"
|
||||
[disabled]="disableRequestOTP"
|
||||
>
|
||||
{{ "sendCode" | i18n }}
|
||||
</button>
|
||||
<span class="ml-2 text-success" role="alert" @sent *ngIf="sentCode">
|
||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||
{{ "codeSent" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="verificationCode">{{ "verificationCode" | i18n }}</label>
|
||||
<input
|
||||
id="verificationCode"
|
||||
type="input"
|
||||
name="verificationCode"
|
||||
class="form-control"
|
||||
[formControl]="secret"
|
||||
required
|
||||
appAutofocus
|
||||
appInputVerbatim
|
||||
/>
|
||||
<small class="form-text text-muted">{{ "confirmIdentity" | i18n }}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
23
apps/web/src/app/components/user-verification.component.ts
Normal file
23
apps/web/src/app/components/user-verification.component.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { animate, style, transition, trigger } from "@angular/animations";
|
||||
import { Component } from "@angular/core";
|
||||
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
import { UserVerificationComponent as BaseComponent } from "@bitwarden/angular/components/user-verification.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-user-verification",
|
||||
templateUrl: "user-verification.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: UserVerificationComponent,
|
||||
},
|
||||
],
|
||||
animations: [
|
||||
trigger("sent", [
|
||||
transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]),
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class UserVerificationComponent extends BaseComponent {}
|
||||
@@ -8,14 +8,14 @@ import {
|
||||
EnvironmentService as EnvironmentServiceAbstraction,
|
||||
Urls,
|
||||
} from "@bitwarden/common/abstractions/environment.service";
|
||||
import { EventService as EventLoggingServiceAbstraction } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/abstractions/state.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
|
||||
import { ContainerService } from "@bitwarden/common/services/container.service";
|
||||
import { EventService as EventLoggingService } from "@bitwarden/common/services/event.service";
|
||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||
import { VaultTimeoutService as VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout/vaultTimeout.service";
|
||||
|
||||
import { I18nService } from "./i18n.service";
|
||||
@@ -28,7 +28,7 @@ export class InitService {
|
||||
private notificationsService: NotificationsServiceAbstraction,
|
||||
private vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
||||
private i18nService: I18nServiceAbstraction,
|
||||
private eventLoggingService: EventLoggingServiceAbstraction,
|
||||
private eventUploadService: EventUploadServiceAbstraction,
|
||||
private twoFactorService: TwoFactorServiceAbstraction,
|
||||
private stateService: StateServiceAbstraction,
|
||||
private cryptoService: CryptoServiceAbstraction,
|
||||
@@ -48,7 +48,7 @@ export class InitService {
|
||||
(this.vaultTimeoutService as VaultTimeoutService).init(true);
|
||||
const locale = await this.stateService.getLocale();
|
||||
await (this.i18nService as I18nService).init(locale);
|
||||
(this.eventLoggingService as EventLoggingService).init(true);
|
||||
(this.eventUploadService as EventUploadService).init(true);
|
||||
this.twoFactorService.init();
|
||||
const htmlEl = this.win.document.documentElement;
|
||||
htmlEl.classList.add("locale_" + this.i18nService.translationLocale);
|
||||
|
||||
115
apps/web/src/app/core/web-platform-utils.service.spec.ts
Normal file
115
apps/web/src/app/core/web-platform-utils.service.spec.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { WebPlatformUtilsService } from "./web-platform-utils.service";
|
||||
|
||||
describe("Web Platform Utils Service", () => {
|
||||
let webPlatformUtilsService: WebPlatformUtilsService;
|
||||
|
||||
beforeEach(() => {
|
||||
webPlatformUtilsService = new WebPlatformUtilsService(null, null, null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.APPLICATION_VERSION;
|
||||
});
|
||||
|
||||
describe("getApplicationVersion", () => {
|
||||
test("null", async () => {
|
||||
delete process.env.APPLICATION_VERSION;
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("-");
|
||||
});
|
||||
|
||||
test("<empty>", async () => {
|
||||
process.env.APPLICATION_VERSION = "";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("-");
|
||||
});
|
||||
|
||||
test("{version number}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("2022.10.2");
|
||||
});
|
||||
|
||||
test("{version number} - {git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2 - 5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("2022.10.2 - 5f8c1c1");
|
||||
});
|
||||
|
||||
test("{version number}-{git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2-5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("2022.10.2-5f8c1c1");
|
||||
});
|
||||
|
||||
test("{version number} + {git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2 + 5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("2022.10.2 + 5f8c1c1");
|
||||
});
|
||||
|
||||
test("{version number}+{git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2+5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersion();
|
||||
expect(result).toBe("2022.10.2+5f8c1c1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getApplicationVersionNumber", () => {
|
||||
test("null", async () => {
|
||||
delete process.env.APPLICATION_VERSION;
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
test("<empty>", async () => {
|
||||
process.env.APPLICATION_VERSION = "";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
test("{version number}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("2022.10.2");
|
||||
});
|
||||
|
||||
test("{version number} - {git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2 - 5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("2022.10.2");
|
||||
});
|
||||
|
||||
test("{version number}-{git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2-5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("2022.10.2");
|
||||
});
|
||||
|
||||
test("{version number} + {git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2 + 5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("2022.10.2");
|
||||
});
|
||||
|
||||
test("{version number}+{git hash}", async () => {
|
||||
process.env.APPLICATION_VERSION = "2022.10.2+5f8c1c1";
|
||||
|
||||
const result = await webPlatformUtilsService.getApplicationVersionNumber();
|
||||
expect(result).toBe("2022.10.2");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -108,6 +108,10 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
return Promise.resolve(process.env.APPLICATION_VERSION || "-");
|
||||
}
|
||||
|
||||
async getApplicationVersionNumber(): Promise<string> {
|
||||
return (await this.getApplicationVersion()).split(RegExp("[+|-]"))[0].trim();
|
||||
}
|
||||
|
||||
supportsWebAuthn(win: Window): boolean {
|
||||
return typeof PublicKeyCredential !== "undefined";
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { Observable } from "rxjs";
|
||||
import { map, Observable } from "rxjs";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import {
|
||||
canAccessAdmin,
|
||||
isNotProviderUser,
|
||||
OrganizationService,
|
||||
} from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
@@ -54,6 +55,7 @@ export class NavbarComponent implements OnInit {
|
||||
this.providers = await this.providerService.getAll();
|
||||
|
||||
this.organizations$ = this.organizationService.organizations$.pipe(
|
||||
map((orgs) => orgs.filter(isNotProviderUser)),
|
||||
canAccessAdmin(this.i18nService)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
class="btn btn-outline-secondary btn-submit"
|
||||
(click)="reinstate()"
|
||||
[appApiAction]="reinstatePromise"
|
||||
[disabled]="reinstateBtn.loading"
|
||||
[disabled]="$any(reinstateBtn).loading"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "reinstateSubscription" | i18n }}</span>
|
||||
@@ -113,8 +113,8 @@
|
||||
</button>
|
||||
<app-change-plan
|
||||
[organizationId]="organizationId"
|
||||
(onChanged)="closeChangePlan(true)"
|
||||
(onCanceled)="closeChangePlan(false)"
|
||||
(onChanged)="closeChangePlan()"
|
||||
(onCanceled)="closeChangePlan()"
|
||||
*ngIf="showChangePlan"
|
||||
></app-change-plan>
|
||||
</ng-container>
|
||||
@@ -143,7 +143,7 @@
|
||||
class="btn btn-outline-danger btn-submit"
|
||||
(click)="removeSponsorship()"
|
||||
[appApiAction]="removeSponsorshipPromise"
|
||||
[disabled]="removeSponsorshipBtn.loading"
|
||||
[disabled]="$any(removeSponsorshipBtn).loading"
|
||||
*ngIf="isSponsoredSubscription"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
@@ -230,7 +230,7 @@
|
||||
class="btn btn-outline-danger btn-submit ml-1"
|
||||
(click)="cancel()"
|
||||
[appApiAction]="cancelPromise"
|
||||
[disabled]="cancelBtn.loading"
|
||||
[disabled]="$any(cancelBtn).loading"
|
||||
*ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
|
||||
@@ -52,11 +52,11 @@
|
||||
type="button"
|
||||
class="btn btn-sm btn-outline-primary ml-3"
|
||||
(click)="loadEvents(true)"
|
||||
[disabled]="loaded && refreshBtn.loading"
|
||||
[disabled]="loaded && $any(refreshBtn).loading"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-refresh bwi-fw"
|
||||
[ngClass]="{ 'bwi-spin': loaded && refreshBtn.loading }"
|
||||
[ngClass]="{ 'bwi-spin': loaded && $any(refreshBtn).loading }"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
{{ "refresh" | i18n }}
|
||||
@@ -101,7 +101,7 @@
|
||||
type="button"
|
||||
class="btn btn-block btn-link btn-submit"
|
||||
(click)="loadEvents(false)"
|
||||
[disabled]="loaded && moreBtn.loading"
|
||||
[disabled]="loaded && $any(moreBtn).loading"
|
||||
*ngIf="continuationToken"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
(click)="loadEvents(false)"
|
||||
[disabled]="loaded && moreBtn.loading"
|
||||
[disabled]="loaded && $any(moreBtn).loading"
|
||||
*ngIf="continuationToken"
|
||||
>
|
||||
<i
|
||||
|
||||
@@ -113,12 +113,23 @@
|
||||
name="userType"
|
||||
id="userTypeCustom"
|
||||
[value]="organizationUserType.Custom"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[(ngModel)]="type"
|
||||
[attr.disabled]="!canUseCustomPermissions || null"
|
||||
/>
|
||||
<label class="form-check-label" for="userTypeCustom">
|
||||
{{ "custom" | i18n }}
|
||||
<small>{{ "customDesc" | i18n }}</small>
|
||||
<ng-container *ngIf="!canUseCustomPermissions; else enterprise">
|
||||
<small
|
||||
>{{ "customDescNonEnterpriseStart" | i18n
|
||||
}}<a href="https://bitwarden.com/contact/" target="_blank">{{
|
||||
"customDescNonEnterpriseLink" | i18n
|
||||
}}</a
|
||||
>{{ "customDescNonEnterpriseEnd" | i18n }}</small
|
||||
>
|
||||
</ng-container>
|
||||
<ng-template #enterprise>
|
||||
<small>{{ "customDesc" | i18n }}</small>
|
||||
</ng-template>
|
||||
</label>
|
||||
</div>
|
||||
<ng-container *ngIf="customUserTypeSelected">
|
||||
@@ -212,7 +223,7 @@
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
name="manageSso"
|
||||
id="managePolicies"
|
||||
id="manageSso"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[(ngModel)]="permissions.manageSso"
|
||||
/>
|
||||
@@ -345,7 +356,7 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[(ngModel)]="c.checked"
|
||||
[(ngModel)]="$any(c).checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
appStopProp
|
||||
/>
|
||||
@@ -359,7 +370,7 @@
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[(ngModel)]="c.hidePasswords"
|
||||
name="Collection[{{ i }}].HidePasswords"
|
||||
[disabled]="!c.checked"
|
||||
[disabled]="!$any(c).checked"
|
||||
/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
@@ -368,7 +379,7 @@
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
[(ngModel)]="c.readOnly"
|
||||
name="Collection[{{ i }}].ReadOnly"
|
||||
[disabled]="!c.checked"
|
||||
[disabled]="!$any(c).checked"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType";
|
||||
@@ -57,6 +58,7 @@ export class MemberDialogComponent implements OnInit {
|
||||
access: "all" | "selected" = "selected";
|
||||
collections: CollectionView[] = [];
|
||||
organizationUserType = OrganizationUserType;
|
||||
canUseCustomPermissions: boolean;
|
||||
|
||||
protected tabIndex: MemberDialogTab;
|
||||
// Stub, to be filled out in upcoming PRs
|
||||
@@ -104,6 +106,7 @@ export class MemberDialogComponent implements OnInit {
|
||||
private i18nService: I18nService,
|
||||
private collectionService: CollectionService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private organizationService: OrganizationService,
|
||||
private logService: LogService,
|
||||
private formBuilder: FormBuilder
|
||||
) {}
|
||||
@@ -111,6 +114,9 @@ export class MemberDialogComponent implements OnInit {
|
||||
async ngOnInit() {
|
||||
this.editMode = this.loading = this.params.organizationUserId != null;
|
||||
this.tabIndex = this.params.initialTab ?? MemberDialogTab.Role;
|
||||
|
||||
const organization = this.organizationService.get(this.params.organizationId);
|
||||
this.canUseCustomPermissions = organization.useCustomPermissions;
|
||||
await this.loadCollections();
|
||||
|
||||
if (this.editMode) {
|
||||
@@ -185,6 +191,14 @@ export class MemberDialogComponent implements OnInit {
|
||||
}
|
||||
|
||||
submit = async () => {
|
||||
if (!this.canUseCustomPermissions && this.type === OrganizationUserType.Custom) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
null,
|
||||
this.i18nService.t("customNonEnterpriseError")
|
||||
);
|
||||
return;
|
||||
}
|
||||
let collections: SelectionReadOnlyRequest[] = null;
|
||||
if (this.access !== "all") {
|
||||
collections = this.collections
|
||||
@@ -194,30 +208,9 @@ export class MemberDialogComponent implements OnInit {
|
||||
|
||||
try {
|
||||
if (this.editMode) {
|
||||
const request = new OrganizationUserUpdateRequest();
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.collections = collections;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
await this.apiService.putOrganizationUser(
|
||||
this.params.organizationId,
|
||||
this.params.organizationUserId,
|
||||
request
|
||||
);
|
||||
await this.updateUser(collections);
|
||||
} else {
|
||||
const request = new OrganizationUserInviteRequest();
|
||||
request.emails = [...new Set(this.emails.trim().split(/\s*,\s*/))];
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
request.collections = collections;
|
||||
await this.apiService.postOrganizationUserInvite(this.params.organizationId, request);
|
||||
await this.inviteUser(collections);
|
||||
}
|
||||
|
||||
this.platformUtilsService.showToast(
|
||||
@@ -331,6 +324,35 @@ export class MemberDialogComponent implements OnInit {
|
||||
private close(result: MemberDialogResult) {
|
||||
this.dialogRef.close(result);
|
||||
}
|
||||
|
||||
async updateUser(collections: SelectionReadOnlyRequest[]) {
|
||||
const request = new OrganizationUserUpdateRequest();
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.collections = collections;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
await this.apiService.putOrganizationUser(
|
||||
this.params.organizationId,
|
||||
this.params.organizationUserId,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
async inviteUser(collections: SelectionReadOnlyRequest[]) {
|
||||
const request = new OrganizationUserInviteRequest();
|
||||
request.emails = [...new Set(this.emails.trim().split(/\s*,\s*/))];
|
||||
request.accessAll = this.access === "all";
|
||||
request.type = this.type;
|
||||
request.permissions = this.setRequestPermissions(
|
||||
request.permissions ?? new PermissionsApi(),
|
||||
request.type !== OrganizationUserType.Custom
|
||||
);
|
||||
request.collections = collections;
|
||||
await this.apiService.postOrganizationUserInvite(this.params.organizationId, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<td class="table-list-checkbox" (click)="check(g)">
|
||||
<input
|
||||
type="checkbox"
|
||||
[(ngModel)]="g.checked"
|
||||
[(ngModel)]="$any(g).checked"
|
||||
name="Groups[{{ i }}].Checked"
|
||||
appStopProp
|
||||
/>
|
||||
|
||||
@@ -2,8 +2,6 @@ import { NgModule } from "@angular/core";
|
||||
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
|
||||
import { SharedModule } from "../shared";
|
||||
|
||||
import { CoreOrganizationModule } from "./core";
|
||||
import { GroupAddEditComponent } from "./manage/group-add-edit.component";
|
||||
import { GroupsComponent } from "./manage/groups.component";
|
||||
@@ -14,12 +12,7 @@ import { GroupService } from "./services/group/group.service";
|
||||
import { SharedOrganizationModule } from "./shared";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
SharedModule,
|
||||
OrganizationsRoutingModule,
|
||||
SharedOrganizationModule,
|
||||
CoreOrganizationModule,
|
||||
],
|
||||
imports: [OrganizationsRoutingModule, SharedOrganizationModule, CoreOrganizationModule],
|
||||
declarations: [GroupsComponent, GroupAddEditComponent, UserGroupsComponent],
|
||||
providers: [
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ export class PoliciesComponent implements OnInit {
|
||||
organization: Organization;
|
||||
|
||||
private orgPolicies: PolicyResponse[];
|
||||
private policiesEnabledMap: Map<PolicyType, boolean> = new Map<PolicyType, boolean>();
|
||||
protected policiesEnabledMap: Map<PolicyType, boolean> = new Map<PolicyType, boolean>();
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
||||
@@ -53,11 +53,13 @@
|
||||
<div class="tw-flex tw-flex-col">
|
||||
<div class="tw-text-sm">
|
||||
{{ item.labelName }}
|
||||
<span *ngIf="item.status == 0" bitBadge badgeType="secondary">
|
||||
<span *ngIf="$any(item).status == 0" bitBadge badgeType="secondary">
|
||||
{{ "invited" | i18n }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="tw-text-xs tw-text-muted" *ngIf="item.status != 0">{{ item.email }}</div>
|
||||
<div class="tw-text-xs tw-text-muted" *ngIf="$any(item).status != 0">
|
||||
{{ $any(item).email }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -110,11 +112,11 @@
|
||||
</td>
|
||||
|
||||
<td bitCell *ngIf="showMemberRoles">
|
||||
{{ item.role | userType: "-" }}
|
||||
{{ $any(item).role | userType: "-" }}
|
||||
</td>
|
||||
|
||||
<td bitCell *ngIf="showGroupColumn">
|
||||
{{ item.viaGroupName ?? "-" }}
|
||||
{{ $any(item).viaGroupName ?? "-" }}
|
||||
</td>
|
||||
|
||||
<td bitCell>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<bit-tab label="{{ 'access' | i18n }}">
|
||||
<bit-access-selector
|
||||
*ngIf="organization.useGroups"
|
||||
permissionMode="edit"
|
||||
[permissionMode]="PermissionMode.Edit"
|
||||
formControlName="access"
|
||||
[items]="accessItems"
|
||||
[columnHeader]="'groupAndMemberColumnHeader' | i18n"
|
||||
@@ -54,7 +54,7 @@
|
||||
></bit-access-selector>
|
||||
<bit-access-selector
|
||||
*ngIf="!organization.useGroups"
|
||||
permissionMode="edit"
|
||||
[permissionMode]="PermissionMode.Edit"
|
||||
formControlName="access"
|
||||
[items]="accessItems"
|
||||
[columnHeader]="'memberColumnHeader' | i18n"
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
AccessItemView,
|
||||
convertToPermission,
|
||||
convertToSelectionView,
|
||||
PermissionMode,
|
||||
} from "../access-selector";
|
||||
|
||||
export interface CollectionDialogParams {
|
||||
@@ -53,6 +54,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
|
||||
parent: null as string | null,
|
||||
access: [[] as AccessItemValue[]],
|
||||
});
|
||||
protected PermissionMode = PermissionMode;
|
||||
|
||||
constructor(
|
||||
@Inject(DIALOG_DATA) private params: CollectionDialogParams,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<label class="tw-sr-only" [for]="id">{{ "search" | i18n }}</label>
|
||||
<div class="tw-relative tw-flex tw-items-center">
|
||||
<label
|
||||
[for]="id"
|
||||
aria-hidden="true"
|
||||
class="tw-absolute tw-left-2 tw-z-20 !tw-mb-0 tw-cursor-text"
|
||||
>
|
||||
<i class="bwi bwi-search bwi-fw tw-text-muted"></i>
|
||||
</label>
|
||||
<input
|
||||
bitInput
|
||||
type="search"
|
||||
[id]="id"
|
||||
[placeholder]="placeholder ?? ('search' | i18n)"
|
||||
class="tw-rounded-l tw-pl-9"
|
||||
[ngModel]="searchText"
|
||||
(ngModelChange)="onChange($event)"
|
||||
(blur)="onTouch()"
|
||||
[disabled]="disabled"
|
||||
/>
|
||||
</div>
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
|
||||
let nextId = 0;
|
||||
|
||||
@Component({
|
||||
selector: "app-search-input",
|
||||
templateUrl: "./search-input.component.html",
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: SearchInputComponent,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class SearchInputComponent implements ControlValueAccessor {
|
||||
private notifyOnChange: (v: string) => void;
|
||||
private notifyOnTouch: () => void;
|
||||
|
||||
protected id = `search-id-${nextId++}`;
|
||||
protected searchText: string;
|
||||
|
||||
@Input() disabled: boolean;
|
||||
@Input() placeholder: string;
|
||||
|
||||
onChange(searchText: string) {
|
||||
if (this.notifyOnChange != undefined) {
|
||||
this.notifyOnChange(searchText);
|
||||
}
|
||||
}
|
||||
|
||||
onTouch() {
|
||||
if (this.notifyOnTouch != undefined) {
|
||||
this.notifyOnTouch();
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: (v: string) => void): void {
|
||||
this.notifyOnChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void): void {
|
||||
this.notifyOnTouch = fn;
|
||||
}
|
||||
|
||||
writeValue(searchText: string): void {
|
||||
this.searchText = searchText;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { InputModule } from "@bitwarden/components/src/input/input.module";
|
||||
|
||||
import { PreloadedEnglishI18nModule } from "../../../../tests/preloaded-english-i18n.module";
|
||||
|
||||
import { SearchInputComponent } from "./search-input.component";
|
||||
|
||||
export default {
|
||||
title: "Web/Organizations/Search Input",
|
||||
component: SearchInputComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
InputModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
PreloadedEnglishI18nModule,
|
||||
JslibModule,
|
||||
],
|
||||
providers: [],
|
||||
}),
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
const Template: Story<SearchInputComponent> = (args: SearchInputComponent) => ({
|
||||
props: args,
|
||||
template: `
|
||||
<app-search-input [(ngModel)]="searchText" [placeholder]="placeholder" [disabled]="disabled"></app-search-input>
|
||||
`,
|
||||
});
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {};
|
||||
@@ -1,10 +1,14 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule } from "../../shared/shared.module";
|
||||
|
||||
import { AccessSelectorModule } from "./components/access-selector";
|
||||
import { CollectionDialogModule } from "./components/collection-dialog";
|
||||
import { SearchInputComponent } from "./components/search-input/search-input.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [CollectionDialogModule, AccessSelectorModule],
|
||||
exports: [CollectionDialogModule, AccessSelectorModule],
|
||||
imports: [SharedModule, CollectionDialogModule, AccessSelectorModule],
|
||||
declarations: [SearchInputComponent],
|
||||
exports: [SharedModule, CollectionDialogModule, AccessSelectorModule],
|
||||
})
|
||||
export class SharedOrganizationModule {}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ActivatedRoute } from "@angular/router";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { ExportService } from "@bitwarden/common/abstractions/export.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
@@ -27,7 +27,7 @@ export class OrganizationExportComponent extends ExportComponent {
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
exportService: ExportService,
|
||||
eventService: EventService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
private route: ActivatedRoute,
|
||||
policyService: PolicyService,
|
||||
logService: LogService,
|
||||
@@ -41,7 +41,7 @@ export class OrganizationExportComponent extends ExportComponent {
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
exportService,
|
||||
eventService,
|
||||
eventCollectionService,
|
||||
policyService,
|
||||
logService,
|
||||
userVerificationService,
|
||||
@@ -76,7 +76,7 @@ export class OrganizationExportComponent extends ExportComponent {
|
||||
}
|
||||
|
||||
async collectEvent(): Promise<void> {
|
||||
await this.eventService.collect(
|
||||
await this.eventCollectionService.collect(
|
||||
EventType.Organization_ClientExportedVault,
|
||||
null,
|
||||
null,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
@@ -18,7 +18,6 @@ import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
|
||||
import { CipherData } from "@bitwarden/common/models/data/cipher.data";
|
||||
import { Cipher } from "@bitwarden/common/models/domain/cipher";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { CipherCreateRequest } from "@bitwarden/common/models/request/cipher-create.request";
|
||||
import { CipherRequest } from "@bitwarden/common/models/request/cipher.request";
|
||||
|
||||
@@ -29,7 +28,6 @@ import { AddEditComponent as BaseAddEditComponent } from "../../vault/add-edit.c
|
||||
templateUrl: "../../vault/add-edit.component.html",
|
||||
})
|
||||
export class AddEditComponent extends BaseAddEditComponent {
|
||||
organization: Organization;
|
||||
originalCipher: Cipher = null;
|
||||
|
||||
constructor(
|
||||
@@ -44,7 +42,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
private apiService: ApiService,
|
||||
messagingService: MessagingService,
|
||||
eventService: EventService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
policyService: PolicyService,
|
||||
logService: LogService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
@@ -61,7 +59,7 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||
totpService,
|
||||
passwordGenerationService,
|
||||
messagingService,
|
||||
eventService,
|
||||
eventCollectionService,
|
||||
policyService,
|
||||
organizationService,
|
||||
logService,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Component, EventEmitter, Output } from "@angular/core";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
@@ -12,21 +12,17 @@ import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { TokenService } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService } from "@bitwarden/common/abstractions/totp.service";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipher.view";
|
||||
|
||||
import { CiphersComponent as BaseCiphersComponent } from "../../vault/ciphers.component";
|
||||
import { VaultItemsComponent as BaseVaultItemsComponent } from "../../vault/vault-items.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-vault-ciphers",
|
||||
templateUrl: "../../vault/ciphers.component.html",
|
||||
selector: "app-org-vault-items",
|
||||
templateUrl: "../../vault/vault-items.component.html",
|
||||
})
|
||||
export class CiphersComponent extends BaseCiphersComponent {
|
||||
export class VaultItemsComponent extends BaseVaultItemsComponent {
|
||||
@Output() onEventsClicked = new EventEmitter<CipherView>();
|
||||
|
||||
organization: Organization;
|
||||
accessEvents = false;
|
||||
|
||||
protected allCiphers: CipherView[] = [];
|
||||
|
||||
constructor(
|
||||
@@ -34,7 +30,7 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
cipherService: CipherService,
|
||||
eventService: EventService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
totpService: TotpService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
logService: LogService,
|
||||
@@ -48,7 +44,7 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
cipherService,
|
||||
eventService,
|
||||
eventCollectionService,
|
||||
totpService,
|
||||
stateService,
|
||||
passwordRepromptService,
|
||||
@@ -86,6 +82,7 @@ export class CiphersComponent extends BaseCiphersComponent {
|
||||
async search(timeout: number = null) {
|
||||
await super.search(timeout, this.allCiphers);
|
||||
}
|
||||
|
||||
events(c: CipherView) {
|
||||
this.onEventsClicked.emit(c);
|
||||
}
|
||||
@@ -19,8 +19,8 @@
|
||||
<div class="page-header d-flex">
|
||||
<h1>
|
||||
{{ "vaultItems" | i18n }}
|
||||
<small #actionSpinner [appApiAction]="ciphersComponent.actionPromise">
|
||||
<ng-container *ngIf="actionSpinner.loading">
|
||||
<small #actionSpinner [appApiAction]="vaultItemsComponent.actionPromise">
|
||||
<ng-container *ngIf="$any(actionSpinner).loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
@@ -32,7 +32,7 @@
|
||||
</h1>
|
||||
<div class="ml-auto d-flex">
|
||||
<app-vault-bulk-actions
|
||||
[ciphersComponent]="ciphersComponent"
|
||||
[vaultItemsComponent]="vaultItemsComponent"
|
||||
[deleted]="activeFilter.isDeleted"
|
||||
[organization]="organization"
|
||||
>
|
||||
@@ -54,7 +54,7 @@
|
||||
>
|
||||
{{ trashCleanupWarning }}
|
||||
</app-callout>
|
||||
<app-org-vault-ciphers
|
||||
<app-org-vault-items
|
||||
(onCipherClicked)="editCipher($event)"
|
||||
(onAttachmentsClicked)="editCipherAttachments($event)"
|
||||
(onAddCipher)="addCipher()"
|
||||
@@ -62,7 +62,7 @@
|
||||
(onEventsClicked)="viewEvents($event)"
|
||||
(onCloneClicked)="cloneCipher($event)"
|
||||
>
|
||||
</app-org-vault-ciphers>
|
||||
</app-org-vault-items>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -29,9 +29,9 @@ import { EntityEventsComponent } from "../manage/entity-events.component";
|
||||
|
||||
import { AddEditComponent } from "./add-edit.component";
|
||||
import { AttachmentsComponent } from "./attachments.component";
|
||||
import { CiphersComponent } from "./ciphers.component";
|
||||
import { CollectionsComponent } from "./collections.component";
|
||||
import { VaultFilterComponent } from "./vault-filter/vault-filter.component";
|
||||
import { VaultItemsComponent } from "./vault-items.component";
|
||||
|
||||
const BroadcasterSubscriptionId = "OrgVaultComponent";
|
||||
|
||||
@@ -42,7 +42,7 @@ const BroadcasterSubscriptionId = "OrgVaultComponent";
|
||||
export class VaultComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("vaultFilter", { static: true })
|
||||
vaultFilterComponent: VaultFilterComponent;
|
||||
@ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent;
|
||||
@ViewChild(VaultItemsComponent, { static: true }) vaultItemsComponent: VaultItemsComponent;
|
||||
@ViewChild("attachments", { read: ViewContainerRef, static: true })
|
||||
attachmentsModalRef: ViewContainerRef;
|
||||
@ViewChild("cipherAddEdit", { read: ViewContainerRef, static: true })
|
||||
@@ -82,11 +82,11 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.parent.params.subscribe(async (params: any) => {
|
||||
this.organization = this.organizationService.get(params.organizationId);
|
||||
this.ciphersComponent.organization = this.organization;
|
||||
this.vaultItemsComponent.organization = this.organization;
|
||||
|
||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
||||
this.ciphersComponent.searchText = this.vaultFilterComponent.searchText = qParams.search;
|
||||
this.vaultItemsComponent.searchText = this.vaultFilterComponent.searchText = qParams.search;
|
||||
if (!this.organization.canViewAllCollections) {
|
||||
await this.syncService.fullSync(false);
|
||||
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
|
||||
@@ -96,7 +96,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
if (message.successfully) {
|
||||
await Promise.all([
|
||||
this.vaultFilterService.reloadCollections(),
|
||||
this.ciphersComponent.refresh(),
|
||||
this.vaultItemsComponent.refresh(),
|
||||
]);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
@@ -106,13 +106,15 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
await this.ciphersComponent.reload(
|
||||
await this.vaultItemsComponent.reload(
|
||||
this.activeFilter.buildFilter(),
|
||||
this.activeFilter.isDeleted
|
||||
);
|
||||
|
||||
if (qParams.viewEvents != null) {
|
||||
const cipher = this.ciphersComponent.ciphers.filter((c) => c.id === qParams.viewEvents);
|
||||
const cipher = this.vaultItemsComponent.ciphers.filter(
|
||||
(c) => c.id === qParams.viewEvents
|
||||
);
|
||||
if (cipher.length > 0) {
|
||||
this.viewEvents(cipher[0]);
|
||||
}
|
||||
@@ -151,8 +153,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
|
||||
async applyVaultFilter(filter: VaultFilter) {
|
||||
this.activeFilter = filter;
|
||||
this.ciphersComponent.showAddNew = !this.activeFilter.isDeleted;
|
||||
await this.ciphersComponent.reload(
|
||||
this.vaultItemsComponent.showAddNew = !this.activeFilter.isDeleted;
|
||||
await this.vaultItemsComponent.reload(
|
||||
this.activeFilter.buildFilter(),
|
||||
this.activeFilter.isDeleted
|
||||
);
|
||||
@@ -160,8 +162,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
filterSearchText(searchText: string) {
|
||||
this.ciphersComponent.searchText = searchText;
|
||||
this.ciphersComponent.search(200);
|
||||
this.vaultItemsComponent.searchText = searchText;
|
||||
this.vaultItemsComponent.search(200);
|
||||
}
|
||||
|
||||
async editCipherAttachments(cipher: CipherView) {
|
||||
@@ -188,7 +190,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
modal.onClosed.subscribe(async () => {
|
||||
if (madeAttachmentChanges) {
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
}
|
||||
madeAttachmentChanges = false;
|
||||
});
|
||||
@@ -207,7 +209,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onSavedCollections.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -247,17 +249,17 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onSavedCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onDeletedCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onRestoredCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2,15 +2,22 @@ import { NgModule } from "@angular/core";
|
||||
|
||||
import { LooseComponentsModule } from "../../shared/loose-components.module";
|
||||
import { SharedModule } from "../../shared/shared.module";
|
||||
import { OrganizationBadgeModule } from "../../vault/organization-badge/organization-badge.module";
|
||||
|
||||
import { CiphersComponent } from "./ciphers.component";
|
||||
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
||||
import { VaultItemsComponent } from "./vault-items.component";
|
||||
import { VaultRoutingModule } from "./vault-routing.module";
|
||||
import { VaultComponent } from "./vault.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [VaultRoutingModule, VaultFilterModule, SharedModule, LooseComponentsModule],
|
||||
declarations: [VaultComponent, CiphersComponent],
|
||||
imports: [
|
||||
VaultRoutingModule,
|
||||
VaultFilterModule,
|
||||
SharedModule,
|
||||
LooseComponentsModule,
|
||||
OrganizationBadgeModule,
|
||||
],
|
||||
declarations: [VaultComponent, VaultItemsComponent],
|
||||
exports: [VaultComponent],
|
||||
})
|
||||
export class VaultModule {}
|
||||
|
||||
@@ -15,7 +15,6 @@ import { LoginWithDeviceComponent } from "./accounts/login/login-with-device.com
|
||||
import { LoginComponent } from "./accounts/login/login.component";
|
||||
import { RecoverDeleteComponent } from "./accounts/recover-delete.component";
|
||||
import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component";
|
||||
import { RegisterComponent } from "./accounts/register.component";
|
||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||
import { SsoComponent } from "./accounts/sso.component";
|
||||
@@ -69,16 +68,15 @@ const routes: Routes = [
|
||||
{ path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] },
|
||||
{
|
||||
path: "register",
|
||||
component: RegisterComponent,
|
||||
component: TrialInitiationComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "createAccount" },
|
||||
},
|
||||
buildFlaggedRoute("showTrial", {
|
||||
{
|
||||
path: "trial",
|
||||
component: TrialInitiationComponent,
|
||||
canActivate: [UnauthGuard],
|
||||
data: { titleId: "startTrial" },
|
||||
}),
|
||||
redirectTo: "register",
|
||||
pathMatch: "full",
|
||||
},
|
||||
{
|
||||
path: "sso",
|
||||
component: SsoComponent,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { PasswordRepromptService } from "@bitwarden/common/abstractions/password
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { CipherType } from "@bitwarden/common/enums/cipherType";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipher.view";
|
||||
import { BadgeTypes } from "@bitwarden/components";
|
||||
|
||||
import { CipherReportComponent } from "./cipher-report.component";
|
||||
|
||||
@@ -16,7 +17,7 @@ import { CipherReportComponent } from "./cipher-report.component";
|
||||
templateUrl: "weak-passwords-report.component.html",
|
||||
})
|
||||
export class WeakPasswordsReportComponent extends CipherReportComponent implements OnInit {
|
||||
passwordStrengthMap = new Map<string, [string, string]>();
|
||||
passwordStrengthMap = new Map<string, [string, BadgeTypes]>();
|
||||
|
||||
private passwordStrengthCache = new Map<string, number>();
|
||||
|
||||
@@ -110,7 +111,7 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
return true;
|
||||
}
|
||||
|
||||
private scoreKey(score: number): [string, string] {
|
||||
private scoreKey(score: number): [string, BadgeTypes] {
|
||||
switch (score) {
|
||||
case 4:
|
||||
return ["strong", "success"];
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { Icon } from "@bitwarden/components";
|
||||
|
||||
import { ReportVariant } from "../models/report-variant";
|
||||
|
||||
@Component({
|
||||
@@ -10,7 +12,7 @@ export class ReportCardComponent {
|
||||
@Input() title: string;
|
||||
@Input() description: string;
|
||||
@Input() route: string;
|
||||
@Input() icon: string;
|
||||
@Input() icon: Icon;
|
||||
@Input() variant: ReportVariant;
|
||||
|
||||
protected get disabled() {
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
id="text"
|
||||
rows="8"
|
||||
name="Text"
|
||||
[(ngModel)]="sendText"
|
||||
[ngModel]="sendText"
|
||||
class="form-control"
|
||||
readonly
|
||||
></textarea>
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
name="Type_{{ o.value }}"
|
||||
id="type_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="typeChanged(o)"
|
||||
(change)="typeChanged()"
|
||||
[checked]="send.type === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="type_{{ o.value }}">
|
||||
@@ -120,14 +120,7 @@
|
||||
<h3 class="mt-5">{{ "share" | i18n }}</h3>
|
||||
<div class="form-group" *ngIf="link">
|
||||
<label for="link">{{ "sendLinkLabel" | i18n }}</label>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
id="link"
|
||||
name="Link"
|
||||
[(ngModel)]="link"
|
||||
class="form-control"
|
||||
/>
|
||||
<input type="text" readonly id="link" name="Link" [ngModel]="link" class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
@@ -286,17 +279,17 @@
|
||||
class="btn btn-outline-danger"
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
*ngIf="editMode"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[disabled]="$any(deleteBtn).loading"
|
||||
[appApiAction]="deletePromise"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-trash bwi-lg bwi-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
[hidden]="$any(deleteBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
[hidden]="!$any(deleteBtn).loading"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
formControlName="fallbackDeletionDate"
|
||||
required
|
||||
placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disableSend"
|
||||
data-date-format="mm/dd/yyyy"
|
||||
/>
|
||||
<input
|
||||
@@ -24,7 +23,6 @@
|
||||
formControlName="fallbackDeletionTime"
|
||||
required
|
||||
placeholder="HH:MM AM/PM"
|
||||
[readOnly]="disableSend"
|
||||
/>
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -38,7 +36,6 @@
|
||||
formControlName="fallbackDeletionDate"
|
||||
required
|
||||
placeholder="MM/DD/YYYY"
|
||||
[readOnly]="disableSend"
|
||||
data-date-format="mm/dd/yyyy"
|
||||
/>
|
||||
<select
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<h1>
|
||||
{{ "send" | i18n }}
|
||||
<small #actionSpinner [appApiAction]="actionPromise">
|
||||
<ng-container *ngIf="actionSpinner.loading">
|
||||
<ng-container *ngIf="$any(actionSpinner).loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
|
||||
@@ -121,16 +121,16 @@
|
||||
(click)="delete()"
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
*ngIf="editMode"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[disabled]="$any(deleteBtn).loading"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-trash bwi-lg bwi-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
[hidden]="$any(deleteBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
[hidden]="!$any(deleteBtn).loading"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Component } from "@angular/core";
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
@@ -38,7 +38,7 @@ export class EmergencyAddEditComponent extends BaseAddEditComponent {
|
||||
totpService: TotpService,
|
||||
passwordGenerationService: PasswordGenerationService,
|
||||
messagingService: MessagingService,
|
||||
eventService: EventService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
policyService: PolicyService,
|
||||
passwordRepromptService: PasswordRepromptService,
|
||||
organizationService: OrganizationService,
|
||||
@@ -55,7 +55,7 @@ export class EmergencyAddEditComponent extends BaseAddEditComponent {
|
||||
totpService,
|
||||
passwordGenerationService,
|
||||
messagingService,
|
||||
eventService,
|
||||
eventCollectionService,
|
||||
policyService,
|
||||
organizationService,
|
||||
logService,
|
||||
|
||||
@@ -240,14 +240,6 @@
|
||||
{{ "monthAbbr" | i18n }} =
|
||||
{{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "year" | i18n }}
|
||||
</small>
|
||||
<small *ngIf="selectablePlan.hasPremiumAccessOption && premiumAccessAddon">
|
||||
{{ "premiumAccess" | i18n }}:
|
||||
{{ selectablePlan.premiumAccessOptionCost / 12 | currency: "$" }} × 12
|
||||
{{ "monthAbbr" | i18n }}
|
||||
=
|
||||
{{ 40 | currency: "$" }}
|
||||
/{{ "year" | i18n }}
|
||||
</small>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!selectablePlan.isAnnual">
|
||||
{{ "monthly" | i18n }}
|
||||
@@ -272,17 +264,6 @@
|
||||
{{ "monthAbbr" | i18n }} =
|
||||
{{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }}
|
||||
</small>
|
||||
<small
|
||||
*ngIf="
|
||||
selectablePlan.hasPremiumAccessOption &&
|
||||
formGroup.controls['premiumAccessAddon'].value
|
||||
"
|
||||
>
|
||||
{{ "premiumAccess" | i18n }}:
|
||||
{{ selectablePlan.premiumAccessOptionCost | currency: "$" }} {{ "monthAbbr" | i18n }} =
|
||||
{{ 40 | currency: "$" }}
|
||||
/{{ "month" | i18n }}
|
||||
</small>
|
||||
</ng-container>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -121,7 +121,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (this.createOrganization) {
|
||||
this.formGroup.controls.name.addValidators(Validators.required);
|
||||
this.formGroup.controls.name.addValidators([Validators.required, Validators.maxLength(50)]);
|
||||
this.formGroup.controls.billingEmail.addValidators(Validators.required);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
*ngIf="!isSelfHosted && !sponsoringOrg.familySponsorshipValidUntil"
|
||||
[appApiAction]="resendEmailPromise"
|
||||
class="dropdown-item btn-submit"
|
||||
[disabled]="resendEmailBtn.loading"
|
||||
[disabled]="$any(resendEmailBtn).loading"
|
||||
(click)="resendEmail()"
|
||||
[attr.aria-label]="'resendEmailLabel' | i18n: sponsoringOrg.familySponsorshipFriendlyName"
|
||||
>
|
||||
@@ -36,7 +36,7 @@
|
||||
#revokeSponsorshipBtn
|
||||
[appApiAction]="revokeSponsorshipPromise"
|
||||
class="dropdown-item text-danger btn-submit"
|
||||
[disabled]="revokeSponsorshipBtn.loading"
|
||||
[disabled]="$any(revokeSponsorshipBtn).loading"
|
||||
(click)="revokeSponsorship()"
|
||||
[attr.aria-label]="'revokeAccount' | i18n: sponsoringOrg.familySponsorshipFriendlyName"
|
||||
>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<app-two-factor-verify
|
||||
[organizationId]="organizationId"
|
||||
[type]="type"
|
||||
(onAuthed)="auth($event)"
|
||||
(onAuthed)="auth($any($event))"
|
||||
*ngIf="!authed"
|
||||
>
|
||||
</app-two-factor-verify>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<app-two-factor-verify
|
||||
[organizationId]="organizationId"
|
||||
[type]="type"
|
||||
(onAuthed)="auth($event)"
|
||||
(onAuthed)="auth($any($event))"
|
||||
*ngIf="!authed"
|
||||
>
|
||||
</app-two-factor-verify>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<app-two-factor-verify
|
||||
[organizationId]="organizationId"
|
||||
[type]="type"
|
||||
(onAuthed)="auth($event)"
|
||||
(onAuthed)="auth($any($event))"
|
||||
*ngIf="!authed"
|
||||
>
|
||||
</app-two-factor-verify>
|
||||
@@ -61,7 +61,7 @@
|
||||
class="btn btn-outline-primary btn-sm btn-submit align-self-start"
|
||||
(click)="sendEmail()"
|
||||
[appApiAction]="emailPromise"
|
||||
[disabled]="sendBtn.loading"
|
||||
[disabled]="$any(sendBtn).loading"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
|
||||
@@ -15,12 +15,7 @@
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<app-two-factor-verify
|
||||
[organizationId]="organizationId"
|
||||
[type]="type"
|
||||
(onAuthed)="auth($event)"
|
||||
*ngIf="!authed"
|
||||
>
|
||||
<app-two-factor-verify [type]="type" (onAuthed)="auth($event)" *ngIf="!authed">
|
||||
</app-two-factor-verify>
|
||||
<ng-container *ngIf="authed">
|
||||
<div class="modal-body text-center">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<app-two-factor-verify
|
||||
[organizationId]="organizationId"
|
||||
[type]="type"
|
||||
(onAuthed)="auth($event)"
|
||||
(onAuthed)="auth($any($event))"
|
||||
*ngIf="!authed"
|
||||
>
|
||||
</app-two-factor-verify>
|
||||
@@ -54,7 +54,7 @@
|
||||
<i class="bwi bwi-li bwi-key"></i>
|
||||
<strong *ngIf="!k.configured || !k.name">{{ "webAuthnkeyX" | i18n: i + 1 }}</strong>
|
||||
<strong *ngIf="k.configured && k.name">{{ k.name }}</strong>
|
||||
<ng-container *ngIf="k.configured && !removeKeyBtn.loading">
|
||||
<ng-container *ngIf="k.configured && !$any(removeKeyBtn).loading">
|
||||
<ng-container *ngIf="k.migrated">
|
||||
<span>{{ "webAuthnMigrated" | i18n }}</span>
|
||||
</ng-container>
|
||||
@@ -63,7 +63,7 @@
|
||||
<i
|
||||
class="bwi bwi-spin bwi-spinner text-muted bwi-fw"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
*ngIf="removeKeyBtn.loading"
|
||||
*ngIf="$any(removeKeyBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
-
|
||||
@@ -96,16 +96,16 @@
|
||||
type="button"
|
||||
(click)="readKey()"
|
||||
class="btn btn-outline-secondary mr-2"
|
||||
[disabled]="readKeyBtn.loading || webAuthnListening || !keyIdAvailable"
|
||||
[disabled]="$any(readKeyBtn).loading || webAuthnListening || !keyIdAvailable"
|
||||
#readKeyBtn
|
||||
[appApiAction]="challengePromise"
|
||||
>
|
||||
{{ "readKey" | i18n }}
|
||||
</button>
|
||||
<ng-container *ngIf="readKeyBtn.loading">
|
||||
<ng-container *ngIf="$any(readKeyBtn).loading">
|
||||
<i class="bwi bwi-spinner bwi-spin text-muted" aria-hidden="true"></i>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!readKeyBtn.loading">
|
||||
<ng-container *ngIf="!$any(readKeyBtn).loading">
|
||||
<ng-container *ngIf="webAuthnListening">
|
||||
<i class="bwi bwi-spinner bwi-spin text-muted" aria-hidden="true"></i>
|
||||
{{ "twoFactorU2fWaiting" | i18n }}...
|
||||
@@ -138,8 +138,7 @@
|
||||
#disableBtn
|
||||
type="button"
|
||||
class="btn btn-outline-secondary btn-submit"
|
||||
[appApiAction]="disablePromise"
|
||||
[disabled]="disableBtn.loading"
|
||||
[disabled]="$any(disableBtn).loading"
|
||||
(click)="disable()"
|
||||
*ngIf="enabled"
|
||||
>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<app-two-factor-verify
|
||||
[organizationId]="organizationId"
|
||||
[type]="type"
|
||||
(onAuthed)="auth($event)"
|
||||
(onAuthed)="auth($any($event))"
|
||||
*ngIf="!authed"
|
||||
>
|
||||
</app-two-factor-verify>
|
||||
@@ -104,7 +104,7 @@
|
||||
type="button"
|
||||
class="btn btn-outline-secondary btn-submit"
|
||||
[appApiAction]="disablePromise"
|
||||
[disabled]="disableBtn.loading"
|
||||
[disabled]="$any(disableBtn).loading"
|
||||
(click)="disable()"
|
||||
*ngIf="enabled"
|
||||
>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
class="btn-submit"
|
||||
(click)="reinstate()"
|
||||
[appApiAction]="reinstatePromise"
|
||||
[disabled]="reinstateBtn.loading"
|
||||
[disabled]="$any(reinstateBtn).loading"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "reinstateSubscription" | i18n }}</span>
|
||||
@@ -147,7 +147,7 @@
|
||||
class="btn-submit tw-ml-auto"
|
||||
(click)="cancel()"
|
||||
[appApiAction]="cancelPromise"
|
||||
[disabled]="cancelBtn.loading"
|
||||
[disabled]="$any(cancelBtn).loading"
|
||||
*ngIf="subscription && !subscription.cancelled && !subscriptionMarkedForCancel"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
class="btn btn-block btn-outline-secondary btn-submit"
|
||||
#sendBtn
|
||||
[appApiAction]="actionPromise"
|
||||
[disabled]="sendBtn.loading"
|
||||
[disabled]="$any(sendBtn).loading"
|
||||
(click)="send()"
|
||||
>
|
||||
<i class="bwi bwi-spin bwi-spinner" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { UserVerificationComponent } from "@bitwarden/angular/components/user-verification.component";
|
||||
|
||||
import { AcceptEmergencyComponent } from "../accounts/accept-emergency.component";
|
||||
import { AcceptOrganizationComponent } from "../accounts/accept-organization.component";
|
||||
import { HintComponent } from "../accounts/hint.component";
|
||||
@@ -9,7 +7,6 @@ import { LockComponent } from "../accounts/lock.component";
|
||||
import { RecoverDeleteComponent } from "../accounts/recover-delete.component";
|
||||
import { RecoverTwoFactorComponent } from "../accounts/recover-two-factor.component";
|
||||
import { RegisterFormModule } from "../accounts/register-form/register-form.module";
|
||||
import { RegisterComponent } from "../accounts/register.component";
|
||||
import { RemovePasswordComponent } from "../accounts/remove-password.component";
|
||||
import { SetPasswordComponent } from "../accounts/set-password.component";
|
||||
import { SsoComponent } from "../accounts/sso.component";
|
||||
@@ -23,6 +20,7 @@ import { OrganizationSwitcherComponent } from "../components/organization-switch
|
||||
import { PasswordRepromptComponent } from "../components/password-reprompt.component";
|
||||
import { PremiumBadgeComponent } from "../components/premium-badge.component";
|
||||
import { UserVerificationPromptComponent } from "../components/user-verification-prompt.component";
|
||||
import { UserVerificationComponent } from "../components/user-verification.component";
|
||||
import { FooterComponent } from "../layouts/footer.component";
|
||||
import { FrontendLayoutComponent } from "../layouts/frontend-layout.component";
|
||||
import { NavbarComponent } from "../layouts/navbar.component";
|
||||
@@ -118,7 +116,6 @@ import { BulkRestoreComponent } from "../vault/bulk-restore.component";
|
||||
import { BulkShareComponent } from "../vault/bulk-share.component";
|
||||
import { CollectionsComponent } from "../vault/collections.component";
|
||||
import { FolderAddEditComponent } from "../vault/folder-add-edit.component";
|
||||
import { OrganizationBadgeModule } from "../vault/organization-badge/organization-badge.module";
|
||||
import { ShareComponent } from "../vault/share.component";
|
||||
|
||||
import { SharedModule } from "./shared.module";
|
||||
@@ -126,7 +123,7 @@ import { SharedModule } from "./shared.module";
|
||||
// Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left.
|
||||
// If you are building new functionality, please create or extend a feature module instead.
|
||||
@NgModule({
|
||||
imports: [SharedModule, OrganizationBadgeModule, OrganizationCreateModule, RegisterFormModule],
|
||||
imports: [SharedModule, OrganizationCreateModule, RegisterFormModule],
|
||||
declarations: [
|
||||
PremiumBadgeComponent,
|
||||
AcceptEmergencyComponent,
|
||||
@@ -207,7 +204,6 @@ import { SharedModule } from "./shared.module";
|
||||
PurgeVaultComponent,
|
||||
RecoverDeleteComponent,
|
||||
RecoverTwoFactorComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
SecurityComponent,
|
||||
SecurityKeysComponent,
|
||||
@@ -324,7 +320,6 @@ import { SharedModule } from "./shared.module";
|
||||
PurgeVaultComponent,
|
||||
RecoverDeleteComponent,
|
||||
RecoverTwoFactorComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
SecurityComponent,
|
||||
SecurityKeysComponent,
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
LinkModule,
|
||||
MenuModule,
|
||||
MultiSelectModule,
|
||||
NavigationModule,
|
||||
TableModule,
|
||||
TabsModule,
|
||||
} from "@bitwarden/components";
|
||||
@@ -40,52 +41,60 @@ import "./locales";
|
||||
CommonModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
InfiniteScrollModule,
|
||||
JslibModule,
|
||||
ReactiveFormsModule,
|
||||
InfiniteScrollModule,
|
||||
RouterModule,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
ToastrModule,
|
||||
JslibModule,
|
||||
|
||||
// Component library
|
||||
AsyncActionsModule,
|
||||
AvatarModule,
|
||||
BadgeModule,
|
||||
BadgeListModule,
|
||||
ButtonModule,
|
||||
MenuModule,
|
||||
CalloutModule,
|
||||
DialogModule,
|
||||
MultiSelectModule,
|
||||
FormFieldModule,
|
||||
IconModule,
|
||||
TabsModule,
|
||||
TableModule,
|
||||
AvatarModule,
|
||||
IconButtonModule,
|
||||
LinkModule,
|
||||
DialogModule,
|
||||
IconModule,
|
||||
MenuModule,
|
||||
NavigationModule,
|
||||
TableModule,
|
||||
TabsModule,
|
||||
|
||||
// Web specific
|
||||
],
|
||||
exports: [
|
||||
CommonModule,
|
||||
AsyncActionsModule,
|
||||
DragDropModule,
|
||||
FormsModule,
|
||||
InfiniteScrollModule,
|
||||
JslibModule,
|
||||
ReactiveFormsModule,
|
||||
InfiniteScrollModule,
|
||||
RouterModule,
|
||||
ToastrModule,
|
||||
JslibModule,
|
||||
|
||||
// Component library
|
||||
AsyncActionsModule,
|
||||
AvatarModule,
|
||||
BadgeModule,
|
||||
BadgeListModule,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
ToastrModule,
|
||||
MenuModule,
|
||||
DialogModule,
|
||||
MultiSelectModule,
|
||||
FormFieldModule,
|
||||
IconModule,
|
||||
TabsModule,
|
||||
TableModule,
|
||||
AvatarModule,
|
||||
IconButtonModule,
|
||||
IconModule,
|
||||
MenuModule,
|
||||
NavigationModule,
|
||||
TableModule,
|
||||
LinkModule,
|
||||
DialogModule,
|
||||
TabsModule,
|
||||
|
||||
// Web specific
|
||||
],
|
||||
providers: [DatePipe],
|
||||
bootstrap: [],
|
||||
|
||||
@@ -266,23 +266,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngIf="usernameOptions.type === 'forwarded'">
|
||||
<div class="form-group">
|
||||
<div class="form-group" role="listbox">
|
||||
<label class="d-block">{{ "service" | i18n }}</label>
|
||||
<div class="form-check" *ngFor="let o of forwardOptions">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
name="ForwardType"
|
||||
id="forwardtype_{{ o.value }}"
|
||||
[value]="o.value"
|
||||
(change)="saveUsernameOptions()"
|
||||
[checked]="usernameOptions.forwardedService === o.value"
|
||||
/>
|
||||
<label class="form-check-label" for="forwardtype_{{ o.value }}">
|
||||
<select
|
||||
id="ForwardTypeDropdown"
|
||||
name="ForwardType"
|
||||
[(ngModel)]="usernameOptions.forwardedService"
|
||||
(change)="saveUsernameOptions()"
|
||||
class="form-control w-auto"
|
||||
>
|
||||
<option *ngFor="let o of forwardOptions" [ngValue]="o.value" role="option">
|
||||
{{ o.name }}
|
||||
</label>
|
||||
</div>
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row" *ngIf="usernameOptions.forwardedService === 'simplelogin'">
|
||||
<div class="form-group col-4">
|
||||
@@ -303,7 +299,7 @@
|
||||
id="duckduckgo-apikey"
|
||||
class="form-control"
|
||||
type="password"
|
||||
name="DuckDudkGoApiKey"
|
||||
name="DuckDuckGoApiKey"
|
||||
[(ngModel)]="usernameOptions.forwardedDuckDuckGoToken"
|
||||
(blur)="saveUsernameOptions()"
|
||||
/>
|
||||
@@ -412,7 +408,7 @@
|
||||
type="button"
|
||||
class="btn btn-submit btn-primary"
|
||||
(click)="regenerate()"
|
||||
[disabled]="form.loading"
|
||||
[disabled]="$any(form).loading"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "regenerateUsername" | i18n }}</span>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<bit-label>{{ "filePassword" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="{{ showFilePassword ? 'text' : 'password' }}"
|
||||
[type]="showFilePassword ? 'text' : 'password'"
|
||||
id="filePassword"
|
||||
formControlName="filePassword"
|
||||
name="password"
|
||||
@@ -123,7 +123,7 @@
|
||||
<bit-label>{{ "confirmFilePassword" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="{{ showConfirmFilePassword ? 'text' : 'password' }}"
|
||||
[type]="showConfirmFilePassword ? 'text' : 'password'"
|
||||
id="confirmFilePassword"
|
||||
formControlName="confirmFilePassword"
|
||||
name="confirmFilePassword"
|
||||
@@ -157,7 +157,8 @@
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-submit"
|
||||
[disabled]="form.loading || disabled"
|
||||
[disabled]="form.loading || disabledByPolicy"
|
||||
[ngClass]="{ manual: disabledByPolicy }"
|
||||
>
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "confirmFormat" | i18n }}</span>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { UntypedFormBuilder } from "@angular/forms";
|
||||
import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/components/export.component";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { ExportService } from "@bitwarden/common/abstractions/export.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
@@ -29,7 +29,7 @@ export class ExportComponent extends BaseExportComponent {
|
||||
i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
exportService: ExportService,
|
||||
eventService: EventService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
policyService: PolicyService,
|
||||
logService: LogService,
|
||||
userVerificationService: UserVerificationService,
|
||||
@@ -42,7 +42,7 @@ export class ExportComponent extends BaseExportComponent {
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
exportService,
|
||||
eventService,
|
||||
eventCollectionService,
|
||||
policyService,
|
||||
window,
|
||||
logService,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<input
|
||||
bitInput
|
||||
required
|
||||
type="{{ showFilePassword ? 'text' : 'password' }}"
|
||||
[type]="showFilePassword ? 'text' : 'password'"
|
||||
name="filePassword"
|
||||
[formControl]="filePassword"
|
||||
appAutofocus
|
||||
|
||||
@@ -80,11 +80,13 @@
|
||||
right) and select "Settings". Go to "Export" and find the "Export to .csv File" option. Click
|
||||
"Export" to save the CSV file.
|
||||
</ng-container>
|
||||
<!--
|
||||
<ng-container *ngIf="format === 'keeperjson'">
|
||||
Log into the Keeper web vault (keepersecurity.com/vault). Click on your "account email" (top
|
||||
right) and select "Settings". Go to "Export" and find the "Export to .json File" option. Click
|
||||
"Export" to save the JSON file.
|
||||
</ng-container>
|
||||
-->
|
||||
<ng-container
|
||||
*ngIf="format === 'chromecsv' || format === 'operacsv' || format === 'vivaldicsv'"
|
||||
>
|
||||
|
||||
@@ -122,8 +122,8 @@
|
||||
[(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Boolean"
|
||||
appTrueFalseValue
|
||||
trueValue="true"
|
||||
falseValue="false"
|
||||
[trueValue]="true"
|
||||
[falseValue]="false"
|
||||
[disabled]="cipher.isDeleted || viewOnly"
|
||||
attr.aria-describedby="fieldName{{ i }}"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent } from "@bitwarden/angular/components/add-edit-custom-fields.component";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
|
||||
@Component({
|
||||
@@ -12,7 +12,7 @@ export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsCompone
|
||||
@Input() viewOnly: boolean;
|
||||
@Input() copy: (value: string, typeI18nKey: string, aType: string) => void;
|
||||
|
||||
constructor(i18nService: I18nService, eventService: EventService) {
|
||||
super(i18nService, eventService);
|
||||
constructor(i18nService: I18nService, eventCollectionService: EventCollectionService) {
|
||||
super(i18nService, eventCollectionService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,13 @@
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-lg bwi-fw bwi-check-circle"
|
||||
[hidden]="checkPasswordBtn.loading"
|
||||
[hidden]="$any(checkPasswordBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-lg bwi-fw bwi-spinner bwi-spin"
|
||||
aria-hidden="true"
|
||||
[hidden]="!checkPasswordBtn.loading"
|
||||
[hidden]="!$any(checkPasswordBtn).loading"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
></i>
|
||||
</a>
|
||||
@@ -838,7 +838,7 @@
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
[(ngModel)]="$any(c).checked"
|
||||
id="collection-{{ i }}"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
[disabled]="cipher.isDeleted || viewOnly"
|
||||
@@ -935,17 +935,17 @@
|
||||
class="btn btn-outline-danger"
|
||||
appA11yTitle="{{ (cipher.isDeleted ? 'permanentlyDelete' : 'delete') | i18n }}"
|
||||
*ngIf="editMode && !cloneMode && !(!cipher.edit && editMode)"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[disabled]="$any(deleteBtn).loading"
|
||||
[appApiAction]="deletePromise"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-trash bwi-lg bwi-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
[hidden]="$any(deleteBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
[hidden]="!$any(deleteBtn).loading"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/com
|
||||
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
@@ -49,7 +49,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
protected totpService: TotpService,
|
||||
protected passwordGenerationService: PasswordGenerationService,
|
||||
protected messagingService: MessagingService,
|
||||
eventService: EventService,
|
||||
eventCollectionService: EventCollectionService,
|
||||
protected policyService: PolicyService,
|
||||
organizationService: OrganizationService,
|
||||
logService: LogService,
|
||||
@@ -64,7 +64,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
stateService,
|
||||
collectionService,
|
||||
messagingService,
|
||||
eventService,
|
||||
eventCollectionService,
|
||||
policyService,
|
||||
logService,
|
||||
passwordRepromptService,
|
||||
@@ -125,11 +125,17 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
|
||||
if (this.editMode) {
|
||||
if (typeI18nKey === "password") {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledHiddenFieldVisible, this.cipherId);
|
||||
this.eventCollectionService.collect(
|
||||
EventType.Cipher_ClientToggledHiddenFieldVisible,
|
||||
this.cipherId
|
||||
);
|
||||
} else if (typeI18nKey === "securityCode") {
|
||||
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, this.cipherId);
|
||||
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, this.cipherId);
|
||||
} else if (aType === "H_Field") {
|
||||
this.eventService.collect(EventType.Cipher_ClientCopiedHiddenField, this.cipherId);
|
||||
this.eventCollectionService.collect(
|
||||
EventType.Cipher_ClientCopiedHiddenField,
|
||||
this.cipherId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,14 @@
|
||||
<tbody>
|
||||
<tr *ngFor="let a of cipher.attachments">
|
||||
<td class="table-list-icon">
|
||||
<i class="bwi bwi-fw bwi-lg bwi-file" *ngIf="!a.downloading" aria-hidden="true"></i>
|
||||
<i
|
||||
class="bwi bwi-fw bwi-lg bwi-file"
|
||||
*ngIf="!$any(a).downloading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-lg bwi-fw bwi-spin"
|
||||
*ngIf="a.downloading"
|
||||
*ngIf="$any(a).downloading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</td>
|
||||
@@ -55,7 +59,7 @@
|
||||
(click)="reupload(a)"
|
||||
#reuploadBtn
|
||||
[appApiAction]="reuploadPromises[a.id]"
|
||||
[disabled]="reuploadBtn.loading"
|
||||
[disabled]="$any(reuploadBtn).loading"
|
||||
>
|
||||
{{ "fix" | i18n }}
|
||||
</button>
|
||||
@@ -72,16 +76,16 @@
|
||||
(click)="delete(a)"
|
||||
#deleteBtn
|
||||
[appApiAction]="deletePromises[a.id]"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[disabled]="$any(deleteBtn).loading"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-trash bwi-lg bwi-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
[hidden]="$any(deleteBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
[hidden]="!$any(deleteBtn).loading"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { BulkDeleteComponent } from "./bulk-delete.component";
|
||||
import { BulkMoveComponent } from "./bulk-move.component";
|
||||
import { BulkRestoreComponent } from "./bulk-restore.component";
|
||||
import { BulkShareComponent } from "./bulk-share.component";
|
||||
import { CiphersComponent } from "./ciphers.component";
|
||||
import { VaultItemsComponent } from "./vault-items.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-vault-bulk-actions",
|
||||
@@ -19,7 +19,7 @@ import { CiphersComponent } from "./ciphers.component";
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class BulkActionsComponent {
|
||||
@Input() ciphersComponent: CiphersComponent;
|
||||
@Input() vaultItemsComponent: VaultItemsComponent;
|
||||
@Input() deleted: boolean;
|
||||
@Input() organization: Organization;
|
||||
|
||||
@@ -44,7 +44,7 @@ export class BulkActionsComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = this.ciphersComponent.getSelectedIds();
|
||||
const selectedIds = this.vaultItemsComponent.getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
@@ -64,7 +64,7 @@ export class BulkActionsComponent {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onDeleted.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -75,7 +75,7 @@ export class BulkActionsComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = this.ciphersComponent.getSelectedIds();
|
||||
const selectedIds = this.vaultItemsComponent.getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
@@ -93,7 +93,7 @@ export class BulkActionsComponent {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onRestored.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -104,7 +104,7 @@ export class BulkActionsComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedCiphers = this.ciphersComponent.getSelected();
|
||||
const selectedCiphers = this.vaultItemsComponent.getSelected();
|
||||
if (selectedCiphers.length === 0) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
@@ -122,7 +122,7 @@ export class BulkActionsComponent {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onShared.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -133,7 +133,7 @@ export class BulkActionsComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedIds = this.ciphersComponent.getSelectedIds();
|
||||
const selectedIds = this.vaultItemsComponent.getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
this.platformUtilsService.showToast(
|
||||
"error",
|
||||
@@ -151,18 +151,18 @@ export class BulkActionsComponent {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onMoved.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
selectAll(select: boolean) {
|
||||
this.ciphersComponent.selectAll(select);
|
||||
this.vaultItemsComponent.selectAll(select);
|
||||
}
|
||||
|
||||
private async promptPassword() {
|
||||
const selectedCiphers = this.ciphersComponent.getSelected();
|
||||
const selectedCiphers = this.vaultItemsComponent.getSelected();
|
||||
const notProtected = !selectedCiphers.find(
|
||||
(cipher) => cipher.reprompt !== CipherRepromptType.None
|
||||
);
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<td class="table-list-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
[(ngModel)]="$any(c).checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
appStopProp
|
||||
/>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<td class="table-list-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
[(ngModel)]="$any(c).checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
appStopProp
|
||||
/>
|
||||
|
||||
@@ -46,17 +46,17 @@
|
||||
class="btn btn-outline-danger"
|
||||
appA11yTitle="{{ 'delete' | i18n }}"
|
||||
*ngIf="editMode"
|
||||
[disabled]="deleteBtn.loading"
|
||||
[disabled]="$any(deleteBtn).loading"
|
||||
[appApiAction]="deletePromise"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-trash bwi-lg bwi-fw"
|
||||
[hidden]="deleteBtn.loading"
|
||||
[hidden]="$any(deleteBtn).loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin bwi-lg bwi-fw"
|
||||
[hidden]="!deleteBtn.loading"
|
||||
[hidden]="!$any(deleteBtn).loading"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<tbody>
|
||||
<tr *ngFor="let c of filteredCiphers">
|
||||
<td (click)="checkCipher(c)" class="table-list-checkbox">
|
||||
<input type="checkbox" [(ngModel)]="c.checked" appStopProp />
|
||||
<input type="checkbox" [(ngModel)]="$any(c).checked" appStopProp />
|
||||
</td>
|
||||
<td (click)="checkCipher(c)" class="table-list-icon">
|
||||
<app-vault-icon [cipher]="c"></app-vault-icon>
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component, EventEmitter, Input, OnDestroy, Output } from "@angular/core";
|
||||
|
||||
import { CiphersComponent as BaseCiphersComponent } from "@bitwarden/angular/components/ciphers.component";
|
||||
import { VaultItemsComponent as BaseVaultItemsComponent } from "@bitwarden/angular/components/vault-items.component";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
@@ -22,10 +22,10 @@ import { Icons } from "@bitwarden/components";
|
||||
const MaxCheckedCount = 500;
|
||||
|
||||
@Component({
|
||||
selector: "app-vault-ciphers",
|
||||
templateUrl: "ciphers.component.html",
|
||||
selector: "app-vault-items",
|
||||
templateUrl: "vault-items.component.html",
|
||||
})
|
||||
export class CiphersComponent extends BaseCiphersComponent implements OnDestroy {
|
||||
export class VaultItemsComponent extends BaseVaultItemsComponent implements OnDestroy {
|
||||
@Input() showAddNew = true;
|
||||
@Output() onAttachmentsClicked = new EventEmitter<CipherView>();
|
||||
@Output() onShareClicked = new EventEmitter<CipherView>();
|
||||
@@ -51,7 +51,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
protected i18nService: I18nService,
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected cipherService: CipherService,
|
||||
protected eventService: EventService,
|
||||
protected eventCollectionService: EventCollectionService,
|
||||
protected totpService: TotpService,
|
||||
protected stateService: StateService,
|
||||
protected passwordRepromptService: PasswordRepromptService,
|
||||
@@ -241,9 +241,12 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
);
|
||||
|
||||
if (typeI18nKey === "password" || typeI18nKey === "verificationCodeTotp") {
|
||||
this.eventService.collect(EventType.Cipher_ClientToggledHiddenFieldVisible, cipher.id);
|
||||
this.eventCollectionService.collect(
|
||||
EventType.Cipher_ClientToggledHiddenFieldVisible,
|
||||
cipher.id
|
||||
);
|
||||
} else if (typeI18nKey === "securityCode") {
|
||||
this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
||||
this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +292,10 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
|
||||
this.onOrganzationBadgeClicked.emit(organizationId);
|
||||
}
|
||||
|
||||
events(c: CipherView) {
|
||||
// TODO: This should be removed but is needed since we reuse the same template
|
||||
}
|
||||
|
||||
protected deleteCipher(id: string, permanent: boolean) {
|
||||
return permanent
|
||||
? this.cipherService.deleteWithServer(id)
|
||||
@@ -20,8 +20,8 @@
|
||||
<div class="page-header d-flex">
|
||||
<h1>
|
||||
{{ "vaultItems" | i18n }}
|
||||
<small #actionSpinner [appApiAction]="ciphersComponent.actionPromise">
|
||||
<ng-container *ngIf="actionSpinner.loading">
|
||||
<small #actionSpinner [appApiAction]="vaultItemsComponent.actionPromise">
|
||||
<ng-container *ngIf="$any(actionSpinner).loading">
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin text-muted"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
@@ -33,7 +33,7 @@
|
||||
</h1>
|
||||
<div class="ml-auto d-flex">
|
||||
<app-vault-bulk-actions
|
||||
[ciphersComponent]="ciphersComponent"
|
||||
[vaultItemsComponent]="vaultItemsComponent"
|
||||
[deleted]="activeFilter.isDeleted"
|
||||
>
|
||||
</app-vault-bulk-actions>
|
||||
@@ -50,7 +50,7 @@
|
||||
<app-callout type="warning" *ngIf="activeFilter.isDeleted" icon="bwi-exclamation-triangle">
|
||||
{{ trashCleanupWarning }}
|
||||
</app-callout>
|
||||
<app-vault-ciphers
|
||||
<app-vault-items
|
||||
(onCipherClicked)="editCipher($event)"
|
||||
(onAttachmentsClicked)="editCipherAttachments($event)"
|
||||
(onAddCipher)="addCipher()"
|
||||
@@ -59,7 +59,7 @@
|
||||
(onCloneClicked)="cloneCipher($event)"
|
||||
(onOrganzationBadgeClicked)="applyOrganizationFilter($event)"
|
||||
>
|
||||
</app-vault-ciphers>
|
||||
</app-vault-items>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="card border-warning mb-4" *ngIf="showUpdateKey">
|
||||
|
||||
@@ -31,7 +31,6 @@ import { UpdateKeyComponent } from "../settings/update-key.component";
|
||||
|
||||
import { AddEditComponent } from "./add-edit.component";
|
||||
import { AttachmentsComponent } from "./attachments.component";
|
||||
import { CiphersComponent } from "./ciphers.component";
|
||||
import { CollectionsComponent } from "./collections.component";
|
||||
import { FolderAddEditComponent } from "./folder-add-edit.component";
|
||||
import { ShareComponent } from "./share.component";
|
||||
@@ -39,6 +38,7 @@ import { VaultFilterComponent } from "./vault-filter/components/vault-filter.com
|
||||
import { VaultFilterService } from "./vault-filter/services/abstractions/vault-filter.service";
|
||||
import { VaultFilter } from "./vault-filter/shared/models/vault-filter.model";
|
||||
import { FolderFilter, OrganizationFilter } from "./vault-filter/shared/models/vault-filter.type";
|
||||
import { VaultItemsComponent } from "./vault-items.component";
|
||||
|
||||
const BroadcasterSubscriptionId = "VaultComponent";
|
||||
|
||||
@@ -48,7 +48,7 @@ const BroadcasterSubscriptionId = "VaultComponent";
|
||||
})
|
||||
export class VaultComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("vaultFilter", { static: true }) filterComponent: VaultFilterComponent;
|
||||
@ViewChild(CiphersComponent, { static: true }) ciphersComponent: CiphersComponent;
|
||||
@ViewChild(VaultItemsComponent, { static: true }) vaultItemsComponent: VaultItemsComponent;
|
||||
@ViewChild("attachments", { read: ViewContainerRef, static: true })
|
||||
attachmentsModalRef: ViewContainerRef;
|
||||
@ViewChild("folderAddEdit", { read: ViewContainerRef, static: true })
|
||||
@@ -118,7 +118,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
await this.editCipher(cipherView);
|
||||
}
|
||||
}
|
||||
await this.ciphersComponent.reload();
|
||||
await this.vaultItemsComponent.reload();
|
||||
|
||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
||||
this.route.queryParams.subscribe(async (params) => {
|
||||
@@ -147,7 +147,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
if (message.successfully) {
|
||||
await Promise.all([
|
||||
this.vaultFilterService.reloadCollections(),
|
||||
this.ciphersComponent.load(this.ciphersComponent.filter),
|
||||
this.vaultItemsComponent.load(this.vaultItemsComponent.filter),
|
||||
]);
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
@@ -177,8 +177,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
|
||||
async applyVaultFilter(filter: VaultFilter) {
|
||||
this.activeFilter = filter;
|
||||
this.ciphersComponent.showAddNew = !this.activeFilter.isDeleted;
|
||||
await this.ciphersComponent.reload(
|
||||
this.vaultItemsComponent.showAddNew = !this.activeFilter.isDeleted;
|
||||
await this.vaultItemsComponent.reload(
|
||||
this.activeFilter.buildFilter(),
|
||||
this.activeFilter.isDeleted
|
||||
);
|
||||
@@ -227,8 +227,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
filterSearchText(searchText: string) {
|
||||
this.ciphersComponent.searchText = searchText;
|
||||
this.ciphersComponent.search(200);
|
||||
this.vaultItemsComponent.searchText = searchText;
|
||||
this.vaultItemsComponent.search(200);
|
||||
}
|
||||
|
||||
async editCipherAttachments(cipher: CipherView) {
|
||||
@@ -264,7 +264,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
modal.onClosed.subscribe(async () => {
|
||||
if (madeAttachmentChanges) {
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
}
|
||||
madeAttachmentChanges = false;
|
||||
});
|
||||
@@ -279,7 +279,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onSharedCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -294,7 +294,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onSavedCollections.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -337,17 +337,17 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onSavedCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onDeletedCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
comp.onRestoredCipher.subscribe(async () => {
|
||||
modal.close();
|
||||
await this.ciphersComponent.refresh();
|
||||
await this.vaultItemsComponent.refresh();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2,10 +2,10 @@ import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule, LooseComponentsModule } from "../shared";
|
||||
|
||||
import { CiphersComponent } from "./ciphers.component";
|
||||
import { OrganizationBadgeModule } from "./organization-badge/organization-badge.module";
|
||||
import { PipesModule } from "./pipes/pipes.module";
|
||||
import { VaultFilterModule } from "./vault-filter/vault-filter.module";
|
||||
import { VaultItemsComponent } from "./vault-items.component";
|
||||
import { VaultRoutingModule } from "./vault-routing.module";
|
||||
import { VaultComponent } from "./vault.component";
|
||||
|
||||
@@ -18,7 +18,7 @@ import { VaultComponent } from "./vault.component";
|
||||
SharedModule,
|
||||
LooseComponentsModule,
|
||||
],
|
||||
declarations: [VaultComponent, CiphersComponent],
|
||||
declarations: [VaultComponent, VaultItemsComponent],
|
||||
exports: [VaultComponent],
|
||||
})
|
||||
export class VaultModule {}
|
||||
|
||||
@@ -2444,7 +2444,7 @@
|
||||
"message": "Eienaar"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "The highest access user that can manage all aspects of your organization."
|
||||
"message": "Manage all aspects of your organization, including billing and subscriptions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization."
|
||||
@@ -2453,13 +2453,13 @@
|
||||
"message": "Admin"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Admins can access and manage all items, collections and users in your organization."
|
||||
"message": "Manage organization access, all collections, members, reporting, and security settings"
|
||||
},
|
||||
"user": {
|
||||
"message": "Gebruiker"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "A regular user with access to assigned collections in your organization."
|
||||
"message": "Access and add items to assigned collections"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Bestuurder"
|
||||
@@ -4117,7 +4117,22 @@
|
||||
"message": "Pasgemaak"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
"message": "Grant customized permissions to members"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Toestemmings"
|
||||
|
||||
@@ -122,16 +122,16 @@
|
||||
"message": "كانون الأول"
|
||||
},
|
||||
"title": {
|
||||
"message": "Title"
|
||||
"message": "اللقب"
|
||||
},
|
||||
"mr": {
|
||||
"message": "Mr"
|
||||
"message": "السيد"
|
||||
},
|
||||
"mrs": {
|
||||
"message": "Mrs"
|
||||
"message": "السيدة"
|
||||
},
|
||||
"ms": {
|
||||
"message": "Ms"
|
||||
"message": "الآنسة"
|
||||
},
|
||||
"dr": {
|
||||
"message": "د"
|
||||
@@ -152,7 +152,7 @@
|
||||
"message": "حقل مخصص جديد"
|
||||
},
|
||||
"value": {
|
||||
"message": "Value"
|
||||
"message": "القيمة"
|
||||
},
|
||||
"dragToSort": {
|
||||
"message": "اسحب للفرز"
|
||||
@@ -164,7 +164,7 @@
|
||||
"message": "مخفي"
|
||||
},
|
||||
"cfTypeBoolean": {
|
||||
"message": "Boolean"
|
||||
"message": "قيمة منطقية"
|
||||
},
|
||||
"cfTypeLinked": {
|
||||
"message": "Linked",
|
||||
@@ -174,7 +174,7 @@
|
||||
"message": "إزالة"
|
||||
},
|
||||
"unassigned": {
|
||||
"message": "Unassigned"
|
||||
"message": "غير معين"
|
||||
},
|
||||
"noneFolder": {
|
||||
"message": "لا يوجد مجلد",
|
||||
@@ -233,7 +233,7 @@
|
||||
"message": "Check if password has been exposed."
|
||||
},
|
||||
"passwordExposed": {
|
||||
"message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.",
|
||||
"message": "تم كشف كلمة المرور هذه {0} مرة (مرات) في عمليات اختراق البيانات. يجب عليك تغييرها.",
|
||||
"placeholders": {
|
||||
"value": {
|
||||
"content": "$1",
|
||||
@@ -242,7 +242,7 @@
|
||||
}
|
||||
},
|
||||
"passwordSafe": {
|
||||
"message": "This password was not found in any known data breaches. It should be safe to use."
|
||||
"message": "لم يتم العثور على كلمة المرور هذه في أي عمليات اختراق معروفة للبيانات. من المفترض أن تكون آمنة للاستخدام."
|
||||
},
|
||||
"save": {
|
||||
"message": "حفظ"
|
||||
@@ -269,16 +269,16 @@
|
||||
"message": "تعديل"
|
||||
},
|
||||
"searchCollection": {
|
||||
"message": "Search collection"
|
||||
"message": "ابحث في المجموعة"
|
||||
},
|
||||
"searchFolder": {
|
||||
"message": "Search folder"
|
||||
"message": "ابحث في المجلّد"
|
||||
},
|
||||
"searchFavorites": {
|
||||
"message": "بحث في المفضلة"
|
||||
},
|
||||
"searchType": {
|
||||
"message": "Search type",
|
||||
"message": "نوع البحث",
|
||||
"description": "Search item type"
|
||||
},
|
||||
"searchVault": {
|
||||
@@ -321,7 +321,7 @@
|
||||
"message": "المجلدات"
|
||||
},
|
||||
"collections": {
|
||||
"message": "Collections"
|
||||
"message": "المجموعات"
|
||||
},
|
||||
"firstName": {
|
||||
"message": "الاسم الأول"
|
||||
@@ -357,7 +357,7 @@
|
||||
"message": "الدولة"
|
||||
},
|
||||
"shared": {
|
||||
"message": "Shared"
|
||||
"message": "مشترك"
|
||||
},
|
||||
"attachments": {
|
||||
"message": "المرفقات"
|
||||
@@ -375,7 +375,7 @@
|
||||
"message": "عرض العنصر"
|
||||
},
|
||||
"ex": {
|
||||
"message": "ex.",
|
||||
"message": "مثال.",
|
||||
"description": "Short abbreviation for 'example'."
|
||||
},
|
||||
"other": {
|
||||
@@ -388,7 +388,7 @@
|
||||
"message": "نقل إلى مؤسسة"
|
||||
},
|
||||
"valueCopied": {
|
||||
"message": "$VALUE$ copied",
|
||||
"message": "تم نسخ $VALUE$",
|
||||
"description": "Value has been copied to the clipboard.",
|
||||
"placeholders": {
|
||||
"value": {
|
||||
@@ -428,7 +428,7 @@
|
||||
"message": "خزنتي"
|
||||
},
|
||||
"allVaults": {
|
||||
"message": "All vaults"
|
||||
"message": "جميع الخزنات"
|
||||
},
|
||||
"vault": {
|
||||
"message": "الخزنة"
|
||||
@@ -479,7 +479,7 @@
|
||||
"message": "الحجم الأقصى للملف هو 500 ميجابايت."
|
||||
},
|
||||
"updateKey": {
|
||||
"message": "You cannot use this feature until you update your encryption key."
|
||||
"message": "لا يمكنك استخدام هذه المِيزة حتى تحديث مفتاح التشفير الخاص بك."
|
||||
},
|
||||
"addedItem": {
|
||||
"message": "تمت إضافة العنصر"
|
||||
@@ -501,7 +501,7 @@
|
||||
}
|
||||
},
|
||||
"movedItemsToOrg": {
|
||||
"message": "Selected items moved to $ORGNAME$",
|
||||
"message": "تم نقل العناصر المحددة إلى $ORGNAME$",
|
||||
"placeholders": {
|
||||
"orgname": {
|
||||
"content": "$1",
|
||||
@@ -519,7 +519,7 @@
|
||||
"message": "حذف المرفق"
|
||||
},
|
||||
"deleteItemConfirmation": {
|
||||
"message": "Do you really want to send to the trash?"
|
||||
"message": "هل تريد فعلًا أن تحذف؟"
|
||||
},
|
||||
"deletedItem": {
|
||||
"message": "تم حذف العنصر"
|
||||
@@ -531,13 +531,13 @@
|
||||
"message": "تم نقل العناصر"
|
||||
},
|
||||
"overwritePasswordConfirmation": {
|
||||
"message": "Are you sure you want to overwrite the current password?"
|
||||
"message": "هل أنت متأكد من أنك تريد الكتابة فوق كلمة المرور الحالية؟"
|
||||
},
|
||||
"editedFolder": {
|
||||
"message": "تم تعديل المجلد"
|
||||
},
|
||||
"addedFolder": {
|
||||
"message": "Folder added"
|
||||
"message": "تمت إضافة المجلد"
|
||||
},
|
||||
"deleteFolderConfirmation": {
|
||||
"message": "هل أنت متأكد من حذف هذا المجلد؟"
|
||||
@@ -546,13 +546,13 @@
|
||||
"message": "تم حذف المجلد"
|
||||
},
|
||||
"loggedOut": {
|
||||
"message": "Logged out"
|
||||
"message": "تم تسجيل الخروج"
|
||||
},
|
||||
"loginExpired": {
|
||||
"message": "Your login session has expired."
|
||||
"message": "انتهت صَلاحِيَة جَلسة الدخول."
|
||||
},
|
||||
"logOutConfirmation": {
|
||||
"message": "Are you sure you want to log out?"
|
||||
"message": "هل أنت متأكد من أنك تريد تسجيل الخروج؟"
|
||||
},
|
||||
"logOut": {
|
||||
"message": "تسجيل الخروج"
|
||||
@@ -567,31 +567,31 @@
|
||||
"message": "لا"
|
||||
},
|
||||
"loginOrCreateNewAccount": {
|
||||
"message": "Log in or create a new account to access your secure vault."
|
||||
"message": "قم بتسجيل الدخول أو أنشئ حساباً جديداً لتتمكن من الوصول إلى خزنتك السرية."
|
||||
},
|
||||
"loginWithDevice": {
|
||||
"message": "Log in with device"
|
||||
"message": "تسجيل الدخول باستخدام جهاز"
|
||||
},
|
||||
"loginWithDeviceEnabledInfo": {
|
||||
"message": "Log in with device must be set up in the settings of the Bitwarden mobile app. Need another option?"
|
||||
"message": "تسجيل الدخول باستخدام الجهاز يجب أن يكون مفعلاً في إعدادات تطبيق بيتواردن على هاتفك. هل تحتاج إلى خِيار آخر؟"
|
||||
},
|
||||
"loginWithMasterPassword": {
|
||||
"message": "Log in with master password"
|
||||
"message": "تسجيل الدخول باستخدام كلمة المرور الرئيسية"
|
||||
},
|
||||
"createAccount": {
|
||||
"message": "إنشاء حساب"
|
||||
},
|
||||
"newAroundHere": {
|
||||
"message": "New around here?"
|
||||
"message": "هل أنت جديد هنا؟"
|
||||
},
|
||||
"startTrial": {
|
||||
"message": "Start trial"
|
||||
"message": "ابدأ تجرِبة مجانية"
|
||||
},
|
||||
"logIn": {
|
||||
"message": "تسجيل الدخول"
|
||||
},
|
||||
"logInInitiated": {
|
||||
"message": "Log in initiated"
|
||||
"message": "بَدْء تسجيل الدخول"
|
||||
},
|
||||
"submit": {
|
||||
"message": "قدِّم"
|
||||
@@ -603,7 +603,7 @@
|
||||
"message": "اسمك"
|
||||
},
|
||||
"yourNameDesc": {
|
||||
"message": "What should we call you?"
|
||||
"message": "ماذا تحب أن ندعوك؟"
|
||||
},
|
||||
"masterPass": {
|
||||
"message": "كلمة المرور الرئيسية"
|
||||
@@ -645,10 +645,10 @@
|
||||
"message": "عنوان البريد الإلكتروني غير صالح."
|
||||
},
|
||||
"masterPasswordRequired": {
|
||||
"message": "Master password is required."
|
||||
"message": "كلمة المرور الرئيسية مطلوبة."
|
||||
},
|
||||
"confirmMasterPasswordRequired": {
|
||||
"message": "Master password retype is required."
|
||||
"message": "مطلوب إعادة إدخال كلمة المرور الرئيسية."
|
||||
},
|
||||
"masterPasswordMinlength": {
|
||||
"message": "Master password must be at least 8 characters long."
|
||||
@@ -700,19 +700,19 @@
|
||||
"message": "قفل الآن"
|
||||
},
|
||||
"noItemsInList": {
|
||||
"message": "There are no items to list."
|
||||
"message": "لا توجد عناصر لعرضها."
|
||||
},
|
||||
"noCollectionsInList": {
|
||||
"message": "There are no collections to list."
|
||||
},
|
||||
"noGroupsInList": {
|
||||
"message": "There are no groups to list."
|
||||
"message": "لا توجد أية مجموعات لعرضها."
|
||||
},
|
||||
"noUsersInList": {
|
||||
"message": "There are no users to list."
|
||||
"message": "ليس هناك مستخدمون لعرضهم."
|
||||
},
|
||||
"noEventsInList": {
|
||||
"message": "There are no events to list."
|
||||
"message": "لا توجد أية أحداث لعرضها."
|
||||
},
|
||||
"newOrganization": {
|
||||
"message": "مؤسسة جديدة"
|
||||
@@ -721,7 +721,7 @@
|
||||
"message": "أنت لا تنتمي إلى أي مؤسسة. تسمح لك المؤسسات بمشاركة العناصر بأمان مع مستخدمين آخرين."
|
||||
},
|
||||
"notificationSentDevice": {
|
||||
"message": "A notification has been sent to your device."
|
||||
"message": "تم إرسال إشعار إلى جهازك."
|
||||
},
|
||||
"versionNumber": {
|
||||
"message": "الإصدار $VERSION_NUMBER$",
|
||||
@@ -757,7 +757,7 @@
|
||||
"message": "تذكرني"
|
||||
},
|
||||
"sendVerificationCodeEmailAgain": {
|
||||
"message": "Send verification code email again"
|
||||
"message": "إرسال رمز التحقق إلى البريد الإلكتروني مرة أخرى"
|
||||
},
|
||||
"useAnotherTwoStepMethod": {
|
||||
"message": "Use another two-step login method"
|
||||
@@ -2444,7 +2444,7 @@
|
||||
"message": "Owner"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "The highest access user that can manage all aspects of your organization."
|
||||
"message": "Manage all aspects of your organization, including billing and subscriptions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization."
|
||||
@@ -2453,19 +2453,19 @@
|
||||
"message": "Admin"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Admins can access and manage all items, collections and users in your organization."
|
||||
"message": "Manage organization access, all collections, members, reporting, and security settings"
|
||||
},
|
||||
"user": {
|
||||
"message": "User"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "A regular user with access to assigned collections in your organization."
|
||||
"message": "Access and add items to assigned collections"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Manager"
|
||||
},
|
||||
"managerDesc": {
|
||||
"message": "Managers can access and manage assigned collections in your organization."
|
||||
"message": "Create, delete, and manage access in assigned collections"
|
||||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
@@ -2567,7 +2567,7 @@
|
||||
}
|
||||
},
|
||||
"viewAllLoginOptions": {
|
||||
"message": "View all log in options"
|
||||
"message": "عرض جميع خيارات تسجيل الدخول"
|
||||
},
|
||||
"viewedItemId": {
|
||||
"message": "Viewed item $ID$.",
|
||||
@@ -2876,7 +2876,7 @@
|
||||
"message": "Invalid date range."
|
||||
},
|
||||
"errorOccurred": {
|
||||
"message": "An error has occurred."
|
||||
"message": "لقد حدث خطأ."
|
||||
},
|
||||
"userAccess": {
|
||||
"message": "User access"
|
||||
@@ -2954,7 +2954,7 @@
|
||||
"message": "Unable to verify your email. Try sending a new verification email."
|
||||
},
|
||||
"emailVerificationRequired": {
|
||||
"message": "Email verification required"
|
||||
"message": "تأكيد البريد الإلكتروني مطلوب"
|
||||
},
|
||||
"emailVerificationRequiredDesc": {
|
||||
"message": "You must verify your email to use this feature."
|
||||
@@ -2990,7 +2990,7 @@
|
||||
}
|
||||
},
|
||||
"rememberEmail": {
|
||||
"message": "Remember email"
|
||||
"message": "تذكرني"
|
||||
},
|
||||
"recoverAccountTwoStepDesc": {
|
||||
"message": "If you cannot access your account through your normal two-step login methods, you can use your two-step login recovery code to turn off all two-step providers on your account."
|
||||
@@ -3426,10 +3426,10 @@
|
||||
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
|
||||
},
|
||||
"fingerprintMatchInfo": {
|
||||
"message": "Please make sure your vault is unlocked and Fingerprint phrase matches the other device."
|
||||
"message": "الرجاء التأكد من أن الخزنة الخاصة بك غير مقفلة وأن بصمة الإصبع تتطابق مع الجهاز الآخر."
|
||||
},
|
||||
"fingerprintPhraseHeader": {
|
||||
"message": "Fingerprint phrase"
|
||||
"message": "بصمة الإصبع"
|
||||
},
|
||||
"dontAskFingerprintAgain": {
|
||||
"message": "Never prompt to verify fingerprint phrases for invited users (not recommended)",
|
||||
@@ -3692,10 +3692,10 @@
|
||||
"message": "Organization identifier"
|
||||
},
|
||||
"ssoLogInWithOrgIdentifier": {
|
||||
"message": "Log in using your organization's single sign-on portal. Please enter your organization's SSO identifier to begin."
|
||||
"message": "قم بتسجيل الدخول بسرعة باستخدام بوابة تسجيل الدخول الأحادي لمؤسستك. الرجاء إدخال معرف الـSSO الخاص بمؤسستك للبدء."
|
||||
},
|
||||
"enterpriseSingleSignOn": {
|
||||
"message": "Enterprise single sign-on"
|
||||
"message": "تسجيل الدخول الأُحادي للمؤسسات – SSO"
|
||||
},
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
@@ -3713,10 +3713,10 @@
|
||||
"message": "SSO validation failed"
|
||||
},
|
||||
"ssoIdentifierRequired": {
|
||||
"message": "Organization SSO identifier is required."
|
||||
"message": "معرف الSSO للمنظمة مطلوب"
|
||||
},
|
||||
"ssoIdentifier": {
|
||||
"message": "SSO identifier"
|
||||
"message": "معرف SSO"
|
||||
},
|
||||
"ssoIdentifierHint": {
|
||||
"message": "Provide this ID to your members to login with SSO."
|
||||
@@ -4117,7 +4117,22 @@
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
"message": "Grant customized permissions to members"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
@@ -4285,10 +4300,10 @@
|
||||
"message": "The deletion date provided is not valid."
|
||||
},
|
||||
"expirationDateAndTimeRequired": {
|
||||
"message": "An expiration date and time are required."
|
||||
"message": "مطلوب تاريخ ووقت انتهاء الصلاحية."
|
||||
},
|
||||
"deletionDateAndTimeRequired": {
|
||||
"message": "A deletion date and time are required."
|
||||
"message": "مطلوب تاريخ ووقت الحذف."
|
||||
},
|
||||
"dateParsingError": {
|
||||
"message": "There was an error saving your deletion and expiration dates."
|
||||
@@ -4441,7 +4456,7 @@
|
||||
"message": "Resend invitations"
|
||||
},
|
||||
"resendNotification": {
|
||||
"message": "Resend notification"
|
||||
"message": "إعادة إرسال الإشعار"
|
||||
},
|
||||
"noSelectedUsersApplicable": {
|
||||
"message": "This action is not applicable to any of the selected users."
|
||||
@@ -5140,7 +5155,7 @@
|
||||
"message": "1 field above needs your attention."
|
||||
},
|
||||
"fieldRequiredError": {
|
||||
"message": "$FIELDNAME$ is required.",
|
||||
"message": "$FIELDNAME$ مطلوب.",
|
||||
"placeholders": {
|
||||
"fieldname": {
|
||||
"content": "$1",
|
||||
@@ -5404,10 +5419,10 @@
|
||||
"description": "the text, 'SCIM', is an acronymn and should not be translated."
|
||||
},
|
||||
"inputRequired": {
|
||||
"message": "Input is required."
|
||||
"message": "هذا الحقل مطلوب."
|
||||
},
|
||||
"inputEmail": {
|
||||
"message": "Input is not an email address."
|
||||
"message": "القيمة المدخلة ليست عنوان بريد إلكتروني."
|
||||
},
|
||||
"inputMinLength": {
|
||||
"message": "Input must be at least $COUNT$ characters long.",
|
||||
@@ -5455,10 +5470,10 @@
|
||||
"message": "Number of users"
|
||||
},
|
||||
"loggingInAs": {
|
||||
"message": "Logging in as"
|
||||
"message": "تسجيل الدخول كـ"
|
||||
},
|
||||
"notYou": {
|
||||
"message": "Not you?"
|
||||
"message": "ليس حسابك؟"
|
||||
},
|
||||
"multiSelectPlaceholder": {
|
||||
"message": "-- Type to Filter --"
|
||||
|
||||
@@ -4119,6 +4119,21 @@
|
||||
"customDesc": {
|
||||
"message": "Qabaqcıl konfiqurasiya üçün istifadəçi icazələrinin daha təfsilatlı nəzarətinə icazə verər."
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Özəl rollar ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "bir müəssisə özəlliyidir",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Abunəliyinizi yüksəltmək üçün dəstək komandamızla əlaqə saxlayın",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "Özəl icazələri fəallaşdırmaq üçün təşkilat, Enterprise 2020 planında olmalıdır."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "İcazələr"
|
||||
},
|
||||
|
||||
@@ -4119,6 +4119,21 @@
|
||||
"customDesc": {
|
||||
"message": "Дазваляе больш паглыблена кантраляваць дазволы карыстальніка для дадатковых канфігурацый."
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Карыстальніцкія ролі з'яўляюцца ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "функцыяй для прадпрыемства",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Звяжыцеся з нашай камандай падтрымкі для паляпшэння падпіскі",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "Каб уключыць дазволы карыстальніка, арганізацыя павінна мець тарыфны план Enterprise 2020."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Дазволы"
|
||||
},
|
||||
|
||||
@@ -582,7 +582,7 @@
|
||||
"message": "Създаване на абонамент"
|
||||
},
|
||||
"newAroundHere": {
|
||||
"message": "New around here?"
|
||||
"message": "За пръв път ли сте тук?"
|
||||
},
|
||||
"startTrial": {
|
||||
"message": "Стартиране на пробния период"
|
||||
@@ -4119,6 +4119,21 @@
|
||||
"customDesc": {
|
||||
"message": "По-специфичен контрол на правата на потребителите при по-сложни варианти."
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Фукционалността „Персонализирани роли“ е ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "за големи организации",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Свържете се с нашия екип по поддръжката, за да надградите абонамента си.",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Права"
|
||||
},
|
||||
|
||||
@@ -2444,7 +2444,7 @@
|
||||
"message": "Owner"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "The highest access user that can manage all aspects of your organization."
|
||||
"message": "Manage all aspects of your organization, including billing and subscriptions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization."
|
||||
@@ -2453,19 +2453,19 @@
|
||||
"message": "Admin"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Admins can access and manage all items, collections and users in your organization."
|
||||
"message": "Manage organization access, all collections, members, reporting, and security settings"
|
||||
},
|
||||
"user": {
|
||||
"message": "User"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "A regular user with access to assigned collections in your organization."
|
||||
"message": "Access and add items to assigned collections"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Manager"
|
||||
},
|
||||
"managerDesc": {
|
||||
"message": "Managers can access and manage assigned collections in your organization."
|
||||
"message": "Create, delete, and manage access in assigned collections"
|
||||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
@@ -4117,7 +4117,22 @@
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
"message": "Grant customized permissions to members"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
|
||||
@@ -2444,7 +2444,7 @@
|
||||
"message": "Owner"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "The highest access user that can manage all aspects of your organization."
|
||||
"message": "Manage all aspects of your organization, including billing and subscriptions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization."
|
||||
@@ -2453,19 +2453,19 @@
|
||||
"message": "Admin"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Admins can access and manage all items, collections and users in your organization."
|
||||
"message": "Manage organization access, all collections, members, reporting, and security settings"
|
||||
},
|
||||
"user": {
|
||||
"message": "User"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "A regular user with access to assigned collections in your organization."
|
||||
"message": "Access and add items to assigned collections"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Manager"
|
||||
},
|
||||
"managerDesc": {
|
||||
"message": "Managers can access and manage assigned collections in your organization."
|
||||
"message": "Create, delete, and manage access in assigned collections"
|
||||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
@@ -4117,7 +4117,22 @@
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
"message": "Grant customized permissions to members"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
|
||||
@@ -2444,7 +2444,7 @@
|
||||
"message": "Propietari"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "L'usuari d'accés més elevat que pot gestionar tots els aspectes de la vostra organització."
|
||||
"message": "Gestioneu tots els aspectes de la vostra organització, incloses la facturació i les subscripcions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "Aquest usuari ha de ser independent del proveïdor. Si el proveïdor està desvinculat de l'organització, aquest usuari mantindrà la propietat de l'organització."
|
||||
@@ -2453,19 +2453,19 @@
|
||||
"message": "Administrador"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Els administradors poden accedir i gestionar tots els elements, col·leccions i usuaris de la vostra organització."
|
||||
"message": "Gestioneu l'accés a l'organització, totes les col·leccions, els membres, els informes i la configuració de seguretat"
|
||||
},
|
||||
"user": {
|
||||
"message": "Usuari"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "Un usuari habitual amb accés a les col·leccions assignades a la vostra organització."
|
||||
"message": "Accediu i afegiu elements a les col·leccions assignades"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Gestor"
|
||||
},
|
||||
"managerDesc": {
|
||||
"message": "Els gestors poden accedir i gestionar les col·leccions assignades a la vostra organització."
|
||||
"message": "Creeu, suprimiu i gestioneu l'accés a les col·leccions assignades"
|
||||
},
|
||||
"all": {
|
||||
"message": "Tot"
|
||||
@@ -4117,7 +4117,22 @@
|
||||
"message": "Personalitzat"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Permet un control més granular dels permisos d'usuari per a configuracions avançades."
|
||||
"message": "Concediu permisos personalitzats als membres"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Els rols personalitzats són una ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "característica empresarial",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contacta amb el nostre equip d'assistència per actualitzar la teua subscripció",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "Per habilitar els permisos personalitzats, l'organització ha de tenir un pla Enterprise 2020."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permisos"
|
||||
@@ -4420,7 +4435,7 @@
|
||||
"message": "La resposta de les claus d’organització és nul·la"
|
||||
},
|
||||
"resetPasswordDetailsError": {
|
||||
"message": "La resposta Restableix detalls de contrasenya és nul·la"
|
||||
"message": "La resposta dels detalls de restabliment de la contrasenya és nul·la"
|
||||
},
|
||||
"trashCleanupWarning": {
|
||||
"message": "Els elements que porten més de 30 dies a la paperera se suprimiran automàticament."
|
||||
@@ -4516,7 +4531,7 @@
|
||||
"message": "Configuració del proveïdor"
|
||||
},
|
||||
"setupProviderLoginDesc": {
|
||||
"message": "T'han convidat a configurar un proveïdor nou. Per continuar, heu d'iniciar sessió o crear un nou compte de Bitwarden."
|
||||
"message": "Us han convidat a configurar un proveïdor nou. Per continuar, heu d'iniciar sessió o crear un nou compte de Bitwarden."
|
||||
},
|
||||
"setupProviderDesc": {
|
||||
"message": "Introduïu els detalls a continuació per completar la configuració del proveïdor. Poseu-vos en contacte amb el servei d'atenció al client si teniu cap pregunta."
|
||||
@@ -4547,7 +4562,7 @@
|
||||
"message": "Els usuaris del servei poden accedir i gestionar totes les organitzacions client."
|
||||
},
|
||||
"providerInviteUserDesc": {
|
||||
"message": "Convida un nou usuari a la vostra organització introduint l'adreça electrònica del compte de Bitwarden a continuació. Si encara no tenen un compte de Bitwarden, se us demanarà que creeu un compte nou."
|
||||
"message": "Convideu un nou usuari a la vostra organització introduint l'adreça electrònica del compte de Bitwarden a continuació. Si encara no tenen un compte de Bitwarden, se us demanarà que creeu un compte nou."
|
||||
},
|
||||
"joinProvider": {
|
||||
"message": "Uniu-vos al proveïdor"
|
||||
@@ -4556,7 +4571,7 @@
|
||||
"message": "Heu estat convidat a unir-vos al proveïdor llistat més amunt. Per acceptar la invitació, heu d'iniciar sessió o crear un compte nou a Bitwarden."
|
||||
},
|
||||
"providerInviteAcceptFailed": {
|
||||
"message": "No es pot acceptar la invitació. Demaneu a l'administrador d'una organització que envie una invitació nova."
|
||||
"message": "No es pot acceptar la invitació. Demaneu a un administrador del proveïdor que envie una nova invitació."
|
||||
},
|
||||
"providerInviteAcceptedDesc": {
|
||||
"message": "Podeu accedir a aquesta organització una vegada que un administrador confirme la vostra pertinença. Rebreu un correu electrònic quan això passe."
|
||||
@@ -4608,10 +4623,10 @@
|
||||
"message": "S'ha inhabilitat el proveïdor."
|
||||
},
|
||||
"providerUpdated": {
|
||||
"message": "S'ha actualitzat el proveïdor"
|
||||
"message": "S'ha guardat el proveïdor"
|
||||
},
|
||||
"yourProviderIs": {
|
||||
"message": "El vostre proveïdor és $PROVIDER$. Tenen privilegis administratius i de facturació en la vostra organització.",
|
||||
"message": "El vostre proveïdor és $PROVIDER$. Té privilegis administratius i de facturació en la vostra organització.",
|
||||
"placeholders": {
|
||||
"provider": {
|
||||
"content": "$1",
|
||||
@@ -4635,7 +4650,7 @@
|
||||
"message": "Afig"
|
||||
},
|
||||
"updatedMasterPassword": {
|
||||
"message": "Contrasenya mestra actualitzada"
|
||||
"message": "Contrasenya mestra guardada"
|
||||
},
|
||||
"updateMasterPassword": {
|
||||
"message": "Actualitza contrasenya mestra"
|
||||
|
||||
@@ -936,10 +936,10 @@
|
||||
"message": "Set a password to encrypt the export and import it to any Bitwarden account using the password for decryption."
|
||||
},
|
||||
"fileTypeHeading": {
|
||||
"message": "File type"
|
||||
"message": "Typ souboru"
|
||||
},
|
||||
"accountBackup": {
|
||||
"message": "Account backup"
|
||||
"message": "Zálohování účtu"
|
||||
},
|
||||
"passwordProtected": {
|
||||
"message": "Password protected"
|
||||
@@ -1199,7 +1199,7 @@
|
||||
"message": "Choose File"
|
||||
},
|
||||
"noFileChosen": {
|
||||
"message": "No file chosen"
|
||||
"message": "Není vybrán žádný soubor"
|
||||
},
|
||||
"orCopyPasteFileContents": {
|
||||
"message": "nebo zkopírujte a vložte obsah souboru"
|
||||
@@ -2453,13 +2453,13 @@
|
||||
"message": "Administrátor"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Administrátoři mohou prohlížet a spravovat všechny položky, sbírky a uživatele ve vaší organizaci."
|
||||
"message": "Administrátoři mohou prohlížet a spravovat všechny položky, kolekce a uživatele ve vaší organizaci."
|
||||
},
|
||||
"user": {
|
||||
"message": "Uživatel"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "Běžný uživatel s přístupem k přiřazeným kolekcím vaší organizace."
|
||||
"message": "A regular user with access to your organization's collections."
|
||||
},
|
||||
"manager": {
|
||||
"message": "Správce"
|
||||
@@ -2897,7 +2897,7 @@
|
||||
"message": "Znovu poslat pozvánku"
|
||||
},
|
||||
"resendEmail": {
|
||||
"message": "Resend email"
|
||||
"message": "Znovu poslat e-mail"
|
||||
},
|
||||
"hasBeenReinvited": {
|
||||
"message": "Uživatel $USER$ byl znovu pozván.",
|
||||
@@ -3345,7 +3345,7 @@
|
||||
"description": "ex. Date this item was updated"
|
||||
},
|
||||
"dateCreated": {
|
||||
"message": "Created",
|
||||
"message": "Vytvořeno",
|
||||
"description": "ex. Date this item was created"
|
||||
},
|
||||
"datePasswordUpdated": {
|
||||
@@ -3418,7 +3418,7 @@
|
||||
"message": "Ve vašem trezoru jsou staré přílohy vyžadující opravu před změnou šifrovacího klíče k vašemu účtu."
|
||||
},
|
||||
"yourAccountsFingerprint": {
|
||||
"message": "Fráze otisku prstu vašeho účtu",
|
||||
"message": "Fráze otisku vašeho účtu",
|
||||
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
|
||||
},
|
||||
"fingerprintEnsureIntegrityVerify": {
|
||||
@@ -3432,7 +3432,7 @@
|
||||
"message": "Fingerprint phrase"
|
||||
},
|
||||
"dontAskFingerprintAgain": {
|
||||
"message": "Již se neptat na ověření fráze otisku prstu",
|
||||
"message": "Již se neptat na ověření fráze otisku účtu (nedoporučeno)",
|
||||
"description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing."
|
||||
},
|
||||
"free": {
|
||||
@@ -4119,14 +4119,29 @@
|
||||
"customDesc": {
|
||||
"message": "Umožňuje větší kontrolu nad uživatelských oprávnění pro pokročilé konfigurace."
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Oprávnění"
|
||||
},
|
||||
"permission": {
|
||||
"message": "Permission"
|
||||
"message": "Oprávnění"
|
||||
},
|
||||
"managerPermissions": {
|
||||
"message": "Manager Permissions"
|
||||
"message": "Spravovat oprávnění"
|
||||
},
|
||||
"adminPermissions": {
|
||||
"message": "Admin Permissions"
|
||||
@@ -5216,7 +5231,7 @@
|
||||
"description": "This is used by screen readers to indicate the organization that is currently being shown to the user."
|
||||
},
|
||||
"accountSettings": {
|
||||
"message": "Account settings"
|
||||
"message": "Nastavení účtu"
|
||||
},
|
||||
"generator": {
|
||||
"message": "Generator"
|
||||
@@ -5443,7 +5458,7 @@
|
||||
"message": "Zapnuto"
|
||||
},
|
||||
"members": {
|
||||
"message": "Members"
|
||||
"message": "Členové"
|
||||
},
|
||||
"reporting": {
|
||||
"message": "Reporting"
|
||||
@@ -5479,7 +5494,7 @@
|
||||
"message": "To"
|
||||
},
|
||||
"member": {
|
||||
"message": "Member"
|
||||
"message": "Člen"
|
||||
},
|
||||
"update": {
|
||||
"message": "Update"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4119,6 +4119,21 @@
|
||||
"customDesc": {
|
||||
"message": "Feinere Kontrolle der Benutzer Berechtigungen für erweiterte Konfigurationen erlauben."
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Benutzerdefinierte Rollen sind eine ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "Funktion für Unternehmen",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Kontaktiere unser Kundenserviceteam, um dein Abo hochzustufen",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "Um benutzerdefinierte Berechtigungen zu ermöglichen, muss sich die Organisation auf einem 2020er Unternehmensabo befinden."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Berechtigungen"
|
||||
},
|
||||
|
||||
@@ -4119,6 +4119,21 @@
|
||||
"customDesc": {
|
||||
"message": "Επιτρέπει πιο κοκκώδη έλεγχο, των δικαιωμάτων χρήστη για προηγμένες ρυθμίσεις."
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Άδειες"
|
||||
},
|
||||
|
||||
@@ -2480,7 +2480,7 @@
|
||||
"message": "Owner"
|
||||
},
|
||||
"ownerDesc": {
|
||||
"message": "The highest access user that can manage all aspects of your organization."
|
||||
"message": "Manage all aspects of your organization, including billing and subscriptions"
|
||||
},
|
||||
"clientOwnerDesc": {
|
||||
"message": "This user should be independent of the Provider. If the Provider is disassociated with the organization, this user will maintain ownership of the organization."
|
||||
@@ -2489,19 +2489,19 @@
|
||||
"message": "Admin"
|
||||
},
|
||||
"adminDesc": {
|
||||
"message": "Admins can access and manage all items, collections and users in your organization."
|
||||
"message": "Manage organization access, all collections, members, reporting, and security settings"
|
||||
},
|
||||
"user": {
|
||||
"message": "User"
|
||||
},
|
||||
"userDesc": {
|
||||
"message": "A regular user with access to assigned collections in your organization."
|
||||
"message": "Access and add items to assigned collections"
|
||||
},
|
||||
"manager": {
|
||||
"message": "Manager"
|
||||
},
|
||||
"managerDesc": {
|
||||
"message": "Managers can access and manage assigned collections in your organization."
|
||||
"message": "Create, delete, and manage access in assigned collections"
|
||||
},
|
||||
"all": {
|
||||
"message": "All"
|
||||
@@ -4162,7 +4162,22 @@
|
||||
"message": "Custom"
|
||||
},
|
||||
"customDesc": {
|
||||
"message": "Allows more granular control of user permissions for advanced configurations."
|
||||
"message": "Grant customized permissions to members"
|
||||
},
|
||||
"customDescNonEnterpriseStart": {
|
||||
"message": "Custom roles is an ",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseLink": {
|
||||
"message": "enterprise feature",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customDescNonEnterpriseEnd": {
|
||||
"message": ". Contact our support team to upgrade your subscription",
|
||||
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Custom roles is an enterprise feature. Contact our support team to upgrade your subscription'"
|
||||
},
|
||||
"customNonEnterpriseError": {
|
||||
"message": "To enable custom permissions the organization must be on an Enterprise 2020 plan."
|
||||
},
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
@@ -5526,6 +5541,258 @@
|
||||
"multiSelectClearAll": {
|
||||
"message": "Clear all"
|
||||
},
|
||||
"projects":{
|
||||
"message": "Projects"
|
||||
},
|
||||
"lastEdited":{
|
||||
"message": "Last Edited"
|
||||
},
|
||||
"editSecret":{
|
||||
"message": "Edit Secret"
|
||||
},
|
||||
"addSecret":{
|
||||
"message": "Add Secret"
|
||||
},
|
||||
"copySecretName":{
|
||||
"message": "Copy Secret Name"
|
||||
},
|
||||
"copySecretValue":{
|
||||
"message": "Copy Secret Value"
|
||||
},
|
||||
"deleteSecret":{
|
||||
"message": "Delete Secret"
|
||||
},
|
||||
"deleteSecrets":{
|
||||
"message": "Delete Secrets"
|
||||
},
|
||||
"project":{
|
||||
"message": "Project"
|
||||
},
|
||||
"editProject":{
|
||||
"message": "Edit Project"
|
||||
},
|
||||
"viewProject":{
|
||||
"message": "View Project"
|
||||
},
|
||||
"deleteProject":{
|
||||
"message": "Delete Project"
|
||||
},
|
||||
"deleteProjects":{
|
||||
"message": "Delete Projects"
|
||||
},
|
||||
"secret":{
|
||||
"message": "Secret"
|
||||
},
|
||||
"serviceAccount":{
|
||||
"message": "Service Account"
|
||||
},
|
||||
"serviceAccounts":{
|
||||
"message": "Service Accounts"
|
||||
},
|
||||
"new":{
|
||||
"message": "New"
|
||||
},
|
||||
"secrets":{
|
||||
"message":"Secrets"
|
||||
},
|
||||
"nameValuePair":{
|
||||
"message":"Name/Value Pair"
|
||||
},
|
||||
"secretEdited":{
|
||||
"message":"Secret edited"
|
||||
},
|
||||
"secretCreated":{
|
||||
"message":"Secret created"
|
||||
},
|
||||
"newSecret":{
|
||||
"message":"New Secret"
|
||||
},
|
||||
"newServiceAccount":{
|
||||
"message":"New Service Account"
|
||||
},
|
||||
"importSecrets":{
|
||||
"message":"Import Secrets"
|
||||
},
|
||||
"secretsNoItemsTitle":{
|
||||
"message":"No secrets to show"
|
||||
},
|
||||
"secretsNoItemsMessage":{
|
||||
"message": "To get started, add a new secret or import secrets."
|
||||
},
|
||||
"serviceAccountsNoItemsTitle":{
|
||||
"message":"Nothing to show yet"
|
||||
},
|
||||
"serviceAccountsNoItemsMessage":{
|
||||
"message": "Create a new Service Account to get started automating secret access."
|
||||
},
|
||||
"searchSecrets":{
|
||||
"message":"Search Secrets"
|
||||
},
|
||||
"deleteServiceAccounts":{
|
||||
"message":"Delete Service Accounts"
|
||||
},
|
||||
"deleteServiceAccount":{
|
||||
"message":"Delete Service Account"
|
||||
},
|
||||
"viewServiceAccount":{
|
||||
"message":"View Service Account"
|
||||
},
|
||||
"searchServiceAccounts":{
|
||||
"message":"Search Service Accounts"
|
||||
},
|
||||
"addProject":{
|
||||
"message": "Add Project"
|
||||
},
|
||||
"projectEdited":{
|
||||
"message":"Project edited"
|
||||
},
|
||||
"projectSaved":{
|
||||
"message":"Project saved"
|
||||
},
|
||||
"projectCreated":{
|
||||
"message":"Project created"
|
||||
},
|
||||
"projectName":{
|
||||
"message":"Project Name"
|
||||
},
|
||||
"newProject":{
|
||||
"message":"New Project"
|
||||
},
|
||||
"softDeleteSecretWarning":{
|
||||
"message":"Deleting secrets can affect existing integrations."
|
||||
},
|
||||
"softDeletesSuccessToast":{
|
||||
"message":"Secrets sent to trash"
|
||||
},
|
||||
"serviceAccountCreated":{
|
||||
"message":"Service Account Created"
|
||||
},
|
||||
"smAccess":{
|
||||
"message":"Access"
|
||||
},
|
||||
"projectCommaSecret":{
|
||||
"message":"Project, Secret"
|
||||
},
|
||||
"serviceAccountName":{
|
||||
"message": "Service account name"
|
||||
},
|
||||
"newSaSelectAccess":{
|
||||
"message": "Type or Select Projects or Secrets"
|
||||
},
|
||||
"newSaTypeToFilter":{
|
||||
"message": "Type to Filter"
|
||||
},
|
||||
"deleteProjectsToast":{
|
||||
"message": "Projects deleted"
|
||||
},
|
||||
"deleteProjectToast":{
|
||||
"message": "The project and all associated secrets have been deleted"
|
||||
},
|
||||
"deleteProjectDialogMessage": {
|
||||
"message": "Deleting project $PROJECT$ is permanent and irreversible.",
|
||||
"placeholders": {
|
||||
"project": {
|
||||
"content": "$1",
|
||||
"example": "project name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteProjectInputLabel": {
|
||||
"message": "Type \"$CONFIRM$\" to continue",
|
||||
"placeholders": {
|
||||
"confirm": {
|
||||
"content": "$1",
|
||||
"example": "Delete 3 Projects"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteProjectConfirmMessage":{
|
||||
"message": "Delete $PROJECT$",
|
||||
"placeholders": {
|
||||
"project": {
|
||||
"content": "$1",
|
||||
"example": "project name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteProjectsConfirmMessage":{
|
||||
"message": "Delete $COUNT$ Projects",
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"content": "$1",
|
||||
"example": "2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteProjectsDialogMessage":{
|
||||
"message": "Deleting projects is permanent and irreversible."
|
||||
},
|
||||
"projectsNoItemsTitle":{
|
||||
"message": "No projects to display"
|
||||
},
|
||||
"projectsNoItemsMessage":{
|
||||
"message": "Add a new project to get started organizing secrets."
|
||||
},
|
||||
"smConfirmationRequired":{
|
||||
"message": "Confirmation required"
|
||||
},
|
||||
"bulkDeleteProjectsErrorMessage":{
|
||||
"message": "The following projects could not be deleted:"
|
||||
},
|
||||
"softDeleteSuccessToast":{
|
||||
"message":"Secret sent to trash"
|
||||
},
|
||||
"searchProjects":{
|
||||
"message":"Search Projects"
|
||||
},
|
||||
"accessTokens": {
|
||||
"message": "Access tokens"
|
||||
},
|
||||
"createAccessToken": {
|
||||
"message": "Create access token"
|
||||
},
|
||||
"expires": {
|
||||
"message": "Expires"
|
||||
},
|
||||
"canRead": {
|
||||
"message": "Can Read"
|
||||
},
|
||||
"accessTokensNoItemsTitle": {
|
||||
"message": "No access tokens to show"
|
||||
},
|
||||
"accessTokensNoItemsDesc": {
|
||||
"message": "To get started, create an access token"
|
||||
},
|
||||
"downloadAccessToken": {
|
||||
"message": "Download or copy before closing."
|
||||
},
|
||||
"expiresOnAccessToken": {
|
||||
"message": "Expires on:"
|
||||
},
|
||||
"accessTokenCallOutTitle": {
|
||||
"message": "Access tokens are not stored and cannot be retrieved"
|
||||
},
|
||||
"copyToken": {
|
||||
"message": "Copy token"
|
||||
},
|
||||
"accessToken": {
|
||||
"message": "Access token"
|
||||
},
|
||||
"accessTokenExpirationRequired": {
|
||||
"message": "Expiration date required"
|
||||
},
|
||||
"accessTokenCreatedAndCopied": {
|
||||
"message": "Access token created and copied to clipboard"
|
||||
},
|
||||
"accessTokenPermissionsBetaNotification": {
|
||||
"message": "Permissions management is unavailable for beta."
|
||||
},
|
||||
"revokeAccessToken": {
|
||||
"message": "Revoke Access Token"
|
||||
},
|
||||
"submenu": {
|
||||
"message": "Submenu"
|
||||
},
|
||||
"from": {
|
||||
"message": "From"
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user