feat(console): update deps, alternative hash function with fixed colors, use preferrenLoginName for hashing, fix iam write permissions, user img upload (#1846)

* chore(deps-dev): bump @types/jasmine from 3.6.9 to 3.7.7 in /console (#1824)

Bumps [@types/jasmine](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jasmine) from 3.6.9 to 3.7.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jasmine)

---
updated-dependencies:
- dependency-name: "@types/jasmine"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump google-protobuf from 3.15.8 to 3.17.2 in /console (#1823)

Bumps [google-protobuf](https://github.com/protocolbuffers/protobuf) from 3.15.8 to 3.17.2.
- [Release notes](https://github.com/protocolbuffers/protobuf/releases)
- [Changelog](https://github.com/protocolbuffers/protobuf/blob/master/generate_changelog.py)
- [Commits](https://github.com/protocolbuffers/protobuf/compare/v3.15.8...v3.17.2)

---
updated-dependencies:
- dependency-name: google-protobuf
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @angular/animations from 12.0.0 to 12.0.3 in /console (#1821)

Bumps [@angular/animations](https://github.com/angular/angular/tree/HEAD/packages/animations) from 12.0.0 to 12.0.3.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/12.0.3/packages/animations)

---
updated-dependencies:
- dependency-name: "@angular/animations"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @angular/material from 12.0.0 to 12.0.3 in /console (#1819)

Bumps [@angular/material](https://github.com/angular/components) from 12.0.0 to 12.0.3.
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/12.0.3/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/12.0.0...12.0.3)

---
updated-dependencies:
- dependency-name: "@angular/material"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump prettier from 2.2.1 to 2.3.1 in /console (#1818)

Bumps [prettier](https://github.com/prettier/prettier) from 2.2.1 to 2.3.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.2.1...2.3.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @angular/platform-browser-dynamic in /console (#1817)

Bumps [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) from 12.0.0 to 12.0.3.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/12.0.3/packages/platform-browser-dynamic)

---
updated-dependencies:
- dependency-name: "@angular/platform-browser-dynamic"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @types/node from 14.14.37 to 15.12.1 in /console (#1815)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.37 to 15.12.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @angular/common from 12.0.0 to 12.0.3 in /console (#1814)

Bumps [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) from 12.0.0 to 12.0.3.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/12.0.3/packages/common)

---
updated-dependencies:
- dependency-name: "@angular/common"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @angular/material-moment-adapter from 12.0.0 to 12.0.3 in /console (#1816)

* chore(deps): bump @angular/material-moment-adapter in /console

Bumps [@angular/material-moment-adapter](https://github.com/angular/components) from 12.0.0 to 12.0.3.
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/12.0.3/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/12.0.0...12.0.3)

---
updated-dependencies:
- dependency-name: "@angular/material-moment-adapter"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

* sort

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* chore(deps-dev): bump @angular/language-service in /console (#1822)

Bumps [@angular/language-service](https://github.com/angular/angular/tree/HEAD/packages/language-service) from 12.0.0 to 12.0.3.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/master/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/12.0.3/packages/language-service)

---
updated-dependencies:
- dependency-name: "@angular/language-service"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Max Peintner <[email protected]>

* image cropper

* fix: avatar bg colors, preferred username, login script

* lint

* membership color

* rem logs

* profile picture component

* pic comp

* dialog tirgger btn

* trigger dialog, styles

* lock

* interceptor for org, upload, remove

* tooltip

* lint

* stylelint

* generate same credentials of username as in login

* deletepic

* fix disable privatelabeling on missing feature, i18n

* lint

* stylelint

* block loading images if no feature

* lint

* optimize feature check

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Max Peintner
2021-06-11 11:15:04 +02:00
committed by GitHub
co-authored by dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
parent 2502f379d9
commit 1e77b8aeae
45 changed files with 11966 additions and 8112 deletions
@@ -2,6 +2,7 @@
<app-avatar
*ngIf="user.human?.profile && (user.human?.profile?.displayName || (user.human?.profile?.firstName && user.human?.profile?.lastName))"
class="avatar"
[forColor]="user.preferredLoginName"
[name]="user.human?.profile?.displayName ? user.human?.profile?.displayName : (user.human?.profile?.firstName + ' '+ user.human?.profile?.lastName)"
[size]="80">
</app-avatar>
@@ -14,8 +15,7 @@
<div class="l-accounts">
<mat-progress-bar *ngIf="loadingUsers" color="primary" mode="indeterminate"></mat-progress-bar>
<a class="row" *ngFor="let session of sessions" (click)="selectAccount(session.loginName)">
<app-avatar *ngIf="session && session.displayName" class="small-avatar"
[name]="session.displayName ? session.displayName : ''" [size]="32">
<app-avatar *ngIf="session && session.displayName" class="small-avatar" [forColor]="session.loginName" [size]="32">
</app-avatar>
<div class="col">
@@ -6,69 +6,70 @@ import { AuthenticationService } from 'src/app/services/authentication.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
@Component({
selector: 'app-accounts-card',
templateUrl: './accounts-card.component.html',
styleUrls: ['./accounts-card.component.scss'],
selector: 'app-accounts-card',
templateUrl: './accounts-card.component.html',
styleUrls: ['./accounts-card.component.scss'],
})
export class AccountsCardComponent implements OnInit {
@Input() public user!: User.AsObject;
@Input() public iamuser: boolean = false;
@Input() public user!: User.AsObject;
@Input() public iamuser: boolean = false;
@Output() public close: EventEmitter<void> = new EventEmitter();
public sessions: Session.AsObject[] = [];
public loadingUsers: boolean = false;
constructor(public authService: AuthenticationService, private router: Router, private userService: GrpcAuthService) {
this.userService.listMyUserSessions().then(sessions => {
this.sessions = sessions.resultList;
const index = this.sessions.findIndex(user => user.loginName === this.user.preferredLoginName);
if (index > -1) {
this.sessions.splice(index, 1);
}
@Output() public close: EventEmitter<void> = new EventEmitter();
public sessions: Session.AsObject[] = [];
public loadingUsers: boolean = false;
constructor(public authService: AuthenticationService, private router: Router, private userService: GrpcAuthService) {
this.userService.listMyUserSessions().then(sessions => {
this.sessions = sessions.resultList;
console.log(sessions.resultList);
const index = this.sessions.findIndex(user => user.loginName === this.user.preferredLoginName);
if (index > -1) {
this.sessions.splice(index, 1);
}
this.loadingUsers = false;
}).catch(() => {
this.loadingUsers = false;
});
this.loadingUsers = false;
}).catch(() => {
this.loadingUsers = false;
});
}
public ngOnInit(): void {
this.loadingUsers = true;
}
public editUserProfile(): void {
this.router.navigate(['users/me']);
this.close.emit();
}
public closeCard(element: HTMLElement): void {
if (!element.classList.contains('dontcloseonclick')) {
this.close.emit();
}
}
public ngOnInit(): void {
this.loadingUsers = true;
public selectAccount(loginHint?: string): void {
const configWithPrompt: Partial<AuthConfig> = {
customQueryParams: {
// prompt: 'select_account',
} as any,
};
if (loginHint) {
(configWithPrompt as any).customQueryParams['login_hint'] = loginHint;
}
this.authService.authenticate(configWithPrompt);
}
public editUserProfile(): void {
this.router.navigate(['users/me']);
this.close.emit();
}
public selectNewAccount(): void {
const configWithPrompt: Partial<AuthConfig> = {
customQueryParams: {
prompt: 'login',
} as any,
};
this.authService.authenticate(configWithPrompt);
}
public closeCard(element: HTMLElement): void {
if (!element.classList.contains('dontcloseonclick')) {
this.close.emit();
}
}
public selectAccount(loginHint?: string): void {
const configWithPrompt: Partial<AuthConfig> = {
customQueryParams: {
// prompt: 'select_account',
} as any,
};
if (loginHint) {
(configWithPrompt as any).customQueryParams['login_hint'] = loginHint;
}
this.authService.authenticate(configWithPrompt);
}
public selectNewAccount(): void {
const configWithPrompt: Partial<AuthConfig> = {
customQueryParams: {
prompt: 'login',
} as any,
};
this.authService.authenticate(configWithPrompt);
}
public logout(): void {
this.authService.signout();
this.close.emit();
}
public logout(): void {
this.authService.signout();
this.close.emit();
}
}
@@ -1,64 +1,87 @@
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-avatar',
templateUrl: './avatar.component.html',
styleUrls: ['./avatar.component.scss'],
selector: 'app-avatar',
templateUrl: './avatar.component.html',
styleUrls: ['./avatar.component.scss'],
})
export class AvatarComponent implements OnInit {
@Input() name: string = '';
@Input() credentials: string = '';
@Input() size: number = 24;
@Input() fontSize: number = 14;
@Input() active: boolean = false;
@Input() color: string = '';
constructor() { }
@Input() name: string = '';
@Input() credentials: string = '';
@Input() size: number = 24;
@Input() fontSize: number = 14;
@Input() active: boolean = false;
@Input() color: string = '';
@Input() forColor: string = '';
constructor() { }
ngOnInit(): void {
if (!this.credentials) {
const split: string[] = this.name.split(' ');
this.credentials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : '');
if (!this.color) {
this.color = this.getColor(this.name);
}
}
if (this.size > 50) {
this.fontSize = 32;
}
ngOnInit(): void {
if (!this.credentials && this.forColor) {
this.credentials = this.getInitials(this.forColor);
if (!this.color) {
this.color = this.getColor(this.forColor || '');
}
}
getColor(userName: string): string {
const colors = [
'linear-gradient(40deg, #B44D51 30%, rgb(241,138,138))',
'linear-gradient(40deg, #B75073 30%, rgb(234,96,143))',
'linear-gradient(40deg, #84498E 30%, rgb(214,116,230))',
'linear-gradient(40deg, #705998 30%, rgb(163,131,220))',
'linear-gradient(40deg, #5C6598 30%, rgb(135,148,222))',
'linear-gradient(40deg, #7F90D3 30%, rgb(181,196,247))',
'linear-gradient(40deg, #3E93B9 30%, rgb(150,215,245))',
'linear-gradient(40deg, #3494A0 30%, rgb(71,205,222))',
'linear-gradient(40deg, #25716A 30%, rgb(58,185,173))',
'linear-gradient(40deg, #427E41 30%, rgb(97,185,96))',
'linear-gradient(40deg, #89A568 30%, rgb(176,212,133))',
'linear-gradient(40deg, #90924D 30%, rgb(187,189,98))',
'linear-gradient(40deg, #E2B032 30%, rgb(245,203,99))',
'linear-gradient(40deg, #C97358 30%, rgb(245,148,118))',
'linear-gradient(40deg, #6D5B54 30%, rgb(152,121,108))',
'linear-gradient(40deg, #6B7980 30%, rgb(134,163,177))',
];
let hash = 0;
if (userName.length === 0) {
return colors[hash];
}
for (let i = 0; i < userName.length; i++) {
// tslint:disable-next-line: no-bitwise
hash = userName.charCodeAt(i) + ((hash << 5) - hash);
// tslint:disable-next-line: no-bitwise
hash = hash & hash;
}
hash = ((hash % colors.length) + colors.length) % colors.length;
return colors[hash];
if (this.size > 50) {
this.fontSize = 32;
}
}
getInitials(fromName: string): string {
const username = fromName.split('@')[0];
let separator = '_';
if (username.includes('-')) {
separator = '-';
}
if (username.includes('.')) {
separator = '.';
}
const split = username.split(separator);
const initials = split[0].charAt(0) + (split[1] ? split[1].charAt(0) : '');
return initials;
}
getColor(userName: string): string {
const colors = [
'linear-gradient(40deg, #B44D51 30%, rgb(241,138,138))',
'linear-gradient(40deg, #B75073 30%, rgb(234,96,143))',
'linear-gradient(40deg, #84498E 30%, rgb(214,116,230))',
'linear-gradient(40deg, #705998 30%, rgb(163,131,220))',
'linear-gradient(40deg, #5C6598 30%, rgb(135,148,222))',
'linear-gradient(40deg, #7F90D3 30%, rgb(181,196,247))',
'linear-gradient(40deg, #3E93B9 30%, rgb(150,215,245))',
'linear-gradient(40deg, #3494A0 30%, rgb(71,205,222))',
'linear-gradient(40deg, #25716A 30%, rgb(58,185,173))',
'linear-gradient(40deg, #427E41 30%, rgb(97,185,96))',
'linear-gradient(40deg, #89A568 30%, rgb(176,212,133))',
'linear-gradient(40deg, #90924D 30%, rgb(187,189,98))',
'linear-gradient(40deg, #E2B032 30%, rgb(245,203,99))',
'linear-gradient(40deg, #C97358 30%, rgb(245,148,118))',
'linear-gradient(40deg, #6D5B54 30%, rgb(152,121,108))',
'linear-gradient(40deg, #6B7980 30%, rgb(134,163,177))',
];
let hash = 0;
if (userName.length === 0) {
return colors[hash];
}
hash = this.hashCode(userName);
return colors[hash % colors.length];
}
// tslint:disable
private hashCode(str: string, seed: number = 0): number {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
}
// tslint:enable
}
@@ -13,7 +13,7 @@
<div class="row">
<app-avatar matTooltip="{{ dayelement.editorDisplayName }}"
*ngIf="dayelement.editorDisplayName; else spacer" class="avatar"
[name]="dayelement.editorDisplayName" [size]="32">
[name]="dayelement.editorDisplayName" [size]="32" [forColor]="dayelement?.preferredLoginName">
</app-avatar>
<ng-template #spacer>
<div class="spacer"></div>
@@ -6,254 +6,256 @@ import { catchError, debounceTime, scan, take, takeUntil, tap } from 'rxjs/opera
import { ListMyUserChangesResponse } from 'src/app/proto/generated/zitadel/auth_pb';
import { Change } from 'src/app/proto/generated/zitadel/change_pb';
import {
ListAppChangesResponse,
ListOrgChangesResponse,
ListProjectChangesResponse,
ListUserChangesResponse,
ListAppChangesResponse,
ListOrgChangesResponse,
ListProjectChangesResponse,
ListUserChangesResponse,
} from 'src/app/proto/generated/zitadel/management_pb';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service';
export enum ChangeType {
MYUSER = 'myuser',
USER = 'user',
ORG = 'org',
PROJECT = 'project',
APP = 'app',
MYUSER = 'myuser',
USER = 'user',
ORG = 'org',
PROJECT = 'project',
APP = 'app',
}
export interface MappedChange {
key: string;
values: Array<{
data: any[];
dates: Timestamp.AsObject[];
editorId: string;
editorName: string;
eventTypes: Array<{ key: string; localizedMessage: string; }>;
sequences: number[];
}>;
key: string;
values: Array<{
data: any[];
dates: Timestamp.AsObject[];
editorId: string;
editorName: string;
eventTypes: Array<{ key: string; localizedMessage: string; }>;
sequences: number[];
}>;
}
type ListChanges = ListMyUserChangesResponse.AsObject |
ListUserChangesResponse.AsObject |
ListProjectChangesResponse.AsObject |
ListOrgChangesResponse.AsObject |
ListAppChangesResponse.AsObject;
ListUserChangesResponse.AsObject |
ListProjectChangesResponse.AsObject |
ListOrgChangesResponse.AsObject |
ListAppChangesResponse.AsObject;
@Component({
selector: 'app-changes',
templateUrl: './changes.component.html',
styleUrls: ['./changes.component.scss'],
selector: 'app-changes',
templateUrl: './changes.component.html',
styleUrls: ['./changes.component.scss'],
})
export class ChangesComponent implements OnInit, OnDestroy {
@Input() public changeType: ChangeType = ChangeType.USER;
@Input() public id: string = '';
@Input() public secId: string = '';
@Input() public sortDirectionAsc: boolean = true;
@Input() public refresh!: Observable<void>;
public bottom: boolean = false;
@Input() public changeType: ChangeType = ChangeType.USER;
@Input() public id: string = '';
@Input() public secId: string = '';
@Input() public sortDirectionAsc: boolean = true;
@Input() public refresh!: Observable<void>;
public bottom: boolean = false;
private _done: BehaviorSubject<any> = new BehaviorSubject(false);
private _loading: BehaviorSubject<any> = new BehaviorSubject(false);
private _data: BehaviorSubject<any> = new BehaviorSubject([]);
private _done: BehaviorSubject<any> = new BehaviorSubject(false);
private _loading: BehaviorSubject<any> = new BehaviorSubject(false);
private _data: BehaviorSubject<any> = new BehaviorSubject([]);
loading: Observable<boolean> = this._loading.asObservable();
public data!: Observable<MappedChange[]>;
public changes!: ListChanges;
private destroyed$: Subject<void> = new Subject();
constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) {
loading: Observable<boolean> = this._loading.asObservable();
public data!: Observable<MappedChange[]>;
public changes!: ListChanges;
private destroyed$: Subject<void> = new Subject();
constructor(private mgmtUserService: ManagementService, private authUserService: GrpcAuthService) {
}
}
ngOnInit(): void {
ngOnInit(): void {
this.init();
if (this.refresh) {
this.refresh.pipe(takeUntil(this.destroyed$), debounceTime(2000)).subscribe(() => {
this.init();
if (this.refresh) {
this.refresh.pipe(takeUntil(this.destroyed$), debounceTime(2000)).subscribe(() => {
this.init();
});
}
});
}
}
ngOnDestroy(): void {
this.destroyed$.next();
}
public scrollHandler(e: any): void {
if (e === 'bottom') {
this.more();
}
}
public init(): void {
let first: Promise<ListChanges>;
switch (this.changeType) {
case ChangeType.MYUSER: first = this.authUserService.listMyUserChanges(20, 0);
break;
case ChangeType.USER: first = this.mgmtUserService.listUserChanges(this.id, 20, 0);
break;
case ChangeType.PROJECT: first = this.mgmtUserService.listProjectChanges(this.id, 20, 0);
break;
case ChangeType.ORG: first = this.mgmtUserService.listOrgChanges(20, 0);
break;
case ChangeType.APP: first = this.mgmtUserService.listAppChanges(this.id, this.secId, 20, 0);
break;
}
ngOnDestroy(): void {
this.destroyed$.next();
this.mapAndUpdate(first);
// Create the observable array for consumption in components
this.data = this._data.asObservable().pipe(
scan((acc, val) => {
return false ? val.concat(acc) : acc.concat(val);
}));
}
private more(): void {
const cursor = this.getCursor();
let more: Promise<ListChanges>;
switch (this.changeType) {
case ChangeType.MYUSER: more = this.authUserService.listMyUserChanges(20, cursor);
break;
case ChangeType.USER: more = this.mgmtUserService.listUserChanges(this.id, 20, cursor);
break;
case ChangeType.PROJECT: more = this.mgmtUserService.listProjectChanges(this.id, 20, cursor);
break;
case ChangeType.ORG: more = this.mgmtUserService.listOrgChanges(20, cursor);
break;
case ChangeType.APP: more = this.mgmtUserService.listAppChanges(this.id, this.secId, 20, cursor);
break;
}
public scrollHandler(e: any): void {
if (e === 'bottom') {
this.more();
}
this.mapAndUpdate(more);
}
// Determines the snapshot to paginate query
private getCursor(): number {
const current = this._data.value;
if (current.length) {
const lastElementValues = current[current.length - 1].values;
const seq = lastElementValues[lastElementValues.length - 1].sequences;
return seq[seq.length - 1];
}
return 0;
}
public init(): void {
let first: Promise<ListChanges>;
switch (this.changeType) {
case ChangeType.MYUSER: first = this.authUserService.listMyUserChanges(20, 0);
break;
case ChangeType.USER: first = this.mgmtUserService.listUserChanges(this.id, 20, 0);
break;
case ChangeType.PROJECT: first = this.mgmtUserService.listProjectChanges(this.id, 20, 0);
break;
case ChangeType.ORG: first = this.mgmtUserService.listOrgChanges(20, 0);
break;
case ChangeType.APP: first = this.mgmtUserService.listAppChanges(this.id, this.secId, 20, 0);
break;
}
// Maps the snapshot to usable format the updates source
private mapAndUpdate(col: Promise<ListChanges>): any {
if (this._done.value || this._loading.value) { return; }
this.mapAndUpdate(first);
// Map snapshot with doc ref (needed for cursor)
if (!this.bottom) {
// loading
this._loading.next(true);
// Create the observable array for consumption in components
this.data = this._data.asObservable().pipe(
scan((acc, val) => {
return false ? val.concat(acc) : acc.concat(val);
}));
return from(col).pipe(
take(1),
tap((res: ListChanges) => {
const values = res.resultList;
const mapped = this.mapChanges(values);
// update source with new values, done loading
// this._data.next(values);
this._data.next(mapped);
this._loading.next(false);
// no more values, mark done
if (!values.length) {
this._done.next(true);
}
}),
catchError(_ => {
this._loading.next(false);
this.bottom = true;
return of([]);
}),
).subscribe();
}
}
private more(): void {
const cursor = this.getCursor();
private mapChanges(changes: Change.AsObject[]): {
key: string; values: any[];
}[] {
const splitted: { [editorId: string]: any[]; } = {};
changes.forEach((change) => {
if (change.changeDate) {
const index = `${this.getDateString(change.changeDate)}`;
// `${this.getDateString(change.changeDate)}:${change.editorId}`;
let more: Promise<ListChanges>;
if (index) {
if (splitted[index]) {
const userData: any = {
editor: change.editorDisplayName,
editorId: change.editorId,
editorDisplayName: change.editorDisplayName,
switch (this.changeType) {
case ChangeType.MYUSER: more = this.authUserService.listMyUserChanges(20, cursor);
break;
case ChangeType.USER: more = this.mgmtUserService.listUserChanges(this.id, 20, cursor);
break;
case ChangeType.PROJECT: more = this.mgmtUserService.listProjectChanges(this.id, 20, cursor);
break;
case ChangeType.ORG: more = this.mgmtUserService.listOrgChanges(20, cursor);
break;
case ChangeType.APP: more = this.mgmtUserService.listAppChanges(this.id, this.secId, 20, cursor);
break;
}
this.mapAndUpdate(more);
}
// Determines the snapshot to paginate query
private getCursor(): number {
const current = this._data.value;
if (current.length) {
const lastElementValues = current[current.length - 1].values;
const seq = lastElementValues[lastElementValues.length - 1].sequences;
return seq[seq.length - 1];
}
return 0;
}
// Maps the snapshot to usable format the updates source
private mapAndUpdate(col: Promise<ListChanges>): any {
if (this._done.value || this._loading.value) { return; }
// Map snapshot with doc ref (needed for cursor)
if (!this.bottom) {
// loading
this._loading.next(true);
return from(col).pipe(
take(1),
tap((res: ListChanges) => {
const values = res.resultList;
const mapped = this.mapChanges(values);
// update source with new values, done loading
// this._data.next(values);
this._data.next(mapped);
this._loading.next(false);
// no more values, mark done
if (!values.length) {
this._done.next(true);
}
}),
catchError(_ => {
this._loading.next(false);
this.bottom = true;
return of([]);
}),
).subscribe();
}
}
private mapChanges(changes: Change.AsObject[]): {
key: string; values: any[];
}[] {
const splitted: { [editorId: string]: any[]; } = {};
changes.forEach((change) => {
if (change.changeDate) {
const index = `${this.getDateString(change.changeDate)}`;
// `${this.getDateString(change.changeDate)}:${change.editorId}`;
if (index) {
if (splitted[index]) {
const userData: any = {
editor: change.editorDisplayName,
editorId: change.editorId,
editorDisplayName: change.editorDisplayName,
dates: [change.changeDate],
// data: [change.data],
eventTypes: [change.eventType],
sequences: [change.sequence],
};
const lastIndex = splitted[index].length - 1;
if (lastIndex > -1 && splitted[index][lastIndex].editor === change.editorDisplayName) {
splitted[index][lastIndex].dates.push(change.changeDate);
// splitted[index][lastIndex].data.push(change.data);
splitted[index][lastIndex].eventTypes.push(change.eventType);
splitted[index][lastIndex].sequences.push(change.sequence);
} else {
splitted[index].push(userData);
}
} else {
splitted[index] = [
{
editor: change.editorDisplayName,
editorId: change.editorId,
editorDisplayName: change.editorDisplayName,
dates: [change.changeDate],
// data: [change.data],
eventTypes: [change.eventType],
sequences: [change.sequence],
},
];
}
}
dates: [change.changeDate],
// data: [change.data],
eventTypes: [change.eventType],
sequences: [change.sequence],
};
const lastIndex = splitted[index].length - 1;
if (lastIndex > -1 && splitted[index][lastIndex].editor === change.editorDisplayName) {
splitted[index][lastIndex].dates.push(change.changeDate);
// splitted[index][lastIndex].data.push(change.data);
splitted[index][lastIndex].eventTypes.push(change.eventType);
splitted[index][lastIndex].sequences.push(change.sequence);
} else {
splitted[index].push(userData);
}
});
const arr = Object.keys(splitted).map(key => {
return { key: key, values: splitted[key] };
});
} else {
splitted[index] = [
{
editor: change.editorDisplayName,
editorId: change.editorId,
editorDisplayName: change.editorDisplayName,
arr.sort((a, b) => {
return parseFloat(b.key) - parseFloat(a.key);
});
dates: [change.changeDate],
// data: [change.data],
eventTypes: [change.eventType],
sequences: [change.sequence],
},
];
}
}
}
});
const arr = Object.keys(splitted).map(key => {
return { key: key, values: splitted[key] };
});
return arr;
}
arr.sort((a, b) => {
return parseFloat(b.key) - parseFloat(a.key);
});
getDateString(ts: Timestamp.AsObject): string {
const date = new Date(ts.seconds * 1000 + ts.nanos / 1000 / 1000);
return date.getUTCFullYear() + this.pad(date.getUTCMonth() + 1) + this.pad(date.getUTCDate());
}
return arr;
}
getTimestampIndex(date: any): number {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000 / 1000);
return ts.getTime();
}
getDateString(ts: Timestamp.AsObject): string {
const date = new Date(ts.seconds * 1000 + ts.nanos / 1000 / 1000);
return date.getUTCFullYear() + this.pad(date.getUTCMonth() + 1) + this.pad(date.getUTCDate());
}
pad(n: number): string {
return n < 10 ? '0' + n : n.toString();
}
getTimestampIndex(date: any): number {
const ts: Date = new Date(date.seconds * 1000 + date.nanos / 1000 / 1000);
return ts.getTime();
}
// Order by ascending property value
valueAscOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
return a.value.localeCompare(b.value);
}
pad(n: number): string {
return n < 10 ? '0' + n : n.toString();
}
// Order by descending property key
keyDescOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0);
}
// Order by ascending property value
// tslint:disable
valueAscOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
return a.value.localeCompare(b.value);
};
// Order by descending property key
keyDescOrder = (a: KeyValue<number, string>, b: KeyValue<number, string>): number => {
return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0);
};
// tslint:enable
}
@@ -14,7 +14,7 @@
[ngStyle]="{'z-index': 100 - i}">
<app-avatar
*ngIf="member && member.displayName && member.firstName && member.lastName; else cog"
class="avatar dontcloseonclick"
class="avatar dontcloseonclick" [forColor]="member?.userName"[forColor]="member?.preferredLoginName"
[name]="member.displayName ? member.displayName : (member.firstName + ' '+ member.lastName)"
[size]="32">
</app-avatar>
@@ -22,7 +22,7 @@
<mat-checkbox [disabled]="!canWrite" color="primary" (click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
<app-avatar *ngIf="row?.displayName && row.firstName && row.lastName; else cog" class="avatar"
[name]="row.displayName" [size]="32">
[name]="row.displayName" [forColor]="row?.preferredLoginName" [size]="32">
</app-avatar>
<ng-template #cog>
<div class="sa-icon">
@@ -24,7 +24,7 @@
<div class="content" *ngIf="loginData">
<div class="row">
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.username_login'] | hasFeature | async) == false" ngDefaultControl
[(ngModel)]="loginData.allowUsernamePassword">
{{'POLICY.DATA.ALLOWUSERNAMEPASSWORD' | translate}}
</mat-slide-toggle>
@@ -62,7 +62,7 @@
</ng-template>
</div>
<div class="row">
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.idp'] | hasFeature | async) == false" ngDefaultControl
[(ngModel)]="loginData.allowExternalIdp">
{{'POLICY.DATA.ALLOWEXTERNALIDP' | translate}}
</mat-slide-toggle>
@@ -79,7 +79,7 @@
</ng-template>
</div>
<div class="row">
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.factors'] | hasFeature | async) == false" ngDefaultControl
[(ngModel)]="loginData.forceMfa">
{{'POLICY.DATA.FORCEMFA' | translate}}
</mat-slide-toggle>
@@ -96,7 +96,7 @@
</ng-template>
</div>
<div class="row">
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled" ngDefaultControl
<mat-slide-toggle class="toggle" color="primary" [disabled]="disabled || serviceType == PolicyComponentServiceType.MGMT && (['login_policy.password_reset'] | hasFeature | async) == false" ngDefaultControl
[(ngModel)]="loginData.hidePasswordReset">
{{'POLICY.DATA.HIDEPASSWORDRESET' | translate}}
</mat-slide-toggle>
@@ -98,7 +98,7 @@ export class LoginPolicyComponent implements OnDestroy {
if (resp.policy) {
this.loginData = resp.policy;
this.loading = false;
this.disabled = ((this.loginData as LoginPolicy.AsObject)?.isDefault) ?? false;
this.disabled = this.isDefault;
}
});
this.getIdps().then(resp => {
@@ -72,7 +72,6 @@
</div>
<span class="fill-space"></span>
<div class="img-wrapper" *ngIf="images['darkLogo']">
<!-- <mat-icon matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" class="dl-btn" (click)="deleteAsset(AssetType.LOGO, theme, Preview.PREVIEW)">remove_circle</mat-icon> -->
<img matTooltip="Current" class="curr" [src]="images['darkLogo']" alt="dark logo"/>
</div>
</div>
@@ -83,7 +82,6 @@
</div>
<span class="fill-space"></span>
<div class="img-wrapper" *ngIf="images['logo']">
<!-- <mat-icon matTooltip="{{'ACTIONS.DELETE' | translate}}" color="warn" class="dl-btn" (click)="deleteAsset(AssetType.LOGO, theme, Preview.PREVIEW)">remove_circle</mat-icon> -->
<img matTooltip="Current" class="curr" [src]="images['logo']" alt="logo"/>
</div>
</div>
@@ -93,10 +91,10 @@
[class.hovering]="isHoveringOverDarkLogo">
<label class="file-label">
<input #selectedFile style="display: none;" class="file-input" type="file" (change)="onDropLogo(theme, $event.target.files)">
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFile.click();" />
<button mat-stroked-button class="btn" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" mat-raised-button type="button" (click)="selectedFile.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</button>
<i class="icon las la-cloud-upload-alt"></i>
<span>{{isHoveringOverDarkLogo ? 'Release': 'Drop your Logo here'}}</span>
<span>{{isHoveringOverDarkLogo ? ('POLICY.PRIVATELABELING.RELEASE' | translate): ('POLICY.PRIVATELABELING.DROP' | translate)}}</span>
</label>
</div>
</div>
@@ -133,10 +131,10 @@
[class.hovering]="isHoveringOverDarkIcon">
<label class="file-label">
<input #selectedFileIcon style="display: none;" class="file-input" type="file" (change)="onDropIcon(theme, $event.target.files)">
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFileIcon.click();" />
<button mat-stroked-button class="btn" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" mat-raised-button type="button" (click)="selectedFileIcon.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</button>
<i class="icon las la-cloud-upload-alt"></i>
<span>{{isHoveringOverDarkIcon ? 'Release': 'Drop your Logo here'}}</span>
<span>{{isHoveringOverDarkIcon ? ('POLICY.PRIVATELABELING.RELEASE' | translate): ('POLICY.PRIVATELABELING.DROP' | translate)}}</span>
</label>
</div>
</div>
@@ -158,19 +156,19 @@
<ng-container *ngIf="theme==Theme.DARK">
<div class="colors" *ngIf="data && previewData">
<div class="color">
<cnsl-color [colorType]="ColorType.BACKGROUNDDARK" (previewChanged)="previewData.backgroundColorDark = $event" name="Background Color Dark" [color]="data.backgroundColorDark" [previewColor]="previewData.backgroundColorDark"></cnsl-color>
<cnsl-color [colorType]="ColorType.BACKGROUNDDARK" (previewChanged)="previewData.backgroundColorDark = $event" name="Background Color" [color]="data.backgroundColorDark" [previewColor]="previewData.backgroundColorDark"></cnsl-color>
</div>
<div class="color">
<cnsl-color [colorType]="ColorType.PRIMARY"(previewChanged)="previewData.primaryColorDark = $event" name="Preview Primary Color Dark" [color]="data.primaryColorDark" [previewColor]="previewData.primaryColorDark"></cnsl-color>
<cnsl-color [colorType]="ColorType.PRIMARY"(previewChanged)="previewData.primaryColorDark = $event" name="Primary Color" [color]="data.primaryColorDark" [previewColor]="previewData.primaryColorDark"></cnsl-color>
</div>
<div class="color">
<cnsl-color [colorType]="ColorType.WARN" (previewChanged)="previewData.warnColorDark = $event" name="Preview Warn Color Dark" [color]="data.warnColorDark" [previewColor]="previewData.warnColorDark"></cnsl-color>
<cnsl-color [colorType]="ColorType.WARN" (previewChanged)="previewData.warnColorDark = $event" name="Warn Color" [color]="data.warnColorDark" [previewColor]="previewData.warnColorDark"></cnsl-color>
</div>
<div class="color">
<cnsl-color [colorType]="ColorType.FONTDARK"(previewChanged)="previewData.fontColorDark = $event" name="Font Color Dark" [color]="data.fontColorDark" [previewColor]="previewData.fontColorDark"></cnsl-color>
<cnsl-color [colorType]="ColorType.FONTDARK"(previewChanged)="previewData.fontColorDark = $event" name="Font Color" [color]="data.fontColorDark" [previewColor]="previewData.fontColorDark"></cnsl-color>
</div>
</div>
</ng-container>
@@ -178,15 +176,15 @@
<ng-container *ngIf="theme==Theme.LIGHT">
<div class="colors" *ngIf="data && previewData">
<div class="color">
<cnsl-color [colorType]="ColorType.BACKGROUNDLIGHT" (previewChanged)="previewData.backgroundColor = $event" name="Background Color Light" [color]="data.backgroundColor" [previewColor]="previewData.backgroundColor"></cnsl-color>
<cnsl-color [colorType]="ColorType.BACKGROUNDLIGHT" (previewChanged)="previewData.backgroundColor = $event" name="Background Color" [color]="data.backgroundColor" [previewColor]="previewData.backgroundColor"></cnsl-color>
</div>
<div class="color">
<cnsl-color [colorType]="ColorType.PRIMARY" (previewChanged)="previewData.primaryColor = $event" name="Preview Primary Color Light" [color]="data.primaryColor" [previewColor]="previewData.primaryColor"></cnsl-color>
<cnsl-color [colorType]="ColorType.PRIMARY" (previewChanged)="previewData.primaryColor = $event" name="Primary Color" [color]="data.primaryColor" [previewColor]="previewData.primaryColor"></cnsl-color>
</div>
<div class="color">
<cnsl-color [colorType]="ColorType.WARN" name="Preview Warn Color Light" (previewChanged)="previewData.warnColor= $event" [color]="data.warnColor" [previewColor]="previewData.warnColor"></cnsl-color>
<cnsl-color [colorType]="ColorType.WARN" name="Warn Color" (previewChanged)="previewData.warnColor= $event" [color]="data.warnColor" [previewColor]="previewData.warnColor"></cnsl-color>
</div>
<div class="color">
@@ -214,7 +212,7 @@
<span>ABC • abc • 123</span>
<span class="fill-space"></span>
<button matTooltip="{{'ACTIONS.REMOVE' | translate}}" mat-icon-button color="warn" (click)="deleteFont()"><mat-icon>remove_circle</mat-icon></button>
<button [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" matTooltip="{{'ACTIONS.REMOVE' | translate}}" mat-icon-button color="warn" (click)="deleteFont()"><mat-icon>remove_circle</mat-icon></button>
</div>
<div class="dropzone" cnslDropzone (hovered)="toggleHoverFont($event)"
@@ -222,10 +220,10 @@
[class.hovering]="isHoveringOverFont">
<label class="file-label">
<input #selectedFontFile style="display: none;" class="file-input" type="file" (change)="onDropFont($event.target.files)">
<input class="btn" mat-raised-button type="button" [value]="'POLICY.PRIVATELABELING.BTN' | translate" (click)="selectedFontFile.click();" />
<button mat-stroked-button class="btn" [disabled]="serviceType == PolicyComponentServiceType.MGMT && (['label_policy.private_label'] | hasFeature | async) == false" mat-raised-button type="button" (click)="selectedFontFile.click();">{{'POLICY.PRIVATELABELING.BTN' | translate}}</button>
<i class="icon las la-cloud-upload-alt"></i>
<span >{{isHoveringOverFont ? 'Release': 'Drop your Logo here'}}</span>
<span >{{isHoveringOverFont ? ('POLICY.PRIVATELABELING.RELEASE' | translate): ('POLICY.PRIVATELABELING.DROP' | translate)}}</span>
</label>
</div>
</div>
@@ -203,14 +203,20 @@
align-items: center;
.btn {
cursor: pointer;
border-radius: 6px;
padding: .5rem 1rem;
background-color: inherit;
border: 1px solid if($is-dark-theme, #ffffff20, #000);
color: if($is-dark-theme, white, #000);
margin-bottom: .5rem;
}
.btn:not[disabled] {
cursor: pointer;
}
i {
font-size: 2.5rem;
}
span {
color: var(--grey);
}
}
.icon {
@@ -274,6 +280,7 @@
max-width: 120px;
.dl-btn {
z-index: 2;
position: absolute;
top: -12px;
left: -12px;
@@ -3,7 +3,7 @@ import { Component, EventEmitter, Injector, OnDestroy, Type } from '@angular/cor
import { DomSanitizer } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { switchMap, take } from 'rxjs/operators';
import {
GetLabelPolicyResponse as AdminGetLabelPolicyResponse,
GetPreviewLabelPolicyResponse as AdminGetPreviewLabelPolicyResponse,
@@ -15,10 +15,13 @@ import {
GetPreviewLabelPolicyResponse as MgmtGetPreviewLabelPolicyResponse,
UpdateCustomLabelPolicyRequest,
} from 'src/app/proto/generated/zitadel/management_pb';
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { LabelPolicy } from 'src/app/proto/generated/zitadel/policy_pb';
import { AdminService } from 'src/app/services/admin.service';
import { AssetEndpoint, AssetService, AssetType } from 'src/app/services/asset.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { StorageService } from 'src/app/services/storage.service';
import { ToastService } from 'src/app/services/toast.service';
import { CnslLinks } from '../../links/links.component';
@@ -45,6 +48,8 @@ export enum ColorType {
BACKGROUNDLIGHT,
}
const ORG_STORAGE_KEY = 'organization';
@Component({
selector: 'app-private-labeling-policy',
templateUrl: './private-labeling-policy.component.html',
@@ -86,14 +91,23 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
public refreshPreview: EventEmitter<void> = new EventEmitter();
public loadingImages: boolean = false;
private org!: Org.AsObject;
constructor(
private authService: GrpcAuthService,
private route: ActivatedRoute,
private toast: ToastService,
private injector: Injector,
private assetService: AssetService,
private sanitizer: DomSanitizer,
private storageService: StorageService,
) {
const org: Org.AsObject | null = (this.storageService.getItem(ORG_STORAGE_KEY));
if (org) {
this.org = org;
}
this.sub = this.route.data.pipe(switchMap(data => {
this.serviceType = data.serviceType;
@@ -134,17 +148,17 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
if (theme === Theme.DARK) {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTDARKLOGO, formData));
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTDARKLOGO, formData, this.org.id));
case PolicyComponentServiceType.ADMIN:
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMDARKLOGO, formData));
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMDARKLOGO, formData, this.org.id));
}
}
if (theme === Theme.LIGHT) {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTLOGO, formData));
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTLOGO, formData, this.org.id));
case PolicyComponentServiceType.ADMIN:
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMLOGO, formData));
return this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMLOGO, formData, this.org.id));
}
}
@@ -158,9 +172,9 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
formData.append('file', file);
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData));
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.MGMTFONT, formData, this.org.id));
case PolicyComponentServiceType.ADMIN:
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.IAMFONT, formData));
return this.handleFontUploadPromise(this.assetService.upload(AssetEndpoint.IAMFONT, formData, this.org.id));
}
}
}
@@ -257,20 +271,20 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
if (theme === Theme.DARK) {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTDARKICON, formData));
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTDARKICON, formData, this.org.id));
break;
case PolicyComponentServiceType.ADMIN:
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMDARKICON, formData));
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMDARKICON, formData, this.org.id));
break;
}
}
if (theme === Theme.LIGHT) {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTICON, formData));
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.MGMTICON, formData, this.org.id));
break;
case PolicyComponentServiceType.ADMIN:
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMICON, formData));
this.handleUploadPromise(this.assetService.upload(AssetEndpoint.IAMICON, formData, this.org.id));
break;
}
}
@@ -291,7 +305,7 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
}
private handleUploadPromise(task: Promise<any>): Promise<any> {
return task.then(() => {
const enhTask = task.then(() => {
this.toast.showInfo('POLICY.TOAST.UPLOADSUCCESS', true);
setTimeout(() => {
this.loadingImages = true;
@@ -304,37 +318,53 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
});
}, 1000);
}).catch(error => this.toast.showError(error));
if (this.serviceType === PolicyComponentServiceType.MGMT && ((this.previewData as LabelPolicy.AsObject).isDefault)) {
return this.savePolicy().then(() => enhTask);
} else {
return enhTask;
}
}
public fetchData(): void {
this.loading = true;
this.getPreviewData().then(data => {
console.log('preview', data);
this.loadingImages = true;
this.authService.canUseFeature(['label_policy.private_label']).pipe(take(1)).subscribe((canUse) => {
this.getPreviewData().then(data => {
console.log('preview', data);
if (data.policy) {
this.previewData = data.policy;
this.loading = false;
if (data.policy) {
this.previewData = data.policy;
this.loading = false;
this.loadPreviewImages();
}
}).catch(error => {
this.toast.showError(error);
if ((canUse === true && this.serviceType === PolicyComponentServiceType.MGMT) ||
this.serviceType === PolicyComponentServiceType.ADMIN) {
this.loadingImages = true;
this.loadPreviewImages();
}
}
}).catch(error => {
this.toast.showError(error);
});
this.getData().then(data => {
console.log('data', data);
if (data.policy) {
this.data = data.policy;
this.loading = false;
if ((canUse === true && this.serviceType === PolicyComponentServiceType.MGMT) ||
this.serviceType === PolicyComponentServiceType.ADMIN) {
// this.loadingImages = true;
this.loadImages();
}
}
}).catch(error => {
this.toast.showError(error);
});
});
this.getData().then(data => {
console.log('data', data);
if (data.policy) {
this.data = data.policy;
this.loading = false;
this.loadImages();
}
}).catch(error => {
this.toast.showError(error);
});
}
private loadImages(): void {
@@ -451,7 +481,7 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
}
private loadAsset(imagekey: string, url: string): Promise<any> {
return this.assetService.load(`${url}`).then(data => {
return this.assetService.load(`${url}`, this.org.id).then(data => {
const objectURL = URL.createObjectURL(data);
this.images[imagekey] = this.sanitizer.bypassSecurityTrustUrl(objectURL);
this.refreshPreview.emit();
@@ -462,7 +492,7 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
public removePolicy(): void {
if (this.service instanceof ManagementService) {
this.service.resetPasswordComplexityPolicyToDefault().then(() => {
this.service.resetLabelPolicyToDefault().then(() => {
this.toast.showInfo('POLICY.TOAST.RESETSUCCESS', true);
setTimeout(() => {
this.fetchData();
@@ -473,14 +503,14 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
}
}
public savePolicy(): void {
public savePolicy(): Promise<any> {
switch (this.serviceType) {
case PolicyComponentServiceType.MGMT:
if ((this.previewData as LabelPolicy.AsObject).isDefault) {
const req0 = new AddCustomLabelPolicyRequest();
this.overwriteValues(req0);
(this.service as ManagementService).addCustomLabelPolicy(req0).then(() => {
return (this.service as ManagementService).addCustomLabelPolicy(req0).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
}).catch((error: HttpErrorResponse) => {
this.toast.showError(error);
@@ -489,22 +519,20 @@ export class PrivateLabelingPolicyComponent implements OnDestroy {
const req1 = new UpdateCustomLabelPolicyRequest();
this.overwriteValues(req1);
(this.service as ManagementService).updateCustomLabelPolicy(req1).then(() => {
return (this.service as ManagementService).updateCustomLabelPolicy(req1).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
}).catch(error => {
this.toast.showError(error);
});
}
break;
case PolicyComponentServiceType.ADMIN:
const req = new UpdateLabelPolicyRequest();
this.overwriteValues(req);
(this.service as AdminService).updateLabelPolicy(req).then(() => {
return (this.service as AdminService).updateLabelPolicy(req).then(() => {
this.toast.showInfo('POLICY.TOAST.SET', true);
}).catch(error => {
this.toast.showError(error);
});
break;
}
}
@@ -22,8 +22,8 @@ export const IAM_LOGIN_POLICY_LINK = {
};
export const IAM_PRIVATELABEL_LINK = {
i18nTitle: 'POLICY.LABEL.TITLE',
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
i18nTitle: 'POLICY.PRIVATELABELING.TITLE',
i18nDesc: 'POLICY.PRIVATELABELING.DESCRIPTION',
routerLink: ['/iam', 'policy', PolicyComponentType.PRIVATELABEL],
withRole: ['iam.policy.read'],
};
@@ -51,8 +51,8 @@ export const ORG_LOGIN_POLICY_LINK = {
export const ORG_PRIVATELABEL_LINK = {
i18nTitle: 'POLICY.LABEL.TITLE',
i18nDesc: 'POLICY.LABEL.DESCRIPTION',
i18nTitle: 'POLICY.PRIVATELABELING.TITLE',
i18nDesc: 'POLICY.PRIVATELABELING.DESCRIPTION',
routerLink: ['/org', 'policy', PolicyComponentType.PRIVATELABEL],
withRole: ['policy.read'],
};
@@ -57,7 +57,7 @@
<div class="circle">
<app-avatar
*ngIf="user.human && user.human.displayName && user.human?.firstName && user.human?.lastName; else cog"
class="avatar" [name]="user.human.displayName" [size]="32">
class="avatar" [name]="user.human.displayName" [forColor]="user?.preferredLoginName" [size]="32">
</app-avatar>
<ng-template #cog>
<div class="sa-icon">
@@ -31,7 +31,7 @@
(change)="$event ? selection.toggle(row) : null" [checked]="selection.isSelected(row)">
<app-avatar
*ngIf="context !== UserGrantContext.USER && row && row?.displayName && row.firstName && row.lastName"
class="avatar" [name]="row.displayName" [size]="32">
class="avatar" [name]="row.displayName" [forColor]="row?.preferredLoginName" [size]="32">
</app-avatar>
</mat-checkbox>
</td>