1
0
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:
Kyle Spearrin
2018-10-23 15:50:21 -04:00
parent 2244519596
commit 7b0063902f
10 changed files with 204 additions and 1 deletions

View File

@@ -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: [],

View File

@@ -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">

View 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>

View 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);
}
}

View 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>

View 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);
}
}

View File

@@ -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>

View File

@@ -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();