Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into dep/update-meta-repo
Browse files Browse the repository at this point in the history
  • Loading branch information
peb-adr committed Jul 8, 2024
2 parents 0778d2c + 7d524fd commit 2663713
Show file tree
Hide file tree
Showing 19 changed files with 350 additions and 15 deletions.
2 changes: 1 addition & 1 deletion client/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
{
"type": "initial",
"maximumWarning": "1500kb",
"maximumError": "3mb"
"maximumError": "3500kb"
},
{
"type": "anyComponentStyle",
Expand Down
2 changes: 2 additions & 0 deletions client/src/app/domain/models/poll/poll-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export interface EntitledUsersEntry {
present: boolean;
voted: boolean;
vote_delegated_to_user_id?: number;
user_merged_into_id?: number;
delegation_user_merged_into_id?: number;
}

export const VOTE_MAJORITY = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,14 @@ export class HistoryPresenterService {
.flatMap(positions => getUniqueItems(positions))
.sort((positionA, positionB) => positionB.timestamp - positionA.timestamp)
.map(position => {
const userView = this.userRepo.getViewModel(position.user_id);
return new HistoryPosition({
...position,
information: Array.isArray(position.information)
? position.information
: position?.information[fqid],
fqid,
user: this.userRepo.getViewModel(position.user_id)?.getFullName()
user: userView?.getFullName() ? userView.getFullName() : `user/${position.user_id}`
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,10 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
return this.createAction<BackendImportRawPreview | void>(UserAction.PARTICIPANT_IMPORT, payload);
}

public mergeTogether(payload: { id: number; user_ids: number[] }[]): Action<void> {
return this.createAction(UserAction.MERGE_TOGETHER, payload);
}

private sanitizePayload(payload: any): any {
const temp = { ...payload };
for (const key of Object.keys(temp).filter(field => !this.isFieldAllowedToBeEmpty(field))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ export abstract class BasePollDetailComponent<V extends PollContentObject, S ext
if (entry.vote_delegated_to_user_id) {
userIds.add(entry.vote_delegated_to_user_id);
}
if (entry.user_merged_into_id) {
userIds.add(entry.user_merged_into_id);
}
if (entry.delegation_user_merged_into_id) {
userIds.add(entry.delegation_user_merged_into_id);
}
}
this.subscriptions.push(
(this.entitledUsersSubscription = this.userRepo
Expand All @@ -248,6 +254,16 @@ export abstract class BasePollDetailComponent<V extends PollContentObject, S ext
voted_verbose: `voted:${entry.voted}`,
vote_delegated_to: entry.vote_delegated_to_user_id
? users.find(user => user.id === entry.vote_delegated_to_user_id)
: null,
user_merged_into: entry.user_merged_into_id
? `${this.translate.instant(`Old account of`)} ${users
.find(user => user.id === entry.user_merged_into_id)
?.getShortName()}`
: null,
delegation_user_merged_into: entry.delegation_user_merged_into_id
? `(${this.translate.instant(`represented by old account of`)}) ${users
.find(user => user.id === entry.delegation_user_merged_into_id)
?.getShortName()}`
: null
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -626,17 +626,26 @@ export abstract class BasePollPdfService {
for (const date of usersData.sort((entryA, entryB) =>
entryA.user?.getName().localeCompare(entryB.user?.getName())
)) {
const name = date.user_merged_into_id
? `${this.translate.instant(`Old account of`)} ` +
this.getUserNameForExport(this.userRepo.getViewModel(date.user_merged_into_id))
: this.getUserNameForExport(date.user);
let represented = ``;
if (date.vote_delegated_to_user_id && !date.delegation_user_merged_into_id) {
represented =
`\n${this.translate.instant(`represented by`)} ` +
this.getUserNameForExport(date.vote_delegated_to);
} else if (date.vote_delegated_to_user_id && date.delegation_user_merged_into_id) {
represented =
`\n${this.translate.instant(`represented by old account of`)} ` +
this.getUserNameForExport(this.userRepo.getViewModel(date.delegation_user_merged_into_id));
}
const tableLine = [
{
text: index
},
{
text:
this.getUserNameForExport(date.user) +
(date.vote_delegated_to
? `\n${this.translate.instant(`represented by`)} ` +
this.getUserNameForExport(date.vote_delegated_to)
: ``)
text: name + represented
},
{
text: this.translate.instant(date.voted ? `Yes` : `No`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,35 @@
</div>

<!-- Delegation -->
<div *ngIf="entry.vote_delegated_to_user_id">
<div *ngIf="entry.vote_delegated_to_user_id && !entry.delegation_user_merged_into_id">
<span>
({{ 'represented by' | translate }} {{ entry.vote_delegated_to.getShortName().trim() }})
({{ 'represented by' | translate }} {{ entry.vote_delegated_to.getShortName() }})
</span>
</div>
<div *ngIf="entry.vote_delegated_to_user_id && entry.delegation_user_merged_into_id">
<span>
{{ entry.delegation_user_merged_into }}
</span>
</div>
</div>
</div>
<div *ngIf="!entry.user && entry.user_merged_into_id">
{{ entry.user_merged_into }}
<div class="user-subtitle">
<!-- Delegation -->
<div *ngIf="entry.vote_delegated_to_user_id && !entry.delegation_user_merged_into_id">
<span>
({{ 'represented by' | translate }} {{ entry.vote_delegated_to?.getShortName() }})
</span>
</div>
<div *ngIf="entry.vote_delegated_to_user_id && entry.delegation_user_merged_into_id">
<span>
{{ entry.delegation_user_merged_into }}
</span>
</div>
</div>
</div>
<i *ngIf="!entry.user">{{ 'Anonymous' | translate }}</i>
<i *ngIf="!entry.user && !entry.user_merged_into_id">{{ 'Anonymous' | translate }}</i>
</div>
<div *osScrollingTableCell="'present'; row as entry; config: { width: 24 }">
<div *osScrollingTableCellLabel>{{ 'Is present' | translate }}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ export class EntitledUsersTableComponent {

public readonly permission = Permission;

public filterPropsEntitledUsersTable = [`user.full_name`, `vote_delegated_to.full_name`, `voted_verbose`];
public filterPropsEntitledUsersTable = [
`user.full_name`,
`vote_delegated_to.full_name`,
`user_merged_into`,
`delegation_user_merged_into`,
`voted_verbose`
];

public constructor(
private controller: ParticipantControllerService,
public filter: EntitledUsersListFilterService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export interface EntitledUsersTableEntry extends EntitledUsersEntry, Identifiabl
user?: ViewUser;
voted_verbose: string;
vote_delegated_to?: ViewUser | null;
user_merged_into?: string | null;
delegation_user_merged_into?: string | null;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatLegacyButtonModule as MatButtonModule } from '@angular/material/legacy-button';
import { MatLegacyMenuModule as MatMenuModule } from '@angular/material/legacy-menu';
import { MatRadioModule } from '@angular/material/radio';
import { MatTableModule } from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { OpenSlidesTranslationModule } from 'src/app/site/modules/translations';
Expand All @@ -22,10 +25,11 @@ import { AccountCommonServiceModule } from '../../services/common/account-common
import { AccountListRoutingModule } from './account-list-routing.module';
import { AccountListComponent } from './components/account-list/account-list.component';
import { AccountListMainComponent } from './components/account-list-main/account-list-main.component';
import { AccountMergeDialogComponent } from './components/account-merge-dialog/account-merge-dialog.component';
import { AccountListServiceModule } from './services/account-list-service.module';

@NgModule({
declarations: [AccountListComponent, AccountListMainComponent],
declarations: [AccountListComponent, AccountListMainComponent, AccountMergeDialogComponent],
imports: [
CommonModule,
AccountListRoutingModule,
Expand All @@ -47,7 +51,10 @@ import { AccountListServiceModule } from './services/account-list-service.module
MatFormFieldModule,
IconContainerModule,
RouterModule,
PipesModule
PipesModule,
MatDialogModule,
MatRadioModule,
MatTableModule
]
})
export class AccountListModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ <h2>
<mat-icon>block</mat-icon>
<span>{{ 'Enable/disable accounts' | translate }} ...</span>
</button>
<button mat-menu-item [disabled]="selectedRows.length < 2" (click)="mergeUsersTogether()">
<mat-icon>merge</mat-icon>
<span>{{ 'Merge users together' | translate }} ...</span>
</button>
</os-user-multiselect-actions>
</div>
</mat-menu>
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { marker as _ } from '@colsen1991/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
import { firstValueFrom } from 'rxjs';
import { Observable } from 'rxjs';
import { getOmlVerboseName } from 'src/app/domain/definitions/organization-permission';
import { OMLMapping } from 'src/app/domain/definitions/organization-permission';
import { mediumDialogSettings } from 'src/app/infrastructure/utils/dialog-settings';
import { BaseListViewComponent } from 'src/app/site/base/base-list-view.component';
import { MeetingControllerService } from 'src/app/site/pages/meetings/services/meeting-controller.service';
import { ViewMeeting } from 'src/app/site/pages/meetings/view-models/view-meeting';
Expand All @@ -19,6 +22,7 @@ import { AccountControllerService } from '../../../../services/common/account-co
import { AccountFilterService } from '../../../../services/common/account-filter.service';
import { AccountListSearchService } from '../../services/account-list-search/account-list-search.service';
import { AccountSortService } from '../../services/account-list-sort.service/account-sort.service';
import { AccountMergeDialogComponent } from '../account-merge-dialog/account-merge-dialog.component';

const ACCOUNT_LIST_STORAGE_INDEX = `account_list`;

Expand Down Expand Up @@ -47,7 +51,8 @@ export class AccountListComponent extends BaseListViewComponent<ViewUser> {
private userController: UserControllerService,
public searchService: AccountListSearchService,
private operator: OperatorService,
private vp: ViewPortService
private vp: ViewPortService,
private dialog: MatDialog
) {
super();
super.setTitle(`Accounts`);
Expand Down Expand Up @@ -126,4 +131,22 @@ export class AccountListComponent extends BaseListViewComponent<ViewUser> {
public getOmlByUser(user: ViewUser): string {
return getOmlVerboseName(user.organization_management_level as keyof OMLMapping);
}

public async mergeUsersTogether(): Promise<void> {
const result = await this.openMergeDialog();
if (result) {
const id = result;
const user_ids = this.selectedRows.map(view => view.id).filter(sRid => sRid !== id);
this.controller.mergeTogether([{ id: id, user_ids: user_ids }]).resolve();
}
}

public async openMergeDialog(): Promise<number | null> {
const data = { choices: this.selectedRows };
const dialogRef = this.dialog.open(AccountMergeDialogComponent, {
...mediumDialogSettings,
data: data
});
return firstValueFrom(dialogRef.afterClosed());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<div mat-dialog-title>{{ 'Merge users' | translate }}</div>
<mat-dialog-content>
<div>{{ 'Please select a primary account.' | translate }}</div>
<mat-radio-group (change)="onChange($event)">
<table mat-table [dataSource]="possibleChoices">
<ng-container matColumnDef="button">
<td mat-cell *matCellDef="let user">
<div class="buttonCell"><mat-radio-button [value]="user.id"></mat-radio-button></div>
</td>
</ng-container>

<ng-container matColumnDef="name">
<td mat-cell *matCellDef="let user">
<div class="nameCell overflow-hidden">
<div class="title-line title-box">
<div *ngIf="!isCrossedOut(user)">
<span class="ellipsis-overflow">{{ user.short_name }}</span>
<span class="meta-info user-subtitle" *ngIf="!!user.gender">
&middot;&nbsp;{{ user.gender }}
</span>
<span class="meta-info user-subtitle" *ngIf="!!user.pronoun">
&middot;&nbsp;{{ user.pronoun }}
</span>
</div>
<div *ngIf="isCrossedOut(user)">
<span class="ellipsis-overflow crossed-out">{{ user.short_name }}</span>
<span class="meta-info user-subtitle" *ngIf="!!user.gender">
&middot;&nbsp;{{ user.gender }}
</span>
<span class="meta-info user-subtitle" *ngIf="!!user.pronoun">
&middot;&nbsp;{{ user.pronoun }}
</span>
</div>
<div style="float: right; display: inline-flex">
&nbsp;
<os-icon-container
icon="account_balance"
*ngIf="!user.is_physical_person"
matTooltip="{{ 'Is no natural person' | translate }}"
></os-icon-container>
<os-icon-container
icon="block"
*ngIf="!user.is_active"
matTooltip="{{ 'Inactive' | translate }}"
></os-icon-container>
<os-icon-container
icon="fingerprint"
*ngIf="user.hasSamlId"
matTooltip="{{ 'Has SSO identification' | translate }}"
></os-icon-container>
</div>
</div>
<div class="meta-info user-subtitle ellipsis-overflow">
{{ user.saml_id || user.username }}
<ng-container *ngIf="user.email">&middot; {{ user.email }}&nbsp;</ng-container>
<ng-container *ngIf="user.member_number">&middot; {{ user.member_number }}</ng-container>
</div>
<div class="meta-info user-subtitle ellipsis-overflow" *ngIf="user?.isLastLogin">
{{ 'Last login' | translate }} {{ user.last_login | localizedDate }}
</div>
</div>
</td>
</ng-container>

<ng-container matColumnDef="icon">
<td mat-cell *matCellDef="let user">
<div class="iconCell">
<os-icon-container
icon="event_available"
[iconTooltip]="userMeetings(user)"
iconTooltipPosition="left"
iconTooltipClass="pre-wrap"
[showIcon]="true"
[noWrap]="true"
>
{{ user.meetings.length }}
</os-icon-container>
<os-icon-container
icon="mail"
*ngIf="user.isLastEmailSent"
iconTooltip="{{ 'Email sent' | translate }} ({{ user.last_email_sent | localizedDate }})"
iconTooltipPosition="left"
></os-icon-container>
</div>
</td>
</ng-container>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</mat-radio-group>
<div class="error-color">{{ 'Attention: Not selected accounts will be merged and then deleted.' | translate }}</div>
<div class="error-color" *ngIf="showMeetingsCollide">
{{ 'Warning: Data loss is possible, because two users are in the same meeting.' | translate }}
{{ 'Meetings effected:' | translate }}
{{ countMeetingsCollide }}
</div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="closeDialog(true)" [disabled]="!selectedUserId">
<span>{{ 'Merge' | translate }}</span>
</button>
<button mat-button (click)="closeDialog(false)">
<span>{{ 'Cancel' | translate }}</span>
</button>
</mat-dialog-actions>
Loading

0 comments on commit 2663713

Please sign in to comment.