2019-09-23 10:17:03 +02:00
|
|
|
|
2019-01-18 18:03:45 +01:00
|
|
|
import { Component, OnInit } from '@angular/core';
|
|
|
|
import { FormControl } from '@angular/forms';
|
2019-12-11 15:51:03 +01:00
|
|
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
|
|
import { RecentActivityType } from '@app/core/common/enum/recent-activity-type';
|
|
|
|
import { DashboardStatisticsModel } from '@app/core/model/dashboard/dashboard-statistics-model';
|
|
|
|
import { SearchBarItem } from '@app/core/model/dashboard/search-bar-item';
|
|
|
|
import { DataTableRequest } from '@app/core/model/data-table/data-table-request';
|
|
|
|
import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing';
|
|
|
|
import { DmpListingModel } from '@app/core/model/dmp/dmp-listing';
|
|
|
|
import { GrantCriteria } from '@app/core/query/grant/grant-criteria';
|
|
|
|
import { RequestItem } from '@app/core/query/request-item';
|
|
|
|
import { AuthService } from '@app/core/services/auth/auth.service';
|
|
|
|
import { DashboardService } from '@app/core/services/dashboard/dashboard.service';
|
|
|
|
import { DatasetService } from '@app/core/services/dataset/dataset.service';
|
|
|
|
import { DmpService } from '@app/core/services/dmp/dmp.service';
|
|
|
|
import { GrantService } from '@app/core/services/grant/grant.service';
|
|
|
|
import { SearchBarService } from '@app/core/services/search-bar/search-bar.service';
|
|
|
|
import { UserService } from '@app/core/services/user/user.service';
|
|
|
|
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
|
|
|
|
import { BaseComponent } from '@common/base/base.component';
|
|
|
|
import { Observable, of as observableOf } from 'rxjs';
|
|
|
|
import { mergeMap, takeUntil } from 'rxjs/operators';
|
2019-05-16 18:11:41 +02:00
|
|
|
import { BreadcrumbItem } from '../misc/breadcrumb/definition/breadcrumb-item';
|
|
|
|
import { IBreadCrumbComponent } from '../misc/breadcrumb/definition/IBreadCrumbComponent';
|
2020-04-16 17:36:17 +02:00
|
|
|
import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria';
|
|
|
|
import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria';
|
2021-09-24 20:52:14 +02:00
|
|
|
import { MatDialog } from '@angular/material/dialog';
|
2020-07-09 12:45:19 +02:00
|
|
|
import { StartNewDmpDialogComponent } from '../dmp/start-new-dmp-dialogue/start-new-dmp-dialog.component';
|
2020-09-18 13:14:48 +02:00
|
|
|
import { StartNewDatasetDialogComponent } from '../dmp/start-new-dataset-dialogue/start-new-dataset-dialog.component';
|
|
|
|
import { DatasetWizardEditorModel } from '../dataset/dataset-wizard/dataset-wizard-editor.model';
|
|
|
|
import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service';
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2020-11-24 18:09:10 +01:00
|
|
|
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
|
|
|
|
import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service';
|
|
|
|
import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants';
|
2020-12-10 14:29:24 +01:00
|
|
|
import { MatomoService } from '@app/core/services/matomo/matomo-service';
|
2019-01-18 18:03:45 +01:00
|
|
|
|
2019-12-11 15:51:03 +01:00
|
|
|
|
2019-01-18 18:03:45 +01:00
|
|
|
@Component({
|
|
|
|
selector: 'app-dashboard',
|
|
|
|
templateUrl: './dashboard.component.html',
|
|
|
|
styleUrls: ['./dashboard.component.scss'],
|
|
|
|
})
|
2019-05-16 18:11:41 +02:00
|
|
|
export class DashboardComponent extends BaseComponent implements OnInit, IBreadCrumbComponent {
|
|
|
|
|
|
|
|
breadCrumbs: Observable<BreadcrumbItem[]>;
|
2019-01-18 18:03:45 +01:00
|
|
|
|
2020-06-26 11:08:51 +02:00
|
|
|
public isVisible = true
|
|
|
|
|
2019-01-18 18:03:45 +01:00
|
|
|
public userInfo: any;
|
|
|
|
datasetActivities: any[];
|
2019-08-01 09:54:40 +02:00
|
|
|
grantActivities: any[];
|
2019-01-18 18:03:45 +01:00
|
|
|
dmpActivities: any[];
|
2019-04-24 11:26:53 +02:00
|
|
|
organisationActivities: any[];
|
2019-01-18 18:03:45 +01:00
|
|
|
public dashboardStatisticsData: DashboardStatisticsModel;
|
|
|
|
public formControl = new FormControl();
|
2019-08-01 09:54:40 +02:00
|
|
|
grantAutoCompleteConfiguration: SingleAutoCompleteConfiguration;
|
2019-01-18 18:03:45 +01:00
|
|
|
public searchControl = new FormControl();
|
|
|
|
filteredOptions: Observable<SearchBarItem[]>;
|
|
|
|
recentActivityTypeEnum = RecentActivityType;
|
|
|
|
public search = false;
|
2019-05-15 17:21:04 +02:00
|
|
|
dmpListingItems: DmpListingModel[] = [];
|
|
|
|
datasetListingItems: DatasetListingModel[] = [];
|
2019-01-18 18:03:45 +01:00
|
|
|
|
2020-07-01 17:25:16 +02:00
|
|
|
totalDatasets: number;
|
|
|
|
totalDmps: number;
|
|
|
|
totalDraftDatasets: number;
|
2020-07-02 18:34:27 +02:00
|
|
|
totalRecents: number;
|
2020-07-01 17:25:16 +02:00
|
|
|
|
2020-11-24 18:09:10 +01:00
|
|
|
dmpText: string;
|
|
|
|
datasetText: string;
|
|
|
|
importFileText: string;
|
|
|
|
startWizardText: string;
|
2020-07-01 17:25:16 +02:00
|
|
|
|
2019-01-18 18:03:45 +01:00
|
|
|
constructor(
|
|
|
|
private router: Router,
|
2019-05-16 18:11:41 +02:00
|
|
|
private route: ActivatedRoute,
|
2019-08-01 09:54:40 +02:00
|
|
|
private grantService: GrantService,
|
2019-05-15 17:21:04 +02:00
|
|
|
private dmpService: DmpService,
|
|
|
|
private datasetService: DatasetService,
|
2020-09-18 13:14:48 +02:00
|
|
|
private datasetWizardService: DatasetWizardService,
|
2019-01-18 18:03:45 +01:00
|
|
|
private dashboardService: DashboardService,
|
|
|
|
private searchBarService: SearchBarService,
|
|
|
|
private authentication: AuthService,
|
2020-07-09 12:45:19 +02:00
|
|
|
private userService: UserService,
|
2020-09-18 13:14:48 +02:00
|
|
|
private dialog: MatDialog,
|
|
|
|
private language: TranslateService,
|
2020-11-24 18:09:10 +01:00
|
|
|
private uiNotificationService: UiNotificationService,
|
2020-12-10 14:29:24 +01:00
|
|
|
private guidedTourService: GuidedTourService,
|
|
|
|
private matomoService: MatomoService
|
2019-01-18 18:03:45 +01:00
|
|
|
) {
|
|
|
|
super();
|
|
|
|
// this.dashboardStatisticsData.totalDataManagementPlanCount = 0;
|
|
|
|
// this.dashboardStatisticsData.totalDataSetCount = 0;
|
2019-08-01 09:54:40 +02:00
|
|
|
// this.dashboardStatisticsData.totalGrantCount = 0;
|
2019-01-18 18:03:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ngOnInit() {
|
2020-12-10 14:29:24 +01:00
|
|
|
this.matomoService.trackPageView('Home Dashboard');
|
2019-05-10 10:33:48 +02:00
|
|
|
// if (this.isAuthenticated()) {
|
|
|
|
// this.userService.getRecentActivity()
|
|
|
|
// .pipe(takeUntil(this._destroyed))
|
|
|
|
// .subscribe(response => {
|
|
|
|
// this.datasetActivities = response['recentDatasetActivities'];
|
|
|
|
// this.dmpActivities = response['recentDmpActivities'];
|
2019-08-01 09:54:40 +02:00
|
|
|
// this.grantActivities = response['recentGrantActivities'];
|
2019-05-10 10:33:48 +02:00
|
|
|
// this.organisationActivities = response['totalOrganisationCount'];
|
|
|
|
// });
|
|
|
|
// }
|
2019-01-18 18:03:45 +01:00
|
|
|
|
2019-08-01 09:54:40 +02:00
|
|
|
// this.grantAutoCompleteConfiguration = {
|
|
|
|
// filterFn: this.searchGrant.bind(this),
|
|
|
|
// items: this.searchGrant(''),
|
2019-01-18 18:03:45 +01:00
|
|
|
// displayFn: (item) => item['label'],
|
2019-01-21 12:14:20 +01:00
|
|
|
// titleFn: (item) => item['label']
|
2019-01-18 18:03:45 +01:00
|
|
|
// };
|
|
|
|
|
2019-05-16 18:11:41 +02:00
|
|
|
const breadCrumbs = [];
|
2019-09-23 10:17:03 +02:00
|
|
|
this.breadCrumbs = observableOf(breadCrumbs);
|
2019-05-16 18:11:41 +02:00
|
|
|
|
2019-01-18 18:03:45 +01:00
|
|
|
if (!this.isAuthenticated()) {
|
|
|
|
this.dashboardService.getStatistics()
|
|
|
|
.pipe(takeUntil(this._destroyed))
|
|
|
|
.subscribe(results => {
|
|
|
|
//let data = results['payload'];
|
|
|
|
this.dashboardStatisticsData = results;
|
|
|
|
});
|
2020-08-03 10:40:38 +02:00
|
|
|
// this.getPublicDmps();
|
|
|
|
// this.getPublicDatasets();
|
2019-01-18 18:03:45 +01:00
|
|
|
} else {
|
|
|
|
this.dashboardService.getUserStatistics()
|
|
|
|
.pipe(takeUntil(this._destroyed))
|
|
|
|
.subscribe(results => {
|
|
|
|
this.dashboardStatisticsData = results;
|
2020-11-24 18:09:10 +01:00
|
|
|
|
|
|
|
if (this.dashboardStatisticsData && this.dashboardStatisticsData.totalDataManagementPlanCount === 0) {
|
|
|
|
this.openDashboardTour();
|
|
|
|
}
|
2019-01-18 18:03:45 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-09-23 10:17:03 +02:00
|
|
|
this.filteredOptions = this.searchControl.valueChanges.pipe(mergeMap(x => {
|
2019-01-18 18:03:45 +01:00
|
|
|
return this.searchBarService.search(x);
|
2019-09-23 10:17:03 +02:00
|
|
|
}));
|
2019-01-18 18:03:45 +01:00
|
|
|
}
|
|
|
|
|
2020-11-24 18:09:10 +01:00
|
|
|
// ngAfterContentChecked(): void {
|
|
|
|
// if (this.dashboardStatisticsData && this.dashboardStatisticsData.totalDataManagementPlanCount === 0) {
|
|
|
|
// this.openTour();
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
2019-01-18 18:03:45 +01:00
|
|
|
public isAuthenticated(): boolean {
|
|
|
|
return !(!this.authentication.current());
|
|
|
|
}
|
|
|
|
|
2019-08-01 09:54:40 +02:00
|
|
|
searchGrant(query: string) {
|
|
|
|
const grantRequestItem: RequestItem<GrantCriteria> = new RequestItem();
|
|
|
|
grantRequestItem.criteria = new GrantCriteria();
|
|
|
|
grantRequestItem.criteria.like = query;
|
|
|
|
return this.grantService.getWithExternal(grantRequestItem);
|
2019-01-18 18:03:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
redirect(id: string, type: RecentActivityType) {
|
|
|
|
switch (type) {
|
2019-08-01 09:54:40 +02:00
|
|
|
case RecentActivityType.Grant: {
|
|
|
|
this.router.navigate(['grants/edit/' + id]);
|
2019-01-18 18:03:45 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
case RecentActivityType.Dataset: {
|
|
|
|
this.router.navigate(['datasets/edit/' + id]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case RecentActivityType.Dmp: {
|
|
|
|
this.router.navigate(['plans/edit/' + id]);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default: throw new Error('Unsupported Activity Type ');
|
|
|
|
}
|
|
|
|
}
|
2019-05-15 17:21:04 +02:00
|
|
|
|
|
|
|
getPublicDmps() {
|
2020-04-16 17:36:17 +02:00
|
|
|
const dmpCriteria = new DmpCriteria();
|
|
|
|
dmpCriteria.allVersions = false;
|
|
|
|
dmpCriteria.isPublic = true;
|
|
|
|
dmpCriteria.onlyPublic = true;
|
2019-05-15 17:21:04 +02:00
|
|
|
const fields: Array<string> = new Array<string>();
|
2019-09-23 17:11:29 +02:00
|
|
|
fields.push('-finalizedAt');
|
2020-04-16 17:36:17 +02:00
|
|
|
const dataTableRequest: DataTableRequest<DmpCriteria> = new DataTableRequest(0, 2, { fields: fields });
|
2019-05-15 17:21:04 +02:00
|
|
|
dataTableRequest.criteria = dmpCriteria;
|
2020-04-16 17:36:17 +02:00
|
|
|
return this.dmpService.getPaged(dataTableRequest, "listing").pipe(takeUntil(this._destroyed)).subscribe(result => { this.dmpListingItems = result.data; });
|
2019-05-15 17:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getPublicDatasets() {
|
2020-04-16 17:36:17 +02:00
|
|
|
const dmpCriteria = new DatasetCriteria();
|
|
|
|
dmpCriteria.allVersions = false;
|
|
|
|
dmpCriteria.isPublic = true;
|
2019-05-15 17:21:04 +02:00
|
|
|
const fields: Array<string> = new Array<string>();
|
2019-12-12 10:26:19 +01:00
|
|
|
fields.push('-modified');
|
2020-04-16 17:36:17 +02:00
|
|
|
const dataTableRequest: DataTableRequest<DatasetCriteria> = new DataTableRequest(0, 4, { fields: fields });
|
2019-05-15 17:21:04 +02:00
|
|
|
dataTableRequest.criteria = dmpCriteria;
|
2020-04-16 17:36:17 +02:00
|
|
|
return this.datasetService.getPaged(dataTableRequest).pipe(takeUntil(this._destroyed)).subscribe(result => { this.datasetListingItems = result.data; });
|
2019-05-15 17:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dmpClicked(dmp: DmpListingModel) {
|
2019-09-20 12:54:37 +02:00
|
|
|
if (!this.isAuthenticated()) {
|
2020-04-16 17:36:17 +02:00
|
|
|
this.router.navigate(['../explore-plans/publicOverview', dmp.id], { relativeTo: this.route });
|
2019-09-20 12:54:37 +02:00
|
|
|
} else {
|
|
|
|
this.router.navigate(['/plans/publicEdit/' + dmp.id]);
|
|
|
|
}
|
2019-05-15 17:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
datasetClicked(dataset: DatasetListingModel) {
|
|
|
|
this.router.navigate(['/datasets/publicEdit/' + dataset.id]);
|
|
|
|
}
|
|
|
|
|
2020-06-26 11:08:51 +02:00
|
|
|
public closeCard(): void {
|
|
|
|
this.isVisible = false;
|
|
|
|
}
|
|
|
|
|
2020-07-01 17:25:16 +02:00
|
|
|
onCountDmps(event): void {
|
|
|
|
this.totalDmps = event;
|
|
|
|
}
|
|
|
|
|
|
|
|
onCountDatasets(event): void {
|
|
|
|
this.totalDatasets = event;
|
|
|
|
}
|
|
|
|
|
|
|
|
onCountDraftDatasets(event): void {
|
|
|
|
this.totalDraftDatasets = event;
|
|
|
|
}
|
|
|
|
|
2020-07-02 18:34:27 +02:00
|
|
|
onCountAllRecent(event): void {
|
|
|
|
this.totalRecents = event;
|
|
|
|
}
|
|
|
|
|
2020-07-09 12:45:19 +02:00
|
|
|
openNewDmpDialog() {
|
|
|
|
if (this.dialog.openDialogs.length > 0) {
|
|
|
|
this.dialog.closeAll();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const dialogRef = this.dialog.open(StartNewDmpDialogComponent, {
|
2020-07-28 15:24:14 +02:00
|
|
|
disableClose: false,
|
2020-07-09 12:45:19 +02:00
|
|
|
data: {
|
|
|
|
isDialog: true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-03 10:40:38 +02:00
|
|
|
public hasDmps(): boolean {
|
|
|
|
if (this.dashboardStatisticsData) {
|
|
|
|
return this.dashboardStatisticsData.totalDataManagementPlanCount !== 0
|
|
|
|
|| this.dashboardStatisticsData.totalDataSetCount !== 0
|
|
|
|
|| this.dashboardStatisticsData.totalGrantCount !== 0
|
|
|
|
|| this.dashboardStatisticsData.totalOrganisationCount !== 0;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:14:48 +02:00
|
|
|
addNewDataset() {
|
|
|
|
const dialogRef = this.dialog.open(StartNewDatasetDialogComponent, {
|
|
|
|
disableClose: false,
|
|
|
|
restoreFocus: false,
|
|
|
|
data: {
|
|
|
|
startNewDmp: false,
|
|
|
|
formGroup: new DatasetWizardEditorModel().buildForm()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
|
|
|
if (result) {
|
|
|
|
if (result.startNewDmp) {
|
|
|
|
this.openNewDmpDialog();
|
|
|
|
} else {
|
|
|
|
this.router.navigate(['/datasets', 'new', result.formGroup.get('dmp').value.id]);
|
|
|
|
// Save dataset direct but missing title and template
|
|
|
|
// this.datasetWizardService.createDataset(result.formGroup.getRawValue())
|
|
|
|
// .pipe(takeUntil(this._destroyed))
|
|
|
|
// .subscribe(
|
|
|
|
// data => {
|
|
|
|
// this.onCallbackSuccess(data);
|
|
|
|
// },
|
|
|
|
// error => this.onCallbackError(error)
|
|
|
|
// );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-11-24 18:09:10 +01:00
|
|
|
public dashboardTour: GuidedTour = {
|
|
|
|
tourId: 'dashboard-tour',
|
|
|
|
useOrb: true,
|
|
|
|
steps: [
|
|
|
|
{
|
|
|
|
selector: '.new-dmp-dialog',
|
|
|
|
content: 'Step 1',
|
|
|
|
orientation: Orientation.BottomRight,
|
|
|
|
isStepUnique: false,
|
|
|
|
highlightPadding: 10,
|
|
|
|
closeAction: () => this.openNewDmpDialog()
|
|
|
|
},
|
|
|
|
{
|
|
|
|
selector: '.import-file',
|
|
|
|
content: 'Step 2',
|
|
|
|
orientation: Orientation.Bottom,
|
|
|
|
isStepUnique: false,
|
|
|
|
highlightPadding: 10
|
|
|
|
},
|
|
|
|
{
|
|
|
|
selector: '.start-wizard',
|
|
|
|
content: 'Step 3',
|
|
|
|
orientation: Orientation.Bottom,
|
|
|
|
isStepUnique: false,
|
|
|
|
highlightPadding: 10,
|
|
|
|
closeAction: () => this.dialog.closeAll()
|
|
|
|
},
|
|
|
|
{
|
|
|
|
selector: '.new-dataset-tour',
|
|
|
|
content: 'Step 4',
|
|
|
|
orientation: Orientation.BottomLeft,
|
|
|
|
isStepUnique: false,
|
|
|
|
highlightPadding: 10
|
|
|
|
}
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
public setDashboardTourDmpText(): void {
|
|
|
|
this.dmpText = this.language.instant('DASHBOARD.TOUR-GUIDE.DMP') + '\n\n' +
|
|
|
|
this.language.instant('DASHBOARD.TOUR-GUIDE.START-NEW');
|
|
|
|
this.dashboardTour.steps[0].title = this.dmpText;
|
|
|
|
}
|
|
|
|
|
|
|
|
public setDashboardImportFileText(): void {
|
|
|
|
this.importFileText = this.language.instant('DASHBOARD.TOUR-GUIDE.IMPORT-DMP');
|
|
|
|
this.dashboardTour.steps[1].title = this.importFileText;
|
|
|
|
}
|
|
|
|
|
|
|
|
public setDashboardStartWizardText(): void {
|
|
|
|
this.startWizardText = this.language.instant('DASHBOARD.TOUR-GUIDE.START-WIZARD');
|
|
|
|
this.dashboardTour.steps[2].title = this.startWizardText;
|
|
|
|
}
|
|
|
|
|
|
|
|
public setDatasetText(): void {
|
|
|
|
this.datasetText = this.language.instant('DASHBOARD.TOUR-GUIDE.DATASET') + '\n\n' +
|
|
|
|
this.language.instant('DASHBOARD.TOUR-GUIDE.NEW-DATASET');
|
|
|
|
this.dashboardTour.steps[3].title = this.datasetText;
|
|
|
|
}
|
|
|
|
|
|
|
|
openDashboardTour() {
|
|
|
|
this.setDashboardTourDmpText();
|
|
|
|
this.setDashboardImportFileText();
|
|
|
|
this.setDashboardStartWizardText();
|
|
|
|
this.setDatasetText();
|
|
|
|
this.guidedTourService.startTour(this.dashboardTour);
|
|
|
|
}
|
|
|
|
|
2020-09-18 13:14:48 +02:00
|
|
|
// onCallbackSuccess(datasetId: String): void {
|
|
|
|
// this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION'), SnackBarNotificationLevel.Success);
|
|
|
|
// this.router.navigate(['/reload']).then(() => { this.router.navigate(['/datasets', 'edit', datasetId]); });
|
|
|
|
// }
|
|
|
|
|
|
|
|
// onCallbackError(error: any) {
|
|
|
|
// this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Error);
|
|
|
|
// }
|
|
|
|
|
2019-10-25 13:54:12 +02:00
|
|
|
// viewAllPublicDmpsClicked() {
|
|
|
|
// this.router.navigate(['/explore-plans']);
|
|
|
|
// }
|
2019-05-15 17:21:04 +02:00
|
|
|
|
2019-10-25 13:54:12 +02:00
|
|
|
// viewAllPublicDatasetsClicked() {
|
|
|
|
// this.router.navigate(['explore']);
|
|
|
|
// }
|
2019-01-18 18:03:45 +01:00
|
|
|
}
|