mirror of
https://github.com/bitwarden/browser
synced 2025-12-19 17:53:39 +00:00
share and collection management
This commit is contained in:
@@ -43,12 +43,14 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe';
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { CiphersComponent } from './vault/ciphers.component';
|
||||
import { CollectionsComponent } from './vault/collections.component';
|
||||
import { ExportComponent } from './vault/export.component';
|
||||
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
|
||||
import { GroupingsComponent } from './vault/groupings.component';
|
||||
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
|
||||
import { PasswordGeneratorComponent } from './vault/password-generator.component';
|
||||
import { PasswordHistoryComponent } from './vault/password-history.component';
|
||||
import { ShareComponent } from './vault/share.component';
|
||||
import { VaultComponent } from './vault/vault.component';
|
||||
import { ViewComponent } from './vault/view.component';
|
||||
|
||||
@@ -138,6 +140,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
BlurClickDirective,
|
||||
BoxRowDirective,
|
||||
CiphersComponent,
|
||||
CollectionsComponent,
|
||||
EnvironmentComponent,
|
||||
ExportComponent,
|
||||
FallbackSrcDirective,
|
||||
@@ -156,6 +159,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
RegisterComponent,
|
||||
SearchCiphersPipe,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
StopClickDirective,
|
||||
StopPropDirective,
|
||||
TrueFalseValueDirective,
|
||||
@@ -166,6 +170,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
],
|
||||
entryComponents: [
|
||||
AttachmentsComponent,
|
||||
CollectionsComponent,
|
||||
EnvironmentComponent,
|
||||
ExportComponent,
|
||||
FolderAddEditComponent,
|
||||
@@ -175,6 +180,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||
PasswordHistoryComponent,
|
||||
PremiumComponent,
|
||||
SettingsComponent,
|
||||
ShareComponent,
|
||||
TwoFactorOptionsComponent,
|
||||
],
|
||||
providers: [],
|
||||
|
||||
@@ -240,6 +240,11 @@
|
||||
<div class="row-main">{{'attachments' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon"></i>
|
||||
</a>
|
||||
<a class="box-content-row box-content-row-flex text-default" href="#" appStopClick appBlurClick
|
||||
(click)="editCollections()" *ngIf="editMode && cipher.organizationId">
|
||||
<div class="row-main">{{'collections' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
@@ -309,6 +314,10 @@
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button appBlurClick type="button" (click)="share()" title="{{'shareItem' | i18n}}"
|
||||
*ngIf="editMode && cipher && !cipher.organizationId">
|
||||
<i class="fa fa-share-alt fa-lg fa-fw"></i>
|
||||
</button>
|
||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
||||
title="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
|
||||
31
src/app/vault/collections.component.html
Normal file
31
src/app/vault/collections.component.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<div class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'collections' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{'noCollectionsInList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
||||
<label for="collection_{{i}}">{{c.name}}</label>
|
||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
||||
name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" title="{{'save' | i18n}}" [disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
19
src/app/vault/collections.component.ts
Normal file
19
src/app/vault/collections.component.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
|
||||
import { CollectionsComponent as BaseCollectionsComponent } from 'jslib/angular/components/collections.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-collections',
|
||||
templateUrl: 'collections.component.html',
|
||||
})
|
||||
export class CollectionsComponent extends BaseCollectionsComponent {
|
||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
||||
collectionService: CollectionService, platformUtilsService: PlatformUtilsService) {
|
||||
super(collectionService, platformUtilsService, i18nService, cipherService);
|
||||
}
|
||||
}
|
||||
52
src/app/vault/share.component.html
Normal file
52
src/app/vault/share.component.html
Normal file
@@ -0,0 +1,52 @@
|
||||
<div class="modal fade">
|
||||
<div class="modal-dialog">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'share' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||
{{'noOrganizationsList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organization">{{'organization' | i18n}}</label>
|
||||
<select id="organization" name="OrganizationId" [(ngModel)]="organizationId"
|
||||
(change)="filterCollections()">
|
||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{'shareDesc' | i18n}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="organizations && organizations.length">
|
||||
<div class="box-header">
|
||||
{{'collections' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
{{'noCollectionsInList' | i18n}}
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index" appBoxRow>
|
||||
<label for="collection_{{i}}">{{c.name}}</label>
|
||||
<input id="collection_{{i}}" type="checkbox" [(ngModel)]="c.checked"
|
||||
name="Collection[{{i}}].Checked">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button appBlurClick type="submit" class="primary" title="{{'save' | i18n}}"
|
||||
[disabled]="form.loading || !canSave" *ngIf="organizations && organizations.length">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'cancel' | i18n}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
21
src/app/vault/share.component.ts
Normal file
21
src/app/vault/share.component.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { CipherService } from 'jslib/abstractions/cipher.service';
|
||||
import { CollectionService } from 'jslib/abstractions/collection.service';
|
||||
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib/abstractions/user.service';
|
||||
|
||||
import { ShareComponent as BaseShareComponent } from 'jslib/angular/components/share.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-share',
|
||||
templateUrl: 'share.component.html',
|
||||
})
|
||||
export class ShareComponent extends BaseShareComponent {
|
||||
constructor(cipherService: CipherService, i18nService: I18nService,
|
||||
collectionService: CollectionService, userService: UserService,
|
||||
platformUtilsService: PlatformUtilsService) {
|
||||
super(collectionService, platformUtilsService, i18nService, userService, cipherService);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@
|
||||
(onDeletedCipher)="deletedCipher($event)"
|
||||
(onEditAttachments)="editCipherAttachments($event)"
|
||||
(onCancelled)="cancelledAddEdit($event)"
|
||||
(onShareCipher)="shareCipher($event)"
|
||||
(onEditCollections)="cipherCollections($event)"
|
||||
(onGeneratePassword)="openPasswordGenerator(true)">
|
||||
</app-vault-add-edit>
|
||||
<div id="logo" *ngIf="action !== 'add' && action !== 'edit' && action !== 'view'">
|
||||
@@ -41,6 +43,8 @@
|
||||
</div>
|
||||
<ng-template #passwordGenerator></ng-template>
|
||||
<ng-template #attachments></ng-template>
|
||||
<ng-template #collections></ng-template>
|
||||
<ng-template #share></ng-template>
|
||||
<ng-template #folderAddEdit></ng-template>
|
||||
<ng-template #passwordHistory></ng-template>
|
||||
<ng-template #exportVault></ng-template>
|
||||
|
||||
@@ -26,11 +26,13 @@ import { ModalComponent } from 'jslib/angular/components/modal.component';
|
||||
import { AddEditComponent } from './add-edit.component';
|
||||
import { AttachmentsComponent } from './attachments.component';
|
||||
import { CiphersComponent } from './ciphers.component';
|
||||
import { CollectionsComponent } from './collections.component';
|
||||
import { ExportComponent } from './export.component';
|
||||
import { FolderAddEditComponent } from './folder-add-edit.component';
|
||||
import { GroupingsComponent } from './groupings.component';
|
||||
import { PasswordGeneratorComponent } from './password-generator.component';
|
||||
import { PasswordHistoryComponent } from './password-history.component';
|
||||
import { ShareComponent } from './share.component';
|
||||
|
||||
import { CipherType } from 'jslib/enums/cipherType';
|
||||
|
||||
@@ -58,6 +60,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('folderAddEdit', { read: ViewContainerRef }) folderAddEditModalRef: ViewContainerRef;
|
||||
@ViewChild('passwordHistory', { read: ViewContainerRef }) passwordHistoryModalRef: ViewContainerRef;
|
||||
@ViewChild('exportVault', { read: ViewContainerRef }) exportVaultModalRef: ViewContainerRef;
|
||||
@ViewChild('share', { read: ViewContainerRef }) shareModalRef: ViewContainerRef;
|
||||
@ViewChild('collections', { read: ViewContainerRef }) collectionsModalRef: ViewContainerRef;
|
||||
|
||||
action: string;
|
||||
cipherId: string = null;
|
||||
@@ -353,6 +357,45 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
shareCipher(cipher: CipherView) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.shareModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<ShareComponent>(ShareComponent, this.shareModalRef);
|
||||
childComponent.cipherId = cipher.id;
|
||||
|
||||
childComponent.onSharedCipher.subscribe(async () => {
|
||||
this.modal.close();
|
||||
this.viewCipher(cipher);
|
||||
await this.ciphersComponent.refresh();
|
||||
});
|
||||
this.modal.onClosed.subscribe(async () => {
|
||||
this.modal = null;
|
||||
});
|
||||
}
|
||||
|
||||
cipherCollections(cipher: CipherView) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
}
|
||||
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
|
||||
this.modal = this.collectionsModalRef.createComponent(factory).instance;
|
||||
const childComponent = this.modal.show<CollectionsComponent>(CollectionsComponent, this.collectionsModalRef);
|
||||
childComponent.cipherId = cipher.id;
|
||||
|
||||
childComponent.onSavedCollections.subscribe(() => {
|
||||
this.modal.close();
|
||||
this.viewCipher(cipher);
|
||||
});
|
||||
this.modal.onClosed.subscribe(async () => {
|
||||
this.modal = null;
|
||||
});
|
||||
}
|
||||
|
||||
viewCipherPasswordHistory(cipher: CipherView) {
|
||||
if (this.modal != null) {
|
||||
this.modal.close();
|
||||
|
||||
Reference in New Issue
Block a user