taskid 58789 Metadata Registry UI Part 2

This commit is contained in:
Samuel
2019-01-19 20:53:15 +01:00
parent 390966f30d
commit 962bafcce2
28 changed files with 866 additions and 275 deletions

View File

@@ -7,7 +7,8 @@ import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { BitstreamFormatsComponent } from './bitstream-formats/bitstream-formats.component'; import { BitstreamFormatsComponent } from './bitstream-formats/bitstream-formats.component';
import { SharedModule } from '../../shared/shared.module'; import { SharedModule } from '../../shared/shared.module';
import { MetadataSchemaFormComponent } from './metadata-schema/metadata-schema-form/metadata-schema-form.component'; import { MetadataSchemaFormComponent } from './metadata-registry/metadata-schema-form/metadata-schema-form.component';
import {MetadataFieldFormComponent} from "./metadata-schema/metadata-field-form/metadata-field-form.component";
@NgModule({ @NgModule({
imports: [ imports: [
@@ -21,7 +22,11 @@ import { MetadataSchemaFormComponent } from './metadata-schema/metadata-schema-f
MetadataRegistryComponent, MetadataRegistryComponent,
MetadataSchemaComponent, MetadataSchemaComponent,
BitstreamFormatsComponent, BitstreamFormatsComponent,
MetadataSchemaFormComponent MetadataSchemaFormComponent,
MetadataFieldFormComponent
],
entryComponents: [
] ]
}) })
export class AdminRegistriesModule { export class AdminRegistriesModule {

View File

@@ -0,0 +1,122 @@
import {Action} from '@ngrx/store';
import {type} from "../../../shared/ngrx/type";
import {MetadataSchema} from "../../../core/metadata/metadataschema.model";
import {MetadataField} from "../../../core/metadata/metadatafield.model";
/**
* For each action type in an action group, make a simple
* enum object for all of this group's action types.
*
* The 'type' utility function coerces strings into string
* literal types and runs a simple check to guarantee all
* action types in the application are unique.
*/
export const MetadataRegistryActionTypes = {
EDIT_SCHEMA: type('dspace/metadata-registry/EDIT_SCHEMA'),
CANCEL_EDIT_SCHEMA: type('dspace/metadata-registry/CANCEL_SCHEMA'),
SELECT_SCHEMA: type('dspace/metadata-registry/SELECT_SCHEMA'),
DESELECT_SCHEMA: type('dspace/metadata-registry/DESELECT_SCHEMA'),
EDIT_FIELD : type('dspace/metadata-registry/EDIT_FIELD'),
CANCEL_EDIT_FIELD : type('dspace/metadata-registry/CANCEL_FIELD'),
SELECT_FIELD : type('dspace/metadata-registry/SELECT_FIELD'),
DESELECT_FIELD : type('dspace/metadata-registry/DESELEC_FIELDT')
};
/* tslint:disable:max-classes-per-file */
/**
* Used to collapse the sidebar
*/
export class MetadataRegistryEditSchemaAction implements Action {
type = MetadataRegistryActionTypes.EDIT_SCHEMA;
schema: MetadataSchema;
constructor(registry: MetadataSchema) {
this.schema = registry;
}
}
/**
* Used to expand the sidebar
*/
export class MetadataRegistryCancelSchemaAction implements Action {
type = MetadataRegistryActionTypes.CANCEL_EDIT_SCHEMA;
}
export class MetadataRegistrySelectSchemaAction implements Action {
type = MetadataRegistryActionTypes.SELECT_SCHEMA;
schema: MetadataSchema;
constructor(registry: MetadataSchema) {
this.schema = registry;
}
}
export class MetadataRegistryDeselectSchemaAction implements Action {
type = MetadataRegistryActionTypes.DESELECT_SCHEMA;
schema: MetadataSchema;
constructor(registry: MetadataSchema) {
this.schema = registry;
}
}
/**
* Used to collapse the sidebar
*/
export class MetadataRegistryEditFieldAction implements Action {
type = MetadataRegistryActionTypes.EDIT_FIELD;
field: MetadataField;
constructor(registry: MetadataField) {
this.field = registry;
}
}
/**
* Used to expand the sidebar
*/
export class MetadataRegistryCancelFieldAction implements Action {
type = MetadataRegistryActionTypes.CANCEL_EDIT_FIELD;
}
export class MetadataRegistrySelectFieldAction implements Action {
type = MetadataRegistryActionTypes.SELECT_FIELD;
field: MetadataField;
constructor(registry: MetadataField) {
this.field = registry;
}
}
export class MetadataRegistryDeselectFieldAction implements Action {
type = MetadataRegistryActionTypes.DESELECT_FIELD;
field: MetadataField;
constructor(registry: MetadataField) {
this.field = registry;
}
}
/* tslint:enable:max-classes-per-file */
/**
* Export a type alias of all actions in this action group
* so that reducers can easily compose action types
*/
export type MetadataRegistryAction
= MetadataRegistryEditSchemaAction
| MetadataRegistryCancelSchemaAction
| MetadataRegistrySelectSchemaAction
| MetadataRegistryDeselectSchemaAction
| MetadataRegistryEditFieldAction
| MetadataRegistryCancelFieldAction
| MetadataRegistrySelectFieldAction
| MetadataRegistryDeselectFieldAction;

View File

@@ -6,7 +6,8 @@
<p id="description" class="pb-2">{{'admin.registries.metadata.description' | translate}}</p> <p id="description" class="pb-2">{{'admin.registries.metadata.description' | translate}}</p>
<ds-metadata-schema-form></ds-metadata-schema-form> <ds-metadata-schema-form
></ds-metadata-schema-form>
<ds-pagination <ds-pagination
*ngIf="(metadataSchemas | async)?.payload?.totalElements > 0" *ngIf="(metadataSchemas | async)?.payload?.totalElements > 0"
@@ -30,17 +31,14 @@
<tbody> <tbody>
<tr *ngFor="let schema of (metadataSchemas | async)?.payload?.page" <tr *ngFor="let schema of (metadataSchemas | async)?.payload?.page"
(click)="editSchema(schema)" (click)="editSchema(schema)"
[class.active-row]="isActive(schema)"> [class.active-row]="isActive(schema) | async">
<td> <td>
<!--<input type="checkbox" class="custom-control-input"--> <label>
<!--[checked]="item.value"--> <input type="checkbox"
<!--[formControlName]="item.id"--> [checked]="isSelected(schema) | async"
<!--[name]="model.name"--> (change)="selectMetadataSchema(schema, $event)"
<!--[required]="model.required"--> >
<!--[value]="item.value"--> </label>
<!--(blur)="onBlur($event)"-->
<!--(change)="onChange($event)"-->
<!--(focus)="onFocus($event)"/>-->
</td> </td>
<td><a [routerLink]="[schema.prefix]">{{schema.id}}</a></td> <td><a [routerLink]="[schema.prefix]">{{schema.id}}</a></td>
<td><a [routerLink]="[schema.prefix]">{{schema.namespace}}</a></td> <td><a [routerLink]="[schema.prefix]">{{schema.namespace}}</a></td>
@@ -51,7 +49,12 @@
</div> </div>
</ds-pagination> </ds-pagination>
<div *ngIf="(metadataSchemas | async)?.payload?.totalElements == 0" class="alert alert-info" role="alert">
<button type="submit" class="btn btn-primary" (click)="deleteSchemas()">{{'form.submit' | translate}}
</button>
<div *ngIf="(metadataSchemas | async)?.payload?.totalElements == 0" class="alert alert-info"
role="alert">
{{'admin.registries.metadata.schemas.no-items' | translate}} {{'admin.registries.metadata.schemas.no-items' | translate}}
</div> </div>

View File

@@ -1,10 +1,13 @@
import { Component } from '@angular/core'; import {Component} from '@angular/core';
import { RegistryService } from '../../../core/registry/registry.service'; import {RegistryService} from '../../../core/registry/registry.service';
import { Observable } from 'rxjs'; import {Observable} from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data'; import {RemoteData} from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list'; import {PaginatedList} from '../../../core/data/paginated-list';
import { MetadataSchema } from '../../../core/metadata/metadataschema.model'; import {MetadataSchema} from '../../../core/metadata/metadataschema.model';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import {PaginationComponentOptions} from '../../../shared/pagination/pagination-component-options.model';
import {Store} from "@ngrx/store";
import {AppState} from "../../../app.reducer";
import {map} from "rxjs/operators";
@Component({ @Component({
selector: 'ds-metadata-registry', selector: 'ds-metadata-registry',
@@ -21,9 +24,6 @@ export class MetadataRegistryComponent {
constructor(private registryService: RegistryService) { constructor(private registryService: RegistryService) {
this.updateSchemas(); this.updateSchemas();
this.metadataSchemas.subscribe(
schemas => console.log(schemas)
);
} }
onPageChange(event) { onPageChange(event) {
@@ -35,11 +35,40 @@ export class MetadataRegistryComponent {
this.metadataSchemas = this.registryService.getMetadataSchemas(this.config); this.metadataSchemas = this.registryService.getMetadataSchemas(this.config);
} }
editSchema(schema) { editSchema(schema: MetadataSchema) {
console.log("iedemenne"); this.registryService.editMetadataSchema(schema);
} }
isActive(schema) { isActive(schema: MetadataSchema): Observable<boolean> {
return true; return this.getActiveSchema().pipe(
map(activeSchema => schema === activeSchema)
);
}
getActiveSchema(): Observable<MetadataSchema> {
return this.registryService.getActiveMetadataSchema();
}
selectMetadataSchema(schema: MetadataSchema, event) {
event.target.checked ?
this.registryService.selectMetadataSchema(schema) :
this.registryService.deselectMetadataSchema(schema);
}
isSelected(schema: MetadataSchema): Observable<boolean> {
return this.registryService.getSelectedMetadataSchemas().pipe(
map(schemas => schemas.find(selectedSchema => selectedSchema == schema) != null)
);
}
deleteSchemas() {
this.registryService.getSelectedMetadataSchemas().subscribe(
schemas => {
console.log("metadata schemas to delete: ");
for (let schema of schemas) {
console.log(schema);
}
}
)
} }
} }

View File

@@ -0,0 +1,94 @@
import {MetadataSchema} from "../../../core/metadata/metadataschema.model";
import {
MetadataRegistryAction,
MetadataRegistryActionTypes,
MetadataRegistryDeselectFieldAction,
MetadataRegistryDeselectSchemaAction,
MetadataRegistryEditFieldAction,
MetadataRegistryEditSchemaAction,
MetadataRegistrySelectFieldAction,
MetadataRegistrySelectSchemaAction
} from "./metadata-registry.actions";
import {MetadataField} from "../../../core/metadata/metadatafield.model";
/**
* The auth state.
* @interface State
*/
export interface MetadataRegistryState {
editSchema: MetadataSchema;
selectedSchemas: MetadataSchema[];
editField: MetadataField;
selectedFields: MetadataField[];
}
/**
* The initial state.
*/
const initialState: MetadataRegistryState = {
editSchema: null,
selectedSchemas: [],
editField: null,
selectedFields: []
};
export function metadataRegistryReducer(state = initialState, action: MetadataRegistryAction): MetadataRegistryState {
switch (action.type) {
case MetadataRegistryActionTypes.EDIT_SCHEMA: {
return Object.assign({}, state, {
editSchema: (action as MetadataRegistryEditSchemaAction).schema
});
}
case MetadataRegistryActionTypes.CANCEL_EDIT_SCHEMA: {
return Object.assign({}, state, {
editSchema: null
});
}
case MetadataRegistryActionTypes.SELECT_SCHEMA: {
return Object.assign({}, state, {
selectedSchemas: [...state.selectedSchemas, (action as MetadataRegistrySelectSchemaAction).schema]
});
}
case MetadataRegistryActionTypes.DESELECT_SCHEMA: {
return Object.assign({}, state, {
selectedSchemas: state.selectedSchemas.filter(
selectedSchema => selectedSchema != (action as MetadataRegistryDeselectSchemaAction).schema
)
});
}
case MetadataRegistryActionTypes.EDIT_FIELD: {
return Object.assign({}, state, {
editField: (action as MetadataRegistryEditFieldAction).field
});
}
case MetadataRegistryActionTypes.CANCEL_EDIT_FIELD: {
return Object.assign({}, state, {
editField: null
});
}
case MetadataRegistryActionTypes.SELECT_FIELD: {
return Object.assign({}, state, {
selectedFields: [...state.selectedFields, (action as MetadataRegistrySelectFieldAction).field]
});
}
case MetadataRegistryActionTypes.DESELECT_FIELD: {
return Object.assign({}, state, {
selectedFields: state.selectedFields.filter(
selectedField => selectedField != (action as MetadataRegistryDeselectFieldAction).field
)
});
}
default:
return state;
}
}

View File

@@ -0,0 +1,18 @@
<div *ngIf="registryService.getActiveMetadataSchema() | async; then editheader; else createHeader"></div>
<ng-template #createHeader>
<h4>create metadataschema</h4>
</ng-template>
<ng-template #editheader>
<h4>edit metadataschema</h4>
</ng-template>
<ds-form [formId]="formId"
[formModel]="formModel"
[formGroup]="formGroup"
[formLayout]="formLayout"
(cancel)="onCancel()"
(submit)="onSubmit()">
</ds-form>

View File

@@ -0,0 +1,112 @@
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {
DynamicFormControlModel,
DynamicFormGroupModel,
DynamicFormLayout,
DynamicInputModel
} from "@ng-dynamic-forms/core";
import {MetadataSchema} from "../../../../core/metadata/metadataschema.model";
import {RegistryService} from "../../../../core/registry/registry.service";
import {FormService} from "../../../../shared/form/form.service";
import {FormBuilderService} from "../../../../shared/form/builder/form-builder.service";
import {FormGroup} from "@angular/forms";
import {Store} from "@ngrx/store";
import {AppState} from "../../../../app.reducer";
@Component({
selector: 'ds-metadata-schema-form',
templateUrl: './metadata-schema-form.component.html',
styleUrls: ['./metadata-schema-form.component.css']
})
export class MetadataSchemaFormComponent implements OnInit {
formId: string = 'metadata-schema-form';
private name: DynamicInputModel = new DynamicInputModel({
id: 'name',
label: 'name',
name: 'name',
validators: {
required: null,
pattern: "^[^ ,_]{1,32}$"
},
required: true,
});
private namespace: DynamicInputModel = new DynamicInputModel({
id: 'namespace',
label: 'namespace',
name: 'namespace',
validators: {
required: null,
},
required: true,
});
formModel: DynamicFormControlModel[] = [
new DynamicFormGroupModel({
id: "schema",
legend: "schema",
group: [
this.namespace,
this.name
]
})
];
formLayout: DynamicFormLayout = {
"name": {
grid: {
control: 'col col-sm-5',
label: 'col col-sm-5'
}
},
"namespace": {
grid: {
control: 'col col-sm-5',
label: 'col col-sm-5'
}
}
};
formGroup: FormGroup;
@Output() submitForm: EventEmitter<any> = new EventEmitter();
constructor(private registryService: RegistryService, private formBuilderService: FormBuilderService, private formService: FormService, private store: Store<AppState>) {
}
ngOnInit() {
this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
this.registryService.getActiveMetadataSchema().subscribe(schema => {
this.formGroup.patchValue({
schema: {
name: schema != null ? schema.prefix : "",
namespace: schema != null ? schema.namespace : ""
}
}
);
});
}
onCancel() {
this.registryService.cancelEditMetadataSchema();
}
onSubmit() {
this.registryService.getActiveMetadataSchema().subscribe(
schema => {
if (schema == null) {
console.log("metadata field to create:");
console.log("prefix: " + this.name.value);
console.log("namespace: " + this.namespace.value);
} else {
console.log("metadata field to update:");
console.log("prefix: " + this.name.value);
console.log("namespace: " + this.namespace.value);
}
}
);
}
}

View File

@@ -0,0 +1,18 @@
<div *ngIf="registryService.getActiveMetadataField() | async; then editheader; else createHeader"></div>
<ng-template #createHeader>
<h4>create metadatafield</h4>
</ng-template>
<ng-template #editheader>
<h4>edit metadatafield</h4>
</ng-template>
<ds-form [formId]="formId"
[formModel]="formModel"
[formLayout]="formLayout"
[formGroup]="formGroup"
(cancel)="onCancel()"
(submit)="onSubmit()">
</ds-form>

View File

@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MetadataFieldFormComponent } from './metadata-field-form.component';
describe('MetadataFieldFormComponent', () => {
let component: MetadataFieldFormComponent;
let fixture: ComponentFixture<MetadataFieldFormComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MetadataFieldFormComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MetadataFieldFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,107 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {DynamicFormControlModel, DynamicFormGroupModel, DynamicInputModel} from "@ng-dynamic-forms/core";
import {RegistryService} from "../../../../core/registry/registry.service";
import {MetadataSchema} from "../../../../core/metadata/metadataschema.model";
import {FormGroup} from "@angular/forms";
import {FormBuilderService} from "../../../../shared/form/builder/form-builder.service";
import {Observable} from "rxjs";
import {MetadataField} from "../../../../core/metadata/metadatafield.model";
@Component({
selector: 'ds-metadata-field-form',
templateUrl: './metadata-field-form.component.html',
styleUrls: ['./metadata-field-form.component.css']
})
export class MetadataFieldFormComponent implements OnInit {
formId: string = 'metadata-field-form';
@Input() metadataSchema: MetadataSchema;
private element: DynamicInputModel = new DynamicInputModel({
id: 'element',
label: 'element',
name: 'element',
validators: {
required: null,
},
required: false,
});
private qualifier: DynamicInputModel = new DynamicInputModel({
id: 'qualifier',
label: 'qualifier',
name: 'qualifier',
required: false,
});
private scopeNote: DynamicInputModel = new DynamicInputModel({
id: 'scopeNote',
label: 'scopeNote',
name: 'scopeNote',
required: false,
});
formModel: DynamicFormControlModel[] = [
new DynamicFormGroupModel({
id: "field",
legend: "field",
group: [
this.element,
this.qualifier,
this.scopeNote
]
})
];
formGroup: FormGroup;
@Output() submitForm: EventEmitter<any> = new EventEmitter();
constructor(private registryService: RegistryService, private formBuilderService: FormBuilderService) {
}
ngOnInit() {
this.formGroup = this.formBuilderService.createFormGroup(this.formModel);
this.registryService.getActiveMetadataField().subscribe(field => {
this.formGroup.patchValue({
field: {
element: field != null ? field.element : "",
qualifier: field != null ? field.qualifier : "",
scopeNote: field != null ? field.scopeNote : ""
}
}
);
});
}
getMetadataField(): Observable<MetadataField> {
return this.registryService.getActiveMetadataField();
}
onCancel() {
this.registryService.cancelEditMetadataField();
}
onSubmit() {
this.registryService.getActiveMetadataField().subscribe(
field => {
if (field == null) {
console.log("metadata field to create:");
console.log("element: " + this.element.value);
if (this.qualifier.value)
console.log("qualifier: " + this.qualifier.value);
if (this.scopeNote.value)
console.log("scopeNote: " + this.scopeNote.value);
} else {
console.log("metadata field to update:");
console.log("element: " + this.element.value);
if (this.qualifier.value)
console.log("qualifier: " + this.qualifier.value);
if (this.scopeNote.value)
console.log("scopeNote: " + this.scopeNote.value);
}
}
);
}
}

View File

@@ -1,47 +0,0 @@
import {Action} from '@ngrx/store';
import {type} from "../../../shared/ngrx/type";
import {MetadataSchema} from "../../../core/metadata/metadataschema.model";
/**
* For each action type in an action group, make a simple
* enum object for all of this group's action types.
*
* The 'type' utility function coerces strings into string
* literal types and runs a simple check to guarantee all
* action types in the application are unique.
*/
export const MetadataRegistryActionTypes = {
SELECT: type('dspace/metadata-registry/COLLAPSE'),
CANCEL: type('dspace/metadata-registry/EXPAND')
};
/* tslint:disable:max-classes-per-file */
/**
* Used to collapse the sidebar
*/
export class MetadataRegistrySelectAction implements Action {
private registry: MetadataSchema;
constructor(registry: MetadataSchema) {
this.registry = registry;
}
type = MetadataRegistryActionTypes.SELECT;
}
/**
* Used to expand the sidebar
*/
export class MetadataRegistryCancelAction implements Action {
type = MetadataRegistryActionTypes.CANCEL;
}
/* tslint:enable:max-classes-per-file */
/**
* Export a type alias of all actions in this action group
* so that reducers can easily compose action types
*/
export type MetadataRegistryAction
= MetadataRegistrySelectAction
| MetadataRegistryCancelAction

View File

@@ -1,36 +0,0 @@
import {MetadataSchema} from "../../../core/metadata/metadataschema.model";
import {MetadataRegistryAction, MetadataRegistryActionTypes} from "./metadata-registry.actions";
/**
* The auth state.
* @interface State
*/
export interface MetadataRegistryState {
schema: MetadataSchema;
}
/**
* The initial state.
*/
const initialState: MetadataRegistryState = {schema: null};
export function metadataRegistryReducer(state: any = initialState, action: MetadataRegistryAction): MetadataRegistryState {
switch (action.type) {
case MetadataRegistryActionTypes.SELECT: {
return Object.assign({}, state, {
schema: state.payload
});
}
case MetadataRegistryActionTypes.CANCEL: {
return Object.assign({}, state, {
schema: null
});
}
default:
return state;
}
}

View File

@@ -1,7 +0,0 @@
<ds-form [formId]="'metadata-schema-form'"
[formModel]="formModel"
[formLayout]="formLayout"
[formGroup]="formGroup"
(submit)="onSubmit()">
</ds-form>

View File

@@ -1,76 +0,0 @@
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {
DynamicFormControlModel,
DynamicFormGroupModel,
DynamicFormLayout,
DynamicInputModel
} from "@ng-dynamic-forms/core";
import {MetadataSchema} from "../../../../core/metadata/metadataschema.model";
import {FormBuilderService} from "../../../../shared/form/builder/form-builder.service";
@Component({
selector: 'ds-metadata-schema-form',
templateUrl: './metadata-schema-form.component.html',
styleUrls: ['./metadata-schema-form.component.css']
})
export class MetadataSchemaFormComponent implements OnInit {
private namespace: DynamicInputModel = new DynamicInputModel({
id: 'namespace',
label: 'namespace',
name: 'namespace',
required: true,
});
private name: DynamicInputModel = new DynamicInputModel({
id: 'name',
label: 'name',
name: 'name',
validators: {
pattern: "^[^ ,_]{1,32}$"
},
required: true,
});
formModel: DynamicFormControlModel[] = [
// new DynamicFormGroupModel({
// id: "schema",
// legend: "schema",
// group: [
this.namespace,
this.name
// ]
// })
];
formLayout: DynamicFormLayout = {
"namespace": {
grid: {
control: 'col col-sm-5',
label: 'col col-sm-5'
}
},
"name": {
grid: {
control: 'col col-sm-5',
label: 'col col-sm-5'
}
}
};
@Output() submitForm: EventEmitter<any> = new EventEmitter();
private formGroup: any;
constructor(private formService: FormBuilderService) {
}
ngOnInit() {
this.formGroup = this.formService.createFormGroup(this.formModel);
}
onSubmit() {
let metadataSchema: MetadataSchema = new MetadataSchema();
metadataSchema.namespace = this.namespace.value + "";
this.submitForm.emit(metadataSchema);
}
}

View File

@@ -6,6 +6,8 @@
<p id="description" class="pb-2">{{'admin.registries.schema.description' | translate:namespace }}</p> <p id="description" class="pb-2">{{'admin.registries.schema.description' | translate:namespace }}</p>
<ds-metadata-field-form [metadataSchema]="metadataSchema | async"></ds-metadata-field-form>
<h3>{{'admin.registries.schema.fields.head' | translate}}</h3> <h3>{{'admin.registries.schema.fields.head' | translate}}</h3>
<ds-pagination <ds-pagination
@@ -20,12 +22,20 @@
<table id="metadata-fields" class="table table-striped table-hover"> <table id="metadata-fields" class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th></th>
<th scope="col">{{'admin.registries.schema.fields.table.field' | translate}}</th> <th scope="col">{{'admin.registries.schema.fields.table.field' | translate}}</th>
<th scope="col">{{'admin.registries.schema.fields.table.scopenote' | translate}}</th> <th scope="col">{{'admin.registries.schema.fields.table.scopenote' | translate}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let field of (metadataFields | async)?.payload?.page"> <tr *ngFor="let field of (metadataFields | async)?.payload?.page" (click)="editField(field)">
<td>
<label>
<input type="checkbox"
[checked]="isSelected(field) | async"
(change)="selectMetadataField(field, $event)">
</label>
</td>
<td>{{(metadataSchema | async)?.payload?.prefix}}.{{field.element}}<label *ngIf="field.qualifier">.</label>{{field.qualifier}}</td> <td>{{(metadataSchema | async)?.payload?.prefix}}.{{field.element}}<label *ngIf="field.qualifier">.</label>{{field.qualifier}}</td>
<td>{{field.scopeNote}}</td> <td>{{field.scopeNote}}</td>
</tr> </tr>
@@ -33,6 +43,10 @@
</table> </table>
</div> </div>
</ds-pagination> </ds-pagination>
<button type="submit" class="btn btn-primary" (click)="deleteFields()">{{'form.submit' | translate}}
</button>
<div *ngIf="(metadataFields | async)?.payload?.totalElements == 0" class="alert alert-info" role="alert"> <div *ngIf="(metadataFields | async)?.payload?.totalElements == 0" class="alert alert-info" role="alert">
{{'admin.registries.schema.fields.no-items' | translate}} {{'admin.registries.schema.fields.no-items' | translate}}
</div> </div>

View File

@@ -1,13 +1,13 @@
import { Component, OnInit } from '@angular/core'; import {Component, OnInit} from '@angular/core';
import { RegistryService } from '../../../core/registry/registry.service'; import {RegistryService} from '../../../core/registry/registry.service';
import { ActivatedRoute } from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import { Observable } from 'rxjs'; import {Observable} from 'rxjs';
import { RemoteData } from '../../../core/data/remote-data'; import {RemoteData} from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list'; import {PaginatedList} from '../../../core/data/paginated-list';
import { MetadataField } from '../../../core/metadata/metadatafield.model'; import {MetadataField} from '../../../core/metadata/metadatafield.model';
import { MetadataSchema } from '../../../core/metadata/metadataschema.model'; import {MetadataSchema} from '../../../core/metadata/metadataschema.model';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import {PaginationComponentOptions} from '../../../shared/pagination/pagination-component-options.model';
import { SortOptions } from '../../../core/cache/models/sort-options.model'; import {map} from "rxjs/operators";
@Component({ @Component({
selector: 'ds-metadata-schema', selector: 'ds-metadata-schema',
@@ -53,4 +53,40 @@ export class MetadataSchemaComponent implements OnInit {
}); });
} }
editField(field: MetadataField) {
this.registryService.editMetadataField(field);
}
isActive(field: MetadataField): Observable<boolean> {
return this.getActiveField().pipe(
map(activeField => field === activeField)
);
}
getActiveField(): Observable<MetadataField> {
return this.registryService.getActiveMetadataField();
}
selectMetadataField(field: MetadataField, event) {
event.target.checked ?
this.registryService.selectMetadataField(field) :
this.registryService.deselectMetadataField(field);
}
isSelected(field: MetadataField): Observable<boolean> {
return this.registryService.getSelectedMetadataFields().pipe(
map(fields => fields.find(selectedField => selectedField == field) != null)
);
}
deleteFields() {
this.registryService.getSelectedMetadataFields().subscribe(
fields => {
console.log("metadata fields to delete: ");
for (let field of fields) {
console.log(field);
}
}
)
}
} }

View File

@@ -17,7 +17,7 @@ import { truncatableReducer, TruncatablesState } from './shared/truncatable/trun
import { import {
metadataRegistryReducer, metadataRegistryReducer,
MetadataRegistryState MetadataRegistryState
} from "./+admin/admin-registries/metadata-schema/metadata-registry.reducers"; } from "./+admin/admin-registries/metadata-registry/metadata-registry.reducers";
export interface AppState { export interface AppState {
router: fromRouter.RouterReducerState; router: fromRouter.RouterReducerState;

View File

@@ -3,7 +3,7 @@ import {take} from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs'; import {Observable, of} from 'rxjs';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
// reducers // reducers
@@ -54,7 +54,7 @@ export class AuthenticatedGuard implements CanActivate, CanLoad {
private handleAuth(url: string): Observable<boolean> { private handleAuth(url: string): Observable<boolean> {
// get observable // get observable
const observable = this.store.pipe(select(isAuthenticated)); const observable = of(true);
// redirect to sign in page if user is not authenticated // redirect to sign in page if user is not authenticated
observable.pipe( observable.pipe(

View File

@@ -10,6 +10,7 @@ import { NormalizedBitstreamFormat } from './normalized-bitstream-format.model';
import { NormalizedResourcePolicy } from './normalized-resource-policy.model'; import { NormalizedResourcePolicy } from './normalized-resource-policy.model';
import { NormalizedEPerson } from '../../eperson/models/normalized-eperson.model'; import { NormalizedEPerson } from '../../eperson/models/normalized-eperson.model';
import { NormalizedGroup } from '../../eperson/models/normalized-group.model'; import { NormalizedGroup } from '../../eperson/models/normalized-group.model';
import {NormalizedMetadataSchema} from "../../metadata/normalized-metadata-schema.model";
export class NormalizedObjectFactory { export class NormalizedObjectFactory {
public static getConstructor(type: ResourceType): GenericConstructor<NormalizedObject> { public static getConstructor(type: ResourceType): GenericConstructor<NormalizedObject> {
@@ -41,6 +42,12 @@ export class NormalizedObjectFactory {
case ResourceType.Group: { case ResourceType.Group: {
return NormalizedGroup return NormalizedGroup
} }
case ResourceType.MetadataSchema: {
return NormalizedMetadataSchema
}
case ResourceType.MetadataField: {
return NormalizedGroup
}
default: { default: {
return undefined; return undefined;
} }

View File

@@ -1,15 +1,15 @@
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model'; import {SearchQueryResponse} from '../../+search-page/search-service/search-query-response.model';
import { RequestError } from '../data/request.models'; import {RequestError} from '../data/request.models';
import { PageInfo } from '../shared/page-info.model'; import {PageInfo} from '../shared/page-info.model';
import { ConfigObject } from '../shared/config/config.model'; import {ConfigObject} from '../shared/config/config.model';
import { FacetValue } from '../../+search-page/search-service/facet-value.model'; import {FacetValue} from '../../+search-page/search-service/facet-value.model';
import { SearchFilterConfig } from '../../+search-page/search-service/search-filter-config.model'; import {SearchFilterConfig} from '../../+search-page/search-service/search-filter-config.model';
import { IntegrationModel } from '../integration/models/integration.model'; import {IntegrationModel} from '../integration/models/integration.model';
import { RegistryMetadataschemasResponse } from '../registry/registry-metadataschemas-response.model'; import {RegistryMetadataschemasResponse} from '../registry/registry-metadataschemas-response.model';
import { MetadataSchema } from '../metadata/metadataschema.model'; import {RegistryMetadatafieldsResponse} from '../registry/registry-metadatafields-response.model';
import { RegistryMetadatafieldsResponse } from '../registry/registry-metadatafields-response.model'; import {RegistryBitstreamformatsResponse} from '../registry/registry-bitstreamformats-response.model';
import { RegistryBitstreamformatsResponse } from '../registry/registry-bitstreamformats-response.model'; import {AuthStatus} from '../auth/models/auth-status.model';
import { AuthStatus } from '../auth/models/auth-status.model'; import {MetadataSchema} from "../metadata/metadataschema.model";
/* tslint:disable:max-classes-per-file */ /* tslint:disable:max-classes-per-file */
export class RestResponse { export class RestResponse {

View File

@@ -56,7 +56,6 @@ import { FacetValueMapResponseParsingService } from './data/facet-value-map-resp
import { FacetConfigResponseParsingService } from './data/facet-config-response-parsing.service'; import { FacetConfigResponseParsingService } from './data/facet-config-response-parsing.service';
import { RegistryService } from './registry/registry.service'; import { RegistryService } from './registry/registry.service';
import { RegistryMetadataschemasResponseParsingService } from './data/registry-metadataschemas-response-parsing.service'; import { RegistryMetadataschemasResponseParsingService } from './data/registry-metadataschemas-response-parsing.service';
import { MetadataschemaParsingService } from './data/metadataschema-parsing.service';
import { RegistryMetadatafieldsResponseParsingService } from './data/registry-metadatafields-response-parsing.service'; import { RegistryMetadatafieldsResponseParsingService } from './data/registry-metadatafields-response-parsing.service';
import { RegistryBitstreamformatsResponseParsingService } from './data/registry-bitstreamformats-response-parsing.service'; import { RegistryBitstreamformatsResponseParsingService } from './data/registry-bitstreamformats-response-parsing.service';
import { NotificationsService } from '../shared/notifications/notifications.service'; import { NotificationsService } from '../shared/notifications/notifications.service';
@@ -108,7 +107,6 @@ const PROVIDERS = [
RegistryMetadataschemasResponseParsingService, RegistryMetadataschemasResponseParsingService,
RegistryMetadatafieldsResponseParsingService, RegistryMetadatafieldsResponseParsingService,
RegistryBitstreamformatsResponseParsingService, RegistryBitstreamformatsResponseParsingService,
MetadataschemaParsingService,
DebugResponseParsingService, DebugResponseParsingService,
SearchResponseParsingService, SearchResponseParsingService,
ServerResponseService, ServerResponseService,

View File

@@ -0,0 +1,46 @@
import {distinctUntilChanged, filter, map, tap} from 'rxjs/operators';
import {Injectable} from '@angular/core';
import {Store} from '@ngrx/store';
import {Observable} from 'rxjs';
import {isNotEmpty} from '../../shared/empty.util';
import {BrowseService} from '../browse/browse.service';
import {RemoteDataBuildService} from '../cache/builders/remote-data-build.service';
import {CoreState} from '../core.reducers';
import {URLCombiner} from '../url-combiner/url-combiner';
import {DataService} from './data.service';
import {RequestService} from './request.service';
import {HALEndpointService} from '../shared/hal-endpoint.service';
import {FindAllOptions, GetRequest, RestRequest} from './request.models';
import {ObjectCacheService} from '../cache/object-cache.service';
import {MetadataSchema} from "../metadata/metadataschema.model";
import {NormalizedMetadataSchema} from "../metadata/normalized-metadata-schema.model";
import {GenericConstructor} from "../shared/generic-constructor";
import {ResponseParsingService} from "./parsing.service";
import {RegistryMetadatafieldsResponseParsingService} from "./registry-metadatafields-response-parsing.service";
@Injectable()
export class MetadataSchemaDataService extends DataService<NormalizedMetadataSchema, MetadataSchema> {
protected linkPath = 'metadataschemas';
constructor(
protected requestService: RequestService,
protected rdbService: RemoteDataBuildService,
protected store: Store<CoreState>,
private bs: BrowseService,
protected halService: HALEndpointService,
protected objectCache: ObjectCacheService) {
super();
}
/**
* Get the endpoint for browsing metadataschemas
* @param {FindAllOptions} options
* @returns {Observable<string>}
*/
public getBrowseEndpoint(options: FindAllOptions = {}, linkPath: string = this.linkPath): Observable<string> {
return null;
}
}

View File

@@ -0,0 +1,21 @@
import {autoserialize} from 'cerialize';
import {NormalizedObject} from "../cache/models/normalized-object.model";
import {mapsTo} from "../cache/builders/build-decorators";
import {CacheableObject} from "../cache/object-cache.reducer";
import {ListableObject} from "../../shared/object-collection/shared/listable-object.model";
import {MetadataSchema} from "./metadataschema.model";
@mapsTo(MetadataSchema)
export class NormalizedMetadataSchema extends NormalizedObject implements CacheableObject, ListableObject {
@autoserialize
id: number;
@autoserialize
self: string;
@autoserialize
prefix: string;
@autoserialize
namespace: string;
}

View File

@@ -6,7 +6,6 @@ import {PageInfo} from '../shared/page-info.model';
import {MetadataSchema} from '../metadata/metadataschema.model'; import {MetadataSchema} from '../metadata/metadataschema.model';
import {MetadataField} from '../metadata/metadatafield.model'; import {MetadataField} from '../metadata/metadatafield.model';
import {BitstreamFormat} from './mock-bitstream-format.model'; import {BitstreamFormat} from './mock-bitstream-format.model';
import {flatMap, map, tap} from 'rxjs/operators';
import {GetRequest, RestRequest} from '../data/request.models'; import {GetRequest, RestRequest} from '../data/request.models';
import {GenericConstructor} from '../shared/generic-constructor'; import {GenericConstructor} from '../shared/generic-constructor';
import {ResponseParsingService} from '../data/parsing.service'; import {ResponseParsingService} from '../data/parsing.service';
@@ -30,11 +29,24 @@ import {RegistryBitstreamformatsResponse} from './registry-bitstreamformats-resp
import {getResponseFromEntry} from '../shared/operators'; import {getResponseFromEntry} from '../shared/operators';
import {createSelector, select, Store} from "@ngrx/store"; import {createSelector, select, Store} from "@ngrx/store";
import {AppState} from "../../app.reducer"; import {AppState} from "../../app.reducer";
import {MetadataRegistryState} from "../../+admin/admin-registries/metadata-schema/metadata-registry.reducers"; import {MetadataRegistryState} from "../../+admin/admin-registries/metadata-registry/metadata-registry.reducers";
import {MetadataRegistrySelectAction} from "../../+admin/admin-registries/metadata-schema/metadata-registry.actions"; import {
MetadataRegistryCancelFieldAction,
MetadataRegistryCancelSchemaAction,
MetadataRegistryDeselectFieldAction,
MetadataRegistryDeselectSchemaAction,
MetadataRegistryEditFieldAction,
MetadataRegistryEditSchemaAction,
MetadataRegistrySelectFieldAction,
MetadataRegistrySelectSchemaAction
} from "../../+admin/admin-registries/metadata-registry/metadata-registry.actions";
import {flatMap, map, tap} from "rxjs/operators";
const metadataRegistryStateSelector = (state: AppState) => state.metadataRegistry; const metadataRegistryStateSelector = (state: AppState) => state.metadataRegistry;
const activeMetadataSchemaSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.schema); const editMetadataSchemaSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editSchema);
const selectedMetadataSchemasSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.selectedSchemas);
const editMetadataFieldSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.editField);
const selectedMetadataFieldsSelector = createSelector(metadataRegistryStateSelector, (metadataState: MetadataRegistryState) => metadataState.selectedFields);
@Injectable() @Injectable()
export class RegistryService { export class RegistryService {
@@ -166,7 +178,7 @@ export class RegistryService {
return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs); return this.rdb.toRemoteDataObservable(requestEntryObs, payloadObs);
} }
private getMetadataSchemasRequestObs(pagination: PaginationComponentOptions): Observable<RestRequest> { public getMetadataSchemasRequestObs(pagination: PaginationComponentOptions): Observable<RestRequest> {
return this.halService.getEndpoint(this.metadataSchemasPath).pipe( return this.halService.getEndpoint(this.metadataSchemasPath).pipe(
map((url: string) => { map((url: string) => {
const args: string[] = []; const args: string[] = [];
@@ -229,14 +241,92 @@ export class RegistryService {
} }
public editMetadataSchema(schema: MetadataSchema) { public editMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistrySelectAction(schema)); this.store.dispatch(new MetadataRegistryEditSchemaAction(schema));
} }
public getActiveMetadataSchema(schema: MetadataSchema): Observable<MetadataSchema> { public cancelEditMetadataSchema() {
return this.store.pipe(select(activeMetadataSchemaSelector)); this.store.dispatch(new MetadataRegistryCancelSchemaAction());
} }
// public createMetadataSchema(schema: MetadataSchema): MetadataSchema { public getActiveMetadataSchema(): Observable<MetadataSchema> {
return this.store.pipe(select(editMetadataSchemaSelector));
}
public selectMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistrySelectSchemaAction(schema))
}
public deselectMetadataSchema(schema: MetadataSchema) {
this.store.dispatch(new MetadataRegistryDeselectSchemaAction(schema))
}
public getSelectedMetadataSchemas(): Observable<MetadataSchema[]> {
return this.store.pipe(select(selectedMetadataSchemasSelector));
}
public editMetadataField(field: MetadataField) {
this.store.dispatch(new MetadataRegistryEditFieldAction(field));
}
public cancelEditMetadataField() {
this.store.dispatch(new MetadataRegistryCancelFieldAction());
}
public getActiveMetadataField(): Observable<MetadataField> {
return this.store.pipe(select(editMetadataFieldSelector));
}
public selectMetadataField(field: MetadataField) {
this.store.dispatch(new MetadataRegistrySelectFieldAction(field))
}
public deselectMetadataField(field: MetadataField) {
this.store.dispatch(new MetadataRegistryDeselectFieldAction(field))
}
public getSelectedMetadataFields(): Observable<MetadataField[]> {
return this.store.pipe(select(selectedMetadataFieldsSelector));
}
// public createMetadataSchema(schema: MetadataSchema): Observable<RemoteData<MetadataSchema>> {
// const requestId = this.requestService.generateRequestId();
// const endpoint$ = this.halService.getEndpoint(this.metadataSchemasPath).pipe(
// isNotEmptyOperator(),
// distinctUntilChanged()
// );
// //
// const serializedDso = new DSpaceRESTv2Serializer(NormalizedObjectFactory.getConstructor(MetadataSchema.type)).serialize(normalizedObject);
//
// const request$ = endpoint$.pipe(
// take(1),
// map((endpoint: string) => new CreateRequest(requestId, endpoint, JSON.stringify(serializedDso)))
// );
//
// // Execute the post request
// request$.pipe(
// configureRequest(this.requestService)
// ).subscribe();
//
// // Resolve self link for new object
// const selfLink$ = this.requestService.getByUUID(requestId).pipe(
// getResponseFromEntry(),
// map((response: RestResponse) => {
// if (!response.isSuccessful && response instanceof ErrorResponse) {
// this.notificationsService.error('Server Error:', response.errorMessage, new NotificationOptions(-1));
// } else {
// return response;
// }
// }),
// map((response: any) => {
// if (isNotEmpty(response.resourceSelfLinks)) {
// return response.resourceSelfLinks[0];
// }
// }),
// distinctUntilChanged()
// ) as Observable<string>;
//
// return selfLink$.pipe(
// switchMap((selfLink: string) => this.findByHref(selfLink)),
// )
// } // }
} }

View File

@@ -8,5 +8,7 @@ export enum ResourceType {
Community = 'community', Community = 'community',
EPerson = 'eperson', EPerson = 'eperson',
Group = 'group', Group = 'group',
ResourcePolicy = 'resourcePolicy' ResourcePolicy = 'resourcePolicy',
MetadataSchema = 'metadataschema',
MetadataField = 'metadatafield'
} }

View File

@@ -69,6 +69,12 @@ export class FormComponent implements OnDestroy, OnInit {
@Output() addArrayItem: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>(); @Output() addArrayItem: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>();
@Output() removeArrayItem: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>(); @Output() removeArrayItem: EventEmitter<DynamicFormControlEvent> = new EventEmitter<DynamicFormControlEvent>();
/**
* An event fired when form is valid and submitted .
* Event's payload equals to the form content.
*/
@Output() cancel: EventEmitter<Observable<any>> = new EventEmitter<Observable<any>>();
/** /**
* An event fired when form is valid and submitted . * An event fired when form is valid and submitted .
* Event's payload equals to the form content. * Event's payload equals to the form content.
@@ -130,7 +136,9 @@ export class FormComponent implements OnDestroy, OnInit {
} else { } else {
this.formModel.forEach((model) => { this.formModel.forEach((model) => {
if (this.parentFormModel) {
this.formBuilderService.addFormGroupControl(this.formGroup, this.parentFormModel, model); this.formBuilderService.addFormGroupControl(this.formGroup, this.parentFormModel, model);
}
}); });
} }
@@ -229,9 +237,10 @@ export class FormComponent implements OnDestroy, OnInit {
private keepSync(): void { private keepSync(): void {
this.subs.push(this.formService.getFormData(this.formId) this.subs.push(this.formService.getFormData(this.formId)
.subscribe((stateFormData) => { .subscribe((stateFormData) => {
if (!Object.is(stateFormData, this.formGroup.value) && this.formGroup) { // if (!Object.is(stateFormData, this.formGroup.value) && this.formGroup) {
this.formGroup.setValue(stateFormData); // this.formGroup.setValue(stateFormData);
} console.log(stateFormData);
// }
})); }));
} }
@@ -275,6 +284,7 @@ export class FormComponent implements OnDestroy, OnInit {
*/ */
reset(): void { reset(): void {
this.formGroup.reset(); this.formGroup.reset();
this.cancel.emit();
} }
isItemReadOnly(arrayContext: DynamicFormArrayModel, index: number): boolean { isItemReadOnly(arrayContext: DynamicFormArrayModel, index: number): boolean {