Merge pull request #4032 from FinnStutzenstein/new_configs
new configs for statute amendments; improved the majorityMethod confi…
This commit is contained in:
commit
10714bbbe4
@ -67,10 +67,10 @@ export class ConfigService extends OpenSlidesComponent {
|
|||||||
*
|
*
|
||||||
* @param key The config value to get from.
|
* @param key The config value to get from.
|
||||||
*/
|
*/
|
||||||
public get(key: string): Observable<any> {
|
public get<T = any>(key: string): Observable<T> {
|
||||||
if (!this.configSubjects[key]) {
|
if (!this.configSubjects[key]) {
|
||||||
this.configSubjects[key] = new BehaviorSubject<any>(this.instant(key));
|
this.configSubjects[key] = new BehaviorSubject<any>(this.instant(key));
|
||||||
}
|
}
|
||||||
return this.configSubjects[key].asObservable();
|
return this.configSubjects[key].asObservable() as Observable<T>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
<mat-form-field [formGroup]="form">
|
<mat-form-field [formGroup]="form">
|
||||||
<mat-select [formControl]="formControl" [placeholder]="listname" [multiple]="multiple" #thisSelector>
|
<mat-select [formControl]="formControl" placeholder="{{ listname | translate }}" [multiple]="multiple" #thisSelector>
|
||||||
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search>
|
<ngx-mat-select-search [formControl]="filterControl"></ngx-mat-select-search>
|
||||||
<div *ngIf="!multiple">
|
<div *ngIf="!multiple && includeNone">
|
||||||
<mat-option [value]="null"><span translate>None</span></mat-option>
|
<mat-option [value]="null">
|
||||||
|
<span translate>None</span>
|
||||||
|
</mat-option>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
</div>
|
</div>
|
||||||
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem.id">
|
<mat-option *ngFor="let selectedItem of filteredItems | async" [value]="selectedItem.id">
|
||||||
{{selectedItem.getTitle(translate)}}
|
{{ selectedItem.getTitle(translate) }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -15,7 +17,8 @@
|
|||||||
<span translate>Selected values</span>:
|
<span translate>Selected values</span>:
|
||||||
</p>
|
</p>
|
||||||
<mat-chip-list #chipList>
|
<mat-chip-list #chipList>
|
||||||
<mat-chip *ngFor="let selectedItem of thisSelector?.value" (removed)="remove(selectedItem)">{{selectedItem.name}}
|
<mat-chip *ngFor="let selectedItem of thisSelector?.value" (removed)="remove(selectedItem)">
|
||||||
|
{{ selectedItem.name }}
|
||||||
<mat-icon (click)="remove(selectedItem)">cancel</mat-icon>
|
<mat-icon (click)="remove(selectedItem)">cancel</mat-icon>
|
||||||
</mat-chip>
|
</mat-chip>
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
|
@ -22,8 +22,8 @@ import { Selectable } from '../selectable';
|
|||||||
* ngDefaultControl
|
* ngDefaultControl
|
||||||
* [multiple]="true"
|
* [multiple]="true"
|
||||||
* placeholder="Placeholder"
|
* placeholder="Placeholder"
|
||||||
* [InputListValues]="myListValues",
|
* [InputListValues]="myListValues"
|
||||||
* [form]="myform_name",
|
* [form]="myform_name"
|
||||||
* [formControl]="myformcontrol">
|
* [formControl]="myformcontrol">
|
||||||
* </os-search-value-selector>
|
* </os-search-value-selector>
|
||||||
* ```
|
* ```
|
||||||
@ -68,6 +68,12 @@ export class SearchValueSelectorComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
public multiple: boolean;
|
public multiple: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide, if none should be included, if multiple is false.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
public includeNone = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
@ -10,6 +10,10 @@ import { DataStoreService } from 'app/core/services/data-store.service';
|
|||||||
import { AgendaRepositoryService } from '../../services/agenda-repository.service';
|
import { AgendaRepositoryService } from '../../services/agenda-repository.service';
|
||||||
import { ViewItem } from '../../models/view-item';
|
import { ViewItem } from '../../models/view-item';
|
||||||
import { OperatorService } from 'app/core/services/operator.service';
|
import { OperatorService } from 'app/core/services/operator.service';
|
||||||
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { Title } from '@angular/platform-browser';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of speakers for agenda items.
|
* The list of speakers for agenda items.
|
||||||
@ -19,7 +23,7 @@ import { OperatorService } from 'app/core/services/operator.service';
|
|||||||
templateUrl: './speaker-list.component.html',
|
templateUrl: './speaker-list.component.html',
|
||||||
styleUrls: ['./speaker-list.component.scss']
|
styleUrls: ['./speaker-list.component.scss']
|
||||||
})
|
})
|
||||||
export class SpeakerListComponent implements OnInit {
|
export class SpeakerListComponent extends BaseViewComponent implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Holds the view item to the given topic
|
* Holds the view item to the given topic
|
||||||
*/
|
*/
|
||||||
@ -52,17 +56,24 @@ export class SpeakerListComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for speaker list component
|
* Constructor for speaker list component
|
||||||
|
* @param title
|
||||||
|
* @param translate
|
||||||
|
* @param snackBar
|
||||||
* @param route Angulars ActivatedRoute
|
* @param route Angulars ActivatedRoute
|
||||||
* @param DS the DataStore
|
* @param DS the DataStore
|
||||||
* @param itemRepo Repository fpr agenda items
|
* @param itemRepo Repository fpr agenda items
|
||||||
* @param op the current operator
|
* @param op the current operator
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
|
title: Title,
|
||||||
|
translate: TranslateService,
|
||||||
|
snackBar: MatSnackBar,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private DS: DataStoreService,
|
private DS: DataStoreService,
|
||||||
private itemRepo: AgendaRepositoryService,
|
private itemRepo: AgendaRepositoryService,
|
||||||
private op: OperatorService
|
private op: OperatorService
|
||||||
) {
|
) {
|
||||||
|
super(title, translate, snackBar)
|
||||||
this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) });
|
this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) });
|
||||||
this.getAgendaItemByUrl();
|
this.getAgendaItemByUrl();
|
||||||
}
|
}
|
||||||
@ -115,9 +126,8 @@ export class SpeakerListComponent implements OnInit {
|
|||||||
* Create a speaker out of an id
|
* Create a speaker out of an id
|
||||||
* @param userId the user id to add to the list. No parameter adds the operators user as speaker.
|
* @param userId the user id to add to the list. No parameter adds the operators user as speaker.
|
||||||
*/
|
*/
|
||||||
public async addNewSpeaker(userId?: number): Promise<void> {
|
public addNewSpeaker(userId?: number): void {
|
||||||
await this.itemRepo.addSpeaker(userId, this.viewItem.item);
|
this.itemRepo.addSpeaker(userId, this.viewItem.item).then(() => this.addSpeakerForm.reset(), this.raiseError);
|
||||||
this.addSpeakerForm.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +138,7 @@ export class SpeakerListComponent implements OnInit {
|
|||||||
public onSortingChange(listInNewOrder: ViewSpeaker[]): void {
|
public onSortingChange(listInNewOrder: ViewSpeaker[]): void {
|
||||||
// extract the ids from the ViewSpeaker array
|
// extract the ids from the ViewSpeaker array
|
||||||
const userIds = listInNewOrder.map(speaker => speaker.id);
|
const userIds = listInNewOrder.map(speaker => speaker.id);
|
||||||
this.itemRepo.sortSpeakers(userIds, this.viewItem.item);
|
this.itemRepo.sortSpeakers(userIds, this.viewItem.item).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -136,14 +146,14 @@ export class SpeakerListComponent implements OnInit {
|
|||||||
* @param item the speaker marked in the list
|
* @param item the speaker marked in the list
|
||||||
*/
|
*/
|
||||||
public onStartButton(item: ViewSpeaker): void {
|
public onStartButton(item: ViewSpeaker): void {
|
||||||
this.itemRepo.startSpeaker(item.id, this.viewItem.item);
|
this.itemRepo.startSpeaker(item.id, this.viewItem.item).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Click on the mic-cross button
|
* Click on the mic-cross button
|
||||||
*/
|
*/
|
||||||
public onStopButton(): void {
|
public onStopButton(): void {
|
||||||
this.itemRepo.stopSpeaker(this.viewItem.item);
|
this.itemRepo.stopSpeaker(this.viewItem.item).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,7 +161,7 @@ export class SpeakerListComponent implements OnInit {
|
|||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
public onMarkButton(item: ViewSpeaker): void {
|
public onMarkButton(item: ViewSpeaker): void {
|
||||||
this.itemRepo.markSpeaker(item.user.id, !item.marked, this.viewItem.item);
|
this.itemRepo.markSpeaker(item.user.id, !item.marked, this.viewItem.item).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,7 +169,7 @@ export class SpeakerListComponent implements OnInit {
|
|||||||
* @param item
|
* @param item
|
||||||
*/
|
*/
|
||||||
public onDeleteButton(item?: ViewSpeaker): void {
|
public onDeleteButton(item?: ViewSpeaker): void {
|
||||||
this.itemRepo.deleteSpeaker(this.viewItem.item, item ? item.id : null);
|
this.itemRepo.deleteSpeaker(this.viewItem.item, item ? item.id : null).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,7 +15,6 @@ type ConfigInputType =
|
|||||||
| 'boolean'
|
| 'boolean'
|
||||||
| 'markupText'
|
| 'markupText'
|
||||||
| 'integer'
|
| 'integer'
|
||||||
| 'majorityMethod'
|
|
||||||
| 'choice'
|
| 'choice'
|
||||||
| 'datetimepicker'
|
| 'datetimepicker'
|
||||||
| 'colorpicker'
|
| 'colorpicker'
|
||||||
|
@ -220,8 +220,7 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
|
|||||||
* @param config
|
* @param config
|
||||||
*/
|
*/
|
||||||
public createViewModel(config: Config): ViewConfig {
|
public createViewModel(config: Config): ViewConfig {
|
||||||
const vm = new ViewConfig(config);
|
return new ViewConfig(config);
|
||||||
return vm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,8 +79,7 @@
|
|||||||
<mat-accordion multi='true' class='on-transition-fade'>
|
<mat-accordion multi='true' class='on-transition-fade'>
|
||||||
|
|
||||||
<!-- MetaInfo Panel-->
|
<!-- MetaInfo Panel-->
|
||||||
<mat-expansion-panel #metaInfoPanel [expanded]="this.editMotion" class='meta-info-block meta-info-panel'>
|
<mat-expansion-panel #metaInfoPanel [expanded]="editMotion" class='meta-info-block meta-info-panel'>
|
||||||
<!-- <mat-expansion-panel #metaInfoPanel [expanded]="this.editReco && this.newReco" class='meta-info-block meta-info-panel'> -->
|
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
<mat-icon>info</mat-icon>
|
<mat-icon>info</mat-icon>
|
||||||
@ -155,8 +154,8 @@
|
|||||||
<div *ngIf="motion && motion.submitters || newMotion">
|
<div *ngIf="motion && motion.submitters || newMotion">
|
||||||
<div *ngIf="newMotion">
|
<div *ngIf="newMotion">
|
||||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('submitters_id')"
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('submitters_id')"
|
||||||
[multiple]="true" listname="{{ 'Submitters' | translate }}" [InputListValues]="this.submitterObserver"></os-search-value-selector>
|
[multiple]="true" listname="{{ 'Submitters' | translate }}" [InputListValues]="submitterObserver"></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!editMotion && !newMotion">
|
<div *ngIf="!editMotion && !newMotion">
|
||||||
@ -172,8 +171,8 @@
|
|||||||
<!-- print all motion supporters -->
|
<!-- print all motion supporters -->
|
||||||
<div *ngIf="editMotion">
|
<div *ngIf="editMotion">
|
||||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('supporters_id')"
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('supporters_id')"
|
||||||
[multiple]="true" listname="{{ 'Supporters' | translate }}" [InputListValues]="this.supporterObserver"></os-search-value-selector>
|
[multiple]="true" listname="{{ 'Supporters' | translate }}" [InputListValues]="supporterObserver"></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!editMotion && motion.hasSupporters()">
|
<div *ngIf="!editMotion && motion.hasSupporters()">
|
||||||
@ -230,8 +229,16 @@
|
|||||||
{{ motion.category }}
|
{{ motion.category }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="editMotion || newMotion">
|
<div *ngIf="editMotion || newMotion">
|
||||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('category_id')"
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('category_id')"
|
||||||
[multiple]="false" listname="{{ 'Category' | translate }}" [InputListValues]="this.categoryObserver"></os-search-value-selector>
|
[multiple]="false" listname="{{ 'Category' | translate }}" [InputListValues]="categoryObserver" includeNone="true"></os-search-value-selector>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Workflow (just during creation) -->
|
||||||
|
<div *ngIf="editMotion">
|
||||||
|
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||||
|
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="metaInfoForm.get('workflow_id')"
|
||||||
|
[multiple]="false" listname="{{ 'Workflow' | translate }}" [InputListValues]="workflowObserver"></os-search-value-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import { User } from '../../../../shared/models/users/user';
|
|||||||
import { DataStoreService } from '../../../../core/services/data-store.service';
|
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { Motion } from '../../../../shared/models/motions/motion';
|
import { Motion } from '../../../../shared/models/motions/motion';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject, Subscription, ReplaySubject, concat } from 'rxjs';
|
||||||
import { LineRange } from '../../services/diff.service';
|
import { LineRange } from '../../services/diff.service';
|
||||||
import {
|
import {
|
||||||
MotionChangeRecommendationComponent,
|
MotionChangeRecommendationComponent,
|
||||||
@ -26,6 +26,8 @@ import { BaseViewComponent } from '../../../base/base-view';
|
|||||||
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
||||||
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
|
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
|
||||||
import { ConfigService } from '../../../../core/services/config.service';
|
import { ConfigService } from '../../../../core/services/config.service';
|
||||||
|
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||||
|
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the motion detail view
|
* Component for the motion detail view
|
||||||
@ -71,9 +73,25 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
public newMotion = false;
|
public newMotion = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target motion. Might be new or old
|
* Sets the motions, e.g. via an autoupdate. Reload important things here:
|
||||||
|
* - Reload the recommendation. Not changed with autoupdates, but if the motion is loaded this needs to run.
|
||||||
*/
|
*/
|
||||||
public motion: ViewMotion;
|
public set motion(value: ViewMotion) {
|
||||||
|
this._motion = value;
|
||||||
|
this.setupRecommender();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the target motion. Might be the new one or old.
|
||||||
|
*/
|
||||||
|
public get motion(): ViewMotion {
|
||||||
|
return this._motion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the target motion. Accessed via the getter and setter.
|
||||||
|
*/
|
||||||
|
private _motion: ViewMotion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value of the configuration variable `motions_statutes_enabled` - are statutes enabled?
|
* Value of the configuration variable `motions_statutes_enabled` - are statutes enabled?
|
||||||
@ -121,6 +139,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public categoryObserver: BehaviorSubject<Category[]>;
|
public categoryObserver: BehaviorSubject<Category[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject for the Categories
|
||||||
|
*/
|
||||||
|
public workflowObserver: BehaviorSubject<Workflow[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subject for the Submitters
|
* Subject for the Submitters
|
||||||
*/
|
*/
|
||||||
@ -141,6 +164,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
public recommender: string;
|
public recommender: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The subscription to the recommender config variable.
|
||||||
|
*/
|
||||||
|
private recommenderSubscription: Subscription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constuct the detail view.
|
* Constuct the detail view.
|
||||||
*
|
*
|
||||||
@ -185,15 +213,17 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
this.submitterObserver = new BehaviorSubject(DS.getAll(User));
|
this.submitterObserver = new BehaviorSubject(DS.getAll(User));
|
||||||
this.supporterObserver = new BehaviorSubject(DS.getAll(User));
|
this.supporterObserver = new BehaviorSubject(DS.getAll(User));
|
||||||
this.categoryObserver = new BehaviorSubject(DS.getAll(Category));
|
this.categoryObserver = new BehaviorSubject(DS.getAll(Category));
|
||||||
|
this.workflowObserver = new BehaviorSubject(DS.getAll(Workflow));
|
||||||
|
|
||||||
// Make sure the subjects are updated, when a new Model for the type arrives
|
// Make sure the subjects are updated, when a new Model for the type arrives
|
||||||
this.DS.changeObservable.subscribe(newModel => {
|
this.DS.changeObservable.subscribe(newModel => {
|
||||||
if (newModel instanceof User) {
|
if (newModel instanceof User) {
|
||||||
this.submitterObserver.next(DS.getAll(User));
|
this.submitterObserver.next(DS.getAll(User));
|
||||||
this.supporterObserver.next(DS.getAll(User));
|
this.supporterObserver.next(DS.getAll(User));
|
||||||
}
|
} else if (newModel instanceof Category) {
|
||||||
if (newModel instanceof Category) {
|
|
||||||
this.categoryObserver.next(DS.getAll(Category));
|
this.categoryObserver.next(DS.getAll(Category));
|
||||||
|
} else if (newModel instanceof Workflow) {
|
||||||
|
this.workflowObserver.next(DS.getAll(Workflow));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.configService.get('motions_statutes_enabled').subscribe(
|
this.configService.get('motions_statutes_enabled').subscribe(
|
||||||
@ -282,6 +312,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
recommendation_id: [''],
|
recommendation_id: [''],
|
||||||
submitters_id: [],
|
submitters_id: [],
|
||||||
supporters_id: [],
|
supporters_id: [],
|
||||||
|
workflow_id: [],
|
||||||
origin: ['']
|
origin: ['']
|
||||||
});
|
});
|
||||||
this.contentForm = this.formBuilder.group({
|
this.contentForm = this.formBuilder.group({
|
||||||
@ -291,6 +322,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
statute_amendment: [''], // Internal value for the checkbox, not saved to the model
|
statute_amendment: [''], // Internal value for the checkbox, not saved to the model
|
||||||
statute_paragraph_id: ['']
|
statute_paragraph_id: ['']
|
||||||
});
|
});
|
||||||
|
this.updateWorkflowIdForCreateForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -474,6 +506,21 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateWorkflowIdForCreateForm(): void {
|
||||||
|
const isStatuteAmendment = !!this.contentForm.get('statute_amendment').value;
|
||||||
|
const configKey = isStatuteAmendment ? 'motions_statute_amendments_workflow' : 'motions_workflow';
|
||||||
|
// TODO: This should just be a takeWhile(id => !id), but should include the last one where the id is OK.
|
||||||
|
// takeWhile will get a inclusive parameter, see https://github.com/ReactiveX/rxjs/pull/4115
|
||||||
|
this.configService.get<string>(configKey).pipe(multicast(
|
||||||
|
() => new ReplaySubject(1),
|
||||||
|
(ids) => ids.pipe(takeWhile(id => !id), o => concat(o, ids.pipe(take(1))))
|
||||||
|
), skipWhile(id => !id)).subscribe(id => {
|
||||||
|
this.metaInfoForm.patchValue({
|
||||||
|
workflow_id: parseInt(id, 10),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the checkbox is deactivated, the statute_paragraph_id-field needs to be reset, as only that field is saved
|
* If the checkbox is deactivated, the statute_paragraph_id-field needs to be reset, as only that field is saved
|
||||||
* @param {MatCheckboxChange} $event
|
* @param {MatCheckboxChange} $event
|
||||||
@ -482,6 +529,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
this.contentForm.patchValue({
|
this.contentForm.patchValue({
|
||||||
statute_paragraph_id: null
|
statute_paragraph_id: null
|
||||||
});
|
});
|
||||||
|
this.updateWorkflowIdForCreateForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -548,9 +596,13 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Observes the repository for changes in the motion recommender
|
* Observes the repository for changes in the motion recommender
|
||||||
*/
|
*/
|
||||||
public getRecommender(): void {
|
public setupRecommender(): void {
|
||||||
this.repo.getRecommenderObservable().subscribe(newRecommender => {
|
const configKey = this.motion.isStatuteAmendment() ? 'motions_statute_recommendations_by' : 'motions_recommendations_by';
|
||||||
this.recommender = newRecommender;
|
if (this.recommenderSubscription) {
|
||||||
|
this.recommenderSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
this.recommenderSubscription = this.configService.get(configKey).subscribe(recommender => {
|
||||||
|
this.recommender = recommender;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,10 +623,9 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Init.
|
* Init.
|
||||||
* Calls getRecommender and sets the surrounding motions to navigate back and forth
|
* Sets the surrounding motions to navigate back and forth
|
||||||
*/
|
*/
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.getRecommender();
|
|
||||||
this.repo.getViewModelListObservable().subscribe(newMotionList => {
|
this.repo.getViewModelListObservable().subscribe(newMotionList => {
|
||||||
if (newMotionList) {
|
if (newMotionList) {
|
||||||
this.allMotions = newMotionList;
|
this.allMotions = newMotionList;
|
||||||
|
@ -126,6 +126,10 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
return this._workflow;
|
return this._workflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get workflow_id(): number {
|
||||||
|
return this.motion ? this.motion.workflow_id : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get state(): WorkflowState {
|
public get state(): WorkflowState {
|
||||||
return this._state;
|
return this._state;
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,6 @@ import { ViewStatuteParagraph } from '../models/view-statute-paragraph';
|
|||||||
import { Identifiable } from '../../../shared/models/base/identifiable';
|
import { Identifiable } from '../../../shared/models/base/identifiable';
|
||||||
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
|
import { CollectionStringModelMapperService } from '../../../core/services/collectionStringModelMapper.service';
|
||||||
import { HttpService } from 'app/core/services/http.service';
|
import { HttpService } from 'app/core/services/http.service';
|
||||||
import { ConfigService } from 'app/core/services/config.service';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { Item } from 'app/shared/models/agenda/item';
|
||||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
|
|
||||||
@ -52,7 +50,6 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
mapperService: CollectionStringModelMapperService,
|
mapperService: CollectionStringModelMapperService,
|
||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private httpService: HttpService,
|
private httpService: HttpService,
|
||||||
private configService: ConfigService,
|
|
||||||
private readonly lineNumbering: LinenumberingService,
|
private readonly lineNumbering: LinenumberingService,
|
||||||
private readonly diff: DiffService
|
private readonly diff: DiffService
|
||||||
) {
|
) {
|
||||||
@ -144,15 +141,6 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
await this.httpService.put(restPath, { recommendation: stateId });
|
await this.httpService.put(restPath, { recommendation: stateId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the motions_recommendations_by observable from the config service
|
|
||||||
*
|
|
||||||
* @return an observable that contains the motions "Recommended by" string
|
|
||||||
*/
|
|
||||||
public getRecommenderObservable(): Observable<string> {
|
|
||||||
return this.configService.get('motions_recommendations_by');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the changed nodes to the server.
|
* Sends the changed nodes to the server.
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from openslides.core.config import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
|
from openslides.poll.majority import majorityMethods
|
||||||
|
|
||||||
|
|
||||||
def get_config_variables():
|
def get_config_variables():
|
||||||
@ -47,8 +48,9 @@ def get_config_variables():
|
|||||||
# TODO: Add server side validation of the choices.
|
# TODO: Add server side validation of the choices.
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='assignments_poll_default_majority_method',
|
name='assignments_poll_default_majority_method',
|
||||||
default_value='simple_majority',
|
default_value=majorityMethods[0]['value'],
|
||||||
input_type='majorityMethod',
|
input_type='choice',
|
||||||
|
choices=majorityMethods,
|
||||||
label='Required majority',
|
label='Required majority',
|
||||||
help_text='Default method to check whether a candidate has reached the required majority.',
|
help_text='Default method to check whether a candidate has reached the required majority.',
|
||||||
weight=425,
|
weight=425,
|
||||||
|
@ -29,7 +29,6 @@ INPUT_TYPE_MAPPING = {
|
|||||||
'choice': str,
|
'choice': str,
|
||||||
'colorpicker': str,
|
'colorpicker': str,
|
||||||
'datetimepicker': int,
|
'datetimepicker': int,
|
||||||
'majorityMethod': str,
|
|
||||||
'static': dict,
|
'static': dict,
|
||||||
'translations': list,
|
'translations': list,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
from openslides.core.config import ConfigVariable
|
from openslides.core.config import ConfigVariable
|
||||||
|
from openslides.poll.majority import majorityMethods
|
||||||
|
|
||||||
from .models import Workflow
|
from .models import Workflow
|
||||||
|
|
||||||
@ -34,6 +35,16 @@ def get_config_variables():
|
|||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='General')
|
subgroup='General')
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='motions_statute_amendments_workflow',
|
||||||
|
default_value='1',
|
||||||
|
input_type='choice',
|
||||||
|
label='Workflow of new statute amendments',
|
||||||
|
choices=get_workflow_choices,
|
||||||
|
weight=312,
|
||||||
|
group='Motions',
|
||||||
|
subgroup='General')
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_identifier',
|
name='motions_identifier',
|
||||||
default_value='per_category',
|
default_value='per_category',
|
||||||
@ -124,6 +135,16 @@ def get_config_variables():
|
|||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='General')
|
subgroup='General')
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='motions_statute_recommendations_by',
|
||||||
|
default_value='',
|
||||||
|
label='Name of statute recommender',
|
||||||
|
help_text='Will be displayed as label before selected statute recommendation. ' +
|
||||||
|
'Use an empty value to disable the statute recommendation system.',
|
||||||
|
weight=333,
|
||||||
|
group='Motions',
|
||||||
|
subgroup='General')
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_recommendation_text_mode',
|
name='motions_recommendation_text_mode',
|
||||||
default_value='original',
|
default_value='original',
|
||||||
@ -134,7 +155,7 @@ def get_config_variables():
|
|||||||
{'value': 'changed', 'display_name': 'Changed version'},
|
{'value': 'changed', 'display_name': 'Changed version'},
|
||||||
{'value': 'diff', 'display_name': 'Diff version'},
|
{'value': 'diff', 'display_name': 'Diff version'},
|
||||||
{'value': 'agreed', 'display_name': 'Final version'}),
|
{'value': 'agreed', 'display_name': 'Final version'}),
|
||||||
weight=333,
|
weight=334,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='General')
|
subgroup='General')
|
||||||
|
|
||||||
@ -145,7 +166,7 @@ def get_config_variables():
|
|||||||
default_value=False,
|
default_value=False,
|
||||||
input_type='boolean',
|
input_type='boolean',
|
||||||
label='Activate statutes',
|
label='Activate statutes',
|
||||||
weight=334,
|
weight=335,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='General')
|
subgroup='General')
|
||||||
|
|
||||||
@ -155,7 +176,7 @@ def get_config_variables():
|
|||||||
default_value=False,
|
default_value=False,
|
||||||
input_type='boolean',
|
input_type='boolean',
|
||||||
label='Activate amendments',
|
label='Activate amendments',
|
||||||
weight=335,
|
weight=336,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Amendments')
|
subgroup='Amendments')
|
||||||
|
|
||||||
@ -233,8 +254,9 @@ def get_config_variables():
|
|||||||
# TODO: Add server side validation of the choices.
|
# TODO: Add server side validation of the choices.
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_poll_default_majority_method',
|
name='motions_poll_default_majority_method',
|
||||||
default_value='simple_majority',
|
default_value=majorityMethods[0]['value'],
|
||||||
input_type='majorityMethod',
|
input_type='choice',
|
||||||
|
choices=majorityMethods,
|
||||||
label='Required majority',
|
label='Required majority',
|
||||||
help_text='Default method to check whether a motion has reached the required majority.',
|
help_text='Default method to check whether a motion has reached the required majority.',
|
||||||
weight=357,
|
weight=357,
|
||||||
|
7
openslides/poll/majority.py
Normal file
7
openslides/poll/majority.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Common majority methods for all apps using polls. The first one should be the default.
|
||||||
|
majorityMethods = (
|
||||||
|
{'value': 'simple_majority', 'display_name': 'Simple majority'},
|
||||||
|
{'value': 'two-thirds_majority', 'display_name': 'Two-thirds majority'},
|
||||||
|
{'value': 'three-quarters_majority', 'display_name': 'Three-quarters majority'},
|
||||||
|
{'value': 'disabled', 'display_name': 'Disabled'},
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user