Merge pull request #6070 from tsiegleauq/stop-voting-publish-prompt
stop-voting shows prompt
This commit is contained in:
commit
c60553e376
@ -48,7 +48,7 @@
|
|||||||
<button
|
<button
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
[ngClass]="pollStateActions[poll.state].css"
|
[ngClass]="pollStateActions[poll.state].css"
|
||||||
(click)="changeState(poll.nextState)"
|
(click)="nextPollState()"
|
||||||
[disabled]="stateChangePending"
|
[disabled]="stateChangePending"
|
||||||
>
|
>
|
||||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||||
|
@ -8,6 +8,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||||
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
import { AssignmentPollRepositoryService } from 'app/core/repositories/assignments/assignment-poll-repository.service';
|
||||||
|
import { ChoiceService } from 'app/core/ui-services/choice.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { VotingService } from 'app/core/ui-services/voting.service';
|
import { VotingService } from 'app/core/ui-services/voting.service';
|
||||||
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
||||||
@ -82,6 +83,7 @@ export class AssignmentPollComponent
|
|||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
dialog: MatDialog,
|
dialog: MatDialog,
|
||||||
promptService: PromptService,
|
promptService: PromptService,
|
||||||
|
choiceService: ChoiceService,
|
||||||
repo: AssignmentPollRepositoryService,
|
repo: AssignmentPollRepositoryService,
|
||||||
pollDialog: AssignmentPollDialogService,
|
pollDialog: AssignmentPollDialogService,
|
||||||
private pollService: AssignmentPollService,
|
private pollService: AssignmentPollService,
|
||||||
@ -90,7 +92,7 @@ export class AssignmentPollComponent
|
|||||||
private operator: OperatorService,
|
private operator: OperatorService,
|
||||||
private votingService: VotingService
|
private votingService: VotingService
|
||||||
) {
|
) {
|
||||||
super(titleService, matSnackBar, translate, dialog, promptService, repo, pollDialog);
|
super(titleService, matSnackBar, translate, dialog, promptService, choiceService, repo, pollDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
<button
|
<button
|
||||||
mat-stroked-button
|
mat-stroked-button
|
||||||
[ngClass]="pollStateActions[poll.state].css"
|
[ngClass]="pollStateActions[poll.state].css"
|
||||||
(click)="changeState(poll.nextState)"
|
(click)="nextPollState()"
|
||||||
[disabled]="stateChangePending"
|
[disabled]="stateChangePending"
|
||||||
>
|
>
|
||||||
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
<mat-icon> {{ pollStateActions[poll.state].icon }}</mat-icon>
|
||||||
|
@ -7,6 +7,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
|
|
||||||
import { OperatorService, Permission } from 'app/core/core-services/operator.service';
|
import { OperatorService, Permission } from 'app/core/core-services/operator.service';
|
||||||
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
import { MotionPollRepositoryService } from 'app/core/repositories/motions/motion-poll-repository.service';
|
||||||
|
import { ChoiceService } from 'app/core/ui-services/choice.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
import { VotingPrivacyWarningComponent } from 'app/shared/components/voting-privacy-warning/voting-privacy-warning.component';
|
||||||
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||||
@ -68,6 +69,7 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll, Motio
|
|||||||
titleService: Title,
|
titleService: Title,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
promptService: PromptService,
|
promptService: PromptService,
|
||||||
|
choiceService: ChoiceService,
|
||||||
pollDialog: MotionPollDialogService,
|
pollDialog: MotionPollDialogService,
|
||||||
protected dialog: MatDialog,
|
protected dialog: MatDialog,
|
||||||
protected pollRepo: MotionPollRepositoryService,
|
protected pollRepo: MotionPollRepositoryService,
|
||||||
@ -76,7 +78,7 @@ export class MotionPollComponent extends BasePollComponent<ViewMotionPoll, Motio
|
|||||||
private pdfService: MotionPollPdfService,
|
private pdfService: MotionPollPdfService,
|
||||||
private operator: OperatorService
|
private operator: OperatorService
|
||||||
) {
|
) {
|
||||||
super(titleService, matSnackBar, translate, dialog, promptService, pollRepo, pollDialog);
|
super(titleService, matSnackBar, translate, dialog, promptService, choiceService, pollRepo, pollDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
public openVotingWarning(): void {
|
public openVotingWarning(): void {
|
||||||
|
@ -6,13 +6,14 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
import { BasePollDialogService } from 'app/core/ui-services/base-poll-dialog.service';
|
||||||
|
import { ChoiceService } from 'app/core/ui-services/choice.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
import { ChartData } from 'app/shared/components/charts/charts.component';
|
import { ChartData } from 'app/shared/components/charts/charts.component';
|
||||||
import { PollState, PollType } from 'app/shared/models/poll/base-poll';
|
import { PollState, PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
import { BaseViewComponentDirective } from 'app/site/base/base-view';
|
||||||
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
|
import { BasePollRepositoryService } from '../services/base-poll-repository.service';
|
||||||
import { PollService } from '../services/poll.service';
|
import { PollService } from '../services/poll.service';
|
||||||
import { ViewBasePoll } from '../models/view-base-poll';
|
import { PollStateChangeActionVerbose, ViewBasePoll } from '../models/view-base-poll';
|
||||||
|
|
||||||
export abstract class BasePollComponent<
|
export abstract class BasePollComponent<
|
||||||
V extends ViewBasePoll,
|
V extends ViewBasePoll,
|
||||||
@ -49,39 +50,47 @@ export abstract class BasePollComponent<
|
|||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
protected dialog: MatDialog,
|
protected dialog: MatDialog,
|
||||||
protected promptService: PromptService,
|
protected promptService: PromptService,
|
||||||
|
protected choiceService: ChoiceService,
|
||||||
protected repo: BasePollRepositoryService,
|
protected repo: BasePollRepositoryService,
|
||||||
protected pollDialog: BasePollDialogService<V, S>
|
protected pollDialog: BasePollDialogService<V, S>
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async changeState(key: PollState): Promise<void> {
|
public async nextPollState(): Promise<void> {
|
||||||
if (key === PollState.Created) {
|
const currentState: PollState = this._poll.state;
|
||||||
const title = this.translate.instant('Are you sure you want to reset this vote?');
|
if (currentState === PollState.Created || currentState === PollState.Finished) {
|
||||||
const content = this.translate.instant('All votes will be lost.');
|
await this.changeState(this._poll.nextState);
|
||||||
if (await this.promptService.open(title, content)) {
|
} else if (currentState === PollState.Started) {
|
||||||
this.stateChangePending = true;
|
const title = this.translate.instant('Are you sure you want to stop this voting?');
|
||||||
this.repo
|
const actions = [this.translate.instant('Stop'), this.translate.instant('Stop & publish')];
|
||||||
.resetPoll(this._poll)
|
const choice = await this.choiceService.open(title, null, false, actions);
|
||||||
.catch(this.raiseError)
|
|
||||||
.finally(() => {
|
if (choice?.action === 'Stop') {
|
||||||
this.stateChangePending = false;
|
await this.changeState(PollState.Finished);
|
||||||
});
|
} else if (choice?.action === 'Stop & publish') {
|
||||||
|
await this.changeState(PollState.Published);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.stateChangePending = true;
|
|
||||||
this.repo
|
|
||||||
.changePollState(this._poll)
|
|
||||||
.catch(this.raiseError)
|
|
||||||
.finally(() => {
|
|
||||||
this.stateChangePending = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetState(): void {
|
private async changeState(targetState: PollState): Promise<void> {
|
||||||
|
this.stateChangePending = true;
|
||||||
|
this.repo
|
||||||
|
.changePollState(this._poll, targetState)
|
||||||
|
.catch(this.raiseError)
|
||||||
|
.finally(() => {
|
||||||
|
this.stateChangePending = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async resetState(): Promise<void> {
|
||||||
|
const title = this.translate.instant('Are you sure you want to reset this vote?');
|
||||||
|
const content = this.translate.instant('All votes will be lost.');
|
||||||
|
if (await this.promptService.open(title, content)) {
|
||||||
this.changeState(PollState.Created);
|
this.changeState(PollState.Created);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for the 'delete poll' button
|
* Handler for the 'delete poll' button
|
||||||
|
@ -56,17 +56,17 @@ export abstract class BasePollRepositoryService<
|
|||||||
return viewModel;
|
return viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public changePollState(poll: BasePoll): Promise<void> {
|
public changePollState(poll: BasePoll, targetState: PollState): Promise<void> {
|
||||||
const path = this.restPath(poll);
|
const path = this.restPath(poll);
|
||||||
switch (poll.state) {
|
switch (targetState) {
|
||||||
case PollState.Created:
|
case PollState.Created:
|
||||||
return this.http.post(`${path}/start/`);
|
return this.http.post(`${path}/reset/`);
|
||||||
case PollState.Started:
|
case PollState.Started:
|
||||||
return this.http.post(`${path}/stop/`);
|
return this.http.post(`${path}/start/`);
|
||||||
case PollState.Finished:
|
case PollState.Finished:
|
||||||
return this.http.post(`${path}/publish/`);
|
return this.http.post(`${path}/stop/`);
|
||||||
case PollState.Published:
|
case PollState.Published:
|
||||||
return this.resetPoll(poll);
|
return this.http.post(`${path}/publish/`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,10 +74,6 @@ export abstract class BasePollRepositoryService<
|
|||||||
return `/rest/${poll.collectionString}/${poll.id}`;
|
return `/rest/${poll.collectionString}/${poll.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetPoll(poll: BasePoll): Promise<void> {
|
|
||||||
return this.http.post(`${this.restPath(poll)}/reset/`);
|
|
||||||
}
|
|
||||||
|
|
||||||
public pseudoanonymize(poll: BasePoll): Promise<void> {
|
public pseudoanonymize(poll: BasePoll): Promise<void> {
|
||||||
return this.http.post(`${this.restPath(poll)}/pseudoanonymize/`);
|
return this.http.post(`${this.restPath(poll)}/pseudoanonymize/`);
|
||||||
}
|
}
|
||||||
|
@ -164,9 +164,13 @@ class BasePollViewSet(ModelViewSet):
|
|||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def publish(self, request, pk):
|
def publish(self, request, pk):
|
||||||
poll = self.get_locked_object()
|
poll = self.get_locked_object()
|
||||||
if poll.state != BasePoll.STATE_FINISHED:
|
if poll.state not in (BasePoll.STATE_STARTED, BasePoll.STATE_FINISHED):
|
||||||
raise ValidationError({"detail": "Wrong poll state"})
|
raise ValidationError({"detail": "Wrong poll state"})
|
||||||
|
|
||||||
|
# stop poll if needed
|
||||||
|
if poll.state == BasePoll.STATE_STARTED:
|
||||||
|
poll.stop()
|
||||||
|
|
||||||
poll.state = BasePoll.STATE_PUBLISHED
|
poll.state = BasePoll.STATE_PUBLISHED
|
||||||
poll.save()
|
poll.save()
|
||||||
inform_changed_data(
|
inform_changed_data(
|
||||||
|
@ -1284,6 +1284,29 @@ class PublishMotionPoll(TestCase):
|
|||||||
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
self.assertHttpStatusVerbose(response, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(MotionPoll.objects.get().state, MotionPoll.STATE_CREATED)
|
self.assertEqual(MotionPoll.objects.get().state, MotionPoll.STATE_CREATED)
|
||||||
|
|
||||||
|
def test_publish_from_started(self):
|
||||||
|
self.poll.state = MotionPoll.STATE_STARTED
|
||||||
|
self.poll.save()
|
||||||
|
response = self.client.post(reverse("motionpoll-publish", args=[self.poll.pk]))
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(MotionPoll.objects.get().state, MotionPoll.STATE_PUBLISHED)
|
||||||
|
|
||||||
|
def test_publish_from_started_with_entitled_users(self):
|
||||||
|
self.poll.state = MotionPoll.STATE_STARTED
|
||||||
|
self.poll.save()
|
||||||
|
admin = get_user_model().objects.get(username="admin")
|
||||||
|
admin.is_present = True
|
||||||
|
admin.save()
|
||||||
|
self.poll.groups.add(GROUP_ADMIN_PK)
|
||||||
|
response = self.client.post(reverse("motionpoll-publish", args=[self.poll.pk]))
|
||||||
|
self.assertHttpStatusVerbose(response, status.HTTP_200_OK)
|
||||||
|
poll = MotionPoll.objects.get()
|
||||||
|
self.assertEqual(poll.state, MotionPoll.STATE_PUBLISHED)
|
||||||
|
self.assertEqual(
|
||||||
|
poll.entitled_users_at_stop,
|
||||||
|
[{"user_id": admin.id, "voted": False, "vote_delegated_to_id": None}],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PseudoanonymizeMotionPoll(TestCase):
|
class PseudoanonymizeMotionPoll(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user