Merge pull request #5069 from jsangmeister/los-edit-mode
Edit mode for List of Speakers
This commit is contained in:
commit
7282c541dd
@ -168,6 +168,16 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
|
|||||||
await this.httpService.post(restUrl, { speakers: speakerIds });
|
await this.httpService.post(restUrl, { speakers: speakerIds });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Readds the last speaker to the list of speakers
|
||||||
|
*
|
||||||
|
* @param listOfSpeakers the list of speakers to modify
|
||||||
|
*/
|
||||||
|
public async readdLastSpeaker(listOfSpeakers: ViewListOfSpeakers): Promise<void> {
|
||||||
|
const restUrl = this.getRestUrl(listOfSpeakers.id, 'readd_last_speaker');
|
||||||
|
await this.httpService.post(restUrl);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks all speakers for a given user
|
* Marks all speakers for a given user
|
||||||
*
|
*
|
||||||
@ -207,7 +217,10 @@ export class ListOfSpeakersRepositoryService extends BaseHasContentObjectReposit
|
|||||||
* @param listOfSpeakersId id of the list of speakers
|
* @param listOfSpeakersId id of the list of speakers
|
||||||
* @param method the desired speaker action
|
* @param method the desired speaker action
|
||||||
*/
|
*/
|
||||||
private getRestUrl(listOfSpeakersId: number, method: 'manage_speaker' | 'sort_speakers' | 'speak'): string {
|
private getRestUrl(
|
||||||
|
listOfSpeakersId: number,
|
||||||
|
method: 'manage_speaker' | 'sort_speakers' | 'speak' | 'readd_last_speaker'
|
||||||
|
): string {
|
||||||
return `/rest/agenda/list-of-speakers/${listOfSpeakersId}/${method}/`;
|
return `/rest/agenda/list-of-speakers/${listOfSpeakersId}/${method}/`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<div cdkDropList [cdkDropListDisabled]="!enable" (cdkDropListDropped)="drop($event)">
|
<div cdkDropList [cdkDropListDisabled]="!enable" (cdkDropListDropped)="drop($event)">
|
||||||
<div class="line" *ngIf="!array.length">
|
<div class="line" *ngIf="!sortedItems.length">
|
||||||
<span translate>No data</span>
|
<span translate>No data</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
[ngClass]="isSelectedRow(i) ? 'backgroundColorSelected line' : 'backgroundColorLight line'"
|
[ngClass]="isSelectedRow(i) ? 'backgroundColorSelected line' : 'backgroundColorLight line'"
|
||||||
*ngFor="let item of array; let i = index"
|
*ngFor="let item of sortedItems; let i = index"
|
||||||
cdkDrag
|
cdkDrag
|
||||||
(click)="onItemClick($event, i)"
|
(click)="onItemClick($event, i)"
|
||||||
(cdkDragStarted)="dragStarted(i)"
|
(cdkDragStarted)="dragStarted(i)"
|
||||||
|
@ -34,7 +34,7 @@ export class SortingListComponent implements OnInit, OnDestroy {
|
|||||||
/**
|
/**
|
||||||
* Sorted and returned
|
* Sorted and returned
|
||||||
*/
|
*/
|
||||||
public array: Selectable[];
|
public sortedItems: Selectable[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The index of multiple selected elements. Allows for multiple items to be
|
* The index of multiple selected elements. Allows for multiple items to be
|
||||||
@ -104,6 +104,11 @@ export class SortingListComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
private inputSubscription: Subscription | null;
|
private inputSubscription: Subscription | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always stores the current items from the last update. Needed for restore and changing between live=true/false
|
||||||
|
*/
|
||||||
|
private currentItems: Selectable[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inform the parent view about sorting.
|
* Inform the parent view about sorting.
|
||||||
* Alternative approach to submit a new order of elements
|
* Alternative approach to submit a new order of elements
|
||||||
@ -118,7 +123,7 @@ export class SortingListComponent implements OnInit, OnDestroy {
|
|||||||
* @param translate the translation service
|
* @param translate the translation service
|
||||||
*/
|
*/
|
||||||
public constructor(protected translate: TranslateService) {
|
public constructor(protected translate: TranslateService) {
|
||||||
this.array = [];
|
this.sortedItems = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,14 +146,21 @@ export class SortingListComponent implements OnInit, OnDestroy {
|
|||||||
* @param newValues The new values to set.
|
* @param newValues The new values to set.
|
||||||
*/
|
*/
|
||||||
private updateArray(newValues: Selectable[]): void {
|
private updateArray(newValues: Selectable[]): void {
|
||||||
if (this.array.length !== newValues.length || this.live) {
|
this.currentItems = newValues.map(val => val);
|
||||||
this.array = [];
|
if (this.sortedItems.length !== newValues.length || this.live) {
|
||||||
this.array = newValues.map(val => val);
|
this.sortedItems = newValues.map(val => val);
|
||||||
} else {
|
} else {
|
||||||
this.array = this.array.map(arrayValue => newValues.find(val => val.id === arrayValue.id));
|
this.sortedItems = this.sortedItems.map(arrayValue => newValues.find(val => val.id === arrayValue.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the old order from the last update
|
||||||
|
*/
|
||||||
|
public restore(): void {
|
||||||
|
this.sortedItems = this.currentItems.map(val => val);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the start of a dragDrop event and clears multiSelect if the ittem dragged
|
* Handles the start of a dragDrop event and clears multiSelect if the ittem dragged
|
||||||
* is not part of the selected items
|
* is not part of the selected items
|
||||||
@ -171,35 +183,35 @@ export class SortingListComponent implements OnInit, OnDestroy {
|
|||||||
dropBehind?: boolean
|
dropBehind?: boolean
|
||||||
): void {
|
): void {
|
||||||
if (!this.multiSelectedIndex.length) {
|
if (!this.multiSelectedIndex.length) {
|
||||||
moveItemInArray(this.array, event.previousIndex, event.currentIndex);
|
moveItemInArray(this.sortedItems, event.previousIndex, event.currentIndex);
|
||||||
} else {
|
} else {
|
||||||
const before: Selectable[] = [];
|
const before: Selectable[] = [];
|
||||||
const insertions: Selectable[] = [];
|
const insertions: Selectable[] = [];
|
||||||
const behind: Selectable[] = [];
|
const behind: Selectable[] = [];
|
||||||
for (let i = 0; i < this.array.length; i++) {
|
for (let i = 0; i < this.sortedItems.length; i++) {
|
||||||
if (!this.multiSelectedIndex.includes(i)) {
|
if (!this.multiSelectedIndex.includes(i)) {
|
||||||
if (i < event.currentIndex) {
|
if (i < event.currentIndex) {
|
||||||
before.push(this.array[i]);
|
before.push(this.sortedItems[i]);
|
||||||
} else if (i > event.currentIndex) {
|
} else if (i > event.currentIndex) {
|
||||||
behind.push(this.array[i]);
|
behind.push(this.sortedItems[i]);
|
||||||
} else {
|
} else {
|
||||||
if (dropBehind === false) {
|
if (dropBehind === false) {
|
||||||
behind.push(this.array[i]);
|
behind.push(this.sortedItems[i]);
|
||||||
} else if (dropBehind === true) {
|
} else if (dropBehind === true) {
|
||||||
before.push(this.array[i]);
|
before.push(this.sortedItems[i]);
|
||||||
} else {
|
} else {
|
||||||
Math.min(...this.multiSelectedIndex) < i
|
Math.min(...this.multiSelectedIndex) < i
|
||||||
? before.push(this.array[i])
|
? before.push(this.sortedItems[i])
|
||||||
: behind.push(this.array[i]);
|
: behind.push(this.sortedItems[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
insertions.push(this.array[i]);
|
insertions.push(this.sortedItems[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.array = [...before, ...insertions, ...behind];
|
this.sortedItems = [...before, ...insertions, ...behind];
|
||||||
}
|
}
|
||||||
this.sortEvent.emit(this.array);
|
this.sortEvent.emit(this.sortedItems);
|
||||||
this.multiSelectedIndex = [];
|
this.multiSelectedIndex = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
<os-head-bar [nav]="false" [goBack]="true">
|
<os-head-bar [nav]="false" [goBack]="true" [editMode]="isSortMode" (cancelEditEvent)="onCancelSorting()" (saveEvent)="onSaveSorting()">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot">
|
<div class="title-slot">
|
||||||
<h2>
|
<h2>
|
||||||
<span *ngIf="!isCurrentListOfSpeakers" translate>List of speakers</span>
|
<span *ngIf="!isCurrentListOfSpeakers && !isSortMode" translate>List of speakers</span>
|
||||||
<span *ngIf="isCurrentListOfSpeakers" translate>Current list of speakers</span>
|
<span *ngIf="isCurrentListOfSpeakers && !isSortMode" translate>Current list of speakers</span>
|
||||||
|
<span *ngIf="isSortMode" translate>Sort mode</span>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-slot" *osPerms="['agenda.can_manage_list_of_speakers', 'core.can_manage_projector']">
|
<div class="menu-slot" *osPerms="['agenda.can_manage_list_of_speakers', 'core.can_manage_projector']">
|
||||||
|
<button type="button" mat-icon-button matTooltip="{{ 'Re-add last speaker' | translate }}" (click)="readdLastSpeaker()" [disabled]="!finishedSpeakers || !finishedSpeakers.length"><mat-icon>undo</mat-icon></button>
|
||||||
<button type="button" mat-icon-button [matMenuTriggerFor]="speakerMenu"><mat-icon>more_vert</mat-icon></button>
|
<button type="button" mat-icon-button [matMenuTriggerFor]="speakerMenu"><mat-icon>more_vert</mat-icon></button>
|
||||||
</div>
|
</div>
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
@ -77,10 +79,9 @@
|
|||||||
<div class="waiting-list" *ngIf="speakers && speakers.length > 0">
|
<div class="waiting-list" *ngIf="speakers && speakers.length > 0">
|
||||||
<os-sorting-list
|
<os-sorting-list
|
||||||
[input]="speakers"
|
[input]="speakers"
|
||||||
[live]="true"
|
[live]="!isSortMode"
|
||||||
[count]="true"
|
[count]="true"
|
||||||
[enable]="opCanManage()"
|
[enable]="opCanManage() && isSortMode"
|
||||||
(sortEvent)="onSortingChange($event)"
|
|
||||||
>
|
>
|
||||||
<!-- implicit speaker references into the component using ng-template slot -->
|
<!-- implicit speaker references into the component using ng-template slot -->
|
||||||
<ng-template let-speaker>
|
<ng-template let-speaker>
|
||||||
@ -154,6 +155,11 @@
|
|||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
<mat-menu #speakerMenu="matMenu">
|
<mat-menu #speakerMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="isSortMode = true">
|
||||||
|
<mat-icon>sort</mat-icon>
|
||||||
|
<span translate>Enable sorting</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<os-projector-button
|
<os-projector-button
|
||||||
*ngIf="viewListOfSpeakers && projectors && projectors.length > 1"
|
*ngIf="viewListOfSpeakers && projectors && projectors.length > 1"
|
||||||
[object]="getClosSlide()"
|
[object]="getClosSlide()"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
@ -15,6 +15,7 @@ import { UserRepositoryService } from 'app/core/repositories/users/user-reposito
|
|||||||
import { ConfigService } from 'app/core/ui-services/config.service';
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { DurationService } from 'app/core/ui-services/duration.service';
|
import { DurationService } from 'app/core/ui-services/duration.service';
|
||||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||||
|
import { SortingListComponent } from 'app/shared/components/sorting-list/sorting-list.component';
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
import { ViewProjector } from 'app/site/projector/models/view-projector';
|
||||||
@ -33,11 +34,19 @@ import { SpeakerState, ViewSpeaker } from '../../models/view-speaker';
|
|||||||
styleUrls: ['./list-of-speakers.component.scss']
|
styleUrls: ['./list-of-speakers.component.scss']
|
||||||
})
|
})
|
||||||
export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit {
|
export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit {
|
||||||
|
@ViewChild(SortingListComponent, { static: false })
|
||||||
|
public listElement: SortingListComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user is viewing the current list if speakers
|
* Determine if the user is viewing the current list if speakers
|
||||||
*/
|
*/
|
||||||
public isCurrentListOfSpeakers = false;
|
public isCurrentListOfSpeakers = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds whether the list is in sort mode or not
|
||||||
|
*/
|
||||||
|
public isSortMode = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the view item to the given topic
|
* Holds the view item to the given topic
|
||||||
*/
|
*/
|
||||||
@ -297,14 +306,25 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* React to manual in the sorting order.
|
* send the current order of the sorting list to the server
|
||||||
* Informs the repo about changes in the order
|
|
||||||
* @param listInNewOrder Contains the newly ordered list of ViewSpeakers
|
|
||||||
*/
|
*/
|
||||||
public onSortingChange(listInNewOrder: ViewSpeaker[]): void {
|
public onSaveSorting(): void {
|
||||||
// extract the ids from the ViewSpeaker array
|
if (this.isSortMode) {
|
||||||
const userIds = listInNewOrder.map(speaker => speaker.id);
|
this.isSortMode = false;
|
||||||
this.listOfSpeakersRepo.sortSpeakers(this.viewListOfSpeakers, userIds).then(null, this.raiseError);
|
this.listOfSpeakersRepo
|
||||||
|
.sortSpeakers(this.viewListOfSpeakers, this.listElement.sortedItems.map(el => el.id))
|
||||||
|
.catch(this.raiseError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore old order on cancel
|
||||||
|
*/
|
||||||
|
public onCancelSorting(): void {
|
||||||
|
if (this.isSortMode) {
|
||||||
|
this.isSortMode = false;
|
||||||
|
this.listElement.restore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -339,9 +359,14 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
|||||||
* @param speaker The speaker clicked on.
|
* @param speaker The speaker clicked on.
|
||||||
*/
|
*/
|
||||||
public onMarkButton(speaker: ViewSpeaker): void {
|
public onMarkButton(speaker: ViewSpeaker): void {
|
||||||
this.listOfSpeakersRepo
|
this.listOfSpeakersRepo.markSpeaker(this.viewListOfSpeakers, speaker, !speaker.marked).catch(this.raiseError);
|
||||||
.markSpeaker(this.viewListOfSpeakers, speaker, !speaker.marked)
|
}
|
||||||
.then(null, this.raiseError);
|
|
||||||
|
/**
|
||||||
|
* Removes the last finished speaker from the list an re-adds him on pole position
|
||||||
|
*/
|
||||||
|
public readdLastSpeaker(): void {
|
||||||
|
this.listOfSpeakersRepo.readdLastSpeaker(this.viewListOfSpeakers).catch(this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -351,11 +376,16 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
|
|||||||
* the operator themself is removed
|
* the operator themself is removed
|
||||||
*/
|
*/
|
||||||
public async onDeleteButton(speaker?: ViewSpeaker): Promise<void> {
|
public async onDeleteButton(speaker?: ViewSpeaker): Promise<void> {
|
||||||
try {
|
const title = this.translate.instant(
|
||||||
await this.listOfSpeakersRepo.delete(this.viewListOfSpeakers, speaker ? speaker.id : null);
|
'Are you sure you want to delete this speaker from this list of speakers?'
|
||||||
this.filterUsers();
|
);
|
||||||
} catch (e) {
|
if (await this.promptService.open(title)) {
|
||||||
this.raiseError(e);
|
try {
|
||||||
|
await this.listOfSpeakersRepo.delete(this.viewListOfSpeakers, speaker ? speaker.id : null);
|
||||||
|
this.filterUsers();
|
||||||
|
} catch (e) {
|
||||||
|
this.raiseError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ export class CategoryMotionsSortComponent extends BaseViewComponent implements O
|
|||||||
if (this.sortSelector.multiSelectedIndex.length) {
|
if (this.sortSelector.multiSelectedIndex.length) {
|
||||||
}
|
}
|
||||||
const content = this.translate.instant('Move selected items ...');
|
const content = this.translate.instant('Move selected items ...');
|
||||||
const choices = this.sortSelector.array
|
const choices = this.sortSelector.sortedItems
|
||||||
.map((item, index) => {
|
.map((item, index) => {
|
||||||
return { id: index, label: item.getTitle() };
|
return { id: index, label: item.getTitle() };
|
||||||
})
|
})
|
||||||
|
@ -290,7 +290,13 @@ class ListOfSpeakersViewSet(
|
|||||||
result = has_perm(self.request.user, "agenda.can_see_list_of_speakers")
|
result = has_perm(self.request.user, "agenda.can_see_list_of_speakers")
|
||||||
# For manage_speaker requests the rest of the check is
|
# For manage_speaker requests the rest of the check is
|
||||||
# done in the specific method. See below.
|
# done in the specific method. See below.
|
||||||
elif self.action in ("update", "partial_update", "speak", "sort_speakers"):
|
elif self.action in (
|
||||||
|
"update",
|
||||||
|
"partial_update",
|
||||||
|
"speak",
|
||||||
|
"sort_speakers",
|
||||||
|
"readd_last_speaker",
|
||||||
|
):
|
||||||
result = has_perm(
|
result = has_perm(
|
||||||
self.request.user, "agenda.can_see_list_of_speakers"
|
self.request.user, "agenda.can_see_list_of_speakers"
|
||||||
) and has_perm(self.request.user, "agenda.can_manage_list_of_speakers")
|
) and has_perm(self.request.user, "agenda.can_manage_list_of_speakers")
|
||||||
@ -344,7 +350,7 @@ class ListOfSpeakersViewSet(
|
|||||||
# Try to add the user. This ensurse that a user is not twice in the
|
# Try to add the user. This ensurse that a user is not twice in the
|
||||||
# list of coming speakers.
|
# list of coming speakers.
|
||||||
try:
|
try:
|
||||||
Speaker.objects.add(user, list_of_speakers)
|
speaker = Speaker.objects.add(user, list_of_speakers)
|
||||||
except OpenSlidesError as e:
|
except OpenSlidesError as e:
|
||||||
raise ValidationError({"detail": str(e)})
|
raise ValidationError({"detail": str(e)})
|
||||||
|
|
||||||
@ -520,3 +526,32 @@ class ListOfSpeakersViewSet(
|
|||||||
|
|
||||||
# Initiate response.
|
# Initiate response.
|
||||||
return Response({"detail": "List of speakers successfully sorted."})
|
return Response({"detail": "List of speakers successfully sorted."})
|
||||||
|
|
||||||
|
@detail_route(methods=["POST"])
|
||||||
|
def readd_last_speaker(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
Special view endpoint to re-add the last finished speaker to the list of speakers.
|
||||||
|
"""
|
||||||
|
list_of_speakers = self.get_object()
|
||||||
|
|
||||||
|
# Retrieve speaker which spoke last and next speaker
|
||||||
|
ordered_speakers = list_of_speakers.speakers.order_by("-end_time")
|
||||||
|
if len(ordered_speakers) == 0:
|
||||||
|
raise ValidationError({"detail": "There is no last speaker at the moment."})
|
||||||
|
|
||||||
|
last_speaker = ordered_speakers[0]
|
||||||
|
if last_speaker.end_time is None:
|
||||||
|
raise ValidationError({"detail": "There is no last speaker at the moment."})
|
||||||
|
|
||||||
|
next_speaker = list_of_speakers.get_next_speaker()
|
||||||
|
new_weight = 1
|
||||||
|
# if there is a next speaker, insert last speaker before it
|
||||||
|
if next_speaker:
|
||||||
|
new_weight = next_speaker.weight - 1
|
||||||
|
|
||||||
|
# reset times of last speaker and prepend it to the list of active speakers
|
||||||
|
last_speaker.begin_time = last_speaker.end_time = None
|
||||||
|
last_speaker.weight = new_weight
|
||||||
|
last_speaker.save()
|
||||||
|
|
||||||
|
return Response()
|
||||||
|
@ -3,6 +3,7 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APIClient
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
@ -295,6 +296,14 @@ class ManageSpeaker(TestCase):
|
|||||||
password="test_password_e6paev4zeeh9n",
|
password="test_password_e6paev4zeeh9n",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def revoke_admin_rights(self):
|
||||||
|
admin = get_user_model().objects.get(username="admin")
|
||||||
|
group_admin = admin.groups.get(name="Admin")
|
||||||
|
group_delegates = type(group_admin).objects.get(name="Delegates")
|
||||||
|
admin.groups.add(group_delegates)
|
||||||
|
admin.groups.remove(group_admin)
|
||||||
|
inform_changed_data(admin)
|
||||||
|
|
||||||
def test_add_oneself_once(self):
|
def test_add_oneself_once(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("listofspeakers-manage-speaker", args=[self.list_of_speakers.pk])
|
reverse("listofspeakers-manage-speaker", args=[self.list_of_speakers.pk])
|
||||||
@ -374,12 +383,7 @@ class ManageSpeaker(TestCase):
|
|||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
def test_add_someone_else_non_admin(self):
|
def test_add_someone_else_non_admin(self):
|
||||||
admin = get_user_model().objects.get(username="admin")
|
self.revoke_admin_rights()
|
||||||
group_admin = admin.groups.get(name="Admin")
|
|
||||||
group_delegates = type(group_admin).objects.get(name="Delegates")
|
|
||||||
admin.groups.add(group_delegates)
|
|
||||||
admin.groups.remove(group_admin)
|
|
||||||
inform_changed_data(admin)
|
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("listofspeakers-manage-speaker", args=[self.list_of_speakers.pk]),
|
reverse("listofspeakers-manage-speaker", args=[self.list_of_speakers.pk]),
|
||||||
@ -415,12 +419,7 @@ class ManageSpeaker(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_remove_someone_else_non_admin(self):
|
def test_remove_someone_else_non_admin(self):
|
||||||
admin = get_user_model().objects.get(username="admin")
|
self.revoke_admin_rights()
|
||||||
group_admin = admin.groups.get(name="Admin")
|
|
||||||
group_delegates = type(group_admin).objects.get(name="Delegates")
|
|
||||||
admin.groups.add(group_delegates)
|
|
||||||
admin.groups.remove(group_admin)
|
|
||||||
inform_changed_data(admin)
|
|
||||||
speaker = Speaker.objects.add(self.user, self.list_of_speakers)
|
speaker = Speaker.objects.add(self.user, self.list_of_speakers)
|
||||||
|
|
||||||
response = self.client.delete(
|
response = self.client.delete(
|
||||||
@ -441,12 +440,7 @@ class ManageSpeaker(TestCase):
|
|||||||
self.assertTrue(Speaker.objects.get().marked)
|
self.assertTrue(Speaker.objects.get().marked)
|
||||||
|
|
||||||
def test_mark_speaker_non_admin(self):
|
def test_mark_speaker_non_admin(self):
|
||||||
admin = get_user_model().objects.get(username="admin")
|
self.revoke_admin_rights()
|
||||||
group_admin = admin.groups.get(name="Admin")
|
|
||||||
group_delegates = type(group_admin).objects.get(name="Delegates")
|
|
||||||
admin.groups.add(group_delegates)
|
|
||||||
admin.groups.remove(group_admin)
|
|
||||||
inform_changed_data(admin)
|
|
||||||
Speaker.objects.add(self.user, self.list_of_speakers)
|
Speaker.objects.add(self.user, self.list_of_speakers)
|
||||||
|
|
||||||
response = self.client.patch(
|
response = self.client.patch(
|
||||||
@ -456,6 +450,79 @@ class ManageSpeaker(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 403)
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
# re-add last speaker
|
||||||
|
def util_add_user_as_last_speaker(self):
|
||||||
|
speaker = Speaker.objects.add(self.user, self.list_of_speakers)
|
||||||
|
speaker.begin_time = timezone.now()
|
||||||
|
speaker.end_time = timezone.now()
|
||||||
|
speaker.weight = None
|
||||||
|
speaker.save()
|
||||||
|
|
||||||
|
def test_readd_last_speaker_no_speaker(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"listofspeakers-readd-last-speaker", args=[self.list_of_speakers.pk]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_readd_last_speaker_no_last_speaker(self):
|
||||||
|
Speaker.objects.add(self.user, self.list_of_speakers)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"listofspeakers-readd-last-speaker", args=[self.list_of_speakers.pk]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
|
def test_readd_last_speaker_has_last_speaker_no_next_speaker(self):
|
||||||
|
self.util_add_user_as_last_speaker()
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"listofspeakers-readd-last-speaker", args=[self.list_of_speakers.pk]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
speaker = Speaker.objects.get()
|
||||||
|
self.assertTrue(
|
||||||
|
speaker.begin_time is None
|
||||||
|
and speaker.end_time is None
|
||||||
|
and speaker.weight is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_readd_last_speaker_has_last_speaker_and_next_speaker(self):
|
||||||
|
self.util_add_user_as_last_speaker()
|
||||||
|
user2 = get_user_model().objects.create_user(
|
||||||
|
username="test_user_KLGHjkHJKBhjJHGGJKJn",
|
||||||
|
password="test_password_JHt678VbhjuGhj76hjGA",
|
||||||
|
)
|
||||||
|
Speaker.objects.add(user2, self.list_of_speakers)
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"listofspeakers-readd-last-speaker", args=[self.list_of_speakers.pk]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
speaker = Speaker.objects.get(user__pk=self.user.pk)
|
||||||
|
self.assertTrue(
|
||||||
|
speaker.begin_time is None
|
||||||
|
and speaker.end_time is None
|
||||||
|
and speaker.weight is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_readd_last_speaker_no_admin(self):
|
||||||
|
self.util_add_user_as_last_speaker()
|
||||||
|
self.revoke_admin_rights()
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse(
|
||||||
|
"listofspeakers-readd-last-speaker", args=[self.list_of_speakers.pk]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
|
||||||
class Speak(TestCase):
|
class Speak(TestCase):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user