check permissions for motions
This commit is contained in:
parent
2d40072f44
commit
fb51b54d5c
@ -1,17 +1,24 @@
|
|||||||
<h4 translate>
|
<h4 translate>
|
||||||
<span translate>Submitters</span>
|
<span translate>Submitters</span>
|
||||||
<button class="small-button" type="button" mat-icon-button disableRipple *ngIf="!isEditMode" (click)="onEdit()">
|
<button
|
||||||
|
class="small-button"
|
||||||
|
type="button"
|
||||||
|
mat-icon-button
|
||||||
|
disableRipple
|
||||||
|
*ngIf="!isEditMode && perms.isAllowed('change_metadata')"
|
||||||
|
(click)="onEdit()"
|
||||||
|
>
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div *ngIf="!isEditMode">
|
<div *ngIf="!isEditMode || !perms.isAllowed('change_metadata')">
|
||||||
<mat-chip-list *ngFor="let submitter of motion.submitters" class="user">
|
<mat-chip-list *ngFor="let submitter of motion.submitters" class="user">
|
||||||
<mat-chip>{{ submitter.full_name }}</mat-chip>
|
<mat-chip>{{ submitter.full_name }}</mat-chip>
|
||||||
</mat-chip-list>
|
</mat-chip-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="isEditMode">
|
<div *ngIf="isEditMode && perms.isAllowed('change_metadata')">
|
||||||
<os-sorting-list
|
<os-sorting-list
|
||||||
[input]="editSubmitterObservable"
|
[input]="editSubmitterObservable"
|
||||||
[live]="true"
|
[live]="true"
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { FormGroup, FormControl } from '@angular/forms';
|
import { FormGroup, FormControl } from '@angular/forms';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { MatSnackBar } from '@angular/material';
|
|
||||||
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
|
||||||
import { ViewMotion } from '../../models/view-motion';
|
|
||||||
import { User } from 'app/shared/models/users/user';
|
|
||||||
import { DataStoreService } from 'app/core/services/data-store.service';
|
|
||||||
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
|
||||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||||
|
import { DataStoreService } from 'app/core/services/data-store.service';
|
||||||
|
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
||||||
|
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
||||||
|
import { User } from 'app/shared/models/users/user';
|
||||||
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the motion comments view
|
* Component for the motion comments view
|
||||||
@ -61,13 +62,15 @@ export class ManageSubmittersComponent extends BaseViewComponent {
|
|||||||
* @param matSnackBar
|
* @param matSnackBar
|
||||||
* @param DS
|
* @param DS
|
||||||
* @param repo
|
* @param repo
|
||||||
|
* @param perms permission checks for the motion
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
title: Title,
|
title: Title,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
private DS: DataStoreService,
|
private DS: DataStoreService,
|
||||||
private repo: MotionRepositoryService
|
private repo: MotionRepositoryService,
|
||||||
|
public perms: LocalPermissionsService
|
||||||
) {
|
) {
|
||||||
super(title, translate, matSnackBar);
|
super(title, translate, matSnackBar);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<os-head-bar
|
<os-head-bar
|
||||||
[mainButton]="opCanEdit()"
|
[mainButton]="perms.isAllowed('update_motion', motion)"
|
||||||
mainButtonIcon="edit"
|
mainButtonIcon="edit"
|
||||||
[nav]="false"
|
[nav]="false"
|
||||||
[editMode]="editMotion"
|
[editMode]="editMotion"
|
||||||
@ -61,13 +61,12 @@
|
|||||||
<mat-menu #motionExtraMenu="matMenu">
|
<mat-menu #motionExtraMenu="matMenu">
|
||||||
<div *ngIf="motion">
|
<div *ngIf="motion">
|
||||||
<!-- PDF -->
|
<!-- PDF -->
|
||||||
<button mat-menu-item
|
<button mat-menu-item (click)="onDownloadPdf()">
|
||||||
(click)="onDownloadPdf()">
|
|
||||||
<mat-icon>picture_as_pdf</mat-icon>
|
<mat-icon>picture_as_pdf</mat-icon>
|
||||||
<span translate>PDF</span>
|
<span translate>PDF</span>
|
||||||
</button>
|
</button>
|
||||||
<!-- List of speakers -->
|
<!-- List of speakers -->
|
||||||
<button mat-menu-item [routerLink]="getSpeakerLink()">
|
<button mat-menu-item [routerLink]="getSpeakerLink()" *osPerms="'agenda.can_see'">
|
||||||
<mat-icon>mic</mat-icon>
|
<mat-icon>mic</mat-icon>
|
||||||
<span translate>List of speakers</span>
|
<span translate>List of speakers</span>
|
||||||
</button>
|
</button>
|
||||||
@ -80,7 +79,7 @@
|
|||||||
<button
|
<button
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
(click)="createAmendment()"
|
(click)="createAmendment()"
|
||||||
*ngIf="amendmentsEnabled && motion && !motion.isParagraphBasedAmendment()"
|
*ngIf="perms.isAllowed('can_create_amendments', motion)"
|
||||||
>
|
>
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
<span translate>New amendment</span>
|
<span translate>New amendment</span>
|
||||||
@ -95,13 +94,14 @@
|
|||||||
<span translate>Show entire motion text</span>
|
<span translate>Show entire motion text</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<mat-divider></mat-divider>
|
<div *ngIf="perms.isAllowed('manage')">
|
||||||
|
<mat-divider></mat-divider>
|
||||||
<!-- Delete -->
|
<!-- Delete -->
|
||||||
<button mat-menu-item class="red-warning-text" (click)="deleteMotionButton()">
|
<button mat-menu-item class="red-warning-text" (click)="deleteMotionButton()">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
<span translate>Delete</span>
|
<span translate>Delete</span>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
@ -110,9 +110,7 @@
|
|||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title on-transition-fade" *ngIf="motion && !editMotion">
|
<div class="title on-transition-fade" *ngIf="motion && !editMotion">
|
||||||
<div class="title-line">
|
<div class="title-line">
|
||||||
<h1>
|
<h1>{{ motion.title }}</h1>
|
||||||
{{ motion.title }}
|
|
||||||
</h1>
|
|
||||||
<button mat-icon-button color="primary" (click)="toggleFavorite()">
|
<button mat-icon-button color="primary" (click)="toggleFavorite()">
|
||||||
<mat-icon>{{ motion.star ? 'star' : 'star_border' }}</mat-icon>
|
<mat-icon>{{ motion.star ? 'star' : 'star_border' }}</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
@ -124,18 +122,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #mobileView>
|
<ng-template #mobileView>
|
||||||
|
|
||||||
<!-- Meta info -->
|
<!-- Meta info -->
|
||||||
<div class="hspacing">
|
<div class="hspacing"><ng-container *ngTemplateOutlet="metaInfoTemplate"></ng-container></div>
|
||||||
<ng-container *ngTemplateOutlet="metaInfoTemplate"></ng-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-divider class="spacer-top-10 spacer-bottom-20"></mat-divider>
|
<mat-divider class="spacer-top-10 spacer-bottom-20"></mat-divider>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="hspacing">
|
<div class="hspacing"><ng-container *ngTemplateOutlet="contentTemplate"></ng-container></div>
|
||||||
<ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<mat-divider class="spacer-top-10 spacer-bottom-20"></mat-divider>
|
<mat-divider class="spacer-top-10 spacer-bottom-20"></mat-divider>
|
||||||
|
|
||||||
@ -145,11 +138,14 @@
|
|||||||
<!-- Personal note -->
|
<!-- Personal note -->
|
||||||
<os-personal-note *ngIf="!editMotion" [motion]="motion"></os-personal-note>
|
<os-personal-note *ngIf="!editMotion" [motion]="motion"></os-personal-note>
|
||||||
|
|
||||||
<!-- Motoin log -->
|
<ng-container *ngTemplateOutlet="motionLogTemplate"></ng-container>
|
||||||
<button mat-button *ngIf="canShowLog" (click)="motionLogExpanded =!motionLogExpanded">
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #motionLogTemplate>
|
||||||
|
<button mat-button *ngIf="canShowLog" (click)="motionLogExpanded = !motionLogExpanded">
|
||||||
<span translate>Show motion log</span>
|
<span translate>Show motion log</span>
|
||||||
</button>
|
</button>
|
||||||
<os-motion-log *ngIf="motionLogExpanded" [motion]="motion"></os-motion-log>
|
<os-motion-log *ngIf="canShowLog && motionLogExpanded" [motion]="motion"></os-motion-log>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #desktopView>
|
<ng-template #desktopView>
|
||||||
@ -162,10 +158,7 @@
|
|||||||
|
|
||||||
<os-motion-comments *ngIf="!editMotion" [motion]="motion"></os-motion-comments>
|
<os-motion-comments *ngIf="!editMotion" [motion]="motion"></os-motion-comments>
|
||||||
<os-personal-note *ngIf="!editMotion" [motion]="motion"></os-personal-note>
|
<os-personal-note *ngIf="!editMotion" [motion]="motion"></os-personal-note>
|
||||||
<button mat-button *ngIf="canShowLog" (click)="motionLogExpanded =!motionLogExpanded">
|
<ng-container *ngTemplateOutlet="motionLogTemplate"></ng-container>
|
||||||
<span translate>Show motion log</span>
|
|
||||||
</button>
|
|
||||||
<os-motion-log *ngIf="motionLogExpanded" [motion]="motion"></os-motion-log>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="desktop-right ">
|
<div class="desktop-right ">
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
@ -229,24 +222,23 @@
|
|||||||
{{ state.name | translate }}
|
{{ state.name | translate }}
|
||||||
</button>
|
</button>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button mat-menu-item (click)="setState(null)">
|
<button mat-menu-item (click)="setState(null)" *ngIf="perms.isAllowed('change_metadata', motion)">
|
||||||
<mat-icon>replay</mat-icon> {{ 'Reset state' | translate }}
|
<mat-icon>replay</mat-icon> {{ 'Reset state' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<mat-basic-chip
|
<mat-basic-chip
|
||||||
*ngIf="motion.state"
|
*ngIf="perms.isAllowed('change_metadata', motion)"
|
||||||
[matMenuTriggerFor]="stateMenu"
|
[matMenuTriggerFor]="stateMenu"
|
||||||
[ngClass]="{
|
[ngClass]="getStateCssColor()"
|
||||||
green: motion.state.css_class === 'success',
|
>
|
||||||
red: motion.state.css_class === 'danger',
|
{{ motion.state.name | translate }}
|
||||||
grey: motion.state.css_class === 'default',
|
</mat-basic-chip>
|
||||||
lightblue: motion.state.css_class === 'primary'
|
<mat-basic-chip
|
||||||
}"
|
*ngIf="!perms.isAllowed('change_metadata', motion)"
|
||||||
|
[ngClass]="getStateCssColor()"
|
||||||
>
|
>
|
||||||
{{ motion.state.name | translate }}
|
{{ motion.state.name | translate }}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
|
|
||||||
<!--*osPerms="['motions.can_manage', 'motions.can_manage_metadata']; -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Recommendation -->
|
<!-- Recommendation -->
|
||||||
@ -261,17 +253,33 @@
|
|||||||
{{ recommendation.recommendation_label | translate }}
|
{{ recommendation.recommendation_label | translate }}
|
||||||
</button>
|
</button>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button mat-menu-item (click)="setRecommendation(null)">
|
<button mat-menu-item *ngIf="perms.isAllowed('change_metadata', motion)"
|
||||||
|
(click)="setRecommendation(null)">
|
||||||
<mat-icon>replay</mat-icon> {{ 'Reset recommendation' | translate }}
|
<mat-icon>replay</mat-icon> {{ 'Reset recommendation' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<mat-basic-chip [matMenuTriggerFor]="recommendationMenu" class="bluegrey">
|
<mat-basic-chip
|
||||||
|
*ngIf="perms.isAllowed('change_metadata', motion)"
|
||||||
|
[matMenuTriggerFor]="recommendationMenu"
|
||||||
|
class="bluegrey"
|
||||||
|
>
|
||||||
{{
|
{{
|
||||||
motion.recommendation
|
motion.recommendation
|
||||||
? (motion.recommendation.recommendation_label | translate)
|
? (motion.recommendation.recommendation_label | translate)
|
||||||
: ('not set' | translate)
|
: ('not set' | translate)
|
||||||
}}
|
}}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
|
<mat-basic-chip
|
||||||
|
*ngIf="!perms.isAllowed('change_metadata', motion)"
|
||||||
|
class="bluegrey"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
motion.recommendation
|
||||||
|
? (motion.recommendation.recommendation_label | translate)
|
||||||
|
: ('not set' | translate)
|
||||||
|
}}
|
||||||
|
</mat-basic-chip>
|
||||||
|
|
||||||
<button mat-button *ngIf="canFollowRecommendation()" (click)="onFollowRecButton()">
|
<button mat-button *ngIf="canFollowRecommendation()" (click)="onFollowRecButton()">
|
||||||
<span translate>Follow recommendation</span>
|
<span translate>Follow recommendation</span>
|
||||||
</button>
|
</button>
|
||||||
@ -291,7 +299,10 @@
|
|||||||
{{ category }}
|
{{ category }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<mat-basic-chip [matMenuTriggerFor]="categoryMenu" class="grey">
|
<mat-basic-chip *ngIf="perms.isAllowed('change_metadata', motion)" [matMenuTriggerFor]="categoryMenu" class="grey">
|
||||||
|
{{ motion.category ? motion.category : '–' }}
|
||||||
|
</mat-basic-chip>
|
||||||
|
<mat-basic-chip *ngIf="!perms.isAllowed('change_metadata', motion)" class="grey">
|
||||||
{{ motion.category ? motion.category : '–' }}
|
{{ motion.category ? motion.category : '–' }}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
</div>
|
</div>
|
||||||
@ -305,7 +316,10 @@
|
|||||||
{{ block }}
|
{{ block }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
<mat-basic-chip [matMenuTriggerFor]="blockMenu" class="grey">
|
<mat-basic-chip *ngIf="perms.isAllowed('change_metadata', motion)" [matMenuTriggerFor]="blockMenu" class="grey">
|
||||||
|
{{ motion.motion_block ? motion.motion_block : '–' }}
|
||||||
|
</mat-basic-chip>
|
||||||
|
<mat-basic-chip *ngIf="!perms.isAllowed('change_metadata', motion)" class="grey">
|
||||||
{{ motion.motion_block ? motion.motion_block : '–' }}
|
{{ motion.motion_block ? motion.motion_block : '–' }}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
</div>
|
</div>
|
||||||
@ -380,7 +394,7 @@
|
|||||||
|
|
||||||
<!-- Submitter -->
|
<!-- Submitter -->
|
||||||
<div *ngIf="newMotion" class="content-field">
|
<div *ngIf="newMotion" class="content-field">
|
||||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
<div *ngIf="perms.isAllowed('change_metadata', motion)">
|
||||||
<os-search-value-selector
|
<os-search-value-selector
|
||||||
ngDefaultControl
|
ngDefaultControl
|
||||||
[form]="contentForm"
|
[form]="contentForm"
|
||||||
@ -537,7 +551,7 @@
|
|||||||
|
|
||||||
<!-- Supporter form -->
|
<!-- Supporter form -->
|
||||||
<div class="content-field" *ngIf="editMotion && minSupporters">
|
<div class="content-field" *ngIf="editMotion && minSupporters">
|
||||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
<div *ngIf="perms.isAllowed('change_metadata', motion)">
|
||||||
<os-search-value-selector
|
<os-search-value-selector
|
||||||
ngDefaultControl
|
ngDefaultControl
|
||||||
[form]="contentForm"
|
[form]="contentForm"
|
||||||
@ -551,7 +565,7 @@
|
|||||||
|
|
||||||
<!-- Workflow -->
|
<!-- Workflow -->
|
||||||
<div class="content-field" *ngIf="editMotion && workflowObserver.value.length > 1">
|
<div class="content-field" *ngIf="editMotion && workflowObserver.value.length > 1">
|
||||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
<div *ngIf="perms.isAllowed('change_metadata', motion)">
|
||||||
<os-search-value-selector
|
<os-search-value-selector
|
||||||
ngDefaultControl
|
ngDefaultControl
|
||||||
[form]="contentForm"
|
[form]="contentForm"
|
||||||
@ -565,7 +579,7 @@
|
|||||||
|
|
||||||
<!-- Origin form -->
|
<!-- Origin form -->
|
||||||
<div class="content-field" *ngIf="editMotion">
|
<div class="content-field" *ngIf="editMotion">
|
||||||
<div *osPerms="['motions.can_manage', 'motions.can_manage_metadata']">
|
<div *ngIf="perms.isAllowed('change_metadata', motion)">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
@ -1,45 +1,43 @@
|
|||||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { BehaviorSubject, Subscription, ReplaySubject, concat } from 'rxjs';
|
||||||
|
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||||
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
|
import { DomSanitizer, SafeHtml, Title } from '@angular/platform-browser';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
import { MatDialog, MatExpansionPanel, MatSnackBar, MatCheckboxChange } from '@angular/material';
|
import { MatDialog, MatExpansionPanel, MatSnackBar, MatCheckboxChange } from '@angular/material';
|
||||||
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
import { take, takeWhile, multicast, skipWhile } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Category } from '../../../../shared/models/motions/category';
|
|
||||||
import { ViewportService } from '../../../../core/services/viewport.service';
|
|
||||||
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
|
||||||
import { ChangeRecoMode, LineNumberingMode, ViewMotion } from '../../models/view-motion';
|
|
||||||
import { User } from '../../../../shared/models/users/user';
|
|
||||||
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 { BehaviorSubject, Subscription, ReplaySubject, concat } from 'rxjs';
|
import { AgendaRepositoryService } from 'app/site/agenda/services/agenda-repository.service';
|
||||||
|
import { BaseViewComponent } from '../../../base/base-view';
|
||||||
|
import { Category } from '../../../../shared/models/motions/category';
|
||||||
|
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
||||||
|
import { ChangeRecoMode, LineNumberingMode, ViewMotion } from '../../models/view-motion';
|
||||||
|
import { CreateMotion } from '../../models/create-motion';
|
||||||
|
import { ConfigService } from '../../../../core/services/config.service';
|
||||||
|
import { DataStoreService } from '../../../../core/services/data-store.service';
|
||||||
import { DiffLinesInParagraph, LineRange } from '../../services/diff.service';
|
import { DiffLinesInParagraph, LineRange } from '../../services/diff.service';
|
||||||
|
import { itemVisibilityChoices, Item } from 'app/shared/models/agenda/item';
|
||||||
|
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
||||||
|
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
||||||
|
import { Motion } from '../../../../shared/models/motions/motion';
|
||||||
|
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
||||||
import {
|
import {
|
||||||
MotionChangeRecommendationComponent,
|
MotionChangeRecommendationComponent,
|
||||||
MotionChangeRecommendationComponentData
|
MotionChangeRecommendationComponentData
|
||||||
} from '../motion-change-recommendation/motion-change-recommendation.component';
|
} from '../motion-change-recommendation/motion-change-recommendation.component';
|
||||||
import { ChangeRecommendationRepositoryService } from '../../services/change-recommendation-repository.service';
|
|
||||||
import { ViewChangeReco } from '../../models/view-change-reco';
|
|
||||||
|
|
||||||
import { ViewUnifiedChange } from '../../models/view-unified-change';
|
|
||||||
import { OperatorService } from '../../../../core/services/operator.service';
|
|
||||||
import { BaseViewComponent } from '../../../base/base-view';
|
|
||||||
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
|
||||||
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
|
|
||||||
import { ConfigService } from '../../../../core/services/config.service';
|
|
||||||
import { Workflow } from 'app/shared/models/motions/workflow';
|
|
||||||
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
|
||||||
import { ViewCreateMotion } from '../../models/view-create-motion';
|
|
||||||
import { CreateMotion } from '../../models/create-motion';
|
|
||||||
import { MotionBlock } from 'app/shared/models/motions/motion-block';
|
|
||||||
import { itemVisibilityChoices, Item } from 'app/shared/models/agenda/item';
|
|
||||||
import { PromptService } from 'app/core/services/prompt.service';
|
|
||||||
import { AgendaRepositoryService } from 'app/site/agenda/services/agenda-repository.service';
|
|
||||||
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
|
|
||||||
import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
|
import { MotionPdfExportService } from '../../services/motion-pdf-export.service';
|
||||||
import { PersonalNoteService } from '../../services/personal-note.service';
|
import { MotionRepositoryService } from '../../services/motion-repository.service';
|
||||||
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
|
||||||
|
import { PersonalNoteService } from '../../services/personal-note.service';
|
||||||
|
import { PromptService } from 'app/core/services/prompt.service';
|
||||||
|
import { StatuteParagraphRepositoryService } from '../../services/statute-paragraph-repository.service';
|
||||||
|
import { User } from '../../../../shared/models/users/user';
|
||||||
|
import { ViewChangeReco } from '../../models/view-change-reco';
|
||||||
|
import { ViewCreateMotion } from '../../models/view-create-motion';
|
||||||
|
import { ViewportService } from '../../../../core/services/viewport.service';
|
||||||
|
import { ViewUnifiedChange } from '../../models/view-unified-change';
|
||||||
|
import { ViewStatuteParagraph } from '../../models/view-statute-paragraph';
|
||||||
|
import { Workflow } from 'app/shared/models/motions/workflow';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for the motion detail view
|
* Component for the motion detail view
|
||||||
@ -107,6 +105,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
if (
|
if (
|
||||||
this.motion &&
|
this.motion &&
|
||||||
!this.editMotion &&
|
!this.editMotion &&
|
||||||
|
this.perms.isAllowed('manage') &&
|
||||||
this.motion.motion.log_messages &&
|
this.motion.motion.log_messages &&
|
||||||
this.motion.motion.log_messages.length
|
this.motion.motion.log_messages.length
|
||||||
) {
|
) {
|
||||||
@ -316,7 +315,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
matSnackBar: MatSnackBar,
|
matSnackBar: MatSnackBar,
|
||||||
public vp: ViewportService,
|
public vp: ViewportService,
|
||||||
public perms: LocalPermissionsService,
|
public perms: LocalPermissionsService,
|
||||||
private op: OperatorService,
|
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
@ -984,15 +982,8 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the user has the correct requirements to alter the motion
|
* Handler for creating a poll
|
||||||
* TODO: All views should probably have a "isAllowedTo" routine to simplify this process
|
|
||||||
*
|
|
||||||
* @returns whether or not the OP is allowed to edit the motion
|
|
||||||
*/
|
*/
|
||||||
public opCanEdit(): boolean {
|
|
||||||
return this.op.hasPerms('motions.can_manage', 'motions.can_manage_metadata');
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createPoll(): Promise<void> {
|
public async createPoll(): Promise<void> {
|
||||||
await this.repo.createPoll(this.motion);
|
await this.repo.createPoll(this.motion);
|
||||||
}
|
}
|
||||||
@ -1000,7 +991,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* Check if a recommendation can be followed. Checks for permissions and additionally if a recommentadion is present
|
* Check if a recommendation can be followed. Checks for permissions and additionally if a recommentadion is present
|
||||||
*/
|
*/
|
||||||
public get canFollowRecommendation(): boolean {
|
public canFollowRecommendation(): boolean {
|
||||||
if (
|
if (
|
||||||
this.perms.isAllowed('createPoll', this.motion) &&
|
this.perms.isAllowed('createPoll', this.motion) &&
|
||||||
this.motion.recommendation &&
|
this.motion.recommendation &&
|
||||||
@ -1024,4 +1015,24 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit {
|
|||||||
public async toggleFavorite(): Promise<void> {
|
public async toggleFavorite(): Promise<void> {
|
||||||
this.personalNoteService.setPersonalNoteStar(this.motion.motion, !this.motion.star);
|
this.personalNoteService.setPersonalNoteStar(this.motion.motion, !this.motion.star);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the state's css class into a color
|
||||||
|
*
|
||||||
|
* @returns a string representing a color
|
||||||
|
*/
|
||||||
|
public getStateCssColor(): string {
|
||||||
|
switch (this.motion.state.css_class) {
|
||||||
|
case 'success':
|
||||||
|
return 'green';
|
||||||
|
case 'danger':
|
||||||
|
return 'red';
|
||||||
|
case 'default':
|
||||||
|
return 'grey';
|
||||||
|
case 'primary':
|
||||||
|
return 'lightblue';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<os-head-bar [mainButton]="true" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
|
<os-head-bar [mainButton]="perms.isAllowed('create')" (mainEvent)="onPlusButton()" [multiSelectMode]="isMultiSelect">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="title-slot"><h2 translate>Motions</h2></div>
|
<div class="title-slot"><h2 translate>Motions</h2></div>
|
||||||
|
|
||||||
@ -17,154 +17,159 @@
|
|||||||
</os-head-bar>
|
</os-head-bar>
|
||||||
|
|
||||||
<mat-drawer-container class="on-transition-fade">
|
<mat-drawer-container class="on-transition-fade">
|
||||||
<os-sort-filter-bar [filterService]="filterService" [sortService]="sortService"
|
<os-sort-filter-bar [filterService]="filterService" [sortService]="sortService"
|
||||||
(searchFieldChange)="searchFilter($event)">
|
(searchFieldChange)="searchFilter($event)">
|
||||||
</os-sort-filter-bar>
|
</os-sort-filter-bar>
|
||||||
|
|
||||||
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
<mat-table class="os-listview-table on-transition-fade" [dataSource]="dataSource" matSort>
|
||||||
<!-- Selector column -->
|
<!-- Selector column -->
|
||||||
<ng-container matColumnDef="selector">
|
<ng-container matColumnDef="selector">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header class="checkbox-cell"></mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header class="checkbox-cell"></mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion" class="checkbox-cell">
|
<mat-cell *matCellDef="let motion" class="checkbox-cell">
|
||||||
<mat-icon>{{ isSelected(motion) ? 'check_circle' : '' }}</mat-icon>
|
<mat-icon>{{ isSelected(motion) ? 'check_circle' : '' }}</mat-icon>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- identifier column -->
|
<!-- identifier column -->
|
||||||
<ng-container matColumnDef="identifier">
|
<ng-container matColumnDef="identifier">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Identifier</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Identifier</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<div class="innerTable">{{ motion.identifier }}</div>
|
<div class="innerTable">{{ motion.identifier }}</div>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- title column -->
|
<!-- title column -->
|
||||||
<ng-container matColumnDef="title">
|
<ng-container matColumnDef="title">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Title</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Title</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<div class="innerTable">
|
<div class="innerTable">
|
||||||
<span class="motion-list-title">{{ motion.title }}
|
<span class="motion-list-title">{{ motion.title }}
|
||||||
<span>
|
<span>
|
||||||
<mat-icon inline>{{ motion.star ? 'star' : 'star_border' }}</mat-icon>
|
<mat-icon inline>{{ motion.star ? 'star' : 'star_border' }}</mat-icon>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
|
|
||||||
<!-- attachments -->
|
<!-- attachments -->
|
||||||
<span class="attached-files" *ngIf="motion.hasAttachments()">
|
<span class="attached-files" *ngIf="motion.hasAttachments()">
|
||||||
<!-- <mat-basic-chip class="bluegrey"> <mat-icon>attach_file</mat-icon> </mat-basic-chip> -->
|
<!-- <mat-basic-chip class="bluegrey"> <mat-icon>attach_file</mat-icon> </mat-basic-chip> -->
|
||||||
<mat-icon>attach_file</mat-icon>
|
<mat-icon>attach_file</mat-icon>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<span class="motion-list-from" *ngIf="motion.submitters.length">
|
<span class="motion-list-from" *ngIf="motion.submitters.length">
|
||||||
<span translate>by</span> {{ motion.submitters }}
|
<span translate>by</span> {{ motion.submitters }}
|
||||||
</span>
|
</span>
|
||||||
<br *ngIf="motion.submitters.length" />
|
<br *ngIf="motion.submitters.length" />
|
||||||
<!-- state -->
|
<!-- state -->
|
||||||
<mat-basic-chip
|
<mat-basic-chip
|
||||||
*ngIf="motion.state"
|
*ngIf="motion.state"
|
||||||
[ngClass]="{
|
[ngClass]="{
|
||||||
green: motion.state.css_class === 'success',
|
green: motion.state.css_class === 'success',
|
||||||
red: motion.state.css_class === 'danger',
|
red: motion.state.css_class === 'danger',
|
||||||
grey: motion.state.css_class === 'default',
|
grey: motion.state.css_class === 'default',
|
||||||
lightblue: motion.state.css_class === 'primary'
|
lightblue: motion.state.css_class === 'primary'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ motion.state.name | translate }}
|
{{ motion.state.name | translate }}
|
||||||
</mat-basic-chip>
|
</mat-basic-chip>
|
||||||
|
|
||||||
<!-- recommendation -->
|
<!-- recommendation -->
|
||||||
<span *ngIf="motion.recommendation">
|
<span *ngIf="motion.recommendation">
|
||||||
<mat-basic-chip class="bluegrey">{{
|
<mat-basic-chip class="bluegrey">{{
|
||||||
motion.recommendation.recommendation_label | translate
|
motion.recommendation.recommendation_label | translate
|
||||||
}}</mat-basic-chip>
|
}}</mat-basic-chip>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</mat-cell>
|
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- state column -->
|
|
||||||
<ng-container matColumnDef="state">
|
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>State</mat-header-cell>
|
|
||||||
<mat-cell *matCellDef="let motion">
|
|
||||||
<div class="innerTable">
|
|
||||||
<div class="small" *ngIf="motion.category">
|
|
||||||
<mat-icon>device_hub</mat-icon>
|
|
||||||
{{ motion.category }}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="small" *ngIf="motion.motion_block">
|
</mat-cell>
|
||||||
<mat-icon>widgets</mat-icon>
|
</ng-container>
|
||||||
{{ motion.motion_block.title }}
|
|
||||||
|
<!-- state column -->
|
||||||
|
<ng-container matColumnDef="state">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>State</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let motion">
|
||||||
|
<div class="innerTable">
|
||||||
|
<div class="small" *ngIf="motion.category">
|
||||||
|
<mat-icon>device_hub</mat-icon>
|
||||||
|
{{ motion.category }}
|
||||||
|
</div>
|
||||||
|
<div class="small" *ngIf="motion.motion_block">
|
||||||
|
<mat-icon>widgets</mat-icon>
|
||||||
|
{{ motion.motion_block.title }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</mat-cell>
|
||||||
</mat-cell>
|
</ng-container>
|
||||||
</ng-container>
|
|
||||||
|
|
||||||
<!-- Speakers column -->
|
<!-- Speakers column -->
|
||||||
<ng-container matColumnDef="speakers">
|
<ng-container matColumnDef="speakers">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Speakers</mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Speakers</mat-header-cell>
|
||||||
<mat-cell *matCellDef="let motion">
|
<mat-cell *matCellDef="let motion">
|
||||||
<button mat-icon-button (click)="onSpeakerIcon(motion, $event)">
|
<button mat-icon-button (click)="onSpeakerIcon(motion, $event)">
|
||||||
<mat-icon
|
<mat-icon
|
||||||
[matBadge]="motion.agendaSpeakerAmount > 0 ? motion.agendaSpeakerAmount : null"
|
[matBadge]="motion.agendaSpeakerAmount > 0 ? motion.agendaSpeakerAmount : null"
|
||||||
matBadgeColor="accent"
|
matBadgeColor="accent"
|
||||||
>
|
>
|
||||||
mic
|
mic
|
||||||
</mat-icon>
|
</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</mat-cell>
|
</mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
||||||
<mat-row
|
<mat-row
|
||||||
[ngClass]="selectedRows.indexOf(row) >= 0 ? 'selected' : ''"
|
[ngClass]="selectedRows.indexOf(row) >= 0 ? 'selected' : ''"
|
||||||
(click)="selectItem(row, $event)"
|
(click)="selectItem(row, $event)"
|
||||||
*matRowDef="let row; columns: getColumnDefinition()"
|
*matRowDef="let row; columns: getColumnDefinition()"
|
||||||
class="lg"
|
class="lg"
|
||||||
>
|
>
|
||||||
</mat-row>
|
</mat-row>
|
||||||
</mat-table>
|
</mat-table>
|
||||||
|
|
||||||
<mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator>
|
<mat-paginator class="on-transition-fade" [pageSizeOptions]="[25, 50, 75, 100, 125]"></mat-paginator>
|
||||||
|
</mat-drawer-container>
|
||||||
|
|
||||||
<mat-menu #motionListMenu="matMenu">
|
<mat-menu #motionListMenu="matMenu">
|
||||||
<div *ngIf="!isMultiSelect">
|
<div *ngIf="!isMultiSelect">
|
||||||
<button mat-menu-item *osPerms="'motions.can_manage'" (click)="toggleMultiSelect()">
|
<div *ngIf="perms.isAllowed('change_metadata')">
|
||||||
<mat-icon>library_add</mat-icon>
|
<button mat-menu-item (click)="toggleMultiSelect()">
|
||||||
<span translate>Multiselect</span>
|
<mat-icon>library_add</mat-icon>
|
||||||
</button>
|
<span translate>Multiselect</span>
|
||||||
<button mat-menu-item *osPerms="'motions.can_manage'" routerLink="call-list">
|
</button>
|
||||||
<mat-icon>sort</mat-icon>
|
</div>
|
||||||
<span translate>Call list</span>
|
<div *ngIf="perms.isAllowed('manage')">
|
||||||
</button>
|
<button mat-menu-item routerLink="call-list">
|
||||||
<button mat-menu-item routerLink="category">
|
<mat-icon>sort</mat-icon>
|
||||||
<mat-icon>device_hub</mat-icon>
|
<span translate>Call list</span>
|
||||||
<span translate>Categories</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item routerLink="category">
|
||||||
<button mat-menu-item routerLink="blocks">
|
<mat-icon>device_hub</mat-icon>
|
||||||
<mat-icon>widgets</mat-icon>
|
<span translate>Categories</span>
|
||||||
<span translate>Motion blocks</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item routerLink="blocks">
|
||||||
<button mat-menu-item routerLink="statute-paragraphs" *ngIf="statutesEnabled">
|
<mat-icon>widgets</mat-icon>
|
||||||
<mat-icon>account_balance</mat-icon>
|
<span translate>Motion blocks</span>
|
||||||
<span translate>Statute</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item routerLink="statute-paragraphs" *ngIf="statutesEnabled">
|
||||||
<button mat-menu-item routerLink="comment-section">
|
<mat-icon>account_balance</mat-icon>
|
||||||
<mat-icon>speaker_notes</mat-icon>
|
<span translate>Statute</span>
|
||||||
<span translate>Comment fields</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item routerLink="comment-section">
|
||||||
<button mat-menu-item routerLink="/tags" *osPerms="'core.can_manage_tags'">
|
<mat-icon>speaker_notes</mat-icon>
|
||||||
<mat-icon>local_offer</mat-icon>
|
<span translate>Comment fields</span>
|
||||||
<span translate>Tags</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item routerLink="/tags" *osPerms="'core.can_manage_tags'">
|
||||||
<button mat-menu-item (click)="csvExportMotionList()">
|
<mat-icon>local_offer</mat-icon>
|
||||||
<mat-icon>archive</mat-icon>
|
<span translate>Tags</span>
|
||||||
<span translate>Export as CSV</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item (click)="csvExportMotionList()">
|
||||||
<button mat-menu-item *osPerms="'motions.can_manage'" routerLink="import">
|
<mat-icon>archive</mat-icon>
|
||||||
<mat-icon>save_alt</mat-icon>
|
<span translate>Export as CSV</span>
|
||||||
<span translate>Import</span><span> ...</span>
|
</button>
|
||||||
</button>
|
<button mat-menu-item routerLink="import">
|
||||||
|
<mat-icon>save_alt</mat-icon>
|
||||||
|
<span translate>Import</span><span> ...</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="isMultiSelect">
|
<div *ngIf="isMultiSelect">
|
||||||
<button mat-menu-item (click)="selectAll()">
|
<button mat-menu-item (click)="selectAll()">
|
||||||
@ -175,7 +180,7 @@
|
|||||||
<mat-icon>clear</mat-icon>
|
<mat-icon>clear</mat-icon>
|
||||||
<span translate>Deselect all</span>
|
<span translate>Deselect all</span>
|
||||||
</button>
|
</button>
|
||||||
<div *osPerms="'motions.can_manage'">
|
<div *ngIf="perms.isAllowed('change_metadata')">
|
||||||
<mat-divider></mat-divider>
|
<mat-divider></mat-divider>
|
||||||
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setStateOfMultiple(selectedRows))">
|
<button mat-menu-item (click)="multiselectWrapper(multiselectService.setStateOfMultiple(selectedRows))">
|
||||||
<mat-icon>label</mat-icon>
|
<mat-icon>label</mat-icon>
|
||||||
@ -217,15 +222,17 @@
|
|||||||
<span translate>Move to agenda item</span>
|
<span translate>Move to agenda item</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<mat-divider></mat-divider>
|
<div *ngIf="perms.isAllowed('manage')">
|
||||||
<button
|
<mat-divider></mat-divider>
|
||||||
mat-menu-item
|
<button
|
||||||
class="red-warning-text"
|
mat-menu-item
|
||||||
(click)="multiselectService.delete(selectedRows); toggleMultiSelect()"
|
class="red-warning-text"
|
||||||
>
|
(click)="multiselectService.delete(selectedRows); toggleMultiSelect()"
|
||||||
<mat-icon>delete</mat-icon>
|
>
|
||||||
<span translate>Delete</span>
|
<mat-icon>delete</mat-icon>
|
||||||
</button>
|
<span translate>Delete</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</mat-drawer-container>
|
|
||||||
|
@ -3,22 +3,23 @@ import { Router, ActivatedRoute } from '@angular/router';
|
|||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ConfigService } from '../../../../core/services/config.service';
|
|
||||||
import { MotionCsvExportService } from '../../services/motion-csv-export.service';
|
|
||||||
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
|
||||||
import { MatSnackBar } from '@angular/material';
|
|
||||||
import { ViewMotion } from '../../models/view-motion';
|
|
||||||
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
|
||||||
import { MotionMultiselectService } from '../../services/motion-multiselect.service';
|
|
||||||
import { TagRepositoryService } from 'app/site/tags/services/tag-repository.service';
|
|
||||||
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
import { CategoryRepositoryService } from '../../services/category-repository.service';
|
||||||
|
import { ConfigService } from '../../../../core/services/config.service';
|
||||||
|
import { ListViewBaseComponent } from '../../../base/list-view-base';
|
||||||
|
import { LocalPermissionsService } from '../../services/local-permissions.service';
|
||||||
|
import { MatSnackBar } from '@angular/material';
|
||||||
import { MotionBlockRepositoryService } from '../../services/motion-block-repository.service';
|
import { MotionBlockRepositoryService } from '../../services/motion-block-repository.service';
|
||||||
|
import { MotionCsvExportService } from '../../services/motion-csv-export.service';
|
||||||
import { MotionFilterListService } from '../../services/motion-filter-list.service';
|
import { MotionFilterListService } from '../../services/motion-filter-list.service';
|
||||||
|
import { MotionMultiselectService } from '../../services/motion-multiselect.service';
|
||||||
import { MotionSortListService } from '../../services/motion-sort-list.service';
|
import { MotionSortListService } from '../../services/motion-sort-list.service';
|
||||||
|
import { TagRepositoryService } from 'app/site/tags/services/tag-repository.service';
|
||||||
|
import { ViewCategory } from '../../models/view-category';
|
||||||
|
import { ViewMotion } from '../../models/view-motion';
|
||||||
|
import { ViewMotionBlock } from '../../models/view-motion-block';
|
||||||
import { ViewTag } from 'app/site/tags/models/view-tag';
|
import { ViewTag } from 'app/site/tags/models/view-tag';
|
||||||
import { ViewWorkflow } from '../../models/view-workflow';
|
import { ViewWorkflow } from '../../models/view-workflow';
|
||||||
import { ViewCategory } from '../../models/view-category';
|
import { WorkflowState } from '../../../../shared/models/motions/workflow-state';
|
||||||
import { ViewMotionBlock } from '../../models/view-motion-block';
|
|
||||||
import { WorkflowRepositoryService } from '../../services/workflow-repository.service';
|
import { WorkflowRepositoryService } from '../../services/workflow-repository.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,6 +76,7 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
* @param userRepo
|
* @param userRepo
|
||||||
* @param sortService
|
* @param sortService
|
||||||
* @param filterService
|
* @param filterService
|
||||||
|
* @param perms LocalPermissionService
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
titleService: Title,
|
titleService: Title,
|
||||||
@ -90,7 +92,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
private motionCsvExport: MotionCsvExportService,
|
private motionCsvExport: MotionCsvExportService,
|
||||||
public multiselectService: MotionMultiselectService,
|
public multiselectService: MotionMultiselectService,
|
||||||
public sortService: MotionSortListService,
|
public sortService: MotionSortListService,
|
||||||
public filterService: MotionFilterListService
|
public filterService: MotionFilterListService,
|
||||||
|
public perms: LocalPermissionsService
|
||||||
) {
|
) {
|
||||||
super(titleService, translate, matSnackBar);
|
super(titleService, translate, matSnackBar);
|
||||||
|
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
<div *ngIf="poll.has_votes" class="on-transition-fade poll-result">
|
<div *ngIf="poll.has_votes" class="on-transition-fade poll-result">
|
||||||
<div *ngFor="let key of pollValues">
|
<div *ngFor="let key of pollValues">
|
||||||
<div class="poll-progress on-transition-fade" *ngIf="poll[key] !== undefined">
|
<div class="poll-progress on-transition-fade" *ngIf="poll[key] !== undefined">
|
||||||
<mat-icon class="main-nav-color" matTooltip="{{ getLabel(key) | translate }}"> {{ getIcon(key) }} </mat-icon>
|
<mat-icon class="main-nav-color" matTooltip="{{ getLabel(key) | translate }}">
|
||||||
|
{{ getIcon(key) }}
|
||||||
|
</mat-icon>
|
||||||
<div class="progress-container">
|
<div class="progress-container">
|
||||||
<div>
|
<div>
|
||||||
<span translate>{{ getLabel(key) }}</span>: {{ getNumber(key) }}
|
<span translate>{{ getLabel(key) }}</span
|
||||||
|
>: {{ getNumber(key) }}
|
||||||
<span *ngIf="!isAbstractValue(key)">({{ getPercent(key) }}%)</span>
|
<span *ngIf="!isAbstractValue(key)">({{ getPercent(key) }}%)</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="!isAbstractValue(key)" class="poll-progress-bar">
|
<div *ngIf="!isAbstractValue(key)" class="poll-progress-bar">
|
||||||
@ -22,39 +25,53 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr *ngIf="key ==='abstain'" flex />
|
<hr *ngIf="key === 'abstain'" flex />
|
||||||
</div>
|
</div>
|
||||||
<!-- quorum -->
|
<!-- quorum -->
|
||||||
<div *ngIf="abstractPoll"><span translate>Quorum not calculable.</span></div>
|
<div *osPerms="'motions.can_manage'">
|
||||||
<div class="poll-quorum-line" *ngIf="!abstractPoll">
|
<div *ngIf="abstractPoll"><span translate>Quorum not calculable.</span></div>
|
||||||
<span>
|
<div class="poll-quorum-line" *ngIf="!abstractPoll">
|
||||||
<span *ngIf="yesQuorum">
|
<span>
|
||||||
<mat-icon color="warn" *ngIf="!quorumYesReached"> thumb_down </mat-icon>
|
<span *ngIf="yesQuorum">
|
||||||
<mat-icon color="primary" *ngIf="quorumYesReached"> thumb_up </mat-icon>
|
<mat-icon color="warn" *ngIf="!quorumYesReached"> thumb_down </mat-icon>
|
||||||
|
<mat-icon color="primary" *ngIf="quorumYesReached"> thumb_up </mat-icon>
|
||||||
|
</span>
|
||||||
|
<button mat-button [matMenuTriggerFor]="majorityMenu">
|
||||||
|
<span translate>{{ getQuorumLabel() }}</span> <span
|
||||||
|
*ngIf="majorityChoice !== 'disabled'"
|
||||||
|
>({{ yesQuorum }})</span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
<span *ngIf="majorityChoice !== 'disabled'">
|
||||||
|
<span *ngIf="quorumYesReached" translate> reached.</span>
|
||||||
|
<span *ngIf="!quorumYesReached" translate> not reached.</span>
|
||||||
|
</span>
|
||||||
|
<span *ngIf="majorityChoice === 'disabled'"
|
||||||
|
> — <span translate>No quorum calculated</span>
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<button mat-button [matMenuTriggerFor]="majorityMenu">
|
</div>
|
||||||
<span translate>{{ getQuorumLabel() }}</span>
|
|
||||||
<span *ngIf="majorityChoice !== 'disabled'">({{ yesQuorum }})</span>
|
|
||||||
</button>
|
|
||||||
<span *ngIf="majorityChoice !== 'disabled'">
|
|
||||||
<span *ngIf="quorumYesReached" translate> reached.</span>
|
|
||||||
<span *ngIf="!quorumYesReached" translate> not reached.</span>
|
|
||||||
</span>
|
|
||||||
<span *ngIf="majorityChoice === 'disabled'"
|
|
||||||
> — <span translate>No quorum calculated</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container class="meta-text-block-action-row" *osPerms="'motions.can_manage'">
|
<ng-container class="meta-text-block-action-row" *osPerms="'motions.can_manage_metadata'">
|
||||||
<button mat-icon-button class="main-nav-color" matTooltip="{{ 'Edit poll' | translate }}" (click)="editPoll()">
|
<button mat-icon-button class="main-nav-color" matTooltip="{{ 'Edit poll' | translate }}" (click)="editPoll()">
|
||||||
<mat-icon inline>edit</mat-icon>
|
<mat-icon inline>edit</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button class="main-nav-color" matTooltip="{{ 'Print ballots' | translate }}" (click)="printBallots()">
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="main-nav-color"
|
||||||
|
matTooltip="{{ 'Print ballots' | translate }}"
|
||||||
|
(click)="printBallots()"
|
||||||
|
>
|
||||||
<mat-icon inline>local_printshop</mat-icon>
|
<mat-icon inline>local_printshop</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button class="main-nav-color" matTooltip="{{ 'Delete poll' | translate }}" (click)="deletePoll()">
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="main-nav-color"
|
||||||
|
matTooltip="{{ 'Delete poll' | translate }}"
|
||||||
|
(click)="deletePoll()"
|
||||||
|
>
|
||||||
<mat-icon inline>delete</mat-icon>
|
<mat-icon inline>delete</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -2,49 +2,124 @@ import { Injectable } from '@angular/core';
|
|||||||
import { OperatorService } from '../../../core/services/operator.service';
|
import { OperatorService } from '../../../core/services/operator.service';
|
||||||
import { ViewMotion } from '../models/view-motion';
|
import { ViewMotion } from '../models/view-motion';
|
||||||
import { ConfigService } from '../../../core/services/config.service';
|
import { ConfigService } from '../../../core/services/config.service';
|
||||||
|
import { ConstantsService } from 'app/core/services/constants.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class LocalPermissionsService {
|
export class LocalPermissionsService {
|
||||||
public configMinSupporters: number;
|
public configMinSupporters: number;
|
||||||
|
private amendmentEnabled: boolean;
|
||||||
|
private amendmentOfAmendment: boolean;
|
||||||
|
|
||||||
public constructor(private operator: OperatorService, private configService: ConfigService) {
|
public constructor(
|
||||||
|
private operator: OperatorService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private constants: ConstantsService
|
||||||
|
) {
|
||||||
// load config variables
|
// load config variables
|
||||||
this.configService
|
this.configService
|
||||||
.get('motions_min_supporters')
|
.get('motions_min_supporters')
|
||||||
.subscribe(supporters => (this.configMinSupporters = supporters));
|
.subscribe(supporters => (this.configMinSupporters = supporters));
|
||||||
|
this.configService.get('motions_amendments_enabled').subscribe(enabled => (this.amendmentEnabled = enabled));
|
||||||
|
this.constants
|
||||||
|
.get('OpenSlidesSettings')
|
||||||
|
.subscribe(settings => (this.amendmentOfAmendment = settings.MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should determine if the user (Operator) has the
|
* Determine if the user (Operator) has the correct permission to perform the given action.
|
||||||
* correct permission to perform the given action.
|
|
||||||
*
|
*
|
||||||
* actions might be:
|
* actions might be:
|
||||||
|
* - create
|
||||||
* - support
|
* - support
|
||||||
* - unsupport
|
* - unsupport
|
||||||
* - createpoll
|
* - createpoll
|
||||||
|
* - update
|
||||||
|
* - update_submitters
|
||||||
|
* - delete
|
||||||
|
* - change_metadata
|
||||||
|
* - reset_state
|
||||||
|
* - change_recommendation
|
||||||
|
* - can_create_amendments
|
||||||
|
* - can_manage_metadata
|
||||||
|
* - manage
|
||||||
*
|
*
|
||||||
* @param action the action the user tries to perform
|
* @param action the action the user tries to perform
|
||||||
|
* @param motion the motion for which to perform the action
|
||||||
*/
|
*/
|
||||||
public isAllowed(action: string, motion?: ViewMotion): boolean {
|
public isAllowed(action: string, motion?: ViewMotion): boolean {
|
||||||
if (motion) {
|
switch (action) {
|
||||||
switch (action) {
|
case 'create':
|
||||||
case 'support':
|
return this.operator.hasPerms('motions.can_create');
|
||||||
return (
|
case 'support':
|
||||||
this.operator.hasPerms('motions.can_support') &&
|
if (!motion) {
|
||||||
this.configMinSupporters > 0 &&
|
|
||||||
motion.state.allow_support &&
|
|
||||||
motion.submitters.indexOf(this.operator.user) === -1 &&
|
|
||||||
motion.supporters.indexOf(this.operator.user) === -1
|
|
||||||
);
|
|
||||||
case 'unsupport':
|
|
||||||
return motion.state.allow_support && motion.supporters.indexOf(this.operator.user) !== -1;
|
|
||||||
case 'createpoll':
|
|
||||||
return this.operator.hasPerms('motions.can_manage') && motion.state.allow_create_poll;
|
|
||||||
default:
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
this.operator.hasPerms('motions.can_support') &&
|
||||||
|
this.configMinSupporters > 0 &&
|
||||||
|
motion.state.allow_support &&
|
||||||
|
motion.submitters.indexOf(this.operator.user) === -1 &&
|
||||||
|
motion.supporters.indexOf(this.operator.user) === -1
|
||||||
|
);
|
||||||
|
case 'unsupport':
|
||||||
|
if (!motion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return motion.state.allow_support && motion.supporters.indexOf(this.operator.user) !== -1;
|
||||||
|
case 'createpoll':
|
||||||
|
if (!motion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(this.operator.hasPerms('motions.can_manage') ||
|
||||||
|
this.operator.hasPerms('motions.can_manage_metadata')) &&
|
||||||
|
motion.state.allow_create_poll
|
||||||
|
);
|
||||||
|
case 'update':
|
||||||
|
if (!motion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
this.operator.hasPerms('motions.can_manage') &&
|
||||||
|
motion.state.allow_submitter_edit &&
|
||||||
|
motion.submitters.some(submitter => submitter.id === this.operator.user.id)
|
||||||
|
);
|
||||||
|
case 'update_submitters':
|
||||||
|
return this.operator.hasPerms('motions.can_manage');
|
||||||
|
case 'delete':
|
||||||
|
if (!motion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
this.operator.hasPerms('motions.can_manage') &&
|
||||||
|
motion.state.allow_submitter_edit &&
|
||||||
|
motion.submitters.some(submitter => submitter.id === this.operator.user.id)
|
||||||
|
);
|
||||||
|
case 'change_metadata':
|
||||||
|
return (
|
||||||
|
this.operator.hasPerms('motions.can_manage') ||
|
||||||
|
this.operator.hasPerms('motions.can_manage_metadata')
|
||||||
|
);
|
||||||
|
case 'can_create_amendments':
|
||||||
|
if (!motion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
this.operator.hasPerms('motions.can_create_amendments') &&
|
||||||
|
this.amendmentEnabled &&
|
||||||
|
(!motion.parent_id || (motion.parent_id && this.amendmentOfAmendment))
|
||||||
|
);
|
||||||
|
case 'can_manage_metadata':
|
||||||
|
return (
|
||||||
|
this.operator.hasPerms('motions.can_manage') &&
|
||||||
|
this.operator.hasPerms('motions.can_manage_metadata')
|
||||||
|
);
|
||||||
|
case 'manage':
|
||||||
|
return this.operator.hasPerms('motions.can_manage');
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -692,4 +692,13 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
await this.httpService.post(restPath);
|
await this.httpService.post(restPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Check if a motion currently has any amendments
|
||||||
|
*
|
||||||
|
* @param motion A viewMotion
|
||||||
|
* @returns True if there is at eleast one amendment
|
||||||
|
*/
|
||||||
|
public hasAmendments(motion: ViewMotion): boolean {
|
||||||
|
return this.getViewModelList().filter(allMotions => allMotions.parent_id === motion.id).length > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user