Show "Deleted user" if a poll user cannot be found

Fixes an error that would freeze OpenSlides
If a user was deleted but registered as a poll option,
The Assignment Detail View would freeze
This commit is contained in:
Sean 2020-11-27 16:50:38 +01:00
parent 96f96f09ee
commit 5e1b5b5658
10 changed files with 36 additions and 16 deletions

View File

@ -14,7 +14,7 @@
<div *ngFor="let option of options" class="votes-grid"> <div *ngFor="let option of options" class="votes-grid">
<div> <div>
<span *ngIf="option.user">{{ option.user.getFullName() }}</span> <span *ngIf="option.user">{{ option.user.getFullName() }}</span>
<span *ngIf="!option.user">{{ 'Unknown user' | translate }}</span> <i *ngIf="!option.user">{{ unknownUserLabel | translate }}</i>
</div> </div>
<div> <div>

View File

@ -19,7 +19,7 @@ import { BasePollDialogComponent } from 'app/site/polls/components/base-poll-dia
import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component'; import { PollFormComponent } from 'app/site/polls/components/poll-form/poll-form.component';
import { PollPropertyVerbose } from 'app/site/polls/models/view-base-poll'; import { PollPropertyVerbose } from 'app/site/polls/models/view-base-poll';
import { ViewUser } from 'app/site/users/models/view-user'; import { ViewUser } from 'app/site/users/models/view-user';
import { AssignmentPollService } from '../../services/assignment-poll.service'; import { AssignmentPollService, UnknownUserLabel } from '../../services/assignment-poll.service';
type OptionsObject = { user_id: number; user: ViewUser }[]; type OptionsObject = { user_id: number; user: ViewUser }[];
@ -34,6 +34,7 @@ type OptionsObject = { user_id: number; user: ViewUser }[];
export class AssignmentPollDialogComponent export class AssignmentPollDialogComponent
extends BasePollDialogComponent<ViewAssignmentPoll, AssignmentPollService> extends BasePollDialogComponent<ViewAssignmentPoll, AssignmentPollService>
implements OnInit { implements OnInit {
public unknownUserLabel = UnknownUserLabel;
/** /**
* The summary values that will have fields in the dialog * The summary values that will have fields in the dialog
*/ */

View File

@ -51,7 +51,7 @@
{{ option.user.getLevelAndNumber() }} {{ option.user.getLevelAndNumber() }}
</div> </div>
</span> </span>
<span *ngIf="!option.user">{{ 'Unknown user' | translate }}</span> <i *ngIf="!option.user">{{ unknownUserLabel | translate }}</i>
</div> </div>
<div *ngFor="let action of voteActions"> <div *ngFor="let action of voteActions">

View File

@ -43,7 +43,8 @@
.vote-candidate-name { .vote-candidate-name {
grid-area: name; grid-area: name;
display: flex; display: flex;
span { span,
i {
margin-top: auto; margin-top: auto;
margin-bottom: auto; margin-bottom: auto;
} }

View File

@ -18,6 +18,7 @@ import { ViewAssignmentOption } from 'app/site/assignments/models/view-assignmen
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll'; import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
import { BasePollVoteComponentDirective, VoteOption } from 'app/site/polls/components/base-poll-vote.component'; import { BasePollVoteComponentDirective, VoteOption } from 'app/site/polls/components/base-poll-vote.component';
import { ViewUser } from 'app/site/users/models/view-user'; import { ViewUser } from 'app/site/users/models/view-user';
import { UnknownUserLabel } from '../../services/assignment-poll.service';
const voteOptions = { const voteOptions = {
Yes: { Yes: {
@ -47,6 +48,7 @@ const voteOptions = {
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class AssignmentPollVoteComponent extends BasePollVoteComponentDirective<ViewAssignmentPoll> implements OnInit { export class AssignmentPollVoteComponent extends BasePollVoteComponentDirective<ViewAssignmentPoll> implements OnInit {
public unknownUserLabel = UnknownUserLabel;
public AssignmentPollMethod = AssignmentPollMethod; public AssignmentPollMethod = AssignmentPollMethod;
public PollType = PollType; public PollType = PollType;
public voteActions: VoteOption[] = []; public voteActions: VoteOption[] = [];

View File

@ -72,15 +72,18 @@ export class AssignmentPollComponent
this.descriptionForm = this.formBuilder.group({ this.descriptionForm = this.formBuilder.group({
description: this.poll ? this.poll.description : '' description: this.poll ? this.poll.description : ''
}); });
console.log('the poll: ', this.poll);
} }
/** /**
* Print the PDF of this poll with the corresponding options and numbers * Print the PDF of this poll with the corresponding options and numbers
*/ */
public printBallot(): void { public printBallot(): void {
this.pdfService.printBallots(this.poll); try {
this.pdfService.printBallots(this.poll);
} catch (e) {
console.error(e);
this.raiseError(e);
}
} }
public openVotingWarning(): void { public openVotingWarning(): void {

View File

@ -141,9 +141,14 @@ export class AssignmentPollPdfService extends PollPdfService {
return a.weight - b.weight; return a.weight - b.weight;
}); });
const resultObject = candidates.map(cand => { const resultObject = candidates.map(cand => {
return poll.pollmethod === AssignmentPollMethod.Y const candidateName = cand.user?.full_name;
? this.createBallotOption(cand.user.full_name) if (candidateName) {
: this.createYNBallotEntry(cand.user.full_name, poll.pollmethod); return poll.pollmethod === AssignmentPollMethod.Y
? this.createBallotOption(candidateName)
: this.createYNBallotEntry(candidateName, poll.pollmethod);
} else {
throw new Error(this.translate.instant('This ballot contains deleted users.'));
}
}); });
if (poll.pollmethod === AssignmentPollMethod.Y) { if (poll.pollmethod === AssignmentPollMethod.Y) {

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ConstantsService } from 'app/core/core-services/constants.service'; import { ConstantsService } from 'app/core/core-services/constants.service';
@ -23,6 +24,7 @@ import {
VotingResult VotingResult
} from 'app/site/polls/services/poll.service'; } from 'app/site/polls/services/poll.service';
export const UnknownUserLabel = _('Deleted user');
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
@ -159,11 +161,13 @@ export class AssignmentPollService extends PollService {
}; };
// Since pollData does not have any subtitle option // Since pollData does not have any subtitle option
if (candidate instanceof ViewAssignmentOption) { if (candidate instanceof ViewAssignmentOption && candidate.user) {
pollTableEntry.votingOption = candidate.user.short_name; pollTableEntry.votingOption = candidate.user.short_name;
pollTableEntry.votingOptionSubtitle = candidate.user.getLevelAndNumber(); pollTableEntry.votingOptionSubtitle = candidate.user.getLevelAndNumber();
} else { } else if (candidate.user) {
pollTableEntry.votingOption = (candidate as PollDataOption).user.short_name; pollTableEntry.votingOption = (candidate as PollDataOption).user.short_name;
} else {
pollTableEntry.votingOption = UnknownUserLabel;
} }
return pollTableEntry; return pollTableEntry;
@ -248,8 +252,8 @@ export class AssignmentPollService extends PollService {
} }
return resultLabel; return resultLabel;
}); });
const optionName = option.user?.short_name ?? UnknownUserLabel;
return `${option.user.short_name} · ${votingResults.join(' · ')}`; return `${optionName} · ${votingResults.join(' · ')}`;
}); });
} }
} }

View File

@ -10,12 +10,12 @@
<ng-container *ngIf="data.data.assignment_related_users && data.data.assignment_related_users.length"> <ng-container *ngIf="data.data.assignment_related_users && data.data.assignment_related_users.length">
<ol *ngIf="data.data.number_poll_candidates"> <ol *ngIf="data.data.number_poll_candidates">
<li *ngFor="let candidate of data.data.assignment_related_users"> <li *ngFor="let candidate of data.data.assignment_related_users">
{{ candidate.user }} {{ getUserName(candidate) }}
</li> </li>
</ol> </ol>
<ul *ngIf="!data.data.number_poll_candidates"> <ul *ngIf="!data.data.number_poll_candidates">
<li *ngFor="let candidate of data.data.assignment_related_users"> <li *ngFor="let candidate of data.data.assignment_related_users">
{{ candidate.user }} {{ getUserName(candidate) }}
</li> </li>
</ul> </ul>
</ng-container> </ng-container>

View File

@ -26,4 +26,8 @@ export class AssignmentSlideComponent extends BaseSlideComponentDirective<Assign
public constructor() { public constructor() {
super(); super();
} }
public getUserName(relatedUser: { user: string }): string {
return relatedUser.user;
}
} }