Merge pull request #3940 from CatoTH/OpenSlides-3-ChangeRecommendations-internal
Internal change recommendations
This commit is contained in:
commit
eeb29d140b
@ -8,6 +8,7 @@ export class MotionChangeReco extends BaseModel<MotionChangeReco> {
|
||||
public id: number;
|
||||
public motion_id: number;
|
||||
public rejected: boolean;
|
||||
public internal: boolean;
|
||||
public type: number;
|
||||
public other_description: string;
|
||||
public line_from: number;
|
||||
|
@ -10,6 +10,8 @@
|
||||
<mat-form-field class="wide-form">
|
||||
<textarea matInput placeholder='Change recommendation Text' formControlName='text'></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox formControlName="public">{{ 'Public' | translate }}</mat-checkbox>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
|
@ -24,7 +24,7 @@ export interface MotionChangeRecommendationComponentData {
|
||||
* editChangeRecommendation: false,
|
||||
* newChangeRecommendation: true,
|
||||
* lineRange: lineRange,
|
||||
* motion: this.motion,
|
||||
* changeReco: this.changeRecommendation,
|
||||
* };
|
||||
* this.dialogService.open(MotionChangeRecommendationComponent, {
|
||||
* height: '400px',
|
||||
@ -104,14 +104,16 @@ export class MotionChangeRecommendationComponent {
|
||||
public createForm(): void {
|
||||
this.contentForm = this.formBuilder.group({
|
||||
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 {
|
||||
this.changeReco.updateChangeReco(
|
||||
this.contentForm.controls.diffType.value,
|
||||
this.contentForm.controls.text.value
|
||||
this.contentForm.controls.text.value,
|
||||
!this.contentForm.controls.public.value
|
||||
);
|
||||
|
||||
if (this.newReco) {
|
||||
|
@ -111,6 +111,10 @@
|
||||
<span translate>Reject</span>
|
||||
<mat-icon *ngIf="change.isRejected()" class="active-indicator">done</mat-icon>
|
||||
</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)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
<span translate>Delete</span>
|
||||
|
@ -172,13 +172,23 @@ export class MotionDetailDiffComponent implements AfterViewInit {
|
||||
*/
|
||||
public setAcceptanceValue(change: ViewChangeReco, value: string): void {
|
||||
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') {
|
||||
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.
|
||||
* 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
|
||||
*/
|
||||
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.preventDefault();
|
||||
}
|
||||
|
@ -36,16 +36,21 @@ export class ViewChangeReco extends BaseViewModel implements ViewUnifiedChange {
|
||||
// @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
|
||||
this._changeReco.type = type;
|
||||
this._changeReco.text = text;
|
||||
this._changeReco.internal = internal;
|
||||
}
|
||||
|
||||
public get rejected(): boolean {
|
||||
return this._changeReco ? this._changeReco.rejected : null;
|
||||
}
|
||||
|
||||
public get internal(): boolean {
|
||||
return this._changeReco ? this._changeReco.internal : null;
|
||||
}
|
||||
|
||||
public get type(): number {
|
||||
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>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
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):
|
||||
"""
|
||||
|
@ -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)
|
||||
"""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)
|
||||
"""Replacement (0), Insertion (1), Deletion (2), Other (3)"""
|
||||
|
||||
|
@ -286,6 +286,7 @@ class MotionChangeRecommendationSerializer(ModelSerializer):
|
||||
'id',
|
||||
'motion',
|
||||
'rejected',
|
||||
'internal',
|
||||
'type',
|
||||
'other_description',
|
||||
'line_from',
|
||||
|
@ -12,6 +12,7 @@ from openslides.motions.models import (
|
||||
Category,
|
||||
Motion,
|
||||
MotionBlock,
|
||||
MotionChangeRecommendation,
|
||||
MotionComment,
|
||||
MotionCommentSection,
|
||||
MotionLog,
|
||||
@ -1235,6 +1236,57 @@ class TestMotionCommentSection(TestCase):
|
||||
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):
|
||||
"""
|
||||
Tests motion change recommendation creation.
|
||||
|
Loading…
Reference in New Issue
Block a user