Put/Post motions to server

Temporarily over dataStore, will need own service
This commit is contained in:
Sean Engelhardt 2018-08-22 11:26:53 +02:00
parent 133ecb4724
commit de61505b00
8 changed files with 166 additions and 62 deletions

View File

@ -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<BaseModel> {
if (!model.id) {
throw new ImproperlyConfiguredError('The model must have an id!');
}
// TODO not tested
return this.http.post<BaseModel>(model.collectionString + '/', model).pipe(
tap(response => {
console.log('the response: ', response);
this.add(model);
})
return this.http.post<BaseModel>('rest/' + model.collectionString + '/', model).pipe(
tap(
response => {
console.log('New Model added. Response : ', response);
},
error => console.log('error. ', error)
)
);
} else {
return this.http.put<BaseModel>('rest/' + model.collectionString + '/' + model.id, model).pipe(
tap(
response => {
console.log('Update model. Response : ', response);
},
error => console.log('error. ', error)
)
);
}
}
/**

View File

@ -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<any>('/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;
}
}

View File

@ -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';

View File

@ -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
*/

View File

@ -9,14 +9,14 @@
<div class='motion-title on-transition-fade'>
<span *ngIf="newMotion">New </span>
<span translate>Motion</span>
<span *ngIf="!editMotion"> {{motion.identifier}}</span>
<span *ngIf="motion && !editMotion"> {{motion.identifier}}</span>
<span *ngIf="editMotion && !newMotion"> {{metaInfoForm.get('identifier').value}}</span>
<span>:</span>
<span *ngIf="!editMotion"> {{motion.currentTitle}}</span>
<span *ngIf="motion && !editMotion"> {{motion.currentTitle}}</span>
<span *ngIf="editMotion"> {{contentForm.get('currentTitle').value}}</span>
<br>
<div class='motion-submitter'>
<span translate>by</span> {{motion.submitterName}}
<div *ngIf="motion" class='motion-submitter'>
<span translate>by</span> {{motion.submitterAsUser}}
</div>
</div>
</mat-toolbar>
@ -49,19 +49,19 @@
</div>
<!-- Submitter -->
<div *ngIf="motion.submitterName || editMotion">
<div *ngIf="motion && motion.submitters || editMotion">
<h3 translate>Submitters</h3>
{{motion.submitterName}}
{{motion.submitterAsUser}}
</div>
<!-- Supporter -->
<div *ngIf='motion.supporters_id && motion.supporters_id.length > 0 || editMotion'>
<div *ngIf='motion && motion.supporters_id && motion.supporters_id.length > 0 || editMotion'>
<h3 translate>Supporters</h3>
<!-- print all motion supporters -->
</div>
<!-- State -->
<div *ngIf='motion.state_id || editMotion'>
<div *ngIf='motion && motion.state_id || editMotion'>
<div *ngIf='!editMotion'>
<h3 translate>State</h3>
{{motion.state.name}}
@ -80,7 +80,7 @@
<!-- Recommendation -->
<!-- The suggestion of the work group weather or not a motion should be accepted -->
<div *ngIf='motion.recomBy && (motion.recommendation_id || editMotion)'>
<div *ngIf='motion && motion.recomBy && (motion.recommendation_id || editMotion)'>
<div *ngIf='!editMotion'>
<h3>{{motion.recomBy}}</h3>
{{motion.recommendation.name}}
@ -98,7 +98,7 @@
</div>
<!-- Category -->
<div *ngIf="motion.category_id || editMotion">
<div *ngIf="motion && motion.category_id || editMotion">
<div *ngIf='!editMotion'>
<h3 translate> Category</h3>
{{motion.category}}
@ -113,7 +113,7 @@
</div>
<!-- Origin -->
<div *ngIf="motion.origin || editMotion">
<div *ngIf="motion && motion.origin || editMotion">
<div *ngIf='!editMotion'>
<h3 translate> Origin</h3>
{{motion.origin}}
@ -153,7 +153,7 @@
<form [formGroup]='contentForm' class='expansion-panel-custom-body' (ngSubmit)='saveMotion()'>
<!-- Title -->
<div *ngIf="motion.currentTitle || editMotion">
<div *ngIf="motion && motion.currentTitle || editMotion">
<div *ngIf='!editMotion'>
<h2>{{motion.currentTitle}}</h2>
</div>
@ -165,15 +165,15 @@
<!-- Text -->
<!-- TODO: this is a config variable. Read it out -->
<h3 translate>The assembly may decide:</h3>
<div *ngIf='!editMotion'>
<div *ngIf='motion && !editMotion'>
<div [innerHtml]='motion.currentText'></div>
</div>
<mat-form-field *ngIf="editMotion" class='wide-text'>
<mat-form-field *ngIf="motion && editMotion" class='wide-text'>
<textarea matInput placeholder='Motion Text' formControlName='currentText' [value]='motion.currentText'></textarea>
</mat-form-field>
<!-- Reason -->
<div *ngIf="motion.currentReason || editMotion">
<div *ngIf="motion && motion.currentReason || editMotion">
<div *ngIf='!editMotion'>
<h4 translate>Reason</h4>
<div [innerHtml]='motion.currentReason'></div>

View File

@ -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[];
}

View File

@ -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<Motion>;
/**
* The table itself.
*/
@ViewChild(MatTable) table: MatTable<Motion>;
/**
* 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');

View File

@ -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();
}
/**