Merge pull request #4925 from FinnStutzenstein/errorhandling
Added missing error handling
This commit is contained in:
commit
bbe294a1ad
@ -422,7 +422,7 @@ export class MotionRepositoryService extends BaseIsAgendaItemAndListOfSpeakersCo
|
||||
}
|
||||
]
|
||||
};
|
||||
this.httpService.post('/rest/motions/motion/manage_multiple_submitters/', requestData);
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_submitters/', requestData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +58,7 @@ export class PersonalNoteService {
|
||||
}
|
||||
|
||||
pnObject.notes[model.collectionString][model.id] = content;
|
||||
this.savePersonalNoteObject(pnObject);
|
||||
await this.savePersonalNoteObject(pnObject);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,7 +200,7 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
|
||||
} else {
|
||||
result.duration = 0;
|
||||
}
|
||||
this.repo.update(result, item);
|
||||
this.repo.update(result, item).catch(this.raiseError);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -262,12 +262,16 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
|
||||
const title = this.translate.instant('Are you sure you want to remove all selected items from the agenda?');
|
||||
const content = this.translate.instant("All topics will be deleted and won't be accessible afterwards.");
|
||||
if (await this.promptService.open(title, content)) {
|
||||
for (const item of this.selectedRows) {
|
||||
if (item.contentObject instanceof ViewTopic) {
|
||||
await this.topicRepo.delete(item.contentObject);
|
||||
} else {
|
||||
await this.repo.removeFromAgenda(item);
|
||||
try {
|
||||
for (const item of this.selectedRows) {
|
||||
if (item.contentObject instanceof ViewTopic) {
|
||||
await this.topicRepo.delete(item.contentObject);
|
||||
} else {
|
||||
await this.repo.removeFromAgenda(item);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,8 +283,12 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
|
||||
* @param closed true if the item is to be considered done
|
||||
*/
|
||||
public async setClosedSelected(closed: boolean): Promise<void> {
|
||||
for (const item of this.selectedRows) {
|
||||
await this.repo.update({ closed: closed }, item);
|
||||
try {
|
||||
for (const item of this.selectedRows) {
|
||||
await this.repo.update({ closed: closed }, item);
|
||||
}
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,8 +299,12 @@ export class AgendaListComponent extends BaseListViewComponent<ViewItem> impleme
|
||||
* @param visible true if the item is to be shown
|
||||
*/
|
||||
public async setAgendaType(agendaType: number): Promise<void> {
|
||||
for (const item of this.selectedRows) {
|
||||
await this.repo.update({ type: agendaType }, item).then(null, this.raiseError);
|
||||
try {
|
||||
for (const item of this.selectedRows) {
|
||||
await this.repo.update({ type: agendaType }, item).then(null, this.raiseError);
|
||||
}
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,10 +261,18 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
|
||||
* Sets the data source to the requested element id.
|
||||
*/
|
||||
private async queryByElementId(elementId: string): Promise<void> {
|
||||
const historyData = await this.http.get<History[]>(`${environment.urlPrefix}/core/history/information/`, null, {
|
||||
type: 'element',
|
||||
value: elementId
|
||||
});
|
||||
this.dataSource.data = historyData.map(data => new History(data));
|
||||
try {
|
||||
const historyData = await this.http.get<History[]>(
|
||||
`${environment.urlPrefix}/core/history/information/`,
|
||||
null,
|
||||
{
|
||||
type: 'element',
|
||||
value: elementId
|
||||
}
|
||||
);
|
||||
this.dataSource.data = historyData.map(data => new History(data));
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1304,10 +1304,9 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
*/
|
||||
public setCategory(id: number): void {
|
||||
if (id === this.motion.category_id) {
|
||||
this.repo.setCatetory(this.motion, null);
|
||||
} else {
|
||||
this.repo.setCatetory(this.motion, id);
|
||||
id = null;
|
||||
}
|
||||
this.repo.setCatetory(this.motion, id).catch(this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1317,7 +1316,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
*/
|
||||
public setTag(event: MouseEvent, id: number): void {
|
||||
event.stopPropagation();
|
||||
this.repo.setTag(this.motion, id);
|
||||
this.repo.setTag(this.motion, id).catch(this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1327,10 +1326,9 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
*/
|
||||
public setBlock(id: number): void {
|
||||
if (id === this.motion.motion_block_id) {
|
||||
this.repo.setBlock(this.motion, null);
|
||||
} else {
|
||||
this.repo.setBlock(this.motion, id);
|
||||
id = null;
|
||||
}
|
||||
this.repo.setBlock(this.motion, id).catch(this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1373,8 +1371,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
|
||||
/**
|
||||
* Handler for creating a poll
|
||||
*/
|
||||
public async createPoll(): Promise<void> {
|
||||
await this.repo.createPoll(this.motion);
|
||||
public createPoll(): void {
|
||||
this.repo.createPoll(this.motion).catch(this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@ -9,6 +11,7 @@ import { CalculablePollKey } from 'app/core/ui-services/poll.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { MotionPoll } from 'app/shared/models/motions/motion-poll';
|
||||
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
|
||||
import { MotionPollPdfService } from 'app/site/motions/services/motion-poll-pdf.service';
|
||||
import { MotionPollService } from 'app/site/motions/services/motion-poll.service';
|
||||
@ -22,7 +25,7 @@ import { MotionPollDialogComponent } from './motion-poll-dialog.component';
|
||||
templateUrl: './motion-poll.component.html',
|
||||
styleUrls: ['./motion-poll.component.scss']
|
||||
})
|
||||
export class MotionPollComponent implements OnInit {
|
||||
export class MotionPollComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* A representation of all values of the current poll.
|
||||
*/
|
||||
@ -77,24 +80,29 @@ export class MotionPollComponent implements OnInit {
|
||||
/**
|
||||
* Constructor. Subscribes to the constants and settings for motion polls
|
||||
*
|
||||
* @param title
|
||||
* @param translate TranslateService
|
||||
* @param matSnackbar
|
||||
* @param dialog Dialog Service for entering poll data
|
||||
* @param pollService MotionPollService
|
||||
* @param motionRepo Subscribing to the motion to update poll from the server
|
||||
* @param constants ConstantsService
|
||||
* @param config ConfigService
|
||||
* @param translate TranslateService
|
||||
* @param perms LocalPermissionService
|
||||
*/
|
||||
public constructor(
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
public dialog: MatDialog,
|
||||
public pollService: MotionPollService,
|
||||
private motionRepo: MotionRepositoryService,
|
||||
private constants: ConstantsService,
|
||||
private translate: TranslateService,
|
||||
private promptService: PromptService,
|
||||
public perms: LocalPermissionsService,
|
||||
private pdfService: MotionPollPdfService
|
||||
) {
|
||||
super(title, translate, matSnackBar);
|
||||
this.pollValues = this.pollService.pollValues;
|
||||
this.majorityChoice = this.pollService.defaultMajorityMethod;
|
||||
this.subscribeMajorityChoices();
|
||||
@ -121,7 +129,7 @@ export class MotionPollComponent implements OnInit {
|
||||
public async deletePoll(): Promise<void> {
|
||||
const title = this.translate.instant('Are you sure you want to delete this vote?');
|
||||
if (await this.promptService.open(title)) {
|
||||
this.motionRepo.deletePoll(this.poll);
|
||||
this.motionRepo.deletePoll(this.poll).catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,8 +197,7 @@ export class MotionPollComponent implements OnInit {
|
||||
});
|
||||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result) {
|
||||
this.motionRepo.updatePoll(result);
|
||||
// TODO error handling
|
||||
this.motionRepo.updatePoll(result).catch(this.raiseError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material';
|
||||
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { BaseComponent } from 'app/base.component';
|
||||
import { PersonalNoteService } from 'app/core/ui-services/personal-note.service';
|
||||
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { ViewMotion } from 'app/site/motions/models/view-motion';
|
||||
import { MotionPdfExportService } from 'app/site/motions/services/motion-pdf-export.service';
|
||||
|
||||
@ -18,7 +19,7 @@ import { MotionPdfExportService } from 'app/site/motions/services/motion-pdf-exp
|
||||
templateUrl: './personal-note.component.html',
|
||||
styleUrls: ['./personal-note.component.scss']
|
||||
})
|
||||
export class PersonalNoteComponent extends BaseComponent {
|
||||
export class PersonalNoteComponent extends BaseViewComponent {
|
||||
/**
|
||||
* The motion, which the personal note belong to.
|
||||
*/
|
||||
@ -50,12 +51,13 @@ export class PersonalNoteComponent extends BaseComponent {
|
||||
public constructor(
|
||||
title: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private personalNoteService: PersonalNoteService,
|
||||
formBuilder: FormBuilder,
|
||||
private pdfService: MotionPdfExportService,
|
||||
private sanitizer: DomSanitizer
|
||||
) {
|
||||
super(title, translate);
|
||||
super(title, translate, matSnackBar);
|
||||
this.personalNoteForm = formBuilder.group({
|
||||
note: ['']
|
||||
});
|
||||
@ -90,7 +92,7 @@ export class PersonalNoteComponent extends BaseComponent {
|
||||
await this.personalNoteService.savePersonalNote(this.motion.motion, content);
|
||||
this.isEditMode = false;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@
|
||||
mat-menu-item
|
||||
class="red-warning-text"
|
||||
[disabled]="!selectedRows.length"
|
||||
(click)="multiselectService.delete(selectedRows); toggleMultiSelect()"
|
||||
(click)="multiselectWrapper(multiselectService.delete(selectedRows)); toggleMultiSelect()"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Delete</span>
|
||||
|
@ -15,6 +15,7 @@ import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflo
|
||||
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
|
||||
import { OsFilterOptionCondition } from 'app/core/ui-services/base-filter-list.service';
|
||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||
import { SpinnerService } from 'app/core/ui-services/spinner.service';
|
||||
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';
|
||||
import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
||||
@ -204,7 +205,8 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
||||
private dialog: MatDialog,
|
||||
public multiselectService: MotionMultiselectService,
|
||||
public perms: LocalPermissionsService,
|
||||
private motionExport: MotionExportService
|
||||
private motionExport: MotionExportService,
|
||||
private spinnerService: SpinnerService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar, storage);
|
||||
this.canMultiSelect = true;
|
||||
@ -369,6 +371,8 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
|
||||
await multiselectPromise;
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
} finally {
|
||||
this.spinnerService.setVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,11 +119,7 @@ export class MotionMultiselectService {
|
||||
if (selectedChoice) {
|
||||
const message = `${motions.length} ` + this.translate.instant(this.messageForSpinner);
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
await this.repo.setMultiState(motions, selectedChoice.items as number).catch(error => {
|
||||
this.spinnerService.setVisibility(false);
|
||||
throw error;
|
||||
});
|
||||
this.spinnerService.setVisibility(false);
|
||||
await this.repo.setMultiState(motions, selectedChoice.items as number);
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,15 +147,9 @@ export class MotionMultiselectService {
|
||||
|
||||
const message = `${motions.length} ` + this.translate.instant(this.messageForSpinner);
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
await this.httpService
|
||||
.post('/rest/motions/motion/manage_multiple_recommendation/', {
|
||||
motions: requestData
|
||||
})
|
||||
.catch(error => {
|
||||
this.spinnerService.setVisibility(false);
|
||||
throw error;
|
||||
});
|
||||
this.spinnerService.setVisibility(false);
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_recommendation/', {
|
||||
motions: requestData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,11 +171,7 @@ export class MotionMultiselectService {
|
||||
if (selectedChoice) {
|
||||
const message = this.translate.instant(this.messageForSpinner);
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
await this.repo.setMultiCategory(motions, selectedChoice.items as number).catch(error => {
|
||||
this.spinnerService.setVisibility(false);
|
||||
throw error;
|
||||
});
|
||||
this.spinnerService.setVisibility(false);
|
||||
await this.repo.setMultiCategory(motions, selectedChoice.items as number);
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +212,6 @@ export class MotionMultiselectService {
|
||||
const message = `${motions.length} ` + this.translate.instant(this.messageForSpinner);
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_submitters/', { motions: requestData });
|
||||
this.spinnerService.setVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -277,7 +262,6 @@ export class MotionMultiselectService {
|
||||
const message = `${motions.length} ` + this.translate.instant(this.messageForSpinner);
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
await this.httpService.post('/rest/motions/motion/manage_multiple_tags/', { motions: requestData });
|
||||
this.spinnerService.setVisibility(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,11 +284,7 @@ export class MotionMultiselectService {
|
||||
const message = this.translate.instant(this.messageForSpinner);
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
const blockId = selectedChoice.action ? null : (selectedChoice.items as number);
|
||||
await this.repo.setMultiMotionBlock(motions, blockId).catch(error => {
|
||||
this.spinnerService.setVisibility(false);
|
||||
throw error;
|
||||
});
|
||||
this.spinnerService.setVisibility(false);
|
||||
await this.repo.setMultiMotionBlock(motions, blockId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,7 +318,7 @@ export class MotionMultiselectService {
|
||||
itemsToMove,
|
||||
selectedChoice.items as number
|
||||
);
|
||||
this.repo.sortMotions(this.treeService.stripTree(sortedChildTree));
|
||||
await this.repo.sortMotions(this.treeService.stripTree(sortedChildTree));
|
||||
} else if (selectedChoice.action === options[1]) {
|
||||
const sortedSiblingTree = this.treeService.insertBranchesIntoTree(
|
||||
partialTree,
|
||||
@ -346,7 +326,7 @@ export class MotionMultiselectService {
|
||||
this.repo.getViewModel(selectedChoice.items as number).parent_id,
|
||||
selectedChoice.items as number
|
||||
);
|
||||
this.repo.sortMotions(this.treeService.stripTree(sortedSiblingTree));
|
||||
await this.repo.sortMotions(this.treeService.stripTree(sortedSiblingTree));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -369,7 +349,6 @@ export class MotionMultiselectService {
|
||||
const star = (selectedChoice.items as number) === choices[0].id;
|
||||
this.spinnerService.setVisibility(true, message);
|
||||
await this.personalNoteService.bulkSetStar(motions, star);
|
||||
this.spinnerService.setVisibility(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,15 +114,19 @@ export class TopicDetailComponent extends BaseViewComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.newTopic) {
|
||||
if (!this.topicForm.value.agenda_parent_id) {
|
||||
delete this.topicForm.value.agenda_parent_id;
|
||||
try {
|
||||
if (this.newTopic) {
|
||||
if (!this.topicForm.value.agenda_parent_id) {
|
||||
delete this.topicForm.value.agenda_parent_id;
|
||||
}
|
||||
await this.repo.create(new CreateTopic(this.topicForm.value));
|
||||
this.router.navigate([`/agenda/`]);
|
||||
} else {
|
||||
await this.repo.update(this.topicForm.value, this.topic);
|
||||
this.setEditMode(false);
|
||||
}
|
||||
await this.repo.create(new CreateTopic(this.topicForm.value));
|
||||
this.router.navigate([`/agenda/`]);
|
||||
} else {
|
||||
this.setEditMode(false);
|
||||
await this.repo.update(this.topicForm.value, this.topic);
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
public async deleteSelected(): Promise<void> {
|
||||
const title = this.translate.instant('Are you sure you want to delete all selected participants?');
|
||||
if (await this.promptService.open(title)) {
|
||||
await this.repo.bulkDelete(this.selectedRows);
|
||||
this.repo.bulkDelete(this.selectedRows).catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +299,9 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
const selectedChoice = await this.choiceService.open(content, this.groupRepo.getViewModelList(), true, choices);
|
||||
if (selectedChoice) {
|
||||
const action = selectedChoice.action === choices[0] ? 'add' : 'remove';
|
||||
await this.repo.bulkAlterGroups(this.selectedRows, action, selectedChoice.items as number[]);
|
||||
this.repo
|
||||
.bulkAlterGroups(this.selectedRows, action, selectedChoice.items as number[])
|
||||
.catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,7 +331,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
const selectedChoice = await this.choiceService.open(content, null, false, options);
|
||||
if (selectedChoice) {
|
||||
const value = selectedChoice.action === options[0];
|
||||
await this.repo.bulkSetState(this.selectedRows, field, value);
|
||||
this.repo.bulkSetState(this.selectedRows, field, value).catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -341,7 +343,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
const title = this.translate.instant('Are you sure you want to send emails to all selected participants?');
|
||||
const content = this.selectedRows.length + ' ' + this.translate.instant('emails');
|
||||
if (await this.promptService.open(title, content)) {
|
||||
await this.repo.bulkSendInvitationEmail(this.selectedRows);
|
||||
this.repo.bulkSendInvitationEmail(this.selectedRows).catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,7 +376,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
)
|
||||
);
|
||||
}
|
||||
this.repo.bulkResetPasswordsToDefault(this.selectedRows).then(null, this.raiseError);
|
||||
this.repo.bulkResetPasswordsToDefault(this.selectedRows).catch(this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,7 +400,7 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
)
|
||||
);
|
||||
}
|
||||
this.repo.bulkGenerateNewPasswords(this.selectedRows).then(null, this.raiseError);
|
||||
this.repo.bulkGenerateNewPasswords(this.selectedRows).catch(this.raiseError);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -407,8 +409,8 @@ export class UserListComponent extends BaseListViewComponent<ViewUser> implement
|
||||
* @param viewUser the viewUser Object
|
||||
* @param event the mouse event (to prevent propagaton to row triggers)
|
||||
*/
|
||||
public async setPresent(viewUser: ViewUser): Promise<void> {
|
||||
public setPresent(viewUser: ViewUser): void {
|
||||
viewUser.user.is_present = !viewUser.user.is_present;
|
||||
await this.repo.update(viewUser.user, viewUser);
|
||||
this.repo.update(viewUser.user, viewUser).catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user