various improvements for polls
This commit is contained in:
parent
bc54a6eb46
commit
df1047fc76
@ -30,6 +30,10 @@ declare global {
|
|||||||
mapToObject(f: (item: T) => { [key: string]: any }): { [key: string]: any };
|
mapToObject(f: (item: T) => { [key: string]: any }): { [key: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Set<T> {
|
||||||
|
equals(other: Set<T>): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enhances the number object to calculate real modulo operations.
|
* Enhances the number object to calculate real modulo operations.
|
||||||
* (not remainder)
|
* (not remainder)
|
||||||
@ -96,6 +100,7 @@ export class AppComponent {
|
|||||||
|
|
||||||
// change default JS functions
|
// change default JS functions
|
||||||
this.overloadArrayFunctions();
|
this.overloadArrayFunctions();
|
||||||
|
this.overloadSetFunctions();
|
||||||
this.overloadModulo();
|
this.overloadModulo();
|
||||||
|
|
||||||
// Wait until the App reaches a stable state.
|
// Wait until the App reaches a stable state.
|
||||||
@ -179,6 +184,27 @@ export class AppComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds some functions to Set.
|
||||||
|
*/
|
||||||
|
private overloadSetFunctions(): void {
|
||||||
|
// equals
|
||||||
|
Object.defineProperty(Set.prototype, 'equals', {
|
||||||
|
value: function<T>(other: Set<T>): boolean {
|
||||||
|
const _difference = new Set(this);
|
||||||
|
for (const elem of other) {
|
||||||
|
if (_difference.has(elem)) {
|
||||||
|
_difference.delete(elem);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !_difference.size;
|
||||||
|
},
|
||||||
|
enumerable: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enhances the number object with a real modulo operation (not remainder).
|
* Enhances the number object with a real modulo operation (not remainder).
|
||||||
* TODO: Remove this, if the remainder operation is changed to modulo.
|
* TODO: Remove this, if the remainder operation is changed to modulo.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<mat-select [formControl]="contentForm" [multiple]="multiple" [panelClass]="{ 'os-search-value-selector': multiple }">
|
<mat-select [formControl]="contentForm" [multiple]="multiple" [panelClass]="{ 'os-search-value-selector': multiple }" [errorStateMatcher]="errorStateMatcher">
|
||||||
<ngx-mat-select-search [formControl]="searchValue"></ngx-mat-select-search>
|
<ngx-mat-select-search [formControl]="searchValue"></ngx-mat-select-search>
|
||||||
<ng-container *ngIf="multiple && showChips">
|
<ng-container *ngIf="multiple && showChips">
|
||||||
<div #chipPlaceholder>
|
<div #chipPlaceholder>
|
||||||
|
@ -17,6 +17,7 @@ import { Observable } from 'rxjs';
|
|||||||
import { auditTime } from 'rxjs/operators';
|
import { auditTime } from 'rxjs/operators';
|
||||||
|
|
||||||
import { BaseFormControlComponent } from 'app/shared/models/base/base-form-control';
|
import { BaseFormControlComponent } from 'app/shared/models/base/base-form-control';
|
||||||
|
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
||||||
import { Selectable } from '../selectable';
|
import { Selectable } from '../selectable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +70,9 @@ export class SearchValueSelectorComponent extends BaseFormControlComponent<Selec
|
|||||||
@Input()
|
@Input()
|
||||||
public noneTitle = '–';
|
public noneTitle = '–';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
public errorStateMatcher: ParentErrorStateMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The inputlist subject. Subscribes to it and updates the selector, if the subject
|
* The inputlist subject. Subscribes to it and updates the selector, if the subject
|
||||||
* changes its values.
|
* changes its values.
|
||||||
|
@ -2,7 +2,7 @@ import { BaseModel } from '../base/base-model';
|
|||||||
|
|
||||||
export interface ConfigChoice {
|
export interface ConfigChoice {
|
||||||
value: string;
|
value: string;
|
||||||
displayName: string;
|
display_name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,7 +17,8 @@ export type ConfigInputType =
|
|||||||
| 'choice'
|
| 'choice'
|
||||||
| 'datetimepicker'
|
| 'datetimepicker'
|
||||||
| 'colorpicker'
|
| 'colorpicker'
|
||||||
| 'translations';
|
| 'translations'
|
||||||
|
| 'groups';
|
||||||
|
|
||||||
export interface ConfigData {
|
export interface ConfigData {
|
||||||
defaultValue: any;
|
defaultValue: any;
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<mat-menu #triggerMenu="matMenu">
|
<mat-menu #triggerMenu="matMenu">
|
||||||
<ng-container *ngIf="poll">
|
<ng-container *ngIf="poll">
|
||||||
<button mat-menu-item (click)="changeState(state.value)" *ngFor="let state of poll.nextStates | keyvalue">
|
<button mat-menu-item (click)="changeState(state.value)" *ngFor="let state of poll.getNextStates() | keyvalue">
|
||||||
<span translate>{{ state.key }}</span>
|
<span translate>{{ state.key }}</span>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
<ng-container *ngSwitchCase="'choice'">
|
<ng-container *ngSwitchCase="'choice'">
|
||||||
<ng-container *ngTemplateOutlet="select"></ng-container>
|
<ng-container *ngTemplateOutlet="select"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngSwitchCase="'groups'">
|
||||||
|
<ng-container *ngTemplateOutlet="groups"></ng-container>
|
||||||
|
</ng-container>
|
||||||
<ng-container *ngSwitchDefault>
|
<ng-container *ngSwitchDefault>
|
||||||
<ng-container *ngTemplateOutlet="input"></ng-container>
|
<ng-container *ngTemplateOutlet="input"></ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@ -29,6 +32,17 @@
|
|||||||
</mat-select>
|
</mat-select>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #groups ngProjectAs="os-search-value-selector">
|
||||||
|
<os-search-value-selector
|
||||||
|
formControlName="value"
|
||||||
|
[multiple]="true"
|
||||||
|
[showChips]="false"
|
||||||
|
[includeNone]="false"
|
||||||
|
[errorStateMatcher]="matcher"
|
||||||
|
[inputListValues]="groupObservable"
|
||||||
|
></os-search-value-selector>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #input ngProjectAs="[matInput]">
|
<ng-template #input ngProjectAs="[matInput]">
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
@ -15,11 +15,14 @@ import { Title } from '@angular/platform-browser';
|
|||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { Moment } from 'moment';
|
import { Moment } from 'moment';
|
||||||
import { distinctUntilChanged } from 'rxjs/operators';
|
import { Observable } from 'rxjs';
|
||||||
|
import { distinctUntilChanged, filter } from 'rxjs/operators';
|
||||||
|
|
||||||
import { BaseComponent } from 'app/base.component';
|
import { BaseComponent } from 'app/base.component';
|
||||||
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
import { ConfigRepositoryService } from 'app/core/repositories/config/config-repository.service';
|
||||||
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
import { ParentErrorStateMatcher } from 'app/shared/parent-error-state-matcher';
|
||||||
|
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||||
import { ConfigItem } from '../config-list/config-list.component';
|
import { ConfigItem } from '../config-list/config-list.component';
|
||||||
import { ViewConfig } from '../../models/view-config';
|
import { ViewConfig } from '../../models/view-config';
|
||||||
|
|
||||||
@ -115,6 +118,9 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit, OnDes
|
|||||||
@Output()
|
@Output()
|
||||||
public update = new EventEmitter<ConfigItem>();
|
public update = new EventEmitter<ConfigItem>();
|
||||||
|
|
||||||
|
/** used by the groups config type */
|
||||||
|
public groupObservable: Observable<ViewGroup[]> = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The usual component constructor. datetime pickers will set their locale
|
* The usual component constructor. datetime pickers will set their locale
|
||||||
* to the current language chosen
|
* to the current language chosen
|
||||||
@ -130,7 +136,8 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit, OnDes
|
|||||||
protected translate: TranslateService,
|
protected translate: TranslateService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
public repo: ConfigRepositoryService
|
public repo: ConfigRepositoryService,
|
||||||
|
private groupRepo: GroupRepositoryService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate);
|
super(titleService, translate);
|
||||||
}
|
}
|
||||||
@ -139,6 +146,9 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit, OnDes
|
|||||||
* Sets up the form for this config field.
|
* Sets up the form for this config field.
|
||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
|
// filter out empty results in group observable. We never have no groups and it messes up the settings change detection
|
||||||
|
this.groupObservable = this.groupRepo.getViewModelListObservable().pipe(filter(groups => !!groups.length));
|
||||||
|
|
||||||
this.form = this.formBuilder.group({
|
this.form = this.formBuilder.group({
|
||||||
value: [''],
|
value: [''],
|
||||||
date: [''],
|
date: [''],
|
||||||
@ -226,6 +236,14 @@ export class ConfigFieldComponent extends BaseComponent implements OnInit, OnDes
|
|||||||
const time = this.form.get('time').value;
|
const time = this.form.get('time').value;
|
||||||
value = this.dateAndTimeToUnix(date, time);
|
value = this.dateAndTimeToUnix(date, time);
|
||||||
}
|
}
|
||||||
|
if (this.configItem.inputType === 'groups') {
|
||||||
|
// we have to check here explicitly if nothing changed because of the search value selector
|
||||||
|
const newS = new Set(value);
|
||||||
|
const oldS = new Set(this.configItem.value);
|
||||||
|
if (newS.equals(oldS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.sendUpdate(value);
|
this.sendUpdate(value);
|
||||||
this.cd.detectChanges();
|
this.cd.detectChanges();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ChartData } from 'app/shared/components/charts/charts.component';
|
import { ChartData } from 'app/shared/components/charts/charts.component';
|
||||||
import { MotionPoll, MotionPollMethods } from 'app/shared/models/motions/motion-poll';
|
import { MotionPoll, MotionPollMethods } from 'app/shared/models/motions/motion-poll';
|
||||||
import { PollColor } from 'app/shared/models/poll/base-poll';
|
import { PollColor, PollState } from 'app/shared/models/poll/base-poll';
|
||||||
import { BaseViewModel } from 'app/site/base/base-view-model';
|
import { BaseViewModel } from 'app/site/base/base-view-model';
|
||||||
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
|
||||||
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
||||||
@ -75,6 +75,16 @@ export class ViewMotionPoll extends ViewBasePoll<MotionPoll> implements MotionPo
|
|||||||
public get pollmethodVerbose(): string {
|
public get pollmethodVerbose(): string {
|
||||||
return MotionPollMethodsVerbose[this.pollmethod];
|
return MotionPollMethodsVerbose[this.pollmethod];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override from base poll to skip started state in analog poll type
|
||||||
|
*/
|
||||||
|
public getNextStates(): { [key: number]: string } {
|
||||||
|
if (this.poll.type === 'analog' && this.state === PollState.Created) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return super.getNextStates();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewMotionPoll extends MotionPoll {
|
export interface ViewMotionPoll extends MotionPoll {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-chip
|
<mat-chip
|
||||||
|
*ngIf="poll.getNextStates()"
|
||||||
disableRipple
|
disableRipple
|
||||||
class="poll-state active"
|
class="poll-state active"
|
||||||
[matMenuTriggerFor]="triggerMenu"
|
[matMenuTriggerFor]="triggerMenu"
|
||||||
@ -27,6 +28,14 @@
|
|||||||
>
|
>
|
||||||
{{ poll.stateVerbose }}
|
{{ poll.stateVerbose }}
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
|
<mat-chip
|
||||||
|
*ngIf="!poll.getNextStates()"
|
||||||
|
disableRipple
|
||||||
|
class="poll-state active"
|
||||||
|
[ngClass]="poll.stateVerbose.toLowerCase()"
|
||||||
|
>
|
||||||
|
{{ poll.stateVerbose }}
|
||||||
|
</mat-chip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -105,7 +114,7 @@
|
|||||||
<!-- Select state menu -->
|
<!-- Select state menu -->
|
||||||
<mat-menu #triggerMenu="matMenu">
|
<mat-menu #triggerMenu="matMenu">
|
||||||
<ng-container *ngIf="poll">
|
<ng-container *ngIf="poll">
|
||||||
<button mat-menu-item (click)="changeState(state.value)" *ngFor="let state of poll.nextStates | keyvalue">
|
<button mat-menu-item (click)="changeState(state.value)" *ngFor="let state of poll.getNextStates() | keyvalue">
|
||||||
<span translate>{{ state.key }}</span>
|
<span translate>{{ state.key }}</span>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -30,8 +30,15 @@ export abstract class BasePollComponent<V extends ViewBasePoll> extends BaseView
|
|||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
public changeState(key: PollState): void {
|
public async changeState(key: PollState): Promise<void> {
|
||||||
key === PollState.Created ? this.repo.resetPoll(this._poll) : this.repo.changePollState(this._poll);
|
if (key === PollState.Created) {
|
||||||
|
const title = this.translate.instant('Are you sure you want to reset this poll? All Votes will be lost.');
|
||||||
|
if (await this.promptService.open(title)) {
|
||||||
|
this.repo.resetPoll(this._poll).catch(this.raiseError);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.repo.changePollState(this._poll).catch(this.raiseError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||||
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
||||||
import { PercentBase } from 'app/shared/models/poll/base-poll';
|
import { PercentBase } from 'app/shared/models/poll/base-poll';
|
||||||
import { PollType } from 'app/shared/models/poll/base-poll';
|
import { PollType } from 'app/shared/models/poll/base-poll';
|
||||||
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
import { ViewAssignmentPoll } from 'app/site/assignments/models/view-assignment-poll';
|
||||||
@ -14,6 +15,7 @@ import { BaseViewComponent } from 'app/site/base/base-view';
|
|||||||
import {
|
import {
|
||||||
MajorityMethodVerbose,
|
MajorityMethodVerbose,
|
||||||
PercentBaseVerbose,
|
PercentBaseVerbose,
|
||||||
|
PollClassType,
|
||||||
PollPropertyVerbose,
|
PollPropertyVerbose,
|
||||||
PollTypeVerbose,
|
PollTypeVerbose,
|
||||||
ViewBasePoll
|
ViewBasePoll
|
||||||
@ -85,7 +87,8 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
|
|||||||
snackbar: MatSnackBar,
|
snackbar: MatSnackBar,
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private groupRepo: GroupRepositoryService,
|
private groupRepo: GroupRepositoryService,
|
||||||
public pollService: PollService
|
public pollService: PollService,
|
||||||
|
private configService: ConfigService
|
||||||
) {
|
) {
|
||||||
super(title, translate, snackbar);
|
super(title, translate, snackbar);
|
||||||
this.initContentForm();
|
this.initContentForm();
|
||||||
@ -104,6 +107,13 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.data) {
|
if (this.data) {
|
||||||
|
if (!this.data.groups_id) {
|
||||||
|
if (this.data.collectionString === ViewAssignmentPoll.COLLECTIONSTRING) {
|
||||||
|
this.data.groups_id = this.configService.instant('assignment_poll_default_groups');
|
||||||
|
} else {
|
||||||
|
this.data.groups_id = this.configService.instant('motion_poll_default_groups');
|
||||||
|
}
|
||||||
|
}
|
||||||
Object.keys(this.contentForm.controls).forEach(key => {
|
Object.keys(this.contentForm.controls).forEach(key => {
|
||||||
if (this.data[key]) {
|
if (this.data[key]) {
|
||||||
this.contentForm.get(key).setValue(this.data[key]);
|
this.contentForm.get(key).setValue(this.data[key]);
|
||||||
@ -150,12 +160,16 @@ export class PollFormComponent extends BaseViewComponent implements OnInit {
|
|||||||
* @param data Passing the properties of the poll.
|
* @param data Passing the properties of the poll.
|
||||||
*/
|
*/
|
||||||
private updatePollValues(data: { [key: string]: any }): void {
|
private updatePollValues(data: { [key: string]: any }): void {
|
||||||
this.pollValues = Object.entries(data)
|
this.pollValues = [
|
||||||
.filter(([key, _]) => key === 'type' || key === 'pollmethod')
|
[this.pollService.getVerboseNameForKey('type'), this.pollService.getVerboseNameForValue('type', data.type)]
|
||||||
.map(([key, value]) => [
|
];
|
||||||
this.pollService.getVerboseNameForKey(key),
|
// show pollmethod only for assignment polls
|
||||||
this.pollService.getVerboseNameForValue(key, value as string)
|
if (this.data.pollClassType === PollClassType.Assignment) {
|
||||||
|
this.pollValues.push([
|
||||||
|
this.pollService.getVerboseNameForKey('pollmethod'),
|
||||||
|
this.pollService.getVerboseNameForValue('pollmethod', data.pollmethod)
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
if (data.type !== 'analog') {
|
if (data.type !== 'analog') {
|
||||||
this.pollValues.push([
|
this.pollValues.push([
|
||||||
this.pollService.getVerboseNameForKey('groups'),
|
this.pollService.getVerboseNameForKey('groups'),
|
||||||
|
@ -8,6 +8,11 @@ import { ViewMotionOption } from 'app/site/motions/models/view-motion-option';
|
|||||||
import { ViewGroup } from 'app/site/users/models/view-group';
|
import { ViewGroup } from 'app/site/users/models/view-group';
|
||||||
import { ViewUser } from 'app/site/users/models/view-user';
|
import { ViewUser } from 'app/site/users/models/view-user';
|
||||||
|
|
||||||
|
export enum PollClassType {
|
||||||
|
Motion = 'motion',
|
||||||
|
Assignment = 'assignment'
|
||||||
|
}
|
||||||
|
|
||||||
export const PollClassTypeVerbose = {
|
export const PollClassTypeVerbose = {
|
||||||
motion: 'Motion poll',
|
motion: 'Motion poll',
|
||||||
assignment: 'Assignment poll'
|
assignment: 'Assignment poll'
|
||||||
@ -20,6 +25,13 @@ export const PollStateVerbose = {
|
|||||||
4: 'Published'
|
4: 'Published'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PollStateChangeActionVerbose = {
|
||||||
|
1: 'Reset',
|
||||||
|
2: 'Start voting',
|
||||||
|
3: 'End voting',
|
||||||
|
4: 'Publish'
|
||||||
|
};
|
||||||
|
|
||||||
export const PollTypeVerbose = {
|
export const PollTypeVerbose = {
|
||||||
analog: 'Analog voting',
|
analog: 'Analog voting',
|
||||||
named: 'Named voting',
|
named: 'Named voting',
|
||||||
@ -55,8 +67,6 @@ export const PercentBaseVerbose = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export abstract class ViewBasePoll<M extends BasePoll<M, any> = any> extends BaseProjectableViewModel<M> {
|
export abstract class ViewBasePoll<M extends BasePoll<M, any> = any> extends BaseProjectableViewModel<M> {
|
||||||
private _tableData: {}[] = [];
|
|
||||||
|
|
||||||
public get tableData(): {}[] {
|
public get tableData(): {}[] {
|
||||||
if (!this._tableData.length) {
|
if (!this._tableData.length) {
|
||||||
this._tableData = this.generateTableData();
|
this._tableData = this.generateTableData();
|
||||||
@ -91,24 +101,25 @@ export abstract class ViewBasePoll<M extends BasePoll<M, any> = any> extends Bas
|
|||||||
public get percentBaseVerbose(): string {
|
public get percentBaseVerbose(): string {
|
||||||
return PercentBaseVerbose[this.onehundred_percent_base];
|
return PercentBaseVerbose[this.onehundred_percent_base];
|
||||||
}
|
}
|
||||||
|
private _tableData: {}[] = [];
|
||||||
/**
|
|
||||||
* returns a mapping "verbose_state" -> "state_id" for all valid next states
|
|
||||||
*/
|
|
||||||
public get nextStates(): { [key: number]: string } {
|
|
||||||
const next_state = (this.state % Object.keys(PollStateVerbose).length) + 1;
|
|
||||||
const states = {};
|
|
||||||
states[PollStateVerbose[next_state]] = next_state;
|
|
||||||
if (this.state === PollState.Finished) {
|
|
||||||
states[PollStateVerbose[PollState.Created]] = PollState.Created;
|
|
||||||
}
|
|
||||||
return states;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract readonly pollClassType: 'motion' | 'assignment';
|
public abstract readonly pollClassType: 'motion' | 'assignment';
|
||||||
|
|
||||||
public canBeVotedFor: () => boolean;
|
public canBeVotedFor: () => boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a mapping "verbose_state" -> "state_id" for all valid next states
|
||||||
|
*/
|
||||||
|
public getNextStates(): { [key: number]: string } {
|
||||||
|
const next_state = (this.state % Object.keys(PollStateVerbose).length) + 1;
|
||||||
|
const states = {};
|
||||||
|
states[PollStateChangeActionVerbose[next_state]] = next_state;
|
||||||
|
if (this.state === PollState.Finished) {
|
||||||
|
states[PollStateChangeActionVerbose[PollState.Created]] = PollState.Created;
|
||||||
|
}
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract getSlide(): ProjectorElementBuildDeskriptor;
|
public abstract getSlide(): ProjectorElementBuildDeskriptor;
|
||||||
|
|
||||||
public abstract getContentObject(): BaseViewModel;
|
public abstract getContentObject(): BaseViewModel;
|
||||||
|
@ -5,7 +5,6 @@ from openslides.core.config import ConfigVariable
|
|||||||
def get_config_variables():
|
def get_config_variables():
|
||||||
"""
|
"""
|
||||||
Generator which yields all config variables of this app.
|
Generator which yields all config variables of this app.
|
||||||
|
|
||||||
They are grouped in 'Ballot and ballot papers' and 'PDF'. The generator has
|
They are grouped in 'Ballot and ballot papers' and 'PDF'. The generator has
|
||||||
to be evaluated during app loading (see apps.py).
|
to be evaluated during app loading (see apps.py).
|
||||||
"""
|
"""
|
||||||
@ -50,6 +49,16 @@ def get_config_variables():
|
|||||||
subgroup="Elections",
|
subgroup="Elections",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="assignment_poll_default_groups",
|
||||||
|
default_value=[],
|
||||||
|
input_type="groups",
|
||||||
|
label="Default groups for named and pseudoanonymous assignment polls",
|
||||||
|
weight=415,
|
||||||
|
group="Voting",
|
||||||
|
subgroup="Elections",
|
||||||
|
)
|
||||||
|
|
||||||
# PDF
|
# PDF
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name="assignments_pdf_title",
|
name="assignments_pdf_title",
|
||||||
|
@ -23,6 +23,7 @@ INPUT_TYPE_MAPPING = {
|
|||||||
"datetimepicker": int,
|
"datetimepicker": int,
|
||||||
"static": dict,
|
"static": dict,
|
||||||
"translations": list,
|
"translations": list,
|
||||||
|
"groups": list,
|
||||||
}
|
}
|
||||||
|
|
||||||
ALLOWED_NONE = ("datetimepicker",)
|
ALLOWED_NONE = ("datetimepicker",)
|
||||||
@ -143,6 +144,13 @@ class ConfigHandler:
|
|||||||
):
|
):
|
||||||
raise ConfigError("Invalid input. Choice does not match.")
|
raise ConfigError("Invalid input. Choice does not match.")
|
||||||
|
|
||||||
|
if config_variable.input_type == "groups":
|
||||||
|
from ..users.models import Group
|
||||||
|
|
||||||
|
groups = set(group.id for group in Group.objects.all())
|
||||||
|
if not groups.issuperset(set(value)):
|
||||||
|
raise ConfigError("Invalid input. Chosen group does not exist.")
|
||||||
|
|
||||||
for validator in config_variable.validators:
|
for validator in config_variable.validators:
|
||||||
try:
|
try:
|
||||||
validator(value)
|
validator(value)
|
||||||
|
@ -418,3 +418,13 @@ def get_config_variables():
|
|||||||
group="Voting",
|
group="Voting",
|
||||||
subgroup="Motions",
|
subgroup="Motions",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name="motion_poll_default_groups",
|
||||||
|
default_value=[],
|
||||||
|
input_type="groups",
|
||||||
|
label="Default groups for named and pseudoanonymous motion polls",
|
||||||
|
weight=430,
|
||||||
|
group="Voting",
|
||||||
|
subgroup="Motions",
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user