mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-22 16:29:09 +01:00
[PM-4209] Enable importing on browser (#6503)
* Split up import/export into separate modules * Fix routing and apply PR feedback * Renamed OrganizationExport exports to OrganizationVaultExport * Make import dialogs standalone and move them to libs/importer * Make import.component re-usable - Move functionality which was previously present on the org-import.component into import.component - Move import.component into libs/importer Make import.component standalone Create import-web.component to represent Web UI Fix module imports and routing Remove unused org-import-files * Enable importing on browser Create import-dialog Add routing and routing animations Settings import items no longer navigates to help page but opens import page Extend messages.json to include all the necessary messages from shared components * Fix back navigation * Renamed filenames according to export rename * Make ImportWebComponent standalone, simplify routing * Pass organizationId as Input to ImportComponent * use formLoading and formDisabled outputs * add loading and disabled state to import-browser * override popup header styles * Emit an event when the import succeeds Remove Angular router from base-component as other clients might not have routing (i.e. desktop) Move logic that happened on web successful import into the import-web.component * Enable importing on browser Create import-dialog Add routing and routing animations Settings import items no longer navigates to help page but opens import page Extend messages.json to include all the necessary messages from shared components * Fix back navigation * add loading and disabled state to import-browser * override popup header styles * Add missing message for importBlockedByPolicy callout * Implement onSuccessfulImport to navigate back to settings * fix table themes on desktop & browser * fix fileSelector button styles * update selectors to use tools prefix; remove unused selectors * rename selector * Wall off UI components in libs/importer Create barrel-file for libs/importer/components Remove components and dialog exports from libs/importer/index.ts Extend libs/shared/tsconfig.libs.json to include @bitwarden/importer/ui -> libs/importer/components Extend apps/web/tsconfig.ts to include @bitwarden/importer/ui Update all usages * Rename @bitwarden/importer to @bitwarden/importer/core Create more barrel files in libs/importer/* Update imports within libs/importer Extend tsconfig files Update imports in web, desktop, browser and cli * Lazy-load the ImportWebComponent via both routes * Fix import path for ImportComponent * Navigate to import opens in popout when navigated from the popup Make import call async and await router navigate - If the user has the popup open and selects import, it will navigate to the import page and popout into a new window. This is necessary as any focus-loss (i.e Choose file) would close the popup. - If the user is using the for example the sidebar or an already popped out window, just navigate to import page * Use SharedModule as import in import-web.component * File selector should be displayed as secondary * Update description of "importData" in messages.json * Add missing messages for file-password-prompt * Add missing messages for import-error-dialog * Add missing message for import-success-dialog * Use bitSubmit to override submit preventDefault (#6607) Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> * Add missing importWarning --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> Co-authored-by: William Martin <contact@willmartian.com>
This commit is contained in:
parent
cdcd1809f0
commit
87dbe8997d
@ -1656,6 +1656,9 @@
|
|||||||
"personalOwnershipPolicyInEffect": {
|
"personalOwnershipPolicyInEffect": {
|
||||||
"message": "An organization policy is affecting your ownership options."
|
"message": "An organization policy is affecting your ownership options."
|
||||||
},
|
},
|
||||||
|
"personalOwnershipPolicyInEffectImports": {
|
||||||
|
"message": "An organization policy has blocked importing items into your individual vault."
|
||||||
|
},
|
||||||
"excludedDomains": {
|
"excludedDomains": {
|
||||||
"message": "Excluded domains"
|
"message": "Excluded domains"
|
||||||
},
|
},
|
||||||
@ -2449,6 +2452,114 @@
|
|||||||
"message": "Turn off master password re-prompt to edit this field",
|
"message": "Turn off master password re-prompt to edit this field",
|
||||||
"description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item."
|
"description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item."
|
||||||
},
|
},
|
||||||
|
"importData": {
|
||||||
|
"message": "Import data",
|
||||||
|
"description": "Used for the header of the import dialog, the import button and within the file-password-prompt"
|
||||||
|
},
|
||||||
|
"importError": {
|
||||||
|
"message": "Import error"
|
||||||
|
},
|
||||||
|
"importErrorDesc": {
|
||||||
|
"message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again."
|
||||||
|
},
|
||||||
|
"resolveTheErrorsBelowAndTryAgain": {
|
||||||
|
"message": "Resolve the errors below and try again."
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"message": "Description"
|
||||||
|
},
|
||||||
|
"importSuccess": {
|
||||||
|
"message": "Data successfully imported"
|
||||||
|
},
|
||||||
|
"importSuccessNumberOfItems": {
|
||||||
|
"message": "A total of $AMOUNT$ items were imported.",
|
||||||
|
"placeholders": {
|
||||||
|
"amount": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"message": "Total"
|
||||||
|
},
|
||||||
|
"importWarning": {
|
||||||
|
"message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?",
|
||||||
|
"placeholders": {
|
||||||
|
"organization": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "My Org Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"importFormatError": {
|
||||||
|
"message": "Data is not formatted correctly. Please check your import file and try again."
|
||||||
|
},
|
||||||
|
"importNothingError": {
|
||||||
|
"message": "Nothing was imported."
|
||||||
|
},
|
||||||
|
"importEncKeyError": {
|
||||||
|
"message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data."
|
||||||
|
},
|
||||||
|
"importDestination": {
|
||||||
|
"message": "Import destination"
|
||||||
|
},
|
||||||
|
"learnAboutImportOptions": {
|
||||||
|
"message": "Learn about your import options"
|
||||||
|
},
|
||||||
|
"selectImportFolder": {
|
||||||
|
"message": "Select a folder"
|
||||||
|
},
|
||||||
|
"selectImportCollection": {
|
||||||
|
"message": "Select a collection"
|
||||||
|
},
|
||||||
|
"importTargetHint": {
|
||||||
|
"message": "Select this option if you want the imported file contents moved to a $DESTINATION$",
|
||||||
|
"description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.",
|
||||||
|
"placeholders": {
|
||||||
|
"destination": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "folder or collection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"importUnassignedItemsError": {
|
||||||
|
"message": "File contains unassigned items."
|
||||||
|
},
|
||||||
|
"selectFormat": {
|
||||||
|
"message": "Select the format of the import file"
|
||||||
|
},
|
||||||
|
"selectImportFile": {
|
||||||
|
"message": "Select the import file"
|
||||||
|
},
|
||||||
|
"chooseFile": {
|
||||||
|
"message": "Choose File"
|
||||||
|
},
|
||||||
|
"noFileChosen": {
|
||||||
|
"message": "No file chosen"
|
||||||
|
},
|
||||||
|
"orCopyPasteFileContents": {
|
||||||
|
"message": "or copy/paste the import file contents"
|
||||||
|
},
|
||||||
|
"instructionsFor": {
|
||||||
|
"message": "$NAME$ Instructions",
|
||||||
|
"description": "The title for the import tool instructions.",
|
||||||
|
"placeholders": {
|
||||||
|
"name": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "LastPass (csv)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"confirmVaultImport": {
|
||||||
|
"message": "Confirm vault import"
|
||||||
|
},
|
||||||
|
"confirmVaultImportDesc": {
|
||||||
|
"message": "This file is password-protected. Please enter the file password to import data."
|
||||||
|
},
|
||||||
|
"confirmFilePassword": {
|
||||||
|
"message": "Confirm file password"
|
||||||
|
},
|
||||||
"passkeyNotCopied": {
|
"passkeyNotCopied": {
|
||||||
"message": "Passkey will not be copied"
|
"message": "Passkey will not be copied"
|
||||||
},
|
},
|
||||||
|
@ -174,6 +174,9 @@ export const routerTransition = trigger("routerTransition", [
|
|||||||
transition("clone-cipher => attachments, clone-cipher => collections", inSlideLeft),
|
transition("clone-cipher => attachments, clone-cipher => collections", inSlideLeft),
|
||||||
transition("attachments => clone-cipher, collections => clone-cipher", outSlideRight),
|
transition("attachments => clone-cipher, collections => clone-cipher", outSlideRight),
|
||||||
|
|
||||||
|
transition("tabs => import", inSlideLeft),
|
||||||
|
transition("import => tabs", outSlideRight),
|
||||||
|
|
||||||
transition("tabs => export", inSlideLeft),
|
transition("tabs => export", inSlideLeft),
|
||||||
transition("export => tabs", outSlideRight),
|
transition("export => tabs", outSlideRight),
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.componen
|
|||||||
import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component";
|
import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component";
|
||||||
import { SendTypeComponent } from "../tools/popup/send/send-type.component";
|
import { SendTypeComponent } from "../tools/popup/send/send-type.component";
|
||||||
import { ExportComponent } from "../tools/popup/settings/export.component";
|
import { ExportComponent } from "../tools/popup/settings/export.component";
|
||||||
|
import { ImportBrowserComponent } from "../tools/popup/settings/import/import-browser.component";
|
||||||
import { Fido2Component } from "../vault/popup/components/fido2/fido2.component";
|
import { Fido2Component } from "../vault/popup/components/fido2/fido2.component";
|
||||||
import { AddEditComponent } from "../vault/popup/components/vault/add-edit.component";
|
import { AddEditComponent } from "../vault/popup/components/vault/add-edit.component";
|
||||||
import { AttachmentsComponent } from "../vault/popup/components/vault/attachments.component";
|
import { AttachmentsComponent } from "../vault/popup/components/vault/attachments.component";
|
||||||
@ -222,6 +223,12 @@ const routes: Routes = [
|
|||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
data: { state: "generator-history" },
|
data: { state: "generator-history" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "import",
|
||||||
|
component: ImportBrowserComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
data: { state: "import" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "export",
|
path: "export",
|
||||||
component: ExportComponent,
|
component: ExportComponent,
|
||||||
|
@ -175,7 +175,7 @@ cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header:not(bit-callout header) {
|
||||||
min-height: 44px;
|
min-height: 44px;
|
||||||
max-height: 44px;
|
max-height: 44px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -473,8 +473,11 @@ export class SettingsComponent implements OnInit {
|
|||||||
BrowserApi.createNewTab(url);
|
BrowserApi.createNewTab(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
import() {
|
async import() {
|
||||||
BrowserApi.createNewTab("https://bitwarden.com/help/import-data/");
|
await this.router.navigate(["/import"]);
|
||||||
|
if (await BrowserApi.isPopupOpen()) {
|
||||||
|
this.popupUtilsService.popOut(window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
export() {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
<header>
|
||||||
|
<div class="left">
|
||||||
|
<button type="button" routerLink="/tabs/settings">
|
||||||
|
<span class="header-icon" aria-hidden="true"><i class="bwi bwi-angle-left"></i></span>
|
||||||
|
<span>{{ "back" | i18n }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<h1 class="center">
|
||||||
|
<span class="title">{{ "importData" | i18n }}</span>
|
||||||
|
</h1>
|
||||||
|
<div class="right">
|
||||||
|
<button form="importForm" type="submit" [disabled]="disabled">
|
||||||
|
<span [hidden]="loading">{{ "importData" | i18n }}</span>
|
||||||
|
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!loading" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div tabindex="-1" class="tw-p-4">
|
||||||
|
<tools-import
|
||||||
|
(formDisabled)="this.disabled = $event"
|
||||||
|
(formLoading)="this.loading = $event"
|
||||||
|
(onSuccessfulImport)="this.onSuccessfulImport($event)"
|
||||||
|
></tools-import>
|
||||||
|
</div>
|
@ -0,0 +1,31 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { Router, RouterLink } from "@angular/router";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components";
|
||||||
|
import { ImportComponent } from "@bitwarden/importer/ui";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "import-browser.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterLink,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
AsyncActionsModule,
|
||||||
|
ButtonModule,
|
||||||
|
ImportComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class ImportBrowserComponent {
|
||||||
|
protected disabled = false;
|
||||||
|
protected loading = false;
|
||||||
|
|
||||||
|
constructor(private router: Router) {}
|
||||||
|
|
||||||
|
protected async onSuccessfulImport(organizationId: string): Promise<void> {
|
||||||
|
this.router.navigate(["/tabs/settings"]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user