mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
63669: Content-Source integrated with FieldUpdate
This commit is contained in:
@@ -155,5 +155,10 @@ module.exports = {
|
||||
edit: {
|
||||
undoTimeout: 10000 // 10 seconds
|
||||
}
|
||||
},
|
||||
collection: {
|
||||
edit: {
|
||||
undoTimeout: 10000 // 10 seconds
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -55,6 +55,20 @@
|
||||
"required": "You must provide a set id of the target collection."
|
||||
}
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
"discarded": {
|
||||
"title": "Changed discarded",
|
||||
"content": "Your changes were discarded. To reinstate your changes click the 'Undo' button"
|
||||
},
|
||||
"invalid": {
|
||||
"title": "Metadata invalid",
|
||||
"content": "Your changes were not saved. Please make sure all fields are valid before you save."
|
||||
},
|
||||
"saved": {
|
||||
"title": "Content Source saved",
|
||||
"content": "Your changes to this collection's content source were saved."
|
||||
}
|
||||
}
|
||||
},
|
||||
"curate": {
|
||||
|
@@ -1,4 +1,22 @@
|
||||
<div class="container-fluid">
|
||||
<div class="d-inline-block float-right">
|
||||
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.discard-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async)"
|
||||
(click)="onSubmit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<h4>{{ 'collection.edit.tabs.source.head' | translate }}</h4>
|
||||
<div class="form-check mb-4">
|
||||
<input type="checkbox" class="form-check-input" id="externalSourceCheck" [checked]="contentSource.enabled" (change)="changeExternalSource()">
|
||||
@@ -11,6 +29,27 @@
|
||||
[formGroup]="formGroup"
|
||||
[formModel]="formModel"
|
||||
[formLayout]="formLayout"
|
||||
[displaySubmit]="false"
|
||||
(dfChange)="onChange($event)"
|
||||
(submitForm)="onSubmit()"
|
||||
(cancel)="onCancel()"></ds-form>
|
||||
<div class="container-fluid">
|
||||
<div class="d-inline-block float-right">
|
||||
<button class=" btn btn-danger" *ngIf="!(isReinstatable() | async)"
|
||||
[disabled]="!(hasChanges() | async)"
|
||||
(click)="discard()"><i
|
||||
class="fas fa-times"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.discard-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
|
||||
(click)="reinstate()"><i
|
||||
class="fas fa-undo-alt"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.reinstate-button" | translate}}</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" [disabled]="!(hasChanges() | async)"
|
||||
(click)="onSubmit()"><i
|
||||
class="fas fa-save"></i>
|
||||
<span class="d-none d-sm-inline"> {{"item.edit.metadata.save-button" | translate}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
|
||||
import { AbstractTrackableComponent } from '../../../shared/trackable/abstract-trackable.component';
|
||||
import {
|
||||
DynamicFormControlModel, DynamicFormGroupModel, DynamicFormLayout, DynamicFormService,
|
||||
@@ -11,13 +11,17 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { ObjectUpdatesService } from '../../../core/data/object-updates/object-updates.service';
|
||||
import { NotificationsService } from '../../../shared/notifications/notifications.service';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { isNotEmpty } from '../../../shared/empty.util';
|
||||
import { hasValue, isNotEmpty } from '../../../shared/empty.util';
|
||||
import { ContentSource } from '../../../core/shared/content-source.model';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Collection } from '../../../core/shared/collection.model';
|
||||
import { first, map } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FieldUpdate, FieldUpdates } from '../../../core/data/object-updates/object-updates.reducer';
|
||||
import { Subscription } from 'rxjs/internal/Subscription';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { GLOBAL_CONFIG, GlobalConfig } from '../../../../config';
|
||||
|
||||
/**
|
||||
* Component for managing the content source of the collection
|
||||
@@ -26,17 +30,22 @@ import { ActivatedRoute } from '@angular/router';
|
||||
selector: 'ds-collection-source',
|
||||
templateUrl: './collection-source.component.html',
|
||||
})
|
||||
export class CollectionSourceComponent extends AbstractTrackableComponent implements OnInit {
|
||||
export class CollectionSourceComponent extends AbstractTrackableComponent implements OnInit, OnDestroy {
|
||||
/**
|
||||
* The current collection's remote data
|
||||
*/
|
||||
collectionRD$: Observable<RemoteData<Collection>>;
|
||||
|
||||
/**
|
||||
* The current collection's content source
|
||||
* The collection's content source
|
||||
*/
|
||||
contentSource: ContentSource;
|
||||
|
||||
/**
|
||||
* The current update to the content source
|
||||
*/
|
||||
update$: Observable<FieldUpdate>;
|
||||
|
||||
/**
|
||||
* @type {string} Key prefix used to generate form labels
|
||||
*/
|
||||
@@ -80,7 +89,6 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
new DynamicSelectModel({
|
||||
id: 'format',
|
||||
name: 'format',
|
||||
value: 'dc',
|
||||
options: [
|
||||
{
|
||||
value: 'dc',
|
||||
@@ -105,7 +113,6 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
new DynamicRadioGroupModel<number>({
|
||||
id: 'harvest',
|
||||
name: 'harvest',
|
||||
value: 3,
|
||||
options: [
|
||||
{
|
||||
value: 1,
|
||||
@@ -171,16 +178,29 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
*/
|
||||
formGroup: FormGroup;
|
||||
|
||||
/**
|
||||
* Subscription to update the current form
|
||||
*/
|
||||
updateSub: Subscription;
|
||||
|
||||
public constructor(public objectUpdatesService: ObjectUpdatesService,
|
||||
public notificationsService: NotificationsService,
|
||||
protected location: Location,
|
||||
protected formService: DynamicFormService,
|
||||
protected translate: TranslateService,
|
||||
protected route: ActivatedRoute) {
|
||||
protected route: ActivatedRoute,
|
||||
protected router: Router,
|
||||
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,) {
|
||||
super(objectUpdatesService, notificationsService, translate);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.notificationsPrefix = 'collection.edit.tabs.source.notifications.';
|
||||
this.discardTimeOut = this.EnvConfig.collection.edit.undoTimeout;
|
||||
this.url = this.router.url;
|
||||
if (this.url.indexOf('?') > 0) {
|
||||
this.url = this.url.substr(0, this.url.indexOf('?'));
|
||||
}
|
||||
this.formGroup = this.formService.createFormGroup(this.formModel);
|
||||
this.collectionRD$ = this.route.parent.data.pipe(first(), map((data) => data.dso));
|
||||
|
||||
@@ -192,6 +212,33 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
.subscribe(() => {
|
||||
this.updateFieldTranslations();
|
||||
});
|
||||
|
||||
this.initializeOriginalContentSource();
|
||||
}
|
||||
|
||||
initializeOriginalContentSource() {
|
||||
const initialContentSource = cloneDeep(this.contentSource);
|
||||
this.objectUpdatesService.initialize(this.url, [initialContentSource], new Date());
|
||||
this.update$ = this.objectUpdatesService.getFieldUpdates(this.url, [initialContentSource]).pipe(
|
||||
map((updates: FieldUpdates) => updates[initialContentSource.uuid])
|
||||
);
|
||||
this.updateSub = this.update$.subscribe((update: FieldUpdate) => {
|
||||
const field = update.field as ContentSource;
|
||||
this.formGroup.patchValue({
|
||||
providerContainer: {
|
||||
provider: field.provider
|
||||
},
|
||||
setContainer: {
|
||||
set: field.set,
|
||||
format: field.format
|
||||
},
|
||||
harvestContainer: {
|
||||
harvest: field.harvest
|
||||
}
|
||||
});
|
||||
this.contentSource = cloneDeep(field);
|
||||
this.switchEnableForm();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,13 +273,14 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
}
|
||||
|
||||
onChange(event) {
|
||||
// TODO: Update ContentSource object and add to field update
|
||||
console.log(event);
|
||||
this.updateContentSourceField(event.model);
|
||||
this.saveFieldUpdate();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
// TODO: Fetch field update and send to REST API
|
||||
console.log('submit');
|
||||
this.initializeOriginalContentSource();
|
||||
this.notificationsService.success(this.getNotificationTitle('saved'), this.getNotificationContent('saved'));
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
@@ -241,10 +289,46 @@ export class CollectionSourceComponent extends AbstractTrackableComponent implem
|
||||
|
||||
changeExternalSource() {
|
||||
this.contentSource.enabled = !this.contentSource.enabled;
|
||||
this.updateContentSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the form depending on the Content Source's enabled property
|
||||
*/
|
||||
switchEnableForm() {
|
||||
if (this.contentSource.enabled) {
|
||||
this.formGroup.enable();
|
||||
} else {
|
||||
this.formGroup.disable();
|
||||
}
|
||||
}
|
||||
|
||||
updateContentSource() {
|
||||
this.formModel.forEach(
|
||||
(fieldModel: DynamicFormGroupModel | DynamicInputModel) => {
|
||||
if (fieldModel instanceof DynamicFormGroupModel) {
|
||||
fieldModel.group.forEach((childModel: DynamicInputModel) => {
|
||||
this.updateContentSourceField(childModel);
|
||||
});
|
||||
} else {
|
||||
this.updateContentSourceField(fieldModel);
|
||||
}
|
||||
}
|
||||
);
|
||||
this.saveFieldUpdate();
|
||||
}
|
||||
|
||||
updateContentSourceField(fieldModel: DynamicInputModel) {
|
||||
if (hasValue(fieldModel)) {
|
||||
this.contentSource[fieldModel.id] = fieldModel.value;
|
||||
}
|
||||
}
|
||||
|
||||
saveFieldUpdate() {
|
||||
this.objectUpdatesService.saveAddFieldUpdate(this.url, cloneDeep(this.contentSource));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.updateSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,35 @@
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export class ContentSource {
|
||||
/**
|
||||
* Unique identifier
|
||||
*/
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* Does this collection harvest its content from an external source ?
|
||||
*/
|
||||
enabled = false;
|
||||
|
||||
/**
|
||||
* OAI Provider
|
||||
*/
|
||||
provider: string;
|
||||
|
||||
/**
|
||||
* OAI Specific set ID
|
||||
*/
|
||||
set: string;
|
||||
|
||||
format: string;
|
||||
/**
|
||||
* Metadata Format
|
||||
*/
|
||||
format = 'dc';
|
||||
|
||||
harvestType: number;
|
||||
/**
|
||||
* Type of content being harvested
|
||||
*/
|
||||
harvest = 3;
|
||||
|
||||
constructor() {
|
||||
this.uuid = uuid();
|
||||
|
@@ -63,7 +63,7 @@ export class AbstractTrackableComponent {
|
||||
* Get translated notification title
|
||||
* @param key
|
||||
*/
|
||||
private getNotificationTitle(key: string) {
|
||||
protected getNotificationTitle(key: string) {
|
||||
return this.translateService.instant(this.notificationsPrefix + key + '.title');
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export class AbstractTrackableComponent {
|
||||
* Get translated notification content
|
||||
* @param key
|
||||
*/
|
||||
private getNotificationContent(key: string) {
|
||||
protected getNotificationContent(key: string) {
|
||||
return this.translateService.instant(this.notificationsPrefix + key + '.content');
|
||||
|
||||
}
|
||||
|
7
src/config/collection-page-config.interface.ts
Normal file
7
src/config/collection-page-config.interface.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Config } from './config.interface';
|
||||
|
||||
export interface CollectionPageConfig extends Config {
|
||||
edit: {
|
||||
undoTimeout: number;
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ import { FormConfig } from './form-config.interfaces';
|
||||
import {LangConfig} from './lang-config.interface';
|
||||
import { BrowseByConfig } from './browse-by-config.interface';
|
||||
import { ItemPageConfig } from './item-page-config.interface';
|
||||
import { CollectionPageConfig } from './collection-page-config.interface';
|
||||
|
||||
export interface GlobalConfig extends Config {
|
||||
ui: ServerConfig;
|
||||
@@ -25,4 +26,5 @@ export interface GlobalConfig extends Config {
|
||||
languages: LangConfig[];
|
||||
browseBy: BrowseByConfig;
|
||||
item: ItemPageConfig;
|
||||
collection: CollectionPageConfig;
|
||||
}
|
||||
|
Reference in New Issue
Block a user