Merge pull request #4785 from FinnStutzenstein/historyQuicklink

Quicklink to history (closes #4777)
This commit is contained in:
Finn Stutzenstein 2019-06-28 08:50:54 +02:00 committed by GitHub
commit b71e73fe7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 12 deletions

View File

@ -112,4 +112,29 @@ export class CollectionStringMapperService {
public getAllRepositories(): BaseRepository<any, any, any>[] { public getAllRepositories(): BaseRepository<any, any, any>[] {
return Object.values(this.collectionStringMapping).map((types: CollectionStringMappedTypes) => types[2]); return Object.values(this.collectionStringMapping).map((types: CollectionStringMappedTypes) => types[2]);
} }
/**
* Validates the given element id. It must have the form `<collection>:<id>`, with
* <collection> being a registered collection and the id a valid integer greater then 0.
*
* @param elementId The element id.
* @returns true, if the element id is valid.
*/
public isElementIdValid(elementId: any): boolean {
if (!elementId || typeof elementId !== 'string') {
return false;
}
const splitted = elementId.split(':');
if (splitted.length !== 2) {
return false;
}
const id = parseInt(splitted[1], 10);
if (isNaN(id) || id <= 0) {
return false;
}
return Object.keys(this.collectionStringMapping).some(collection => collection === splitted[0]);
}
} }

View File

@ -38,6 +38,13 @@ export abstract class BaseViewModel<M extends BaseModel = any>
return this._collectionString; return this._collectionString;
} }
/**
* @returns the element id of the model
*/
public get elementId(): string {
return `${this.collectionString}:${this.id}`;
}
public getTitle: () => string; public getTitle: () => string;
public getListTitle: () => string; public getListTitle: () => string;

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { MatSnackBar, MatTableDataSource } from '@angular/material'; import { MatSnackBar, MatTableDataSource } from '@angular/material';
import { Router } from '@angular/router'; import { Router, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@ -21,6 +21,7 @@ import { MotionRepositoryService } from 'app/core/repositories/motions/motion-re
import { BaseViewModel } from 'app/site/base/base-view-model'; import { BaseViewModel } from 'app/site/base/base-view-model';
import { Motion } from 'app/shared/models/motions/motion'; import { Motion } from 'app/shared/models/motions/motion';
import { PromptService } from 'app/core/ui-services/prompt.service'; import { PromptService } from 'app/core/ui-services/prompt.service';
import { CollectionStringMapperService } from 'app/core/core-services/collection-string-mapper.service';
/** /**
* A list view for the history. * A list view for the history.
@ -40,10 +41,6 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
public dataSource: MatTableDataSource<History> = new MatTableDataSource<History>(); public dataSource: MatTableDataSource<History> = new MatTableDataSource<History>();
public get isSuperAdmin(): boolean {
return this.operator.isSuperAdmin();
}
public pageSizes = [50, 100, 150, 200, 250]; public pageSizes = [50, 100, 150, 200, 250];
/** /**
@ -67,6 +64,10 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
return this.modelSelectForm.controls.model.value; return this.modelSelectForm.controls.model.value;
} }
public get isSuperAdmin(): boolean {
return this.operator.isSuperAdmin();
}
/** /**
* Constructor for the history list component * Constructor for the history list component
* *
@ -89,7 +90,9 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
private http: HttpService, private http: HttpService,
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private motionRepo: MotionRepositoryService, private motionRepo: MotionRepositoryService,
private promptService: PromptService private promptService: PromptService,
private activatedRoute: ActivatedRoute,
private collectionMapper: CollectionStringMapperService
) { ) {
super(titleService, translate, matSnackBar); super(titleService, translate, matSnackBar);
@ -99,7 +102,15 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
this.collectionObserver = this.motionRepo.getViewModelListBehaviorSubject(); this.collectionObserver = this.motionRepo.getViewModelListBehaviorSubject();
this.modelSelectForm.controls.model.valueChanges.subscribe((id: number) => { this.modelSelectForm.controls.model.valueChanges.subscribe((id: number) => {
this.queryElementId(this.currentCollection, id); const elementId = `${this.currentCollection}:${id}`;
this.queryByElementId(elementId);
// Update the URL.
this.router.navigate([], {
relativeTo: this.activatedRoute,
queryParams: { element: elementId },
replaceUrl: true
});
}); });
} }
@ -136,6 +147,18 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
.indexOf(filter) >= 0 .indexOf(filter) >= 0
); );
}; };
// If an element id is given, validate it and update the view.
const params = this.activatedRoute.snapshot.queryParams;
if (this.collectionMapper.isElementIdValid(params.element)) {
this.queryByElementId(params.element);
this.modelSelectForm.patchValue(
{
model: parseInt(params.element.split(':')[1], 10)
},
{ emitEvent: false }
);
}
} }
/** /**
@ -201,7 +224,7 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
public refresh(): void { public refresh(): void {
if (this.currentCollection && this.currentModelId) { if (this.currentCollection && this.currentModelId) {
this.queryElementId(this.currentCollection, this.currentModelId); this.queryByElementId(`${this.currentCollection}:${this.currentModelId}`);
} }
} }
@ -238,12 +261,12 @@ export class HistoryListComponent extends BaseViewComponent implements OnInit {
} }
/** /**
* Sets the data source to the request element id given by the collection string and the id. * Sets the data source to the requested element id.
*/ */
private async queryElementId(collectionString: string, id: number): Promise<void> { private async queryByElementId(elementId: string): Promise<void> {
const historyData = await this.http.get<History[]>(`${environment.urlPrefix}/core/history/information/`, null, { const historyData = await this.http.get<History[]>(`${environment.urlPrefix}/core/history/information/`, null, {
type: 'element', type: 'element',
value: `${collectionString}:${id}` value: elementId
}); });
this.dataSource.data = historyData.map(data => new History(data)); this.dataSource.data = historyData.map(data => new History(data));
} }

View File

@ -101,6 +101,13 @@
<span translate>Show entire motion text</span> <span translate>Show entire motion text</span>
</button> </button>
<button mat-menu-item *osPerms="'core.can_see_history'" [routerLink]="['/history']" [queryParams]="{element: motion.elementId}">
<mat-icon>history</mat-icon>
<span translate>
History
</span>
</button>
<div *ngIf="perms.isAllowed('manage')"> <div *ngIf="perms.isAllowed('manage')">
<mat-divider></mat-divider> <mat-divider></mat-divider>
<!-- Delete --> <!-- Delete -->

View File

@ -504,7 +504,7 @@ class HistoryInformationView(utils_views.APIView):
""" """
Checks permission and parses query parameters. Checks permission and parses query parameters.
""" """
if not has_perm(self.request.user, "users.can_see_history"): if not has_perm(self.request.user, "core.can_see_history"):
self.permission_denied(self.request) self.permission_denied(self.request)
type = self.request.query_params.get("type") type = self.request.query_params.get("type")
value = self.request.query_params.get("value") value = self.request.query_params.get("value")