Merge pull request #3996 from FinnStutzenstein/client_perms_for_motion
can_manage_metadata and categories on motion create
This commit is contained in:
commit
a2d736bae9
@ -1,6 +1,6 @@
|
||||
import { Component, OnInit, Input, ViewChild } from '@angular/core';
|
||||
import { Component, OnInit, Input, ViewChild, OnDestroy } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { Subject, ReplaySubject, BehaviorSubject } from 'rxjs';
|
||||
import { Subject, ReplaySubject, BehaviorSubject, Subscription } from 'rxjs';
|
||||
import { MatSelect } from '@angular/material';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -35,7 +35,7 @@ import { Selectable } from '../selectable';
|
||||
templateUrl: './search-value-selector.component.html',
|
||||
styleUrls: ['./search-value-selector.component.scss']
|
||||
})
|
||||
export class SearchValueSelectorComponent implements OnInit {
|
||||
export class SearchValueSelectorComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* ngModel variable - Deprecated with Angular 7
|
||||
* DO NOT USE: READ AT remove() FUNCTION!
|
||||
@ -52,6 +52,16 @@ export class SearchValueSelectorComponent implements OnInit {
|
||||
*/
|
||||
public filteredItems: ReplaySubject<Selectable[]> = new ReplaySubject<Selectable[]>(1);
|
||||
|
||||
/**
|
||||
* The inputlist subject.
|
||||
*/
|
||||
private _inputListSubject: BehaviorSubject<Selectable[]>;
|
||||
|
||||
/**
|
||||
* Saves the current subscription to _inputListSubject.
|
||||
*/
|
||||
private _inputListSubscription: Subscription = null;
|
||||
|
||||
/**
|
||||
* Decide if this should be a single or multi-select-field
|
||||
*/
|
||||
@ -59,10 +69,20 @@ export class SearchValueSelectorComponent implements OnInit {
|
||||
public multiple: boolean;
|
||||
|
||||
/**
|
||||
* The Input List Values
|
||||
* The inputlist subject. Subscribes to it and updates the selector, if the subject
|
||||
* changes its values.
|
||||
*/
|
||||
@Input()
|
||||
public InputListValues: BehaviorSubject<Selectable[]>;
|
||||
public set InputListValues(value: BehaviorSubject<Selectable[]>) {
|
||||
// unsubscribe to old subscription.
|
||||
if (this._inputListSubscription) {
|
||||
this._inputListSubscription.unsubscribe();
|
||||
}
|
||||
this._inputListSubject = value;
|
||||
this._inputListSubscription = this._inputListSubject.subscribe(values => {
|
||||
this.filterItems();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder of the List
|
||||
@ -111,31 +131,43 @@ export class SearchValueSelectorComponent implements OnInit {
|
||||
* onInit with filter ans subscription on filter
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
this.filteredItems.next(this.InputListValues.getValue());
|
||||
if (this._inputListSubject) {
|
||||
this.filteredItems.next(this._inputListSubject.getValue());
|
||||
}
|
||||
// listen to value changes
|
||||
this.filterControl.valueChanges.pipe(takeUntil(this._onDestroy)).subscribe(() => {
|
||||
this.filterItems();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe on destroing.
|
||||
*/
|
||||
public ngOnDestroy(): void {
|
||||
if (this._inputListSubscription) {
|
||||
this._inputListSubscription.unsubscribe();
|
||||
}
|
||||
this._onDestroy.next();
|
||||
}
|
||||
|
||||
/**
|
||||
* the filter function itself
|
||||
*/
|
||||
private filterItems(): void {
|
||||
if (!this.InputListValues) {
|
||||
if (!this._inputListSubject) {
|
||||
return;
|
||||
}
|
||||
// get the search keyword
|
||||
let search = this.filterControl.value;
|
||||
if (!search) {
|
||||
this.filteredItems.next(this.InputListValues.getValue());
|
||||
this.filteredItems.next(this._inputListSubject.getValue());
|
||||
return;
|
||||
} else {
|
||||
search = search.toLowerCase();
|
||||
}
|
||||
// filter the values
|
||||
this.filteredItems.next(
|
||||
this.InputListValues.getValue().filter(
|
||||
this._inputListSubject.getValue().filter(
|
||||
selectedItem =>
|
||||
selectedItem
|
||||
.toString()
|
||||
|
@ -39,6 +39,13 @@ export class PermsDirective extends OpenSlidesComponent {
|
||||
*/
|
||||
private alternative: boolean;
|
||||
|
||||
/**
|
||||
* Switch, to invert the result of checkPermission. Usefull for using osPerms as if-else:
|
||||
* For one element you can use `*osPerms="'perm'"` and for the else-element use
|
||||
* `*osPerms="'perm';complement: true"`.
|
||||
*/
|
||||
private complement: boolean;
|
||||
|
||||
/**
|
||||
* Constructs the directive once. Observes the operator for it's groups so the
|
||||
* directive can perform changes dynamically
|
||||
@ -85,6 +92,15 @@ export class PermsDirective extends OpenSlidesComponent {
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* COmes from the view.
|
||||
*/
|
||||
@Input('osPermsComplement')
|
||||
public set osPermsComplement(value: boolean) {
|
||||
this.complement = value;
|
||||
this.updateView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows or hides certain content in the view.
|
||||
*/
|
||||
@ -108,6 +124,11 @@ export class PermsDirective extends OpenSlidesComponent {
|
||||
* Returns true if the users permissions fit.
|
||||
*/
|
||||
private checkPermissions(): boolean {
|
||||
return this.permissions.length === 0 || this.operator.hasPerms(...this.permissions);
|
||||
const hasPerms = this.permissions.length === 0 || this.operator.hasPerms(...this.permissions);
|
||||
if (this.complement) {
|
||||
return !hasPerms;
|
||||
} else {
|
||||
return hasPerms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,8 +94,8 @@
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<os-motion-comments [motion]="motion"></os-motion-comments>
|
||||
<os-personal-note [motion]="motion"></os-personal-note>
|
||||
<os-motion-comments *ngIf="!newMotion" [motion]="motion"></os-motion-comments>
|
||||
<os-personal-note *ngIf="!newMotion" [motion]="motion"></os-personal-note>
|
||||
|
||||
<!-- Content -->
|
||||
<mat-expansion-panel #contentPanel [expanded]='true'>
|
||||
@ -122,8 +122,8 @@
|
||||
<ng-container *ngTemplateOutlet="metaInfoTemplate"></ng-container>
|
||||
</div>
|
||||
|
||||
<os-motion-comments [motion]="motion"></os-motion-comments>
|
||||
<os-personal-note [motion]="motion"></os-personal-note>
|
||||
<os-motion-comments *ngIf="!newMotion" [motion]="motion"></os-motion-comments>
|
||||
<os-personal-note *ngIf="!newMotion" [motion]="motion"></os-personal-note>
|
||||
|
||||
</div>
|
||||
<div class="desktop-right ">
|
||||
@ -153,8 +153,8 @@
|
||||
|
||||
<!-- Submitter -->
|
||||
<div *ngIf="motion && motion.submitters || editMotion">
|
||||
<div *ngIf="editMotion && newMotion">
|
||||
<div *ngIf="motion && editMotion">
|
||||
<div *ngIf="editMotion">
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('submitters_id')"
|
||||
[multiple]="true" listname="Submitter" [InputListValues]="this.submitterObserver"></os-search-value-selector>
|
||||
</div>
|
||||
@ -171,7 +171,7 @@
|
||||
<div *ngIf='motion && motion.hasSupporters() || editMotion'>
|
||||
<!-- print all motion supporters -->
|
||||
<div *ngIf="editMotion">
|
||||
<div *ngIf="motion && editMotion">
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('supporters_id')"
|
||||
[multiple]="true" listname="Supporter" [InputListValues]="this.supporterObserver"></os-search-value-selector>
|
||||
</div>
|
||||
@ -179,24 +179,33 @@
|
||||
<div *ngIf="!editMotion && motion.hasSupporters()">
|
||||
<h3 translate>Supporters</h3>
|
||||
<ul *ngFor="let supporter of motion.supporters">
|
||||
<li>{{supporter.full_name}}</li>
|
||||
<li>{{ supporter.full_name }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- State -->
|
||||
<div *ngIf='motion && motion.state'>
|
||||
<mat-form-field *ngIf="!editMotion && !newMotion">
|
||||
<mat-select placeholder='State' formControlName='state_id' (selectionChange)="onChangeState($event)">
|
||||
<mat-option [value]="motion.state_id">{{motion.state}}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option *ngFor="let state of motion.nextStates" [value]="state.id">{{state}}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option [value]="null">
|
||||
<mat-icon>replay</mat-icon><span translate>Reset State</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata'];complement:true">
|
||||
<ng-container *ngIf="!newMotion">
|
||||
<h3 translate>State</h3>
|
||||
{{ motion.state }}
|
||||
</ng-container>
|
||||
</div>
|
||||
<div *ngIf="!editMotion">
|
||||
<mat-form-field *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<mat-select placeholder='State' formControlName='state_id' (selectionChange)="onChangeState($event)">
|
||||
<mat-option [value]="motion.state_id">{{ motion.state }}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option *ngFor="let state of motion.nextStates" [value]="state.id">{{ state }}</mat-option>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-option [value]="null">
|
||||
<mat-icon>replay</mat-icon>
|
||||
<span translate>Reset State</span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recommendation -->
|
||||
@ -204,7 +213,7 @@
|
||||
<div *ngIf='motion && motion.state && recommender'>
|
||||
<mat-form-field *ngIf="!editMotion && !newMotion">
|
||||
<mat-select [placeholder]=recommender formControlName='recommendation_id' (selectionChange)="onChangerRecommenderState($event)">
|
||||
<mat-option *ngFor="let state of motion.possibleRecommendations" [value]="state.id">{{state}}</mat-option>
|
||||
<mat-option *ngFor="let recommendation of motion.possibleRecommendations" [value]="recommendation.id">{{ recommendation }}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@ -215,7 +224,7 @@
|
||||
<h3 translate>Category</h3>
|
||||
{{motion.category}}
|
||||
</div>
|
||||
<div *ngIf="editMotion">
|
||||
<div *ngIf="editMotion || newMotion">
|
||||
<os-search-value-selector ngDefaultControl [form]="metaInfoForm" [formControl]="this.metaInfoForm.get('category_id')"
|
||||
[multiple]="false" listname="Category" [InputListValues]="this.categoryObserver"></os-search-value-selector>
|
||||
</div>
|
||||
@ -225,11 +234,13 @@
|
||||
<div *ngIf="motion && motion.origin || editMotion">
|
||||
<div *ngIf='!editMotion'>
|
||||
<h3 translate> Origin</h3>
|
||||
{{motion.origin}}
|
||||
{{ motion.origin }}
|
||||
</div>
|
||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
||||
<mat-form-field *ngIf="editMotion">
|
||||
<input matInput placeholder='Origin' formControlName='origin' [value]='motionCopy.origin'>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<mat-form-field *ngIf="editMotion">
|
||||
<input matInput placeholder='Origin' formControlName='origin' [value]='motionCopy.origin'>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Voting -->
|
||||
@ -258,7 +269,8 @@
|
||||
<h4>{{motion.title}}</h4>
|
||||
</div>
|
||||
<mat-form-field *ngIf="editMotion" class="wide-form">
|
||||
<input matInput osAutofocus placeholder='Title' formControlName='title' [value]='motionCopy.title'>
|
||||
<input matInput osAutofocus placeholder='Title' formControlName='title' [value]='motionCopy.title'
|
||||
required>
|
||||
</mat-form-field>
|
||||
|
||||
</div>
|
||||
@ -278,7 +290,7 @@
|
||||
[scrollToChange]="scrollToChange" (createChangeRecommendation)="createChangeRecommendation($event)"></os-motion-detail-diff>
|
||||
</ng-container>
|
||||
<mat-form-field *ngIf="motion && editMotion" class="wide-form">
|
||||
<textarea matInput placeholder='Motion Text' formControlName='text' [value]='motionCopy.text'></textarea>
|
||||
<textarea matInput placeholder='Motion Text' formControlName='text' [value]='motionCopy.text' required></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
|
@ -105,17 +105,17 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
/**
|
||||
* Subject for the Categories
|
||||
*/
|
||||
public categoryObserver: BehaviorSubject<Array<Category>>;
|
||||
public categoryObserver: BehaviorSubject<Category[]>;
|
||||
|
||||
/**
|
||||
* Subject for the Submitters
|
||||
*/
|
||||
public submitterObserver: BehaviorSubject<Array<User>>;
|
||||
public submitterObserver: BehaviorSubject<User[]>;
|
||||
|
||||
/**
|
||||
* Subject for the Supporters
|
||||
*/
|
||||
public supporterObserver: BehaviorSubject<Array<User>>;
|
||||
public supporterObserver: BehaviorSubject<User[]>;
|
||||
|
||||
/**
|
||||
* Value for os-motion-detail-diff: when this is set, that component scrolls to the given change
|
||||
@ -503,7 +503,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
||||
* Determine if the user has the correct requirements to alter the motion
|
||||
*/
|
||||
public opCanEdit(): boolean {
|
||||
return this.op.hasPerms('motions.can_manage');
|
||||
return this.op.hasPerms('motions.can_manage', 'motions.can_manage_metadata');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,14 +134,15 @@ class MotionViewSet(ModelViewSet):
|
||||
'title',
|
||||
'text',
|
||||
'reason',
|
||||
'category_id',
|
||||
]
|
||||
if parent_motion is not None:
|
||||
# For creating amendments.
|
||||
whitelist.extend([
|
||||
'parent_id',
|
||||
'amendment_paragraphs',
|
||||
'category_id', # This will be set to the matching
|
||||
'motion_block_id', # values from parent_motion.
|
||||
'motion_block_id', # This and the category_id will be set to the matching
|
||||
# values from parent_motion.
|
||||
])
|
||||
request.data['category_id'] = parent_motion.category_id
|
||||
request.data['motion_block_id'] = parent_motion.motion_block_id
|
||||
|
Loading…
Reference in New Issue
Block a user