From: Bill Erickson <berickxx@gmail.com> Date: Wed, 8 Jan 2020 21:49:35 +0000 (-0500) Subject: LP1852782 Linker links to auth record editor X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=37885d3a0ba1008747f674387530d0497b1a6992;p=contrib%2FConifer.git LP1852782 Linker links to auth record editor Adds a new UI at /staff/cat/authority/edit/ for finding authority records by ID and editing authority records via the Angular MARC editor. Modifies the "Cataloging" => "Retrieve Authority Record By ID" nav menu entry to point to the Angular version of the interface. Augments the MARC edit authority linking dialog to turn authority ID's into links which open the authority record in its own MARC editor in a new tab. Misc. MARC editor repairs related to loading authority records by ID. Signed-off-by: Bill Erickson <berickxx@gmail.com> Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu> --- diff --git a/Open-ILS/src/eg2/src/app/common.module.ts b/Open-ILS/src/eg2/src/app/common.module.ts index 56eddab1eb..5250670fde 100644 --- a/Open-ILS/src/eg2/src/app/common.module.ts +++ b/Open-ILS/src/eg2/src/app/common.module.ts @@ -13,9 +13,9 @@ Note core services are injected into 'root'. They do not have to be added to the providers list. */ -// consider moving these to core... import {HtmlToTxtService} from '@eg/share/util/htmltotxt.service'; import {PrintService} from '@eg/share/print/print.service'; +import {AnonCacheService} from '@eg/share/util/anon-cache.service'; // Globally available components import {PrintComponent} from '@eg/share/print/print.component'; @@ -79,6 +79,7 @@ export class EgCommonModule { return { ngModule: EgCommonModule, providers: [ + AnonCacheService, HtmlToTxtService, PrintService, ToastService diff --git a/Open-ILS/src/eg2/src/app/share/catalog/catalog-common.module.ts b/Open-ILS/src/eg2/src/app/share/catalog/catalog-common.module.ts index 5b45d00a60..f9e628ed0e 100644 --- a/Open-ILS/src/eg2/src/app/share/catalog/catalog-common.module.ts +++ b/Open-ILS/src/eg2/src/app/share/catalog/catalog-common.module.ts @@ -1,7 +1,6 @@ import {NgModule} from '@angular/core'; import {EgCommonModule} from '@eg/common.module'; import {CatalogService} from './catalog.service'; -import {AnonCacheService} from '@eg/share/util/anon-cache.service'; import {BasketService} from './basket.service'; import {CatalogUrlService} from './catalog-url.service'; import {BibRecordService} from './bib-record.service'; @@ -20,12 +19,11 @@ import {MarcHtmlComponent} from './marc-html.component'; MarcHtmlComponent ], providers: [ - AnonCacheService, CatalogService, CatalogUrlService, UnapiService, BibRecordService, - BasketService, + BasketService ] }) diff --git a/Open-ILS/src/eg2/src/app/staff/cat/authority/authority.module.ts b/Open-ILS/src/eg2/src/app/staff/cat/authority/authority.module.ts new file mode 100644 index 0000000000..ded954a9c7 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/cat/authority/authority.module.ts @@ -0,0 +1,23 @@ +import {NgModule} from '@angular/core'; +import {StaffCommonModule} from '@eg/staff/common.module'; +import {CommonWidgetsModule} from '@eg/share/common-widgets.module'; +import {AuthorityRoutingModule} from './routing.module'; +import {MarcEditModule} from '@eg/staff/share/marc-edit/marc-edit.module'; +import {AuthorityMarcEditComponent} from './marc-edit.component'; + +@NgModule({ + declarations: [ + AuthorityMarcEditComponent + ], + imports: [ + StaffCommonModule, + CommonWidgetsModule, + MarcEditModule, + AuthorityRoutingModule + ], + providers: [ + ] +}) + +export class AuthorityModule { +} diff --git a/Open-ILS/src/eg2/src/app/staff/cat/authority/marc-edit.component.html b/Open-ILS/src/eg2/src/app/staff/cat/authority/marc-edit.component.html new file mode 100644 index 0000000000..e563e3a9b7 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/cat/authority/marc-edit.component.html @@ -0,0 +1,32 @@ + +<ng-container *ngIf="!authorityId"> + <!-- If we don't have an authority ID, prompt the user to enter one --> + <eg-staff-banner bannerText="Find Authority Record By ID" i18n-bannerText> + </eg-staff-banner> + + <div class="row"> + <div class="col-lg-6 form-inline"> + <div class="input-group"> + <div class="input-group-prepend"> + <span class="input-group-text" i18n>Authorty Record Id</span> + </div> + <input type="text" class="form-control" + id='auth-id-input' + i18n-placeholder placeholder="Authorty Record Id" + i18n-aria-label aria-label="Authorty Record Id" + (keyup.enter)="goToAuthority()" [(ngModel)]="loadId"/> + <button class="btn btn-success" (click)="goToAuthority()" i18n>Submit</button> + </div> + </div> + </div> +</ng-container> + +<ng-container *ngIf="authorityId"> + <eg-staff-banner bannerText="Edit Authority Record #{{authorityId}}" i18n-bannerText> + </eg-staff-banner> + + <eg-marc-editor #marcEditor recordType="authority" [recordId]="authorityId"> + </eg-marc-editor> +</ng-container> + + diff --git a/Open-ILS/src/eg2/src/app/staff/cat/authority/marc-edit.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/authority/marc-edit.component.ts new file mode 100644 index 0000000000..88f2cd032d --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/cat/authority/marc-edit.component.ts @@ -0,0 +1,35 @@ +import {Component, OnInit, AfterViewInit, ViewChild, Renderer2} from '@angular/core'; +import {Router, ActivatedRoute, ParamMap} from '@angular/router'; +import {MarcSavedEvent} from '@eg/staff/share/marc-edit/editor.component'; + +@Component({ + templateUrl: 'marc-edit.component.html' +}) +export class AuthorityMarcEditComponent implements AfterViewInit { + + authorityId: number; + + // Avoid setting authorityId during lookup because it can + // cause the marc editor to load prematurely. + loadId: number; + + constructor( + private router: Router, + private route: ActivatedRoute, + private renderer: Renderer2) { + this.authorityId = +this.route.snapshot.paramMap.get('id'); + } + + ngAfterViewInit() { + if (!this.authorityId) { + this.renderer.selectRootElement('#auth-id-input').focus(); + } + } + + goToAuthority() { + if (this.loadId) { + this.router.navigate([`/staff/cat/authority/edit/${this.loadId}`]); + } + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/cat/authority/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/cat/authority/routing.module.ts new file mode 100644 index 0000000000..cd6b3a138f --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/cat/authority/routing.module.ts @@ -0,0 +1,20 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {AuthorityMarcEditComponent} from './marc-edit.component'; + +const routes: Routes = [{ + path: 'edit', + component: AuthorityMarcEditComponent + }, { + path: 'edit/:id', + component: AuthorityMarcEditComponent +}]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [] +}) + +export class AuthorityRoutingModule {} + diff --git a/Open-ILS/src/eg2/src/app/staff/cat/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/cat/routing.module.ts index a923b46822..67fb59b56c 100644 --- a/Open-ILS/src/eg2/src/app/staff/cat/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/cat/routing.module.ts @@ -4,6 +4,9 @@ import {RouterModule, Routes} from '@angular/router'; const routes: Routes = [ { path: 'vandelay', loadChildren: '@eg/staff/cat/vandelay/vandelay.module#VandelayModule' + }, { + path: 'authority', + loadChildren: '@eg/staff/cat/authority/authority.module#AuthorityModule' } ]; diff --git a/Open-ILS/src/eg2/src/app/staff/nav.component.html b/Open-ILS/src/eg2/src/app/staff/nav.component.html index 265368a62a..5310b5b340 100644 --- a/Open-ILS/src/eg2/src/app/staff/nav.component.html +++ b/Open-ILS/src/eg2/src/app/staff/nav.component.html @@ -222,7 +222,7 @@ <span class="material-icons">lock</span> <span i18n>Manage Authorities</span> </a> - <a href="/eg/staff/cat/catalog/retrieve_by_authority_id" class="dropdown-item"> + <a routerLink="/staff/cat/authority/edit" class="dropdown-item"> <span class="material-icons">collections</span> <span i18n>Retrieve Authority Record by ID</span> </a> diff --git a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/authority-linking-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/authority-linking-dialog.component.html index 2c8c3fcc1d..03d1eb5270 100644 --- a/Open-ILS/src/eg2/src/app/staff/share/marc-edit/authority-linking-dialog.component.html +++ b/Open-ILS/src/eg2/src/app/staff/share/marc-edit/authority-linking-dialog.component.html @@ -90,26 +90,29 @@ </div> </div> <ul *ngFor="let entry of browseData"> - <li class="d-flex"> + <li class="d-flex mt-1"> <div class="flex-1"> <ng-container *ngTemplateOutlet="headingField;context:{field:entry.main_heading, authId: entry.authority_id}"> </ng-container> </div> - <div class="font-italic" i18n-title i18n - title="Authority Record ID {{entry.authority_id}}"> - #{{entry.authority_id}} + <div class="font-italic"> + <a target="_blank" + i18n-title title="Authority Record ID {{entry.authority_id}}" + routerLink="/staff/cat/authority/edit/{{entry.authority_id}}"> + #{{entry.authority_id}} + </a> </div> </li> <ul *ngFor="let from of entry.see_froms"> - <li i18n> + <li class="mt-1"> <ng-container *ngTemplateOutlet="headingField;context:{field:from, from:true}"> </ng-container> </li> </ul> <ul *ngFor="let also of entry.see_alsos"> - <li i18n> + <li class="mt-1"> <ng-container *ngTemplateOutlet="headingField;context:{field:also, also:true}"> </ng-container> 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 index 77d4f009f9..e3daf9093e 100644 --- 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 @@ -19,13 +19,15 @@ <div class="row d-flex p-2 m-2"> - <div class="form-check"> - <input class="form-check-input" type="checkbox" - [(ngModel)]="showFastAdd" id="fast-add-item"/> - <label class="form-check-label" for="fast-add-item"> - Add Item - </label> - </div> + <ng-container *ngIf="recordType === 'biblio'"> + <div class="form-check"> + <input class="form-check-input" type="checkbox" + [(ngModel)]="showFastAdd" id="fast-add-item"/> + <label class="form-check-label" for="fast-add-item"> + Add Item + </label> + </div> + </ng-container> <ng-container *ngIf="showFastAdd"> <div class="form-inline"> @@ -65,7 +67,6 @@ [disabled]="record && record.deleted" i18n>Save Changes</button> </div> - <ng-container *ngIf="dataSaving"> <div class="row mt-5"> <div class="offset-lg-3 col-lg-6"> 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 index 7c0a224f45..157ff0abc4 100644 --- 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 @@ -16,7 +16,7 @@ import {MarcEditContext} from './editor-context'; import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap'; import {HoldingsService} from '@eg/staff/share/holdings/holdings.service'; -interface MarcSavedEvent { +export interface MarcSavedEvent { marcXml: string; bibSource?: number; recordId?: number; @@ -42,14 +42,24 @@ export class MarcEditorComponent implements OnInit { @Input() recordType: 'biblio' | 'authority' = 'biblio'; + _pendingRecordId: number; @Input() set recordId(id: number) { - if (!id) { return; } if (this.record && this.record.id === id) { return; } - this.fromId(id); + + // Avoid fetching the record by ID before OnInit since we may + // not yet know our recordType. + if (this.initCalled) { + this._pendingRecordId = null; + this.fromId(id); + + } else { + // fetch later in OnInit + this._pendingRecordId = id; + } } get recordId(): number { - return this.record ? this.record.id : null; + return this.record ? this.record.id : this._pendingRecordId; } @Input() set recordXml(xml: string) { @@ -86,6 +96,7 @@ export class MarcEditorComponent implements OnInit { fastItemLabel: string; fastItemBarcode: string; showFastAdd: boolean; + initCalled = false; constructor( private evt: EventService, @@ -107,11 +118,17 @@ export class MarcEditorComponent implements OnInit { ngOnInit() { + this.initCalled = true; + this.context.recordType = this.recordType; this.store.getItem('cat.marcedit.flateditor').then( useFlat => this.editorTab = useFlat ? 'flat' : 'rich'); + if (!this.record && this.recordId) { + this.fromId(this.recordId); + } + if (this.recordType !== 'biblio') { return; } this.pcrud.retrieveAll('cbs').subscribe( @@ -247,13 +264,15 @@ export class MarcEditorComponent implements OnInit { } fromId(id: number): Promise<any> { - return this.pcrud.retrieve('bre', id) - .toPromise().then(bib => { - this.context.record = new MarcRecord(bib.marc()); + const idlClass = this.recordType === 'authority' ? 'are' : 'bre'; + + return this.pcrud.retrieve(idlClass, id) + .toPromise().then(rec => { + this.context.record = new MarcRecord(rec.marc()); this.record.id = id; - this.record.deleted = bib.deleted() === 't'; - if (bib.source()) { - this.sourceSelector.applyEntryId(+bib.source()); + this.record.deleted = rec.deleted() === 't'; + if (idlClass === 'bre' && rec.source()) { + this.sourceSelector.applyEntryId(+rec.source()); } }); } 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 index 531822bf02..72381e9db0 100644 --- 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 @@ -40,7 +40,7 @@ <ng-template #postSubfieldsChunk let-field="field"> <ng-container *ngIf="isControlledBibTag(field.tag)"> - <button class="btn btn-sm btn-info link-button" + <button class="btn btn-sm btn-outline-info link-button" i18n-title title="Create authority link" (click)="openLinkerDialog(field)"> <span class="material-icons">link</span> @@ -155,7 +155,7 @@ </eg-marc-editable-content> <ng-container *ngIf="field.tag === '007'"> - <button class="btn btn-sm btn-info link-button" + <button class="btn btn-sm btn-outline-info link-button" i18n-title title="Open physical characteristics wizard" (click)="openPhysCharDialog(field)"> <span class="material-icons">launch</span> diff --git a/Open-ILS/src/templates/staff/navbar.tt2 b/Open-ILS/src/templates/staff/navbar.tt2 index 1028f42bb7..220c3d1d1f 100644 --- a/Open-ILS/src/templates/staff/navbar.tt2 +++ b/Open-ILS/src/templates/staff/navbar.tt2 @@ -342,7 +342,7 @@ </a> </li> <li> - <a href="./cat/catalog/retrieve_by_authority_id" target="_self"> + <a href="/eg2/staff/cat/authority/edit" target="_self"> <span class="glyphicon glyphicon-file"></span> [% l('Retrieve Authority Record by ID') %] </a>