();
- constructor(private apiService: ApiService, private route: ActivatedRoute, private eventService: EventService,
- private i18nService: I18nService, private toasterService: ToasterService, private userService: UserService,
- private exportService: ExportService, private platformUtilsService: PlatformUtilsService,
- private router: Router, private userNamePipe: UserNamePipe) { }
+ constructor(private apiService: ApiService, private route: ActivatedRoute, eventService: EventService,
+ i18nService: I18nService, toasterService: ToasterService, private userService: UserService,
+ exportService: ExportService, platformUtilsService: PlatformUtilsService, private router: Router,
+ logService: LogService, private userNamePipe: UserNamePipe) {
+ super(eventService, i18nService, toasterService, exportService, platformUtilsService, logService);
+ }
async ngOnInit() {
this.route.parent.parent.params.subscribe(async params => {
this.organizationId = params.organizationId;
- const organization = await this.userService.getOrganization(this.organizationId);
- if (organization == null || !organization.useEvents) {
+ this.organization = await this.userService.getOrganization(this.organizationId);
+ if (this.organization == null || !this.organization.useEvents) {
this.router.navigate(['/organizations', this.organizationId]);
return;
}
- const defaultDates = this.eventService.getDefaultDateFilters();
- this.start = defaultDates[0];
- this.end = defaultDates[1];
+
await this.load();
});
}
@@ -62,124 +56,40 @@ export class EventsComponent implements OnInit {
const response = await this.apiService.getOrganizationUsers(this.organizationId);
response.data.forEach(u => {
const name = this.userNamePipe.transform(u);
- this.orgUsersIdMap.set(u.id, { name: name, email: u.email });
this.orgUsersUserIdMap.set(u.userId, { name: name, email: u.email });
});
+
+ if (this.organization.providerId != null && (await this.userService.getProvider(this.organization.providerId)) != null) {
+ const providerUsersResponse = await this.apiService.getProviderUsers(this.organization.providerId);
+ providerUsersResponse.data.forEach(u => {
+ const name = this.userNamePipe.transform(u);
+ this.orgUsersUserIdMap.set(u.userId, { name: `${name} (${this.organization.providerName})`, email: u.email });
+ });
+ }
+
await this.loadEvents(true);
this.loaded = true;
}
- async exportEvents() {
- if (this.appApiPromiseUnfulfilled() || this.dirtyDates) {
- return;
- }
-
- this.loading = true;
-
- const dates = this.parseDates();
- if (dates == null) {
- return;
- }
-
- try {
- this.exportPromise = this.export(dates[0], dates[1]);
-
- await this.exportPromise;
- } catch { }
-
- this.exportPromise = null;
- this.loading = false;
+ protected requestEvents(startDate: string, endDate: string, continuationToken: string) {
+ return this.apiService.getEventsOrganization(this.organizationId, startDate, endDate, continuationToken);
}
- async loadEvents(clearExisting: boolean) {
- if (this.appApiPromiseUnfulfilled()) {
- return;
- }
-
- const dates = this.parseDates();
- if (dates == null) {
- return;
- }
-
- this.loading = true;
- let events: EventView[] = [];
- try {
- const promise = this.loadAndParseEvents(dates[0], dates[1], clearExisting ? null : this.continuationToken);
- if (clearExisting) {
- this.refreshPromise = promise;
- } else {
- this.morePromise = promise;
- }
- const result = await promise;
- this.continuationToken = result.continuationToken;
- events = result.events;
- } catch { }
-
- if (!clearExisting && this.events != null && this.events.length > 0) {
- this.events = this.events.concat(events);
- } else {
- this.events = events;
- }
-
- this.dirtyDates = false;
- this.loading = false;
- this.morePromise = null;
- this.refreshPromise = null;
- }
-
- private async export(start: string, end: string) {
- let continuationToken = this.continuationToken;
- let events = [].concat(this.events);
-
- while (continuationToken != null) {
- const result = await this.loadAndParseEvents(start, end, continuationToken);
- continuationToken = result.continuationToken;
- events = events.concat(result.events);
- }
-
- const data = await this.exportService.getEventExport(events);
- const fileName = this.exportService.getFileName('org-events', 'csv');
- this.platformUtilsService.saveFile(window, data, { type: 'text/plain' }, fileName);
- }
-
- private async loadAndParseEvents(startDate: string, endDate: string, continuationToken: string) {
- const response = await this.apiService.getEventsOrganization(this.organizationId, startDate, endDate,
- continuationToken);
-
- const events = await Promise.all(response.data.map(async r => {
- const userId = r.actingUserId == null ? r.userId : r.actingUserId;
- const eventInfo = await this.eventService.getEventInfo(r);
- const user = userId != null && this.orgUsersUserIdMap.has(userId) ?
- this.orgUsersUserIdMap.get(userId) : null;
- return new EventView({
- message: eventInfo.message,
- humanReadableMessage: eventInfo.humanReadableMessage,
- appIcon: eventInfo.appIcon,
- appName: eventInfo.appName,
- userId: userId,
- userName: user != null ? user.name : this.i18nService.t('unknown'),
- userEmail: user != null ? user.email : '',
- date: r.date,
- ip: r.ipAddress,
- type: r.type,
- });
- }));
- return { continuationToken: response.continuationToken, events: events };
- }
-
- private parseDates() {
- let dates: string[] = null;
- try {
- dates = this.eventService.formatDateFilters(this.start, this.end);
- } catch (e) {
- this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
- this.i18nService.t('invalidDateRange'));
+ protected getUserName(r: EventResponse, userId: string) {
+ if (userId == null) {
return null;
}
- return dates;
- }
- private appApiPromiseUnfulfilled() {
- return this.refreshPromise != null || this.morePromise != null || this.exportPromise != null;
+ if (this.orgUsersUserIdMap.has(userId)) {
+ return this.orgUsersUserIdMap.get(userId);
+ }
+
+ if (r.providerId != null && r.providerId === this.organization.providerId) {
+ return {
+ 'name': this.organization.providerName,
+ };
+ }
+
+ return null;
}
}
diff --git a/src/app/organizations/manage/people.component.html b/src/app/organizations/manage/people.component.html
index 3afabc06d1..167b3a44f5 100644
--- a/src/app/organizations/manage/people.component.html
+++ b/src/app/organizations/manage/people.component.html
@@ -8,14 +8,14 @@
{{allCount}}
+ [ngClass]="{active: status == userStatusType.Invited}"
+ (click)="filter(userStatusType.Invited)">
{{'invited' | i18n}}
{{invitedCount}}
+ [ngClass]="{active: status == userStatusType.Accepted}"
+ (click)="filter(userStatusType.Accepted)">
{{'accepted' | i18n}}
{{acceptedCount}}
@@ -86,9 +86,9 @@
{{u.email}}
{{'invited' | i18n}}
+ *ngIf="u.status === userStatusType.Invited">{{'invited' | i18n}}
{{'accepted' | i18n}}
+ *ngIf="u.status === userStatusType.Accepted">{{'accepted' | i18n}}
{{u.name}}
@@ -102,11 +102,11 @@
- {{'owner' | i18n}}
- {{'admin' | i18n}}
- {{'manager' | i18n}}
- {{'user' | i18n}}
- {{'custom' | i18n}}
+ {{'owner' | i18n}}
+ {{'admin' | i18n}}
+ {{'manager' | i18n}}
+ {{'user' | i18n}}
+ {{'custom' | i18n}}
@@ -117,12 +117,12 @@
+
+
+
+ {{'yourProviderIs' | i18n : userOrg.providerName}}
+
diff --git a/src/app/organizations/settings/organization-subscription.component.ts b/src/app/organizations/settings/organization-subscription.component.ts
index 5ac864b32a..216c1d91d8 100644
--- a/src/app/organizations/settings/organization-subscription.component.ts
+++ b/src/app/organizations/settings/organization-subscription.component.ts
@@ -3,9 +3,9 @@ import {
OnInit,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
-
import { ToasterService } from 'angular2-toaster';
+import { Organization } from 'jslib-common/models/domain/organization';
import { OrganizationSubscriptionResponse } from 'jslib-common/models/response/organizationSubscriptionResponse';
import { ApiService } from 'jslib-common/abstractions/api.service';
@@ -13,6 +13,7 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service';
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
+import { UserService } from 'jslib-common/abstractions';
import { PlanType } from 'jslib-common/enums/planType';
@Component({
@@ -33,12 +34,15 @@ export class OrganizationSubscriptionComponent implements OnInit {
sub: OrganizationSubscriptionResponse;
selfHosted = false;
+ userOrg: Organization;
+
cancelPromise: Promise
;
reinstatePromise: Promise;
constructor(private apiService: ApiService, private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService, private toasterService: ToasterService,
- private messagingService: MessagingService, private route: ActivatedRoute) {
+ private messagingService: MessagingService, private route: ActivatedRoute,
+ private userService: UserService) {
this.selfHosted = platformUtilsService.isSelfHost();
}
@@ -54,7 +58,9 @@ export class OrganizationSubscriptionComponent implements OnInit {
if (this.loading) {
return;
}
+
this.loading = true;
+ this.userOrg = await this.userService.getOrganization(this.organizationId);
this.sub = await this.apiService.getOrganizationSubscription(this.organizationId);
this.loading = false;
}
diff --git a/src/app/app-routing.module.ts b/src/app/oss-routing.module.ts
similarity index 99%
rename from src/app/app-routing.module.ts
rename to src/app/oss-routing.module.ts
index bee8416fc1..eb712de2cd 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/oss-routing.module.ts
@@ -438,4 +438,4 @@ const routes: Routes = [
})],
exports: [RouterModule],
})
-export class AppRoutingModule { }
+export class OssRoutingModule { }
diff --git a/src/app/oss.module.ts b/src/app/oss.module.ts
index 0dc54c8b2e..26c2025895 100644
--- a/src/app/oss.module.ts
+++ b/src/app/oss.module.ts
@@ -171,6 +171,8 @@ import { GroupingsComponent } from './vault/groupings.component';
import { ShareComponent } from './vault/share.component';
import { VaultComponent } from './vault/vault.component';
+import { ProvidersComponent } from './providers/providers.component';
+
import { CalloutComponent } from 'jslib-angular/components/callout.component';
import { IconComponent } from 'jslib-angular/components/icon.component';
@@ -433,6 +435,22 @@ registerLocaleData(localeZhTw, 'zh-TW');
VerifyEmailTokenComponent,
VerifyRecoverDeleteComponent,
WeakPasswordsReportComponent,
+ ProvidersComponent,
+ ],
+ exports: [
+ A11yTitleDirective,
+ AvatarComponent,
+ CalloutComponent,
+ ApiActionDirective,
+ StopClickDirective,
+ StopPropDirective,
+ I18nPipe,
+ SearchPipe,
+ UserNamePipe,
+ ModalComponent,
+ NavbarComponent,
+ FooterComponent,
+ OrganizationPlansComponent,
],
providers: [DatePipe, SearchPipe, UserNamePipe],
bootstrap: [],
diff --git a/src/app/providers/providers.component.html b/src/app/providers/providers.component.html
new file mode 100644
index 0000000000..6ff84f4c8c
--- /dev/null
+++ b/src/app/providers/providers.component.html
@@ -0,0 +1,20 @@
+
+
+
+ {{'loading' | i18n}}
+
+
+
+
+
diff --git a/src/app/providers/providers.component.ts b/src/app/providers/providers.component.ts
new file mode 100644
index 0000000000..18c8b2a68b
--- /dev/null
+++ b/src/app/providers/providers.component.ts
@@ -0,0 +1,36 @@
+import {
+ Component,
+ OnInit,
+} from '@angular/core';
+
+import { I18nService } from 'jslib-common/abstractions/i18n.service';
+import { SyncService } from 'jslib-common/abstractions/sync.service';
+import { UserService } from 'jslib-common/abstractions/user.service';
+
+import { Provider } from 'jslib-common/models/domain/provider';
+
+import { Utils } from 'jslib-common/misc/utils';
+
+@Component({
+ selector: 'app-providers',
+ templateUrl: 'providers.component.html',
+})
+export class ProvidersComponent implements OnInit {
+ providers: Provider[];
+ loaded: boolean = false;
+ actionPromise: Promise;
+
+ constructor(private userService: UserService, private i18nService: I18nService, private syncService: SyncService) { }
+
+ async ngOnInit() {
+ await this.syncService.fullSync(false);
+ await this.load();
+ }
+
+ async load() {
+ const providers = await this.userService.getAllProviders();
+ providers.sort(Utils.getSortFunction(this.i18nService, 'name'));
+ this.providers = providers;
+ this.loaded = true;
+ }
+}
diff --git a/src/app/services/event.service.ts b/src/app/services/event.service.ts
index 2ee4fc103c..07a0b97b33 100644
--- a/src/app/services/event.service.ts
+++ b/src/app/services/event.service.ts
@@ -228,7 +228,23 @@ export class EventService {
humanReadableMsg = this.i18nService.t('modifiedPolicyId', p1);
break;
-
+ // Provider users:
+ case EventType.ProviderUser_Invited:
+ msg = this.i18nService.t('invitedUserId', this.formatProviderUserId(ev));
+ humanReadableMsg = this.i18nService.t('invitedUserId', this.getShortId(ev.providerUserId));
+ break;
+ case EventType.ProviderUser_Confirmed:
+ msg = this.i18nService.t('confirmedUserId', this.formatProviderUserId(ev));
+ humanReadableMsg = this.i18nService.t('confirmedUserId', this.getShortId(ev.providerUserId));
+ break;
+ case EventType.ProviderUser_Updated:
+ msg = this.i18nService.t('editedUserId', this.formatProviderUserId(ev));
+ humanReadableMsg = this.i18nService.t('editedUserId', this.getShortId(ev.providerUserId));
+ break;
+ case EventType.ProviderUser_Removed:
+ msg = this.i18nService.t('removedUserId', this.formatProviderUserId(ev));
+ humanReadableMsg = this.i18nService.t('removedUserId', this.getShortId(ev.providerUserId));
+ break;
default:
break;
}
@@ -318,6 +334,14 @@ export class EventService {
return a.outerHTML;
}
+ private formatProviderUserId(ev: EventResponse) {
+ const shortId = this.getShortId(ev.providerUserId);
+ const a = this.makeAnchor(shortId);
+ a.setAttribute('href', '#/providers/' + ev.providerId + '/manage/people?search=' + shortId +
+ '&viewEvents=' + ev.providerUserId);
+ return a.outerHTML;
+ }
+
private formatPolicyId(ev: EventResponse) {
const shortId = this.getShortId(ev.policyId);
const a = this.makeAnchor(shortId);
diff --git a/src/app/services/services.module.ts b/src/app/services/services.module.ts
index 231edc5154..3b6231a2d6 100644
--- a/src/app/services/services.module.ts
+++ b/src/app/services/services.module.ts
@@ -54,6 +54,7 @@ import { UserService } from 'jslib-common/services/user.service';
import { VaultTimeoutService } from 'jslib-common/services/vaultTimeout.service';
import { WebCryptoFunctionService } from 'jslib-common/services/webCryptoFunction.service';
+import { LogService } from 'jslib-common/abstractions';
import { ApiService as ApiServiceAbstraction } from 'jslib-common/abstractions/api.service';
import { AuditService as AuditServiceAbstraction } from 'jslib-common/abstractions/audit.service';
import { AuthService as AuthServiceAbstraction } from 'jslib-common/abstractions/auth.service';
@@ -223,6 +224,7 @@ export function initFactory(): Function {
{ provide: PolicyServiceAbstraction, useValue: policyService },
{ provide: SendServiceAbstraction, useValue: sendService },
{ provide: PasswordRepromptServiceAbstraction, useValue: passwordRepromptService },
+ { provide: LogService, useValue: consoleLogService },
{
provide: APP_INITIALIZER,
useFactory: initFactory,
diff --git a/src/app/settings/organization-plans.component.ts b/src/app/settings/organization-plans.component.ts
index be163b8beb..2c17c9cc23 100644
--- a/src/app/settings/organization-plans.component.ts
+++ b/src/app/settings/organization-plans.component.ts
@@ -10,8 +10,6 @@ import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
-import { PaymentMethodType } from 'jslib-common/enums/paymentMethodType';
-
import { ApiService } from 'jslib-common/abstractions/api.service';
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
import { I18nService } from 'jslib-common/abstractions/i18n.service';
@@ -23,8 +21,12 @@ import { UserService } from 'jslib-common/abstractions/user.service';
import { PaymentComponent } from './payment.component';
import { TaxInfoComponent } from './tax-info.component';
+import { EncString } from 'jslib-common/models/domain/encString';
+import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
+
import { OrganizationUserStatusType } from 'jslib-common/enums/organizationUserStatusType';
import { OrganizationUserType } from 'jslib-common/enums/organizationUserType';
+import { PaymentMethodType } from 'jslib-common/enums/paymentMethodType';
import { PlanType } from 'jslib-common/enums/planType';
import { PolicyType } from 'jslib-common/enums/policyType';
import { ProductType } from 'jslib-common/enums/productType';
@@ -33,7 +35,6 @@ import { OrganizationCreateRequest } from 'jslib-common/models/request/organizat
import { OrganizationKeysRequest } from 'jslib-common/models/request/organizationKeysRequest';
import { OrganizationUpgradeRequest } from 'jslib-common/models/request/organizationUpgradeRequest';
-import { EncString } from 'jslib-common/models/domain';
import { PlanResponse } from 'jslib-common/models/response/planResponse';
@Component({
@@ -49,6 +50,7 @@ export class OrganizationPlansComponent implements OnInit {
@Input() showCancel = false;
@Input() product: ProductType = ProductType.Free;
@Input() plan: PlanType = PlanType.Free;
+ @Input() providerId: string;
@Output() onSuccess = new EventEmitter();
@Output() onCanceled = new EventEmitter();
@@ -237,7 +239,7 @@ export class OrganizationPlansComponent implements OnInit {
if (this.selfHosted) {
orgId = await this.createSelfHosted(key, collectionCt, orgKeys);
} else {
- orgId = await this.createCloudHosted(key, collectionCt, orgKeys);
+ orgId = await this.createCloudHosted(key, collectionCt, orgKeys, shareKey[1]);
}
this.toasterService.popAsync('success', this.i18nService.t('organizationCreated'), this.i18nService.t('organizationReadyToGo'));
@@ -296,7 +298,7 @@ export class OrganizationPlansComponent implements OnInit {
return this.organizationId;
}
- private async createCloudHosted(key: string, collectionCt: string, orgKeys: [string, EncString]) {
+ private async createCloudHosted(key: string, collectionCt: string, orgKeys: [string, EncString], orgKey: SymmetricCryptoKey) {
const request = new OrganizationCreateRequest();
request.key = key;
request.collectionName = collectionCt;
@@ -327,8 +329,14 @@ export class OrganizationPlansComponent implements OnInit {
request.billingAddressState = this.taxComponent.taxInfo.state;
}
}
- const response = await this.apiService.postOrganization(request);
- return response.id;
+
+ if (this.providerId) {
+ const providerKey = await this.cryptoService.getProviderKey(this.providerId);
+ request.key = (await this.cryptoService.encrypt(orgKey.key, providerKey)).encryptedString;
+ return (await this.apiService.postProviderCreateOrganization(this.providerId, request)).id;
+ } else {
+ return (await this.apiService.postOrganization(request)).id;
+ }
}
private async createSelfHosted(key: string, collectionCt: string, orgKeys: [string, EncString]) {
diff --git a/src/app/vault/vault.component.html b/src/app/vault/vault.component.html
index 9b8818f606..4ed97a6aae 100644
--- a/src/app/vault/vault.component.html
+++ b/src/app/vault/vault.component.html
@@ -85,6 +85,18 @@
+
diff --git a/src/app/vault/vault.component.ts b/src/app/vault/vault.component.ts
index 41216eadff..c60d293abf 100644
--- a/src/app/vault/vault.component.ts
+++ b/src/app/vault/vault.component.ts
@@ -64,6 +64,7 @@ export class VaultComponent implements OnInit, OnDestroy {
showBrowserOutdated = false;
showUpdateKey = false;
showPremiumCallout = false;
+ showProviders = false;
deleted: boolean = false;
trashCleanupWarning: string = null;
@@ -92,6 +93,8 @@ export class VaultComponent implements OnInit, OnDestroy {
this.showPremiumCallout = !this.showVerifyEmail && !canAccessPremium &&
!this.platformUtilsService.isSelfHost();
+ this.showProviders = (await this.userService.getAllProviders()).length > 0;
+
await Promise.all([
this.groupingsComponent.load(),
this.organizationsComponent.load(),
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index d502ea9393..adcad3a6f8 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -4017,5 +4017,123 @@
},
"resetPasswordManageUsers": {
"message": "Manage Users must also be enabled with the Manage Password Reset permission"
+ },
+ "setupProvider": {
+ "message": "Setup Provider"
+ },
+ "setupProviderLoginDesc": {
+ "message": "You've been invited to setup a new provider. To continue, you need to log in or create a new Bitwarden account."
+ },
+ "setupProviderDesc": {
+ "message": "You have been invited to create a Provider, please enter the details below to complete the setup. Contact customer support if you have any questions."
+ },
+ "providerName": {
+ "message": "Provider Name"
+ },
+ "providerSetup": {
+ "message": "The provider has been setup."
+ },
+ "clients": {
+ "message": "Clients"
+ },
+ "providerAdmin": {
+ "message": "Provider Admin"
+ },
+ "providerAdminDesc": {
+ "message": "The highest access user that can manage all aspects of your provider, and client organizations."
+ },
+ "serviceUser": {
+ "message": "Service User"
+ },
+ "serviceUserDesc": {
+ "message": "Service users can access and manage all client organizations."
+ },
+ "providerInviteUserDesc": {
+ "message": "Invite a new user to your provider by entering their Bitwarden account email address below. If they do not have a Bitwarden account already, they will be prompted to create a new account."
+ },
+ "joinProvider": {
+ "message": "Join Provider"
+ },
+ "joinProviderDesc": {
+ "message": "You've been invited to join the provider listed above. To accept the invitation, you need to log in or create a new Bitwarden account."
+ },
+ "providerInviteAcceptFailed": {
+ "message": "Unable to accept invitation. Ask a provider admin to send a new invitation."
+ },
+ "providerInviteAcceptedDesc": {
+ "message": "You can access this provider once an administrator confirms your membership. We'll send you an email when that happens."
+ },
+ "providerUsersNeedConfirmed": {
+ "message": "You have users that have accepted their invitation, but still need to be confirmed. Users will not have access to the provider until they are confirmed."
+ },
+ "provider": {
+ "message": "Provider"
+ },
+ "newClientOrganization": {
+ "message": "New client organization"
+ },
+ "newClientOrganizationDesc": {
+ "message": "Organizations allow you to share parts of your vault with others as well as manage related users for a specific entity such as a family, small team, or large company."
+ },
+ "addExistingOrganization": {
+ "message": "Add existing organization"
+ },
+ "myProvider": {
+ "message": "My Provider"
+ },
+ "addOrganizationConfirmation": {
+ "message": "Are you sure you want to add $ORGANIZATION$ as a client to $PROVIDER$?",
+ "placeholders": {
+ "organization": {
+ "content": "$1",
+ "example": "My Org Name"
+ },
+ "provider": {
+ "content": "$2",
+ "example": "My Provider Name"
+ }
+ }
+ },
+ "organizationJoinedProvider": {
+ "message": "Organization was successfully added to the provider"
+ },
+ "accessingUsingProvider": {
+ "message": "Accessing organization using provider $PROVIDER$",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "My Provider Name"
+ }
+ }
+ },
+ "providerIsDisabled": {
+ "message": "Provider is disabled."
+ },
+ "providerUpdated": {
+ "message": "Provider updated"
+ },
+ "yourProviderIs": {
+ "message": "Your provider is $PROVIDER$. They have administrative and billing privileges for your organization.",
+ "placeholders": {
+ "provider": {
+ "content": "$1",
+ "example": "My Provider Name"
+ }
+ }
+ },
+ "detachedOrganization": {
+ "message": "The organization $ORGANIZATION$ has been detached from your provider.",
+ "placeholders": {
+ "organization": {
+ "content": "$1",
+ "example": "My Org Name"
+ }
+ }
+ },
+ "detachOrganizationConfirmation": {
+ "message": "Are you sure you want to detach this organization? The organization will continue to exist but will no longer be managed by the provider."
+ },
+ "add": {
+ "message": "Add"
}
}
diff --git a/tsconfig.json b/tsconfig.json
index 452496e309..16f8f67b45 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -22,6 +22,9 @@
],
"jslib-angular/*": [
"jslib/angular/src/*"
+ ],
+ "src/*": [
+ "src/*"
]
}
},