From 5535f9276e786650f784927f7d0465eb79a97741 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Fri, 28 Jun 2019 12:29:07 -0400 Subject: [PATCH] LP1834665 Angular catalog MARC flat text editor Adds a set of components for editing MARC records. The main component acts as a container with various actions (source selector, delete, undelete, and save options). The body of this component is a tabbed interface, one tab for the Enriched editor and one for the Flat Text editor. The Enriched editor tab directs the user to the AngJS version of the page. the Flat Text editor tab implements the standard MARC flat text editor interface. Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg --- .../src/app/staff/catalog/catalog.module.ts | 4 +- .../catalog/record/record.component.html | 9 +- .../staff/catalog/record/record.component.ts | 5 + .../share/marc-edit/editor.component.html | 73 +++++++ .../staff/share/marc-edit/editor.component.ts | 182 ++++++++++++++++++ .../share/marc-edit/flat-editor.component.css | 11 ++ .../marc-edit/flat-editor.component.html | 7 + .../share/marc-edit/flat-editor.component.ts | 45 +++++ .../staff/share/marc-edit/marc-edit.module.ts | 24 +++ .../app/staff/share/marc-edit/marcrecord.ts | 31 +++ .../share/marc-edit/rich-editor.component.css | 0 .../marc-edit/rich-editor.component.html | 0 .../share/marc-edit/rich-editor.component.ts | 28 +++ 13 files changed, 412 insertions(+), 7 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.css create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts index e0fbff851d..064c21553c 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts @@ -27,6 +27,7 @@ import {ConjoinedComponent} from './record/conjoined.component'; import {CnBrowseComponent} from './cnbrowse.component'; import {CnBrowseResultsComponent} from './cnbrowse/results.component'; import {SearchTemplatesComponent} from './search-templates.component'; +import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module'; @NgModule({ declarations: [ @@ -58,7 +59,8 @@ import {SearchTemplatesComponent} from './search-templates.component'; CatalogRoutingModule, HoldsModule, HoldingsModule, - BookingModule + BookingModule, + MarcEditModule ], providers: [ StaffCatalogService diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html index f562123c4c..98476aacb8 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html +++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.html @@ -35,12 +35,9 @@ -
- MARC Edit not yet implemented. See the - - AngularJS MARC Edit Tab. - +
+
diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts index c70b5658be..e397444819 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts @@ -110,6 +110,11 @@ export class RecordComponent implements OnInit { } return null; } + + handleMarcRecordSaved() { + this.staffCat.currentDetailRecordSummary = null; + this.loadRecord(); + } } diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html new file mode 100644 index 0000000000..fdaf7e50cd --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.html @@ -0,0 +1,73 @@ + + + + + + + + + + +
+
+
+ + +
+ + + + + + + +
+ +
+
+ + + +
+ Enhanced MARC Editor is not yet implemented. See the + + + AngularJS MARC Editor. + + + + + AngularJS MARC Editor. + + +
+
+
+ + + + + +
+
+
+ +
+ +
+ + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts new file mode 100644 index 0000000000..cea199052a --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/editor.component.ts @@ -0,0 +1,182 @@ +import {Component, Input, Output, OnInit, EventEmitter, ViewChild} from '@angular/core'; +import {IdlService} from '@eg/core/idl.service'; +import {EventService} from '@eg/core/event.service'; +import {NetService} from '@eg/core/net.service'; +import {AuthService} from '@eg/core/auth.service'; +import {OrgService} from '@eg/core/org.service'; +import {PcrudService} from '@eg/core/pcrud.service'; +import {MarcRecord} from './marcrecord'; +import {ComboboxEntry, ComboboxComponent + } from '@eg/share/combobox/combobox.component'; +import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component'; + + +/** + * MARC Record editor main interface. + */ + +@Component({ + selector: 'eg-marc-editor', + templateUrl: './editor.component.html' +}) + +export class MarcEditorComponent implements OnInit { + + record: MarcRecord; + editorTab: 'rich' | 'flat'; + sources: ComboboxEntry[]; + + @Input() set recordId(id: number) { + if (!id) { return; } + if (this.record && this.record.id === id) { return; } + this.fromId(id); + } + + @Input() set recordXml(xml: string) { + if (xml) { this.fromXml(xml); } + } + + // If true, saving records to the database is assumed to + // happen externally. IOW, the record editor is just an + // in-place MARC modification interface. + inPlaceMode: boolean; + + // In inPlaceMode, this is emitted in lieu of saving the record + // in th database. When inPlaceMode is false, this is emitted after + // the record is successfully saved. + @Output() recordSaved: EventEmitter; + + @ViewChild('sourceSelector') sourceSelector: ComboboxComponent; + @ViewChild('confirmDelete') confirmDelete: ConfirmDialogComponent; + @ViewChild('confirmUndelete') confirmUndelete: ConfirmDialogComponent; + @ViewChild('cannotDelete') cannotDelete: ConfirmDialogComponent; + + constructor( + private evt: EventService, + private idl: IdlService, + private net: NetService, + private auth: AuthService, + private org: OrgService, + private pcrud: PcrudService + ) { + this.sources = []; + this.recordSaved = new EventEmitter(); + } + + ngOnInit() { + // Default to flat for now since it's all that's supported. + this.editorTab = 'flat'; + + this.pcrud.retrieveAll('cbs').subscribe( + src => this.sources.push({id: +src.id(), label: src.source()}), + _ => {}, + () => { + this.sources = this.sources.sort((a, b) => + a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1 + ); + } + ); + } + + saveRecord(): Promise { + const xml = this.record.toXml(); + + if (this.inPlaceMode) { + // Let the caller have the modified XML and move on. + this.recordSaved.emit(xml); + return Promise.resolve(); + } + + const source = this.sourceSelector.selected ? + this.sourceSelector.selected.label : null; // 'label' not a typo + + if (this.record.id) { // Editing an existing record + + const method = 'open-ils.cat.biblio.record.marc.replace'; + + return this.net.request('open-ils.cat', method, + this.auth.token(), this.record.id, xml, source + ).toPromise().then(response => { + + const evt = this.evt.parse(response); + if (evt) { + console.error(evt); + // TODO: toast + } + + // TODO: toast + this.recordSaved.emit(xml); + return response; + }); + + } else { + // TODO: create a new record + } + } + + fromId(id: number): Promise { + return this.pcrud.retrieve('bre', id) + .toPromise().then(bib => { + this.record = new MarcRecord(bib.marc()); + this.record.id = id; + this.record.deleted = bib.deleted() === 't'; + if (bib.source()) { + this.sourceSelector.applyEntryId(+bib.source()); + } + }); + } + + fromXml(xml: string) { + this.record = new MarcRecord(xml); + this.record.id = null; + } + + deleteRecord(): Promise { + + return this.confirmDelete.open().toPromise() + .then(yes => { + if (!yes) { return; } + + return this.net.request('open-ils.cat', + 'open-ils.cat.biblio.record_entry.delete', + this.auth.token(), this.record.id).toPromise() + + .then(resp => { + + const evt = this.evt.parse(resp); + if (evt) { + if (evt.textcode === 'RECORD_NOT_EMPTY') { + return this.cannotDelete.open().toPromise(); + } else { + console.error(evt); + return alert(evt); + } + } + return this.fromId(this.record.id) + .then(_ => this.recordSaved.emit(this.record.toXml())); + }); + }); + } + + undeleteRecord(): Promise { + + return this.confirmUndelete.open().toPromise() + .then(yes => { + if (!yes) { return; } + + return this.net.request('open-ils.cat', + 'open-ils.cat.biblio.record_entry.undelete', + this.auth.token(), this.record.id).toPromise() + + .then(resp => { + + const evt = this.evt.parse(resp); + if (evt) { console.error(evt); return alert(evt); } + + return this.fromId(this.record.id) + .then(_ => this.recordSaved.emit(this.record.toXml())); + }); + }); + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css new file mode 100644 index 0000000000..12e912b8f1 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.css @@ -0,0 +1,11 @@ + + +.flat-editor-content { + font-family: 'Lucida Console', Monaco, monospace; + display: inline-block; + /* + min-width: 1ch; + margin: 0 -1px; + */ + padding: 0; +} diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html new file mode 100644 index 0000000000..eaf54a92c1 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.html @@ -0,0 +1,7 @@ + +
+ +
diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts new file mode 100644 index 0000000000..b5e2f41277 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/flat-editor.component.ts @@ -0,0 +1,45 @@ +import {Component, Input, OnInit, Host} from '@angular/core'; +import {IdlService} from '@eg/core/idl.service'; +import {OrgService} from '@eg/core/org.service'; +import {ServerStoreService} from '@eg/core/server-store.service'; +import {MarcEditorComponent} from './editor.component'; +import {MarcRecord} from './marcrecord'; + +/** + * MARC Record flat text (marc-breaker) editor. + */ + +@Component({ + selector: 'eg-marc-flat-editor', + templateUrl: './flat-editor.component.html', + styleUrls: ['flat-editor.component.css'] +}) + +export class MarcFlatEditorComponent implements OnInit { + + get record(): MarcRecord { + return this.editor.record; + } + + constructor( + private idl: IdlService, + private org: OrgService, + private store: ServerStoreService, + @Host() private editor: MarcEditorComponent + ) { + } + + ngOnInit() {} + + // When we have breaker text, limit the vertical expansion of the + // text area to the size of the data plus a little padding. + rowCount(): number { + if (this.record && this.record.breakerText) { + return this.record.breakerText.split(/\n/).length + 2; + } + return 40; + } +} + + + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts new file mode 100644 index 0000000000..a18eb0b7a4 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marc-edit.module.ts @@ -0,0 +1,24 @@ +import {NgModule} from '@angular/core'; +import {StaffCommonModule} from '@eg/staff/common.module'; +import {MarcEditorComponent} from './editor.component'; +import {MarcRichEditorComponent} from './rich-editor.component'; +import {MarcFlatEditorComponent} from './flat-editor.component'; + +@NgModule({ + declarations: [ + MarcEditorComponent, + MarcRichEditorComponent, + MarcFlatEditorComponent + ], + imports: [ + StaffCommonModule + ], + exports: [ + MarcEditorComponent + ], + providers: [ + ] +}) + +export class MarcEditModule { } + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts new file mode 100644 index 0000000000..1b0c488e46 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/marcrecord.ts @@ -0,0 +1,31 @@ +/** + * Simple wrapper class for our external MARC21.Record JS library. + */ + +declare var MARC21; + +export class MarcRecord { + + id: number; // Database ID when known. + deleted: boolean; + record: any; // MARC21.Record object + breakerText: string; + + constructor(xml: string) { + this.record = new MARC21.Record({marcxml: xml}); + this.breakerText = this.record.toBreaker(); + } + + toXml(): string { + return this.record.toXmlString(); + } + + toBreaker(): string { + return this.record.toBreaker(); + } + + absorbBreakerChanges() { + this.record = new MARC21.Record({marcbreaker: this.breakerText}); + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.css b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts new file mode 100644 index 0000000000..7f8ac334e3 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts @@ -0,0 +1,28 @@ +import {Component, Input, Output, OnInit, AfterViewInit, EventEmitter, + OnDestroy} from '@angular/core'; +import {IdlService} from '@eg/core/idl.service'; +import {OrgService} from '@eg/core/org.service'; + +/** + * MARC Record rich editor interface. + */ + +@Component({ + selector: 'eg-marc-rich-editor', + templateUrl: './rich-editor.component.html', + styleUrls: ['rich-editor.component.css'] +}) + +export class MarcRichEditorComponent implements OnInit { + + constructor( + private idl: IdlService, + private org: OrgService + ) { + } + + ngOnInit() {} +} + + + -- 2.43.2