LP#1793005 Angular6 Hatch support / printer settings
authorBill Erickson <berickxx@gmail.com>
Mon, 17 Sep 2018 15:26:11 +0000 (11:26 -0400)
committerBill Erickson <berickxx@gmail.com>
Wed, 24 Apr 2019 19:51:27 +0000 (15:51 -0400)
Adds support for Hatch print communication to the Angular(6) app.
Migrates the print preferences settings (eg.print.config.*) from
in-Hatch settings to server-stored workstation settings.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: blake <blake@mobiusconsortium.org>

Open-ILS/src/eg2/src/app/common.module.ts
Open-ILS/src/eg2/src/app/share/print/hatch.service.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/share/print/print.component.ts
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.html
Open-ILS/src/eg2/src/app/staff/sandbox/sandbox.component.ts
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-settings.sql [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/services/hatch.js

index ec06a91..f1d4467 100644 (file)
@@ -14,6 +14,7 @@ They do not have to be added to the providers list.
 
 // consider moving these to core...
 import {FormatService, FormatValuePipe} from '@eg/core/format.service';
+import {HatchService} from '@eg/share/print/hatch.service';
 import {PrintService} from '@eg/share/print/print.service';
 
 // Globally available components
@@ -67,6 +68,7 @@ export class EgCommonModule {
             providers: [
                 DatePipe,
                 CurrencyPipe,
+                HatchService,
                 PrintService,
                 FormatService
             ]
diff --git a/Open-ILS/src/eg2/src/app/share/print/hatch.service.ts b/Open-ILS/src/eg2/src/app/share/print/hatch.service.ts
new file mode 100644 (file)
index 0000000..0150887
--- /dev/null
@@ -0,0 +1,109 @@
+import {Injectable, EventEmitter} from '@angular/core';
+
+export class HatchMessage {
+    msgid: number;
+    resolver: (HatchMessage) => void; // promise resolver
+    rejector: (HatchMessage) => void; // promise rejector
+    status: number;
+    message: string; // error message
+    from: string;
+    action: string;
+    settings: any;
+    content: string;
+    // Response from Hatch.
+    response: any;
+    contentType: string;
+    showDialog: boolean;
+
+    constructor(hash: any) {
+        if (hash) {
+            Object.keys(hash).forEach(key => this[key] = hash[key]);
+        }
+    }
+}
+
+@Injectable()
+export class HatchService {
+
+    isAvailable: boolean;
+    msgId: number;
+    messages: {[msgid:number]: HatchMessage};
+
+    constructor() {
+        this.isAvailable = null;
+        this.messages = {};
+        this.msgId = 1;
+    }
+
+    connect(): boolean {
+
+        if (this.isAvailable !== null) {
+            return this.isAvailable;
+        }
+
+        // When the Hatch extension loads, it tacks an attribute onto
+        // the top-level documentElement to indicate it's available.
+        if (!window.document.documentElement.getAttribute('hatch-is-open')) {
+            console.warn('Could not connect to Hatch');
+            return this.isAvailable = false;
+        }
+
+        window.addEventListener('message', event => {
+
+            // We only accept messages from our own content script.
+            if (event.source !== window) { return; }
+
+            // We only care about messages from the Hatch extension.
+            if (event.data && event.data.from === 'extension') {
+
+                // Avoid logging full Hatch responses. they can get large.
+                console.debug(
+                    `Hatch responded to message ID ${event.data.msgid}`);
+
+                this.handleResponse(event.data);
+            }
+        }); 
+
+        return this.isAvailable = true;
+    }
+
+    // Send a request from the browser to Hatch.
+    sendRequest(msg: HatchMessage): Promise<HatchMessage> {
+        if (this.isAvailable === false) {
+            return Promise.reject('Hatch is not connected');
+        }
+
+        msg.msgid = this.msgId++;
+        msg.from = 'page';
+        this.messages[msg.msgid] = msg;
+        window.postMessage(msg, window.location.origin);
+
+        return new Promise((resolve, reject) => {
+            msg.resolver = resolve;
+            msg.rejector = reject;
+        });
+    }
+
+    // Handle the data sent back to the browser from Hatch.
+    handleResponse(data: any) {
+
+        const msg = this.messages[data.msgid]; 
+        if (!msg) {
+            console.warn(`No Hatch request found with ID ${data.msgid}`);
+            return;
+        }
+
+        delete this.messages[data.msgid];
+        msg.response = data.content;
+        msg.message = data.message;
+        msg.status = Number(data.status);
+
+        if (msg.status === 200) {
+            msg.resolver(msg);
+        } else {
+            console.error(`Hatch request returned status ${msg.status}`, msg);
+            msg.rejector(msg);
+        }
+    }
+}
+
index 4f69949..e7754ab 100644 (file)
@@ -1,6 +1,8 @@
 import {Component, OnInit, TemplateRef, ElementRef, Renderer2} from '@angular/core';
 import {PrintService, PrintRequest} from './print.service';
 import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {HatchService, HatchMessage} from './hatch.service';
 
 @Component({
     selector: 'eg-print',
@@ -26,6 +28,8 @@ export class PrintComponent implements OnInit {
         private renderer: Renderer2,
         private elm: ElementRef,
         private store: StoreService,
+        private serverStore: ServerStoreService,
+        private hatch: HatchService,
         private printer: PrintService) {
         this.isPrinting = false;
         this.printQueue = [];
@@ -67,7 +71,7 @@ export class PrintComponent implements OnInit {
             return;
         }
 
-        if (printReq.text && true /* !this.hatch.isActive */) {
+        if (printReq.text && !this.useHatch()) {
             // Insert HTML into the browser DOM for in-browser printing only.
 
             if (printReq.contentType === 'text/plain') {
@@ -109,7 +113,7 @@ export class PrintComponent implements OnInit {
             show_dialog: printReq.showDialog
         });
 
-        if (0 /* this.hatch.isActive */) {
+        if (this.useHatch()) {
             this.printViaHatch(printReq);
         } else {
             // Here the needed HTML is already in the page.
@@ -117,17 +121,32 @@ export class PrintComponent implements OnInit {
         }
     }
 
+    useHatch(): boolean {
+        return this.store.getLocalItem('eg.hatch.enable.printing')
+            && this.hatch.connect();
+    }
+
     printViaHatch(printReq: PrintRequest) {
 
         // Send a full HTML document to Hatch
         const html = `<html><body>${printReq.text}</body></html>`;
 
-        /*
-        this.hatch.print({
-            printContext: printReq.printContext,
-            content: html
+        this.serverStore.getItem(`eg.print.config.${printReq.printContext}`)
+        .then(config => {
+
+            const msg = new HatchMessage({
+                action: 'print',
+                content: html,
+                settings: config || {},
+                contentType: 'text/html',
+                showDialog: printReq.showDialog
+            });
+
+            this.hatch.sendRequest(msg).then(
+                ok  => console.debug('Print request succeeded'),
+                err => console.warn('Print request failed', err)
+            );
         });
-        */
     }
 }
 
index 84e127e..54cd87d 100644 (file)
 <button class="btn btn-secondary" (click)="doPrint()">Test Print</button>
 <ng-template #printTemplate let-context>Hello, {{context.world}}!</ng-template>
 
+<button class="btn btn-secondary" (click)="printWithDialog()">Print with dialog</button>
+
 <br/><br/>
 <div class="row">
   <div class="col-lg-3">
index 9b058cd..543e3cb 100644 (file)
@@ -187,7 +187,15 @@ export class SandboxComponent implements OnInit {
             text: '<b>hello</b>',
             printContext: 'default'
         });
+    }
 
+    printWithDialog() {
+        this.printer.print({
+            template: this.printTemplate,
+            contextData: {world : this.world},
+            printContext: 'default',
+            showDialog: true
+        });
     }
 
     changeDate(date) {
index 3c8a257..928cdbe 100644 (file)
@@ -19874,6 +19874,45 @@ INSERT INTO config.org_unit_setting_type
         'bool'
     );
 
+
+INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
+VALUES (
+    'eg.print.config.default', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.default',
+        'Print config for default context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.receipt', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.receipt',
+        'Print config for receipt context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.label', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.label',
+        'Print config for label context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.mail', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.mail',
+        'Print config for mail context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.offline', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.offline',
+        'Print config for offline context',
+        'cwst', 'label'
+    )
+);
+
 INSERT INTO config.usr_activity_type 
     (id, ewhat, ehow, egroup, enabled, transient, label)
 VALUES (
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-settings.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.data.hatch-settings.sql
new file mode 100644 (file)
index 0000000..15d9513
--- /dev/null
@@ -0,0 +1,43 @@
+BEGIN;
+
+--SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
+VALUES (
+    'eg.print.config.default', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.default',
+        'Print config for default context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.receipt', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.receipt',
+        'Print config for receipt context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.label', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.label',
+        'Print config for label context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.mail', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.mail',
+        'Print config for mail context',
+        'cwst', 'label'
+    )
+), (
+    'eg.print.config.offline', 'gui', 'object',
+    oils_i18n_gettext (
+        'eg.print.config.offline',
+        'Print config for offline context',
+        'cwst', 'label'
+    )
+);
+
+COMMIT;
index 467091a..5b6f923 100644 (file)
@@ -200,11 +200,11 @@ angular.module('egCoreMod')
     }
 
     service.getPrintConfig = function(context) {
-        return service.getRemoteItem('eg.print.config.' + context);
+        return service.getItem('eg.print.config.' + context);
     }
 
     service.setPrintConfig = function(context, config) {
-        return service.setRemoteItem('eg.print.config.' + context, config);
+        return service.setItem('eg.print.config.' + context, config);
     }
 
     service.getPrinterOptions = function(name) {