From de61505b0008ba33a113e3795125d4c4a578dc31 Mon Sep 17 00:00:00 2001 From: Sean Engelhardt Date: Wed, 22 Aug 2018 11:26:53 +0200 Subject: [PATCH] Put/Post motions to server Temporarily over dataStore, will need own service --- .../app/core/services/dataStore.service.ts | 38 +++++++--- .../src/app/core/services/operator.service.ts | 24 ++++++- client/src/app/openslides.component.ts | 1 + .../src/app/shared/models/motions/motion.ts | 29 ++++---- .../motion-detail.component.html | 30 ++++---- .../motion-detail/motion-detail.component.ts | 70 +++++++++++++++---- .../motion-list/motion-list.component.ts | 33 +++++++-- client/src/app/site/start/start.component.ts | 3 +- 8 files changed, 166 insertions(+), 62 deletions(-) diff --git a/client/src/app/core/services/dataStore.service.ts b/client/src/app/core/services/dataStore.service.ts index 1ae5ea506..29af1e678 100644 --- a/client/src/app/core/services/dataStore.service.ts +++ b/client/src/app/core/services/dataStore.service.ts @@ -29,6 +29,12 @@ interface Storage { * Use this.DS in an OpenSlides Component to Access the store. * Used by a lot of components, classes and services. * Changes can be observed + * + * FIXME: The injector does not init the HttpClient Service. + * Either remove it from DataStore and make an own Service + * fix it somehow + * or just do-not let the OpenSlidesComponent inject DataStore to it's + * children. */ @Injectable({ providedIn: 'root' @@ -49,7 +55,9 @@ export class DataStoreService { * Empty constructor for dataStore * @param http use HttpClient to send models back to the server */ - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) { + console.log('constructor of dataStore. http: ', this.http); + } /** * Read one, multiple or all ID's from dataStore @@ -157,21 +165,29 @@ export class DataStoreService { /** * Saves the given model on the server - * @param model the BaseModel that shall be removed + * @param model the BaseModel that shall be saved * @return Observable of BaseModel */ save(model: BaseModel): Observable { if (!model.id) { - throw new ImproperlyConfiguredError('The model must have an id!'); + return this.http.post('rest/' + model.collectionString + '/', model).pipe( + tap( + response => { + console.log('New Model added. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); + } else { + return this.http.put('rest/' + model.collectionString + '/' + model.id, model).pipe( + tap( + response => { + console.log('Update model. Response : ', response); + }, + error => console.log('error. ', error) + ) + ); } - - // TODO not tested - return this.http.post(model.collectionString + '/', model).pipe( - tap(response => { - console.log('the response: ', response); - this.add(model); - }) - ); } /** diff --git a/client/src/app/core/services/operator.service.ts b/client/src/app/core/services/operator.service.ts index 9655a8b17..30d25ee31 100644 --- a/client/src/app/core/services/operator.service.ts +++ b/client/src/app/core/services/operator.service.ts @@ -4,6 +4,7 @@ import { HttpClient } from '@angular/common/http'; import { tap, catchError, share } from 'rxjs/operators'; import { OpenSlidesComponent } from 'app/openslides.component'; import { Group } from 'app/shared/models/users/group'; +import { User } from '../../shared/models/users/user'; /** * The operator represents the user who is using OpenSlides. @@ -38,6 +39,8 @@ export class OperatorService extends OpenSlidesComponent { username: string; logged_in: boolean; + private _user: User; + /** * The subject that can be observed by other instances using observing functions. */ @@ -65,7 +68,7 @@ export class OperatorService extends OpenSlidesComponent { } } - // observe the datastore now to avoid race conditions. Ensures to + // observe the DataStore now to avoid race conditions. Ensures to // find the groups in time this.observeDataStore(); } @@ -77,7 +80,7 @@ export class OperatorService extends OpenSlidesComponent { return this.http.get('/users/whoami/').pipe( tap(whoami => { if (whoami && whoami.user) { - this.storeUser(whoami.user); + this.storeUser(whoami.user as User); } }), catchError(this.handleError()) @@ -87,8 +90,11 @@ export class OperatorService extends OpenSlidesComponent { /** * Store the user Information in the operator, the localStorage and update the Observable * @param user usually a http response that represents a user. + * + * Todo: Could be refractored to use the actual User Object. + * Operator is older than user, so this is still a traditional JS way */ - public storeUser(user: any): void { + public storeUser(user: User): void { // store in file this.about_me = user.about_me; this.comment = user.comment; @@ -106,6 +112,7 @@ export class OperatorService extends OpenSlidesComponent { this.structure_level = user.structure_level; this.title = user.title; this.username = user.username; + // also store in localstorrage this.updateLocalStorage(); // update mode to inform observers @@ -187,6 +194,10 @@ export class OperatorService extends OpenSlidesComponent { if (newModel instanceof Group) { this.addGroup(newModel); } + + if (newModel instanceof User && this.id === newModel.id) { + this._user = newModel; + } }); } @@ -243,4 +254,11 @@ export class OperatorService extends OpenSlidesComponent { this.setObservable(newGroup); } } + + /** + * get the user that corresponds to operator. + */ + get user(): User { + return this._user; + } } diff --git a/client/src/app/openslides.component.ts b/client/src/app/openslides.component.ts index 951646758..570684bfb 100644 --- a/client/src/app/openslides.component.ts +++ b/client/src/app/openslides.component.ts @@ -1,4 +1,5 @@ import { Injector } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { DataStoreService } from 'app/core/services/dataStore.service'; diff --git a/client/src/app/shared/models/motions/motion.ts b/client/src/app/shared/models/motions/motion.ts index ff7b39322..f78e69542 100644 --- a/client/src/app/shared/models/motions/motion.ts +++ b/client/src/app/shared/models/motions/motion.ts @@ -42,6 +42,10 @@ export class Motion extends BaseModel { // by the config above workflow: Workflow; + // for request + title: string; + text: string; + constructor( id?: number, identifier?: string, @@ -73,7 +77,7 @@ export class Motion extends BaseModel { this.category_id = category_id; this.motion_block_id = motion_block_id; this.origin = origin || ''; - this.submitters = submitters || [new MotionSubmitter()]; + this.submitters = submitters || []; this.supporters_id = supporters_id; this.comments = comments; this.state_id = state_id; @@ -83,7 +87,7 @@ export class Motion extends BaseModel { this.attachments_id = attachments_id; this.polls = polls; this.agenda_item_id = agenda_item_id; - this.log_messages = log_messages || [new MotionLog()]; + this.log_messages = log_messages || []; this.initDataStoreValues(); } @@ -120,6 +124,13 @@ export class Motion extends BaseModel { } } + /** add a new motionSubmitter from user-object */ + addSubmitter(user: User) { + const newSubmitter = new MotionSubmitter(null, user.id); + this.submitters.push(newSubmitter); + console.log('did addSubmitter. this.submitters: ', this.submitters); + } + /** * returns the most current title from versions */ @@ -176,7 +187,7 @@ export class Motion extends BaseModel { */ get submitterAsUser() { const submitterIds = []; - if (this.submitters) { + if (this.submitters && this.submitters.length > 0) { this.submitters.forEach(submitter => { submitterIds.push(submitter.user_id); }); @@ -187,18 +198,6 @@ export class Motion extends BaseModel { } } - /** - * returns the name of the first submitter - */ - get submitterName() { - const submitters = this.submitterAsUser; - if (submitters) { - return submitters[0]; - } else { - return ''; - } - } - /** * get the category of a motion as object */ diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.html b/client/src/app/site/motions/motion-detail/motion-detail.component.html index 482e3c543..6a4d9bc26 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.html +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.html @@ -9,14 +9,14 @@
New Motion - {{motion.identifier}} + {{motion.identifier}} {{metaInfoForm.get('identifier').value}} : - {{motion.currentTitle}} + {{motion.currentTitle}} {{contentForm.get('currentTitle').value}}
-
- by {{motion.submitterName}} +
+ by {{motion.submitterAsUser}}
@@ -49,19 +49,19 @@
-
+

Submitters

- {{motion.submitterName}} + {{motion.submitterAsUser}}
-
+

Supporters

-
+

State

{{motion.state.name}} @@ -80,7 +80,7 @@ -
+

{{motion.recomBy}}

{{motion.recommendation.name}} @@ -98,7 +98,7 @@
-
+

Category

{{motion.category}} @@ -113,7 +113,7 @@
-
+

Origin

{{motion.origin}} @@ -153,7 +153,7 @@
-
+

{{motion.currentTitle}}

@@ -165,15 +165,15 @@

The assembly may decide:

-
+
- + -
+

Reason

diff --git a/client/src/app/site/motions/motion-detail/motion-detail.component.ts b/client/src/app/site/motions/motion-detail/motion-detail.component.ts index d000e1a4e..df8b8bf89 100644 --- a/client/src/app/site/motions/motion-detail/motion-detail.component.ts +++ b/client/src/app/site/motions/motion-detail/motion-detail.component.ts @@ -1,37 +1,78 @@ import { Component, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { BaseComponent } from '../../../base.component'; import { Motion } from '../../../shared/models/motions/motion'; import { Category } from '../../../shared/models/motions/category'; import { FormGroup, FormBuilder } from '@angular/forms'; import { MatExpansionPanel } from '@angular/material'; +import { DataStoreService } from '../../../core/services/dataStore.service'; +import { OperatorService } from '../../../core/services/operator.service'; +/** + * Component for the motion detail view + */ @Component({ selector: 'app-motion-detail', templateUrl: './motion-detail.component.html', styleUrls: ['./motion-detail.component.scss'] }) -export class MotionDetailComponent extends BaseComponent implements OnInit { +// export class MotionDetailComponent extends BaseComponent implements OnInit { +export class MotionDetailComponent implements OnInit { + /** + * MatExpansionPanel for the meta info + */ @ViewChild('metaInfoPanel') metaInfoPanel: MatExpansionPanel; + + /** + * MatExpansionPanel for the content panel + */ @ViewChild('contentPanel') contentPanel: MatExpansionPanel; + /** + * Target motion. Might be new or old + */ motion: Motion; + + /** + * Motions meta-info + */ metaInfoForm: FormGroup; + + /** + * Motion content. Can be a new version + */ contentForm: FormGroup; + + /** + * Determine if the motion is edited + */ editMotion = false; + + /** + * Determine if the motion is new + */ newMotion = false; /** + * Constuct the detail view. + * + * TODO: DataStore needs removed and added via the parent. + * Own service for put and post required * * @param route determine if this is a new or an existing motion * @param formBuilder For reactive forms. Form Group and Form Control */ - constructor(private route: ActivatedRoute, private formBuilder: FormBuilder) { - super(); + constructor( + private router: Router, + private route: ActivatedRoute, + private formBuilder: FormBuilder, + private operator: OperatorService, + private myDataStore: DataStoreService + ) { + // TODO: Add super again + // super(); this.createForm(); - console.log('route: ', route.snapshot.url[0].path); - if (route.snapshot.url[0].path === 'new') { this.newMotion = true; this.editMotion = true; @@ -42,10 +83,10 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { console.log('params ', params); // has the motion of the DataStore was initialized before. - this.motion = this.DS.get(Motion, params.id) as Motion; + this.motion = this.myDataStore.get(Motion, params.id) as Motion; // Observe motion to get the motion in the parameter and also get the changes - this.DS.getObservable().subscribe(newModel => { + this.myDataStore.getObservable().subscribe(newModel => { if (newModel instanceof Motion) { if (newModel.id === +params.id) { this.motion = newModel as Motion; @@ -105,10 +146,15 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { const newMotionValues = { ...this.metaInfoForm.value, ...this.contentForm.value }; this.motion.patchValues(newMotionValues); - console.log('save motion: this: ', this); + // TODO: This is DRAFT. Reads out Motion version directly. Potentially insecure. + this.motion.title = this.motion.currentTitle; + this.motion.text = this.motion.currentText; - this.DS.save(this.motion).subscribe(answer => { - console.log(answer); + this.myDataStore.save(this.motion).subscribe(answer => { + console.log('answer, ', answer); + if (answer && answer.id && this.newMotion) { + this.router.navigate(['./motions/' + answer.id]); + } }); } @@ -116,7 +162,7 @@ export class MotionDetailComponent extends BaseComponent implements OnInit { * return all Categories. */ getMotionCategories(): Category[] { - const categories = this.DS.get(Category); + const categories = this.myDataStore.get(Category); return categories as Category[]; } diff --git a/client/src/app/site/motions/motion-list/motion-list.component.ts b/client/src/app/site/motions/motion-list/motion-list.component.ts index 8e9903a7a..820a25602 100644 --- a/client/src/app/site/motions/motion-list/motion-list.component.ts +++ b/client/src/app/site/motions/motion-list/motion-list.component.ts @@ -7,6 +7,9 @@ import { Motion } from '../../../shared/models/motions/motion'; import { MatTable, MatPaginator, MatSort, MatTableDataSource } from '@angular/material'; import { Workflow } from '../../../shared/models/motions/workflow'; +/** + * Component that displays all the motions in a Table using DataSource. + */ @Component({ selector: 'app-motion-list', templateUrl: './motion-list.component.html', @@ -28,14 +31,26 @@ export class MotionListComponent extends BaseComponent implements OnInit { */ dataSource: MatTableDataSource; + /** + * The table itself. + */ @ViewChild(MatTable) table: MatTable; + + /** + * Pagination. Might be turned off to all motions at once. + */ @ViewChild(MatPaginator) paginator: MatPaginator; + + /** + * Sort the Table + */ @ViewChild(MatSort) sort: MatSort; /** * Use for minimal width */ columnsToDisplayMinWidth = ['identifier', 'title', 'state']; + /** * Use for maximal width */ @@ -43,13 +58,16 @@ export class MotionListComponent extends BaseComponent implements OnInit { /** * Constructor implements title and translation Module. - * @param titleService - * @param translate + * + * @param titleService Title + * @param translate Translation + * @param router Router + * @param route Current route */ constructor( - public router: Router, - titleService: Title, + protected titleService: Title, protected translate: TranslateService, + private router: Router, private route: ActivatedRoute ) { super(titleService, translate); @@ -78,6 +96,11 @@ export class MotionListComponent extends BaseComponent implements OnInit { }); } + /** + * Select a motion from list. Executed via click. + * + * @param motion The row the user clicked at + */ selectMotion(motion) { this.router.navigate(['./' + motion.id], { relativeTo: this.route }); } @@ -102,6 +125,8 @@ export class MotionListComponent extends BaseComponent implements OnInit { /** * Download all motions As PDF and DocX + * + * TODO: Currently does nothing */ downloadMotionsButton() { console.log('Download Motions Button'); diff --git a/client/src/app/site/start/start.component.ts b/client/src/app/site/start/start.component.ts index 6030d5b84..cc4db77d3 100644 --- a/client/src/app/site/start/start.component.ts +++ b/client/src/app/site/start/start.component.ts @@ -111,8 +111,7 @@ export class StartComponent extends BaseComponent implements OnInit { * function to print datastore */ giveDataStore() { - // this.DS.printWhole(); - console.log('only the motions: \n', this.DS.get(Motion) as Motion[]); + this.DS.printWhole(); } /**