server template : angular admin ui
authorBill Erickson <berickxx@gmail.com>
Thu, 18 Apr 2019 20:05:07 +0000 (16:05 -0400)
committerBill Erickson <berickxx@gmail.com>
Fri, 19 Apr 2019 17:31:22 +0000 (13:31 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/eg2/src/app/share/print/print.service.ts
Open-ILS/src/eg2/src/app/staff/admin/server/print-template.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/admin/server/print-template.component.ts
Open-ILS/src/perlmods/lib/OpenILS/WWW/PrintTemplate.pm

index 135cbb1..d70568e 100644 (file)
@@ -12817,7 +12817,7 @@ SELECT  usr,
                oils_obj:fieldmapper="config::print_template" 
                oils_persist:tablename="config.print_template" 
                reporter:label="Print Templates">
-               <fields oils_persist:primary="name">
+               <fields oils_persist:primary="id">
                        <field name="id" reporter:datatype="id"  reporter:selector="label"/>
                        <field name="name" reporter:datatype="text" oils_obj:required="true"/>
                        <field name="label" reporter:datatype="text" oils_obj:required="true" oils_persist:i18n="true"/>
index 54dcd9c..d4d0263 100644 (file)
@@ -11,6 +11,8 @@ const PRINT_TEMPLATE_PATH = '/print_template';
 export interface PrintRequest {
     template?: TemplateRef<any>;
     templateName?: string;
+    templateOwner?: number; // org unit ID, follows ancestors
+    templateId?: number; // useful for testing templates
     contextData?: any;
     text?: string;
     printContext: string;
@@ -60,7 +62,15 @@ export class PrintService {
         const formData: FormData = new FormData();
 
         formData.append('ses', this.auth.token());
-        formData.append('template_name', printReq.templateName);
+        if (printReq.templateName) {
+            formData.append('template_name', printReq.templateName);
+        }
+        if (printReq.templateId) {
+            formData.append('template_id', '' + printReq.templateId);
+        }
+        if (printReq.templateOwner) {
+            formData.append('owner', '' + printReq.templateOwner);
+        }
         formData.append('template_data', js2JSON(printReq.contextData));
         formData.append('locale', this.locale.currentLocaleCode());
 
diff --git a/Open-ILS/src/eg2/src/app/staff/admin/server/print-template.component.html b/Open-ILS/src/eg2/src/app/staff/admin/server/print-template.component.html
new file mode 100644 (file)
index 0000000..c061cc7
--- /dev/null
@@ -0,0 +1,85 @@
+
+<eg-title i18n-prefix prefix="Print Template Administration"></eg-title>
+<eg-staff-banner bannerText="Print Template Administration" i18n-bannerText>
+</eg-staff-banner>
+
+<div class="row mb-3">
+  <div class="col-lg-3">
+    <div class="input-group">
+      <div class="input-group-prepend">
+        <span class="input-group-text" i18n>Owner</span>
+      </div>
+      <eg-org-select 
+        [limitPerms]="['ADMIN_PRINT_TEMPLATE']"
+        [initialOrg]="contextOrg"
+        (onChange)="orgOnChange($event)">
+      </eg-org-select>
+    </div>
+  </div>
+  <div class="col-lg-3">
+    <div class="input-group">
+      <div class="input-group-prepend">
+        <span class="input-group-text" i18n>Template</span>
+      </div>
+      <eg-combobox [entries]="entries" #templateSelector
+        (onChange)="selectTemplate($event ? $event.id : null)">
+      </eg-combobox>
+    </div>
+  </div>
+  <div class="col-lg-3" *ngIf="localeEntries.length > 0">
+    <div class="input-group">
+      <div class="input-group-prepend">
+        <span class="input-group-text" i18n>Locale</span>
+      </div>
+      <eg-combobox [entries]="localeEntries"
+        [startId]="localeCode"
+        (onChange)="localeOnChange($event ? $event.id : null)">
+      </eg-combobox>
+    </div>
+  </div>
+</div>
+
+<ngb-tabset *ngIf="template" #tabs (tabChange)="onTabChange($event)">
+  <ngb-tab title="Template Info" i18n-title id='info'>
+    <ng-template ngbTabContent>
+    </ng-template>
+  </ngb-tab>
+  <ngb-tab title="Template" i18n-title id='template'>
+    <ng-template ngbTabContent>
+      <div class="row">
+        <div class="col-lg-6 d-flex justify-content-end mt-3">
+          <button class="btn btn-success" (click)="applyChanges()" i18n>
+            Save and Refresh 
+          </button>
+        </div>
+      </div>
+      <div class="row mt-2">
+        <div class="col-lg-6">
+         <h4 i18n>Template</h4>
+         <textarea rows="30" class="form-control"
+           spellcheck="false"
+           [ngModel]="template.template()"
+           (ngModelChange)="template.template($event); template.ischanged(true)">
+         </textarea>
+        </div>
+        <div class="col-lg-6">
+          <h4 i18n>Preview</h4>
+          <div class="border border-dark w-100" id="template-preview-pane">
+          </div>
+          <h4 class="mt-3" i18n>Compiled Content</h4>
+          <div class="border border-dark w-100">
+            <pre>{{compiledContent}}</pre>
+          </div>
+        </div>
+      </div>
+    </ng-template>
+  </ngb-tab>
+  <ngb-tab title="Sample Data" i18n-title id='data'>
+    <ng-template ngbTabContent>
+      <textarea rows="20" [(ngModel)]="sampleJson" 
+        spellcheck="false" class="form-control">
+      </textarea>
+    </ng-template>
+  </ngb-tab>
+</ngb-tabset>
+
index aa482e5..c692f6e 100644 (file)
 import {Component, OnInit,  ViewChild, TemplateRef} from '@angular/core';
 import {ActivatedRoute} from '@angular/router';
-import {IdlService} from '@eg/core/idl.service';
-import {FmFieldOptions} from '@eg/share/fm-editor/fm-editor.component';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {OrgService} from '@eg/core/org.service';
+import {ComboboxComponent, ComboboxEntry
+    } from '@eg/share/combobox/combobox.component';
+import {PrintService} from '@eg/share/print/print.service';
+import {LocaleService} from '@eg/core/locale.service';
+import {NgbTabset, NgbTabChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+
+const SAMPLE_JSON_DATA: any = {
+    patron: {
+        first_given_name: 'Leela',
+        second_given_name: '',
+        family_name: 'Turanga'
+    },
+    address: {
+        street1: '123 Pineapple Rd',
+        city: 'North Southland',
+        state: 'HI',
+        post_code: 12321
+    }
+};
+
+const SAMPLE_TEMPLATE_DATA: any = {
+    'address-label': {
+        patron: SAMPLE_JSON_DATA.patron,
+        address: SAMPLE_JSON_DATA.address
+    }
+};
+
 
 /**
- * Generic IDL class editor page.
+ * Print Template Admin Page
  */
 
 @Component({
-    template: `
-      <eg-title i18n-prefix prefix="{{classLabel}} Administration"></eg-title>
-      <ng-template #templateTemplate let-record="record">
-        <textarea class="form-control" rows="20" [ngModel]="record.template()"
-          (ngModelChange)="record.template($event)"></textarea>
-      </ng-template>
-      <eg-staff-banner bannerText="{{classLabel}} Configuration" i18n-bannerText>
-      </eg-staff-banner>
-      <eg-admin-page idlClass="cpt" [fieldOptions]="fieldOptions"></eg-admin-page>
-    `
+    templateUrl: 'print-template.component.html'
 })
 
 export class PrintTemplateComponent implements OnInit {
 
-    idlClass: string;
-    classLabel: string;
-    persistKeyPfx: string;
-    fieldOptions: {[field: string]: FmFieldOptions};
+    contextOrg: IdlObject;
+    entries: ComboboxEntry[];
+    template: IdlObject;
+    sampleJson: string;
+    localeCode: string;
+    localeEntries: ComboboxEntry[];
+    compiledContent: string;
+
+    @ViewChild('templateSelector') templateSelector: ComboboxComponent;
+    @ViewChild('tabs') tabs: NgbTabset;
 
-    @ViewChild('templateTemplate') templateTemplate: TemplateRef<any>;
+    // Define some sample data that can be used for various templates
 
     constructor(
         private route: ActivatedRoute,
-        private idl: IdlService) {}
+        private idl: IdlService,
+        private org: OrgService,
+        private pcrud: PcrudService,
+        private auth: AuthService,
+        private locale: LocaleService,
+        private printer: PrintService
+    ) {
+        this.entries = [];
+        this.localeEntries = [];
+    }
 
     ngOnInit() {
+        this.contextOrg = this.org.get(this.auth.user().ws_ou());
+        this.localeCode = this.locale.currentLocaleCode();
+        this.locale.supportedLocales().subscribe(
+            l => this.localeEntries.push({id: l.code(), label: l.name()}));
+        this.setTemplateInfo();
+    }
+
+    onTabChange(evt: NgbTabChangeEvent) {
+        if (evt.nextId === 'template') {
+            this.refreshPreview();
+        }
+    }
+
+
+    container(): any {
+        // Only present when its tab is visible
+        return document.getElementById('template-preview-pane');
+    }
+
+    orgOnChange(org: IdlObject) {
+        this.contextOrg = org;
+        this.setTemplateInfo();
+    }
+
+    localeOnChange(code: string) {
+        if (code) {
+            this.localeCode = code;
+            this.setTemplateInfo();
+        }
+    }
+
+    // Fetch name/id for all templates in range.
+    // Avoid fetching the template content until needed.
+    setTemplateInfo() {
+        this.entries = [];
+        this.template = null;
+        this.templateSelector.applyEntryId(null);
+        this.compiledContent = '';
+
+        this.pcrud.search('cpt', 
+            {
+                owner: this.contextOrg.id(),
+                locale: this.localeCode
+            },
+            {
+                select: {cpt: ['id', 'label']}, 
+                order_by: {cpt: 'label'}
+            }
+        ).subscribe(tmpl =>
+            this.entries.push({id: tmpl.id(), label: tmpl.label()})
+        );
+    }
 
-        this.fieldOptions = {
-            template: {
-                customTemplate: {
-                    template: this.templateTemplate
-                }
+    selectTemplate(id: number) {
+        this.pcrud.retrieve('cpt', id).subscribe(t => {
+            this.template = t;
+            const data = SAMPLE_TEMPLATE_DATA[t.name()];
+            if (data) {
+                this.sampleJson = JSON.stringify(data, null, 2);
             }
-        };
+        });
+    }
+
+    refreshPreview() {
+        if (!this.sampleJson) return;
+        this.compiledContent = '';
+
+        let data;
+        try {
+            data = JSON.parse(this.sampleJson);  
+        } catch (E) {
+            // TODO: i18n/AlertDialog
+            alert('Invalid Sample Data JSON');
+        }
+
+        this.printer.compileRemoteTemplate({
+            templateId: this.template.id(),
+            contextData: data,
+            printContext: 'default' // required, has no impact here
+
+        }).then(response => {
+            
+            this.compiledContent = response.content;
+            if (response.contentType === 'text/html') {
+                this.container().innerHTML = response.content;
+            } else {
+                // Assumes text/plain or similar
+                this.container().innerHTML = '<pre>' + response.content + '</pre>';
+            }
+        });
+    }
 
-        this.idlClass = 'cpt';
-        const classDef = this.idl.classes[this.idlClass];
-        this.classLabel = classDef.label;
+    applyChanges() {
+        this.container().innerHTML = '';
+        this.pcrud.update(this.template).toPromise()
+            .then(() =>this.refreshPreview());
     }
 }
 
index 194afd3..f8209d4 100644 (file)
@@ -76,14 +76,15 @@ sub handler {
     # Let pcrud handle the authz
     $e->personality('open-ils.pcrud');
 
-    my $owner = $e->requestor->ws_ou;
+    my $template_id = $cgi->param('template_id');
+    my $owner = $cgi->param('owner') || $e->requestor->ws_ou;
     my $locale = $cgi->param('locale') || 'en-US';
     my $template_name = $cgi->param('template_name');
     my $template_data = $cgi->param('template_data');
 
-    return Apache2::Const::FORBIDDEN unless $template_name;
+    return Apache2::Const::FORBIDDEN unless $template_name || $template_id;
 
-    my $template = find_template($e, $template_name, $locale, $owner)
+    my $template = find_template($e, $template_id, $template_name, $locale, $owner)
         or return Apache2::Const::NOT_FOUND;
 
     my $output = '';
@@ -123,7 +124,13 @@ sub handler {
 # Find the template closest to the specific org unit owner.
 my %template_cache;
 sub find_template {
-    my ($e, $name, $locale, $owner) = @_;
+    my ($e, $template_id, $name, $locale, $owner) = @_;
+
+    if ($template_id) {
+        # Requesting by ID, generally used for testing, 
+        # always pulls the latest value.
+        return $e->retrieve_config_print_template($template_id);
+    }
 
     return  $template_cache{$owner}{$name}{$locale}
         if  $template_cache{$owner} &&