From b1ea59ea289e7f70208703b0649a0856643ae02b Mon Sep 17 00:00:00 2001 From: FinnStutzenstein Date: Mon, 1 Oct 2018 15:36:16 +0200 Subject: [PATCH] Motion comment section list --- client/src/app/core/core.module.ts | 4 +- .../app/core/services/prompt.service.spec.ts | 17 ++ .../src/app/core/services/prompt.service.ts | 30 ++++ .../prompt-dialog.component.html | 6 + .../prompt-dialog.component.spec.ts | 26 +++ .../prompt-dialog/prompt-dialog.component.ts | 21 +++ client/src/app/shared/shared.module.ts | 7 +- ...motion-comment-section-list.component.html | 108 +++++++++++ ...motion-comment-section-list.component.scss | 49 +++++ ...ion-comment-section-list.component.spec.ts | 26 +++ .../motion-comment-section-list.component.ts | 170 ++++++++++++++++++ .../motion-detail.component.html | 2 +- .../motion-list/motion-list.component.ts | 11 ++ .../models/view-motion-comment-section.ts | 90 ++++++++++ .../site/motions/motions-routing.module.ts | 2 + client/src/app/site/motions/motions.module.ts | 3 +- ...comment-section-repository.service.spec.ts | 20 +++ ...tion-comment-section-repository.service.ts | 61 +++++++ client/src/e2e-imports.module.ts | 1 - openslides/motions/serializers.py | 7 + 20 files changed, 655 insertions(+), 6 deletions(-) create mode 100644 client/src/app/core/services/prompt.service.spec.ts create mode 100644 client/src/app/core/services/prompt.service.ts create mode 100644 client/src/app/shared/components/prompt-dialog/prompt-dialog.component.html create mode 100644 client/src/app/shared/components/prompt-dialog/prompt-dialog.component.spec.ts create mode 100644 client/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts create mode 100644 client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.html create mode 100644 client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.scss create mode 100644 client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.spec.ts create mode 100644 client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.ts create mode 100644 client/src/app/site/motions/models/view-motion-comment-section.ts create mode 100644 client/src/app/site/motions/services/motion-comment-section-repository.service.spec.ts create mode 100644 client/src/app/site/motions/services/motion-comment-section-repository.service.ts diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index 8ed78ed26..ba50e38cd 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -13,6 +13,7 @@ import { WebsocketService } from './services/websocket.service'; import { AddHeaderInterceptor } from './http-interceptor'; import { DataSendService } from './services/data-send.service'; import { ViewportService } from './services/viewport.service'; +import { PromptDialogComponent } from '../shared/components/prompt-dialog/prompt-dialog.component'; /** Global Core Module. Contains all global (singleton) services * @@ -34,7 +35,8 @@ import { ViewportService } from './services/viewport.service'; useClass: AddHeaderInterceptor, multi: true } - ] + ], + entryComponents: [PromptDialogComponent] }) export class CoreModule { /** make sure CoreModule is imported only by one NgModule, the AppModule */ diff --git a/client/src/app/core/services/prompt.service.spec.ts b/client/src/app/core/services/prompt.service.spec.ts new file mode 100644 index 000000000..a13720e8e --- /dev/null +++ b/client/src/app/core/services/prompt.service.spec.ts @@ -0,0 +1,17 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { PromptService } from './prompt.service'; +import { E2EImportsModule } from 'e2e-imports.module'; + +describe('PromptService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [PromptService] + }); + }); + + it('should be created', inject([PromptService], (service: PromptService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/client/src/app/core/services/prompt.service.ts b/client/src/app/core/services/prompt.service.ts new file mode 100644 index 000000000..4ea605d40 --- /dev/null +++ b/client/src/app/core/services/prompt.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { OpenSlidesComponent } from 'app/openslides.component'; +import { PromptDialogComponent } from '../../shared/components/prompt-dialog/prompt-dialog.component'; +import { MatDialog } from '@angular/material'; + +/** + * A general service for prompting 'yes' or 'cancel' thinks from the user. + */ +@Injectable({ + providedIn: 'root' +}) +export class PromptService extends OpenSlidesComponent { + public constructor(private dialog: MatDialog) { + super(); + } + + /** + * Opens the dialog. Returns true, if the user accepts. + * @param title The title to display in the dialog + * @param content The content in the dialog + */ + public async open(title: string, content: string): Promise { + const dialogRef = this.dialog.open(PromptDialogComponent, { + width: '250px', + data: { title: title, content: content } + }); + + return dialogRef.afterClosed().toPromise(); + } +} diff --git a/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.html b/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.html new file mode 100644 index 000000000..0c7be3b64 --- /dev/null +++ b/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.html @@ -0,0 +1,6 @@ +

{{ data.title | translate }}

+{{ data.content | translate }} + + + + diff --git a/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.spec.ts b/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.spec.ts new file mode 100644 index 000000000..5555c0e08 --- /dev/null +++ b/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.spec.ts @@ -0,0 +1,26 @@ +import { async, TestBed } from '@angular/core/testing'; + +// import { PromptDialogComponent } from './prompt-dialog.component'; +import { E2EImportsModule } from 'e2e-imports.module'; + +describe('PromptDialogComponent', () => { + // let component: PromptDialogComponent; + // let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule] + }).compileComponents(); + })); + + // TODO: You cannot create this component in the standard way. Needs different testing. + beforeEach(() => { + /*fixture = TestBed.createComponent(PromptDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges();*/ + }); + + /*it('should create', () => { + expect(component).toBeTruthy(); + });*/ +}); diff --git a/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts b/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts new file mode 100644 index 000000000..6b5369668 --- /dev/null +++ b/client/src/app/shared/components/prompt-dialog/prompt-dialog.component.ts @@ -0,0 +1,21 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; + +interface PromptDialogData { + title: string; + content: string; +} + +/** + * A simple prompt dialog. Takes a title and content. + */ +@Component({ + selector: 'os-prompt-dialog', + templateUrl: './prompt-dialog.component.html' +}) +export class PromptDialogComponent { + public constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: PromptDialogData + ) {} +} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 74c0f4d98..6d0de2b40 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -50,6 +50,7 @@ import { LegalNoticeContentComponent } from './components/legal-notice-content/l import { PrivacyPolicyContentComponent } from './components/privacy-policy-content/privacy-policy-content.component'; import { SearchValueSelectorComponent } from './components/search-value-selector/search-value-selector.component'; import { OpenSlidesDateAdapter } from './date-adapter'; +import { PromptDialogComponent } from './components/prompt-dialog/prompt-dialog.component'; library.add(fas); @@ -127,7 +128,8 @@ library.add(fas); HeadBarComponent, SearchValueSelectorComponent, LegalNoticeContentComponent, - PrivacyPolicyContentComponent + PrivacyPolicyContentComponent, + PromptDialogComponent ], declarations: [ PermsDirective, @@ -136,7 +138,8 @@ library.add(fas); FooterComponent, LegalNoticeContentComponent, PrivacyPolicyContentComponent, - SearchValueSelectorComponent + SearchValueSelectorComponent, + PromptDialogComponent ], providers: [{ provide: DateAdapter, useClass: OpenSlidesDateAdapter }] }) diff --git a/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.html b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.html new file mode 100644 index 000000000..0a17cbf97 --- /dev/null +++ b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.html @@ -0,0 +1,108 @@ + + +
+ + Create new comment section + +
+

+ + + + Required + + +

+

+ +

+

+ +

+
+
+ + + + +
+ + + + +
+
+ {{ section.name }} +
+
+ + {{ section.read_groups }} + + – + +
+
+ + {{ section.write_groups }} + + – + +
+
+
+
+
+ Edit section details: +

+ + + + Required + + +

+

+ +

+

+ +

+
+ +

Name

+
{{ section.name }}
+

Groups with read permissions

+
    +
  • {{ group.getTitle() }}
  • +
+
No groups selected
+

Groups with write permissions

+
    +
  • {{ group.getTitle() }}
  • +
+
No groups selected
+
+ + + + + + +
+
diff --git a/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.scss b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.scss new file mode 100644 index 000000000..326f0bbde --- /dev/null +++ b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.scss @@ -0,0 +1,49 @@ +.head-spacer { + width: 100%; + height: 60px; + line-height: 60px; + text-align: right; + background: white; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); +} + +mat-card { + margin-bottom: 20px; +} + +.header-container { + display: grid; + grid-template-rows: auto; + grid-template-columns: 33.333% 33.333% 33.333%; + width: 100%; + + > div { + grid-row-start: 1; + grid-row-end: span 1; + grid-column-end: span 1; + } + + .title { + grid-column-start: 1; + } + + .read { + grid-column-start: 2; + } + + .write { + grid-column-start: 3; + } +} + +h3 { + display: block; + margin-top: 12px; //distance between heading and text + margin-bottom: 3px; //distance between heading and text + font-size: 90%; + color: gray; +} + +.spacer-left { + margin-left: 40px; +} diff --git a/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.spec.ts b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.spec.ts new file mode 100644 index 000000000..a89c54d39 --- /dev/null +++ b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.spec.ts @@ -0,0 +1,26 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MotionCommentSectionListComponent } from './motion-comment-section-list.component'; +import { E2EImportsModule } from 'e2e-imports.module'; + +describe('MotionCommentSectionListComponent', () => { + let component: MotionCommentSectionListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + declarations: [MotionCommentSectionListComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MotionCommentSectionListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.ts b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.ts new file mode 100644 index 000000000..b1b118bd6 --- /dev/null +++ b/client/src/app/site/motions/components/motion-comment-section-list/motion-comment-section-list.component.ts @@ -0,0 +1,170 @@ +import { Component, OnInit } from '@angular/core'; +import { Title } from '@angular/platform-browser'; + +import { TranslateService } from '@ngx-translate/core'; + +import { BaseComponent } from '../../../../base.component'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { MotionCommentSection } from '../../../../shared/models/motions/motion-comment-section'; +import { ViewMotionCommentSection } from '../../models/view-motion-comment-section'; +import { MotionCommentSectionRepositoryService } from '../../services/motion-comment-section-repository.service'; +import { PromptService } from '../../../../core/services/prompt.service'; +import { BehaviorSubject } from 'rxjs'; +import { Group } from '../../../../shared/models/users/group'; +import { DataStoreService } from '../../../../core/services/data-store.service'; + +/** + * List view for the categories. + */ +@Component({ + selector: 'os-motion-comment-section-list', + templateUrl: './motion-comment-section-list.component.html', + styleUrls: ['./motion-comment-section-list.component.scss'] +}) +export class MotionCommentSectionListComponent extends BaseComponent implements OnInit { + public commentSectionToCreate: MotionCommentSection | null; + + /** + * Source of the Data + */ + public commentSections: ViewMotionCommentSection[] = []; + + /** + * The current focussed formgroup + */ + public updateForm: FormGroup; + + public createForm: FormGroup; + + public openId: number | null; + public editId: number | null; + + public groups: BehaviorSubject>; + + /** + * The usual component constructor + * @param titleService + * @param translate + * @param repo + * @param formBuilder + */ + public constructor( + protected titleService: Title, + protected translate: TranslateService, + private repo: MotionCommentSectionRepositoryService, + private formBuilder: FormBuilder, + private promptService: PromptService, + private DS: DataStoreService + ) { + super(titleService, translate); + const form = { + name: ['', Validators.required], + read_groups_id: [[]], + write_groups_id: [[]] + }; + this.createForm = this.formBuilder.group(form); + this.updateForm = this.formBuilder.group(form); + } + + /** + * Event on Key Down in update or create form. Do not provide the viewSection for the create form. + */ + public keyDownFunction(event: KeyboardEvent, viewSection?: ViewMotionCommentSection): void { + if (event.keyCode === 13) { + if (viewSection) { + this.onSaveButton(viewSection); + } else { + this.create(); + } + } + } + + /** + * Init function. + * + * Sets the title and gets/observes categories from DataStore + */ + public ngOnInit(): void { + super.setTitle('Comment Sections'); + this.groups = new BehaviorSubject(this.DS.getAll(Group)); + this.DS.changeObservable.subscribe(model => { + if (model instanceof Group) { + this.groups.next(this.DS.getAll(Group)); + } + }); + this.repo.getViewModelListObservable().subscribe(newViewSections => { + this.commentSections = newViewSections; + }); + } + + /** + * Add a new Section. + */ + public onPlusButton(): void { + if (!this.commentSectionToCreate) { + this.commentSectionToCreate = new MotionCommentSection(); + this.createForm.setValue({ + name: '', + read_groups_id: [], + write_groups_id: [] + }); + } + } + + public create(): void { + if (this.createForm.valid) { + this.commentSectionToCreate.patchValues(this.createForm.value as MotionCommentSection); + this.repo.create(this.commentSectionToCreate).subscribe(resp => { + this.commentSectionToCreate = null; + }); + } + } + + /** + * Executed on edit button + * @param viewSection + */ + public onEditButton(viewSection: ViewMotionCommentSection): void { + this.editId = viewSection.id; + + this.updateForm.setValue({ + name: viewSection.name, + read_groups_id: viewSection.read_groups_id, + write_groups_id: viewSection.write_groups_id + }); + } + + /** + * Saves the categories + */ + public onSaveButton(viewSection: ViewMotionCommentSection): void { + if (this.updateForm.valid) { + this.repo.update(this.updateForm.value as Partial, viewSection).subscribe(resp => { + this.openId = this.editId = null; + }); + } + } + + /** + * is executed, when the delete button is pressed + */ + public async onDeleteButton(viewSection: ViewMotionCommentSection): Promise { + const content = this.translate.instant('Delete') + ` ${viewSection.name}?`; + if (await this.promptService.open('Are you sure?', content)) { + this.repo.delete(viewSection).subscribe(resp => { + this.openId = this.editId = null; + }); + } + } + + /** + * Is executed when a mat-extension-panel is closed + * @param viewSection the category in the panel + */ + public panelClosed(viewSection: ViewMotionCommentSection): void { + this.openId = null; + if (this.editId) { + this.onSaveButton(viewSection); + } + } +} diff --git a/client/src/app/site/motions/components/motion-detail/motion-detail.component.html b/client/src/app/site/motions/components/motion-detail/motion-detail.component.html index e38308f18..9a99f55c8 100644 --- a/client/src/app/site/motions/components/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/components/motion-detail/motion-detail.component.html @@ -191,7 +191,7 @@ {{state}} - Reset State + Reset State diff --git a/client/src/app/site/motions/components/motion-list/motion-list.component.ts b/client/src/app/site/motions/components/motion-list/motion-list.component.ts index d35b48e6a..d72613cdd 100644 --- a/client/src/app/site/motions/components/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/components/motion-list/motion-list.component.ts @@ -42,6 +42,10 @@ export class MotionListComponent extends ListViewBaseComponent imple { text: 'Categories', action: 'toCategories' + }, + { + text: 'Motion comment sections', + action: 'toMotionCommentSections' } ]; @@ -130,6 +134,13 @@ export class MotionListComponent extends ListViewBaseComponent imple this.router.navigate(['./category'], { relativeTo: this.route }); } + /** + * navigate to 'motion/comment-section' + */ + public toMotionCommentSections(): void { + this.router.navigate(['./comment-section'], { relativeTo: this.route }); + } + /** * Download all motions As PDF and DocX * diff --git a/client/src/app/site/motions/models/view-motion-comment-section.ts b/client/src/app/site/motions/models/view-motion-comment-section.ts new file mode 100644 index 000000000..cf7d98bd4 --- /dev/null +++ b/client/src/app/site/motions/models/view-motion-comment-section.ts @@ -0,0 +1,90 @@ +import { TranslateService } from '@ngx-translate/core'; +import { BaseViewModel } from '../../base/base-view-model'; +import { MotionCommentSection } from '../../../shared/models/motions/motion-comment-section'; +import { Group } from '../../../shared/models/users/group'; +import { BaseModel } from '../../../shared/models/base/base-model'; + +/** + * Motion comment section class for the View + * + * Stores a motion comment section including all (implicit) references + * Provides "safe" access to variables and functions in {@link MotionCommentSection} + * @ignore + */ +export class ViewMotionCommentSection extends BaseViewModel { + private _section: MotionCommentSection; + + private _read_groups: Group[]; + private _write_groups: Group[]; + + public edit = false; + public open = false; + + public get section(): MotionCommentSection { + return this._section; + } + + public get id(): number { + return this.section ? this.section.id : null; + } + + public get name(): string { + return this.section ? this.section.name : null; + } + + public get read_groups_id(): number[] { + return this.section ? this.section.read_groups_id : []; + } + + public get write_groups_id(): number[] { + return this.section ? this.section.write_groups_id : []; + } + + public get read_groups(): Group[] { + return this._read_groups; + } + + public get write_groups(): Group[] { + return this._write_groups; + } + + public set name(name: string) { + this._section.name = name; + } + + public constructor(section: MotionCommentSection, read_groups: Group[], write_groups: Group[]) { + super(); + this._section = section; + this._read_groups = read_groups; + this._write_groups = write_groups; + } + + public getTitle(translate?: TranslateService): string { + return this.name; + } + + /** + * Updates the local objects if required + * @param section + */ + public updateValues(update: BaseModel): void { + if (update instanceof MotionCommentSection) { + this._section = update as MotionCommentSection; + } + if (update instanceof Group) { + this.updateGroup(update as Group); + } + } + + // TODO: Implement updating of groups + public updateGroup(group: Group): void { + console.log(this._section, group); + } + + /** + * Duplicate this motion into a copy of itself + */ + public copy(): ViewMotionCommentSection { + return new ViewMotionCommentSection(this._section, this._read_groups, this._write_groups); + } +} diff --git a/client/src/app/site/motions/motions-routing.module.ts b/client/src/app/site/motions/motions-routing.module.ts index 67f954cf2..0d90247bc 100644 --- a/client/src/app/site/motions/motions-routing.module.ts +++ b/client/src/app/site/motions/motions-routing.module.ts @@ -3,10 +3,12 @@ import { Routes, RouterModule } from '@angular/router'; import { MotionListComponent } from './components/motion-list/motion-list.component'; import { MotionDetailComponent } from './components/motion-detail/motion-detail.component'; import { CategoryListComponent } from './components/category-list/category-list.component'; +import { MotionCommentSectionListComponent } from './components/motion-comment-section-list/motion-comment-section-list.component'; const routes: Routes = [ { path: '', component: MotionListComponent }, { path: 'category', component: CategoryListComponent }, + { path: 'comment-section', component: MotionCommentSectionListComponent }, { path: 'new', component: MotionDetailComponent }, { path: ':id', component: MotionDetailComponent } ]; diff --git a/client/src/app/site/motions/motions.module.ts b/client/src/app/site/motions/motions.module.ts index 8f60922ad..e615a0bcb 100644 --- a/client/src/app/site/motions/motions.module.ts +++ b/client/src/app/site/motions/motions.module.ts @@ -6,9 +6,10 @@ import { SharedModule } from '../../shared/shared.module'; import { MotionListComponent } from './components/motion-list/motion-list.component'; import { MotionDetailComponent } from './components/motion-detail/motion-detail.component'; import { CategoryListComponent } from './components/category-list/category-list.component'; +import { MotionCommentSectionListComponent } from './components/motion-comment-section-list/motion-comment-section-list.component'; @NgModule({ imports: [CommonModule, MotionsRoutingModule, SharedModule], - declarations: [MotionListComponent, MotionDetailComponent, CategoryListComponent] + declarations: [MotionListComponent, MotionDetailComponent, CategoryListComponent, MotionCommentSectionListComponent] }) export class MotionsModule {} diff --git a/client/src/app/site/motions/services/motion-comment-section-repository.service.spec.ts b/client/src/app/site/motions/services/motion-comment-section-repository.service.spec.ts new file mode 100644 index 000000000..ec2a984ad --- /dev/null +++ b/client/src/app/site/motions/services/motion-comment-section-repository.service.spec.ts @@ -0,0 +1,20 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { MotionCommentSectionRepositoryService } from './motion-comment-section-repository.service'; +import { E2EImportsModule } from 'e2e-imports.module'; + +describe('MotionCommentSectionRepositoryService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [E2EImportsModule], + providers: [MotionCommentSectionRepositoryService] + }); + }); + + it('should be created', inject( + [MotionCommentSectionRepositoryService], + (service: MotionCommentSectionRepositoryService) => { + expect(service).toBeTruthy(); + } + )); +}); diff --git a/client/src/app/site/motions/services/motion-comment-section-repository.service.ts b/client/src/app/site/motions/services/motion-comment-section-repository.service.ts new file mode 100644 index 000000000..78b143096 --- /dev/null +++ b/client/src/app/site/motions/services/motion-comment-section-repository.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { DataSendService } from '../../../core/services/data-send.service'; +import { Observable } from 'rxjs'; +import { DataStoreService } from '../../../core/services/data-store.service'; +import { BaseRepository } from '../../base/base-repository'; +import { ViewMotionCommentSection } from '../models/view-motion-comment-section'; +import { MotionCommentSection } from '../../../shared/models/motions/motion-comment-section'; +import { Group } from '../../../shared/models/users/group'; + +/** + * Repository Services for Categories + * + * The repository is meant to process domain objects (those found under + * shared/models), so components can display them and interact with them. + * + * Rather than manipulating models directly, the repository is meant to + * inform the {@link DataSendService} about changes which will send + * them to the Server. + */ +@Injectable({ + providedIn: 'root' +}) +export class MotionCommentSectionRepositoryService extends BaseRepository< + ViewMotionCommentSection, + MotionCommentSection +> { + /** + * Creates a CategoryRepository + * Converts existing and incoming category to ViewCategories + * Handles CRUD using an observer to the DataStore + * @param DataSend + */ + public constructor(protected DS: DataStoreService, private dataSend: DataSendService) { + super(DS, MotionCommentSection, [Group]); + } + + protected createViewModel(section: MotionCommentSection): ViewMotionCommentSection { + const read_groups = this.DS.getMany(Group, section.read_groups_id); + const write_groups = this.DS.getMany(Group, section.write_groups_id); + return new ViewMotionCommentSection(section, read_groups, write_groups); + } + + public create(section: MotionCommentSection): Observable { + return this.dataSend.createModel(section); + } + + public update(section: Partial, viewSection?: ViewMotionCommentSection): Observable { + let updateSection: MotionCommentSection; + if (viewSection) { + updateSection = viewSection.section; + } else { + updateSection = new MotionCommentSection(); + } + updateSection.patchValues(section); + return this.dataSend.updateModel(updateSection, 'put'); + } + + public delete(viewSection: ViewMotionCommentSection): Observable { + return this.dataSend.delete(viewSection.section); + } +} diff --git a/client/src/e2e-imports.module.ts b/client/src/e2e-imports.module.ts index e8f9f92d1..f24c12518 100644 --- a/client/src/e2e-imports.module.ts +++ b/client/src/e2e-imports.module.ts @@ -23,7 +23,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; AppModule, CommonModule, SharedModule, - HttpClientModule, TranslateModule.forRoot({ loader: { diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index 530aaa807..aa847c16b 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _ from ..poll.serializers import default_votes_validator from ..utils.auth import get_group_model +from ..utils.autoupdate import inform_changed_data from ..utils.rest_api import ( CharField, DecimalField, @@ -314,6 +315,12 @@ class MotionCommentSectionSerializer(ModelSerializer): 'read_groups', 'write_groups',) + def create(self, validated_data): + """ Call inform_changed_data on creation, so the cache includes the groups. """ + section = super().create(validated_data) + inform_changed_data(section) + return section + class MotionCommentSerializer(ModelSerializer): """