LP1881607 Angular catalog e-resource links display user/berick/lp1881607-ang-cat-eresource-links
authorBill Erickson <berickxx@gmail.com>
Mon, 1 Jun 2020 16:32:21 +0000 (12:32 -0400)
committerBill Erickson <berickxx@gmail.com>
Mon, 1 Jun 2020 16:35:29 +0000 (12:35 -0400)
Display electronic resource links (MARC 856's) in the Angular staff
catalog.  The extraction logic, which matches the TPAC, has been put
into its own API.

To test in concernto, navigate to:

/eg2/staff/catalog/record/208

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/share/catalog/bib-record.service.ts
Open-ILS/src/eg2/src/app/staff/catalog/record/record.component.ts
Open-ILS/src/eg2/src/app/staff/share/bib-summary/bib-summary.component.html
Open-ILS/src/perlmods/lib/OpenILS/Application/AppUtils.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Cat/Authority.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm

index 83d66c0..5aa739f 100644 (file)
@@ -17,6 +17,11 @@ export const NAMESPACE_MAPS = {
 export const HOLDINGS_XPATH =
     '/holdings:holdings/holdings:counts/holdings:count';
 
+interface EResourceUrl {
+    href: string;
+    note: string;
+    label: string;
+}
 
 export class BibRecordSummary {
     id: number; // == record.id() for convenience
@@ -32,6 +37,7 @@ export class BibRecordSummary {
     bibCallNumber: string;
     net: NetService;
     displayHighlights: {[name: string]: string | string[]} = {};
+    eResourceUrls: EResourceUrl[] = null;
 
     constructor(record: IdlObject, orgId: number, orgDepth: number) {
         this.id = Number(record.id());
@@ -81,6 +87,25 @@ export class BibRecordSummary {
         });
     }
 
+    // True if this record has any E-Resource URLs.
+    // this.eResourceUrls starts null so we can detect whether a
+    // retrieval attempt has been made.
+    hasEResourceUrls(): boolean {
+        return this.eResourceUrls && this.eResourceUrls.length > 0;
+    }
+
+    getEResourceUrls(): Promise<EResourceUrl[]> {
+        if (this.hasEResourceUrls()) {
+            return Promise.resolve(this.eResourceUrls);
+        }
+
+        return this.net.request(
+            'open-ils.search',
+            'open-ils.search.biblio.record.resource_urls.retrieve',
+            this.id
+        ).toPromise().then(urlInfo => this.eResourceUrls = urlInfo.urls);
+    }
+
     // Get -> Set -> Return bib hold count
     getHoldCount(): Promise<number> {
 
index b900ea8..f94ae5c 100644 (file)
@@ -148,6 +148,7 @@ export class RecordComponent implements OnInit {
             this.summary =
                 this.staffCat.currentDetailRecordSummary = summary;
             this.bib.fleshBibUsers([summary.record]);
+            this.summary.getEResourceUrls();
         });
     }
 
index 3fd9558..84a0226 100644 (file)
               <div class="flex-1">{{summary.record.edit_date() | date:'short'}}</div>
             </div>
           </li>
+          <ng-container *ngIf="expand && summary.hasEResourceUrls()">
+            <li class="list-group-item" *ngFor="let url of summary.eResourceUrls">
+              <div class="d-flex">
+                <div class="flex-1 font-weight-bold" i18n>
+                  Electronic Resource:
+                </div>
+                <div class="flex-5">
+                  <a href="{{url.href}}">{{url.label}}</a>
+                </div>
+                <div class="flex-4">{{url.note}}</div>
+              </div>
+            </li>
+          </ng-container>
         </ul>
       </div>
     </div><!-- col -->
index f44e6ef..16b1eb0 100644 (file)
@@ -1,5 +1,7 @@
 package OpenILS::Application::AppUtils;
 use strict; use warnings;
+use MARC::Record;
+use MARC::File::XML (BinaryEncoding => 'utf8', RecordFormat => 'USMARC');
 use OpenILS::Application;
 use base qw/OpenILS::Application/;
 use OpenSRF::Utils::Cache;
@@ -23,7 +25,7 @@ use Digest::MD5 qw(md5_hex);
 # Pile of utilty methods used accross applications.
 # ---------------------------------------------------------------------------
 my $cache_client = "OpenSRF::Utils::Cache";
-
+my $MARC_NAMESPACE = 'http://www.loc.gov/MARC21/slim';
 
 # ---------------------------------------------------------------------------
 # on sucess, returns the created session, on failure throws ERROR exception
@@ -2413,5 +2415,16 @@ sub verify_migrated_user_password {
 }
 
 
+# generate a MARC XML document from a MARC XML string
+sub marc_xml_to_doc {
+    my ($class, $xml) = @_;
+    my $marc_doc = XML::LibXML->new->parse_string($xml);
+    $marc_doc->documentElement->setNamespace($MARC_NAMESPACE, 'marc', 1);
+    $marc_doc->documentElement->setNamespace($MARC_NAMESPACE);
+    return $marc_doc;
+}
+
+
+
 1;
 
index 82d04f5..746f528 100644 (file)
@@ -11,19 +11,13 @@ use OpenILS::Utils::Fieldmapper;
 use OpenILS::Const qw/:const/;
 use OpenILS::Event;
 my $U = 'OpenILS::Application::AppUtils';
-my $MARC_NAMESPACE = 'http://www.loc.gov/MARC21/slim';
-
 
 # generate a MARC XML document from a MARC XML string
 sub marc_xml_to_doc {
     my $xml = shift;
-    my $marc_doc = XML::LibXML->new->parse_string($xml);
-    $marc_doc->documentElement->setNamespace($MARC_NAMESPACE, 'marc', 1);
-    $marc_doc->documentElement->setNamespace($MARC_NAMESPACE);
-    return $marc_doc;
+    return $U->marc_xml_to_doc($xml);
 }
 
-
 __PACKAGE__->register_method(
     method  => 'import_authority_record',
     api_name    => 'open-ils.cat.authority.record.import',
index 159ecd7..04c7673 100644 (file)
@@ -2743,6 +2743,73 @@ sub mk_copy_query {
     return $query;
 }
 
+__PACKAGE__->register_method(
+    method    => 'record_urls',
+    api_name  => 'open-ils.search.biblio.record.resource_urls.retrieve',
+    argc      => 1,
+    stream    => 1,
+    signature => {
+        desc   => q/Returns bib record 856 URL content./,
+        params => [
+            {desc => 'Record ID or Array of Record IDs', type => 'number or array'}
+        ],
+        return => {
+            desc => 'Stream of URL objects, one collection object per record',
+            type => 'object'
+        }
+    }
+);
+
+sub record_urls {
+    my ($self, $client, $record_ids) = @_;
+
+    $record_ids = [$record_ids] unless ref $record_ids eq 'ARRAY';
+
+    my $e = new_editor();
+
+    for my $record_id (@$record_ids) {
+        my $bib = $e->retrieve_biblio_record_entry($record_id)
+            or return $e->event;
+
+        my $marc_doc = $U->marc_xml_to_doc($bib->marc);
+
+        # Logic copied from TPAC misc_utils.tts
+        my @urls;
+
+        for my $node ($marc_doc->findnodes(
+            '//*[@tag="856" and @ind1="4" and (@ind2="0" or @ind2="1")]')) {
+
+            # asset.uri's
+            next if $node->findnodes('./*[@code="9" or @code="w" or @code="n"]');
+
+            my $url = {};
+            my ($label) = $node->findnodes('./*[@code="y"]');
+            my ($notes) = $node->findnodes('./*[@code="z" or @code="3"]');
+
+            my $first = 1;
+            for my $href_node ($node->findnodes('./*[@code="u"]')) {
+                next unless $href_node;
+
+                # it's possible for multiple $u's to exist within 1 856 tag.
+                # in that case, honor the label/notes data for the first $u, but
+                # leave any subsequent $u's as unadorned href's.
+                # use href/link/note keys to be consistent with args.uri's
+
+                my $href = $href_node->textContent;
+                push(@urls, {
+                    href => $href,
+                    label => ($first && $label) ?  $label->textContent : $href,
+                    note => ($first && $notes) ? $notes->textContent : ''
+                });
+                $first = 0;
+            }
+        }
+
+        $client->respond({id => $record_id, urls => \@urls});
+    }
+
+    return undef;
+}
 
 1;