]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/eg2/src/app/staff/share/marc-edit/rich-editor.component.ts
LP2061136 - Stamping 1405 DB upgrade script
[working/Evergreen.git] / Open-ILS / src / eg2 / src / app / staff / share / marc-edit / rich-editor.component.ts
1 import {Component, Input, OnInit, ViewChild} from '@angular/core';
2 import {filter} from 'rxjs/operators';
3 import {IdlService} from '@eg/core/idl.service';
4 import {NetService} from '@eg/core/net.service';
5 import {OrgService} from '@eg/core/org.service';
6 import {ServerStoreService} from '@eg/core/server-store.service';
7 import {TagTableService} from './tagtable.service';
8 import {MarcRecord, MarcField} from './marcrecord';
9 import {MarcEditContext} from './editor-context';
10 import {AuthorityLinkingDialogComponent} from './authority-linking-dialog.component';
11 import {PhysCharDialogComponent} from './phys-char-dialog.component';
12
13
14 /**
15  * MARC Record rich editor interface.
16  */
17
18 @Component({
19     selector: 'eg-marc-rich-editor',
20     templateUrl: './rich-editor.component.html',
21     styleUrls: ['rich-editor.component.css']
22 })
23
24 export class MarcRichEditorComponent implements OnInit {
25
26     @Input() context: MarcEditContext;
27     get record(): MarcRecord { return this.context.record; }
28
29     dataLoaded: boolean;
30     showHelp: boolean;
31     // eslint-disable-next-line no-magic-numbers
32     randId = Math.floor(Math.random() * 100000);
33     stackSubfields: boolean;
34     controlledBibTags: string[] = [];
35
36     @ViewChild('authLinker', {static: false})
37         authLinker: AuthorityLinkingDialogComponent;
38
39     @ViewChild('physCharDialog', {static: false})
40         physCharDialog: PhysCharDialogComponent;
41
42     constructor(
43         private idl: IdlService,
44         private net: NetService,
45         private org: OrgService,
46         private store: ServerStoreService,
47         private tagTable: TagTableService
48     ) {}
49
50     ngOnInit() {
51
52         this.store.getItem('cat.marcedit.stack_subfields')
53             .then(stack => this.stackSubfields = stack);
54
55         this.init().then(_ =>
56             this.context.recordChange.subscribe(__ => this.init()));
57
58         // Changing the Type fixed field means loading new meta-metadata.
59         this.record.fixedFieldChange.pipe(filter(code => code === 'Type'))
60             .subscribe(_ => this.init());
61     }
62
63     init(): Promise<any> {
64         this.dataLoaded = false;
65
66         if (!this.record) { return Promise.resolve(); }
67
68         return Promise.all([
69             this.tagTable.loadTags({
70                 marcRecordType: this.context.recordType,
71                 ffType: this.record.recordType()
72             }).then(table => this.context.tagTable = table),
73             this.tagTable.getControlledBibTags().then(
74                 tags => this.controlledBibTags = tags),
75             this.fetchSettings()
76         ]).then(_ =>
77             // setTimeout forces all of our sub-components to rerender
78             // themselves each time init() is called.  Without this,
79             // changing the record Type would only re-render the fixed
80             // fields editor when data had to be fetched from the
81             // network.  (Sometimes the data is cached).
82             setTimeout(() => this.dataLoaded = true)
83         );
84     }
85
86     fetchSettings(): Promise<any> {
87         // Fetch at rich editor load time to cache.
88         return this.org.settings(['cat.marc_control_number_identifier']);
89     }
90
91     stackSubfieldsChange() {
92         if (this.stackSubfields) {
93             this.store.setItem('cat.marcedit.stack_subfields', true);
94         } else {
95             this.store.removeItem('cat.marcedit.stack_subfields');
96         }
97     }
98
99     undoCount(): number {
100         return this.context.undoCount();
101     }
102
103     redoCount(): number {
104         return this.context.redoCount();
105     }
106
107     undo() {
108         this.context.requestUndo();
109     }
110
111     redo() {
112         this.context.requestRedo();
113     }
114
115     controlFields(): MarcField[] {
116         return this.record.fields.filter(f => f.isCtrlField);
117     }
118
119     dataFields(): MarcField[] {
120         return this.record.fields.filter(f => !f.isCtrlField);
121     }
122
123     validate() {
124         const fields = [];
125
126         this.record.fields.filter(f => this.isControlledBibTag(f.tag))
127             .forEach(f => {
128                 f.authValid = false;
129                 fields.push({
130                     id: f.fieldId, // ignored and echoed by server
131                     tag: f.tag,
132                     ind1: f.ind1,
133                     ind2: f.ind2,
134                     subfields: f.subfields.map(sf => [sf[0], sf[1]])
135                 });
136             });
137
138         this.net.request('open-ils.cat',
139             'open-ils.cat.authority.validate.bib_field', fields)
140             .subscribe(checkedField => {
141                 const bibField = this.record.fields
142                     .filter(f => f.fieldId === +checkedField.id)[0];
143
144                 bibField.authChecked = true;
145                 bibField.authValid = checkedField.valid;
146             });
147     }
148
149     isControlledBibTag(tag: string): boolean {
150         return this.controlledBibTags && this.controlledBibTags.includes(tag);
151     }
152
153     openLinkerDialog(field: MarcField) {
154         this.authLinker.bibField = field;
155         this.authLinker.open({size: 'xl'}).subscribe(newField => {
156
157             // The presence of newField here means the linker wants to
158             // replace the field with a new field from the authority
159             // record.  Otherwise, the original field may have been
160             // directly modified or the dialog canceled.
161             if (!newField) { return; }
162
163             // Performs an insert followed by a delete, so the two
164             // fields can be tracked separately for undo/redo actions.
165             const marcField = this.record.newField(newField);
166             this.context.insertField(field, marcField);
167             this.context.deleteField(field);
168
169             // Mark the insert and delete as an atomic undo/redo action.
170             this.context.setUndoGroupSize(2);
171         });
172     }
173
174     // 007 Physical characteristics wizard.
175     openPhysCharDialog(field: MarcField) {
176         this.physCharDialog.fieldData = field.data;
177
178         this.physCharDialog.open({size: 'lg'}).subscribe(
179             newData => {
180                 if (newData) {
181                     this.context.requestFieldFocus({
182                         fieldId: field.fieldId,
183                         target: 'cfld',
184                         newText: newData
185                     });
186                 }
187             }
188         );
189     }
190 }
191
192
193