Setting change recommendations internal
This commit is contained in:
parent
3d65a244cc
commit
e59497bc54
@ -8,6 +8,7 @@ export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
|||||||
public id: number;
|
public id: number;
|
||||||
public motion_id: number;
|
public motion_id: number;
|
||||||
public rejected: boolean;
|
public rejected: boolean;
|
||||||
|
public internal: boolean;
|
||||||
public type: number;
|
public type: number;
|
||||||
public other_description: string;
|
public other_description: string;
|
||||||
public line_from: number;
|
public line_from: number;
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
<mat-form-field class="wide-form">
|
<mat-form-field class="wide-form">
|
||||||
<textarea matInput placeholder='Change recommendation Text' formControlName='text'></textarea>
|
<textarea matInput placeholder='Change recommendation Text' formControlName='text'></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-checkbox formControlName="public">{{ 'Public' | translate }}</mat-checkbox>
|
||||||
</form>
|
</form>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
|
@ -24,7 +24,7 @@ export interface MotionChangeRecommendationComponentData {
|
|||||||
* editChangeRecommendation: false,
|
* editChangeRecommendation: false,
|
||||||
* newChangeRecommendation: true,
|
* newChangeRecommendation: true,
|
||||||
* lineRange: lineRange,
|
* lineRange: lineRange,
|
||||||
* motion: this.motion,
|
* changeReco: this.changeRecommendation,
|
||||||
* };
|
* };
|
||||||
* this.dialogService.open(MotionChangeRecommendationComponent, {
|
* this.dialogService.open(MotionChangeRecommendationComponent, {
|
||||||
* height: '400px',
|
* height: '400px',
|
||||||
@ -104,14 +104,16 @@ export class MotionChangeRecommendationComponent {
|
|||||||
public createForm(): void {
|
public createForm(): void {
|
||||||
this.contentForm = this.formBuilder.group({
|
this.contentForm = this.formBuilder.group({
|
||||||
text: [this.changeReco.text, Validators.required],
|
text: [this.changeReco.text, Validators.required],
|
||||||
diffType: [this.changeReco.type, Validators.required]
|
diffType: [this.changeReco.type, Validators.required],
|
||||||
|
public: [!this.changeReco.internal]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveChangeRecommendation(): void {
|
public saveChangeRecommendation(): void {
|
||||||
this.changeReco.updateChangeReco(
|
this.changeReco.updateChangeReco(
|
||||||
this.contentForm.controls.diffType.value,
|
this.contentForm.controls.diffType.value,
|
||||||
this.contentForm.controls.text.value
|
this.contentForm.controls.text.value,
|
||||||
|
!this.contentForm.controls.public.value
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.newReco) {
|
if (this.newReco) {
|
||||||
|
@ -111,6 +111,10 @@
|
|||||||
<span translate>Reject</span>
|
<span translate>Reject</span>
|
||||||
<mat-icon *ngIf="change.isRejected()" class="active-indicator">done</mat-icon>
|
<mat-icon *ngIf="change.isRejected()" class="active-indicator">done</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" mat-menu-item (click)="setInternal(change, !change.internal)">
|
||||||
|
<mat-icon>{{ change.internal ? "check_box_outline_blank" : "check_box" }}</mat-icon>
|
||||||
|
<span translate>Public</span>
|
||||||
|
</button>
|
||||||
<button type="button" mat-menu-item (click)="deleteChangeRecommendation(change, $event)">
|
<button type="button" mat-menu-item (click)="deleteChangeRecommendation(change, $event)">
|
||||||
<mat-icon>delete</mat-icon>
|
<mat-icon>delete</mat-icon>
|
||||||
<span translate>Delete</span>
|
<span translate>Delete</span>
|
||||||
|
@ -172,13 +172,23 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
|||||||
*/
|
*/
|
||||||
public setAcceptanceValue(change: ViewChangeReco, value: string): void {
|
public setAcceptanceValue(change: ViewChangeReco, value: string): void {
|
||||||
if (value === 'accepted') {
|
if (value === 'accepted') {
|
||||||
this.recoRepo.setAccepted(change).subscribe(() => {}); // Subscribe to trigger HTTP request
|
this.recoRepo.setAccepted(change).subscribe(); // Subscribe to trigger HTTP request
|
||||||
}
|
}
|
||||||
if (value === 'rejected') {
|
if (value === 'rejected') {
|
||||||
this.recoRepo.setRejected(change).subscribe(() => {}); // Subscribe to trigger HTTP request
|
this.recoRepo.setRejected(change).subscribe(); // Subscribe to trigger HTTP request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if a change recommendation is internal or not
|
||||||
|
*
|
||||||
|
* @param {ViewChangeReco} change
|
||||||
|
* @param {boolean} internal
|
||||||
|
*/
|
||||||
|
public setInternal(change: ViewChangeReco, internal: boolean): void {
|
||||||
|
this.recoRepo.setInternal(change, internal).subscribe(); // Subscribe to trigger HTTP request
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a change recommendation.
|
* Deletes a change recommendation.
|
||||||
* The template has to make sure only to pass change recommendations to this method.
|
* The template has to make sure only to pass change recommendations to this method.
|
||||||
@ -187,7 +197,7 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
|||||||
* @param {MouseEvent} $event
|
* @param {MouseEvent} $event
|
||||||
*/
|
*/
|
||||||
public deleteChangeRecommendation(reco: ViewChangeReco, $event: MouseEvent): void {
|
public deleteChangeRecommendation(reco: ViewChangeReco, $event: MouseEvent): void {
|
||||||
this.recoRepo.delete(reco).subscribe(() => {}); // Subscribe to trigger HTTP request
|
this.recoRepo.delete(reco).subscribe(); // Subscribe to trigger HTTP request
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -36,16 +36,21 @@ export class ViewChangeReco extends BaseViewModel implements ViewUnifiedChange {
|
|||||||
// @TODO Is there any need for this function?
|
// @TODO Is there any need for this function?
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateChangeReco(type: number, text: string): void {
|
public updateChangeReco(type: number, text: string, internal: boolean): void {
|
||||||
// @TODO HTML sanitazion
|
// @TODO HTML sanitazion
|
||||||
this._changeReco.type = type;
|
this._changeReco.type = type;
|
||||||
this._changeReco.text = text;
|
this._changeReco.text = text;
|
||||||
|
this._changeReco.internal = internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get rejected(): boolean {
|
public get rejected(): boolean {
|
||||||
return this._changeReco ? this._changeReco.rejected : null;
|
return this._changeReco ? this._changeReco.rejected : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get internal(): boolean {
|
||||||
|
return this._changeReco ? this._changeReco.internal : null;
|
||||||
|
}
|
||||||
|
|
||||||
public get type(): number {
|
public get type(): number {
|
||||||
return this._changeReco ? this._changeReco.type : ModificationType.TYPE_REPLACEMENT;
|
return this._changeReco ? this._changeReco.type : ModificationType.TYPE_REPLACEMENT;
|
||||||
}
|
}
|
||||||
|
@ -133,4 +133,18 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<ViewCh
|
|||||||
});
|
});
|
||||||
return this.dataSend.updateModel(changeReco, HTTPMethod.PATCH) as Observable<MotionChangeReco>;
|
return this.dataSend.updateModel(changeReco, HTTPMethod.PATCH) as Observable<MotionChangeReco>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if a change recommendation is internal (for the administrators) or not.
|
||||||
|
*
|
||||||
|
* @param {ViewChangeReco} change
|
||||||
|
* @param {boolean} internal
|
||||||
|
*/
|
||||||
|
public setInternal(change: ViewChangeReco, internal: boolean): Observable<MotionChangeReco> {
|
||||||
|
const changeReco = change.changeRecommendation;
|
||||||
|
changeReco.patchValues({
|
||||||
|
internal: internal
|
||||||
|
});
|
||||||
|
return this.dataSend.updateModel(changeReco, HTTPMethod.PATCH) as Observable<MotionChangeReco>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,27 @@ class MotionChangeRecommendationAccessPermissions(BaseAccessPermissions):
|
|||||||
|
|
||||||
return MotionChangeRecommendationSerializer
|
return MotionChangeRecommendationSerializer
|
||||||
|
|
||||||
|
def get_restricted_data(
|
||||||
|
self,
|
||||||
|
full_data: List[Dict[str, Any]],
|
||||||
|
user: Optional[CollectionElement]) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Removes change recommendations if they are internal and the user has
|
||||||
|
not the can_manage permission. To see change recommendation the user needs
|
||||||
|
the can_see permission.
|
||||||
|
"""
|
||||||
|
# Parse data.
|
||||||
|
if has_perm(user, 'motions.can_see'):
|
||||||
|
has_manage_perms = has_perm(user, 'motion.can_manage')
|
||||||
|
data = []
|
||||||
|
for full in full_data:
|
||||||
|
if not full['internal'] or has_manage_perms:
|
||||||
|
data.append(full)
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class MotionCommentSectionAccessPermissions(BaseAccessPermissions):
|
class MotionCommentSectionAccessPermissions(BaseAccessPermissions):
|
||||||
"""
|
"""
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 2.1 on 2018-10-20 18:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('motions', '0013_motion_sorting_and_statute'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='motionchangerecommendation',
|
||||||
|
name='internal',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
@ -738,6 +738,9 @@ class MotionChangeRecommendation(RESTModelMixin, models.Model):
|
|||||||
rejected = models.BooleanField(default=False)
|
rejected = models.BooleanField(default=False)
|
||||||
"""If true, this change recommendation has been rejected"""
|
"""If true, this change recommendation has been rejected"""
|
||||||
|
|
||||||
|
internal = models.BooleanField(default=True)
|
||||||
|
"""If true, this change recommendation can not be seen by regular users"""
|
||||||
|
|
||||||
type = models.PositiveIntegerField(default=0)
|
type = models.PositiveIntegerField(default=0)
|
||||||
"""Replacement (0), Insertion (1), Deletion (2), Other (3)"""
|
"""Replacement (0), Insertion (1), Deletion (2), Other (3)"""
|
||||||
|
|
||||||
|
@ -286,6 +286,7 @@ class MotionChangeRecommendationSerializer(ModelSerializer):
|
|||||||
'id',
|
'id',
|
||||||
'motion',
|
'motion',
|
||||||
'rejected',
|
'rejected',
|
||||||
|
'internal',
|
||||||
'type',
|
'type',
|
||||||
'other_description',
|
'other_description',
|
||||||
'line_from',
|
'line_from',
|
||||||
|
@ -12,6 +12,7 @@ from openslides.motions.models import (
|
|||||||
Category,
|
Category,
|
||||||
Motion,
|
Motion,
|
||||||
MotionBlock,
|
MotionBlock,
|
||||||
|
MotionChangeRecommendation,
|
||||||
MotionComment,
|
MotionComment,
|
||||||
MotionCommentSection,
|
MotionCommentSection,
|
||||||
MotionLog,
|
MotionLog,
|
||||||
@ -1235,6 +1236,57 @@ class TestMotionCommentSection(TestCase):
|
|||||||
self.assertEqual(MotionCommentSection.objects.count(), 1)
|
self.assertEqual(MotionCommentSection.objects.count(), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveMotionChangeRecommendation(TestCase):
|
||||||
|
"""
|
||||||
|
Tests retrieving motion change recommendations.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username='admin', password='admin')
|
||||||
|
|
||||||
|
motion = Motion(
|
||||||
|
title='test_title_3kd)K23,c9239mdj2wcG',
|
||||||
|
text='test_text_f8FLP,gvprC;wovVEwlQ')
|
||||||
|
motion.save()
|
||||||
|
|
||||||
|
self.public_cr = MotionChangeRecommendation(
|
||||||
|
motion=motion,
|
||||||
|
internal=False,
|
||||||
|
line_from=1,
|
||||||
|
line_to=1)
|
||||||
|
self.public_cr.save()
|
||||||
|
|
||||||
|
self.internal_cr = MotionChangeRecommendation(
|
||||||
|
motion=motion,
|
||||||
|
internal=True,
|
||||||
|
line_from=2,
|
||||||
|
line_to=2)
|
||||||
|
self.internal_cr.save()
|
||||||
|
|
||||||
|
def test_simple(self):
|
||||||
|
"""
|
||||||
|
Test retrieving all change recommendations.
|
||||||
|
"""
|
||||||
|
response = self.client.get(reverse('motionchangerecommendation-list'))
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data), 2)
|
||||||
|
|
||||||
|
def test_non_admin(self):
|
||||||
|
"""
|
||||||
|
Test retrieving of all change recommendations that are public, if the user
|
||||||
|
has no manage perms.
|
||||||
|
"""
|
||||||
|
self.admin = get_user_model().objects.get(username='admin')
|
||||||
|
self.admin.groups.add(GROUP_DELEGATE_PK)
|
||||||
|
self.admin.groups.remove(GROUP_ADMIN_PK)
|
||||||
|
inform_changed_data(self.admin)
|
||||||
|
|
||||||
|
response = self.client.get(reverse('motionchangerecommendation-list'))
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data), 1)
|
||||||
|
self.assertEqual(response.data[0]['id'], self.public_cr.id)
|
||||||
|
|
||||||
|
|
||||||
class CreateMotionChangeRecommendation(TestCase):
|
class CreateMotionChangeRecommendation(TestCase):
|
||||||
"""
|
"""
|
||||||
Tests motion change recommendation creation.
|
Tests motion change recommendation creation.
|
||||||
|
Loading…
Reference in New Issue
Block a user