From: Bill Erickson Date: Thu, 30 Nov 2017 12:59:21 +0000 (-0500) Subject: LP#626157 Ang2 experiments X-Git-Url: https://old-git.evergreen-ils.org/?a=commitdiff_plain;h=5b62351063b76701c595aa3cc47fe69a4d8b58a6;p=working%2FEvergreen.git LP#626157 Ang2 experiments Signed-off-by: Bill Erickson --- diff --git a/Open-ILS/webby-src/src/app/base.module.ts b/Open-ILS/webby-src/src/app/base.module.ts index 0bd2a07278..7f2df7c7ec 100644 --- a/Open-ILS/webby-src/src/app/base.module.ts +++ b/Open-ILS/webby-src/src/app/base.module.ts @@ -14,13 +14,13 @@ import {EgBaseRoutingModule} from './routing.module'; import {WelcomeComponent} from './welcome.component'; // Import and 'provide' globally required services. -import {EgEventService} from '@eg/core/event.service'; -import {EgStoreService} from '@eg/core/store.service'; -import {EgIdlService} from '@eg/core/idl.service'; +import {EgEventService} from '@eg/core/event'; +import {EgStoreService} from '@eg/core/store'; +import {EgIdlService} from '@eg/core/idl'; import {EgNetService} from '@eg/core/net.service'; -import {EgAuthService} from '@eg/core/auth.service'; -import {EgPcrudService} from '@eg/core/pcrud.service'; -import {EgOrgService} from '@eg/core/org.service'; +import {EgAuthService} from '@eg/core/auth'; +import {EgPcrudService} from '@eg/core/pcrud'; +import {EgOrgService} from '@eg/core/org'; @NgModule({ declarations: [ diff --git a/Open-ILS/webby-src/src/app/core/auth.service.ts b/Open-ILS/webby-src/src/app/core/auth.service.ts deleted file mode 100644 index 4a956a598e..0000000000 --- a/Open-ILS/webby-src/src/app/core/auth.service.ts +++ /dev/null @@ -1,239 +0,0 @@ -/** - * - */ -import { Injectable, EventEmitter } from '@angular/core'; -import { Observable } from 'rxjs/Rx'; -import { EgNetService } from './net.service'; -import { EgEventService, EgEvent } from './event.service'; -import { EgIdlService, EgIdlObject } from './idl.service'; -import { EgStoreService } from './store.service'; - -// Models a login instance. -class EgAuthUser { - user: EgIdlObject; - workstation: string; // workstation name - token: string; - authtime: number; - - constructor(token: string, authtime: number, workstation?: string) { - this.token = token; - this.workstation = workstation; - this.authtime = authtime; - } -} - -// Params required for calling the login() method. -interface EgAuthLoginArgs { - username: string, - password: string, - type: string, - workstation?: string -} - -export enum EgAuthWsState { - PENDING, - NOT_USED, - NOT_FOUND_SERVER, - NOT_FOUND_LOCAL, - VALID -}; - -@Injectable() -export class EgAuthService { - - private activeUser: EgAuthUser; - - // opChangeUser refers to the user that has been superseded during - // an op-change event. This use will become the activeUser once - // again, when the op-change cycle has completed. - private opChangeUser: EgAuthUser; - - workstationState: EgAuthWsState = EgAuthWsState.PENDING; - - redirectUrl: string; - - constructor( - private egEvt: EgEventService, - private egNet: EgNetService, - private egStore: EgStoreService - ) {} - - // - Accessor functions alway refer to the active user. - - user(): EgIdlObject { - return this.activeUser.user - }; - - // Workstation name. - workstation(): string { - return this.activeUser.workstation; - }; - - token(): string { - return this.activeUser ? this.activeUser.token : null; - }; - - authtime(): Number { - return this.activeUser.authtime - }; - - // NOTE: EgNetService emits an event if the auth session has expired. - testAuthToken(): Promise { - - this.activeUser = new EgAuthUser( - this.egStore.getLoginSessionItem('eg.auth.token'), - this.egStore.getLoginSessionItem('eg.auth.time') - ); - - if (!this.token()) return Promise.reject('no authtoken'); - - return new Promise( (resolve, reject) => { - this.egNet.request( - 'open-ils.auth', - 'open-ils.auth.session.retrieve', this.token() - ).subscribe( - user => { - // EgNetService interceps NO_SESSION events. - // We can only get here if the session is valid. - this.activeUser.user = user; - this.sessionPoll(); - resolve(); - }, - err => { reject(); } - ); - }); - } - - checkWorkstation(): void { - // TODO: - // Emits event on invalid workstation. - } - - login(args: EgAuthLoginArgs, isOpChange?: boolean): Promise { - - return new Promise((resolve, reject) => { - this.egNet.request('open-ils.auth', 'open-ils.auth.login', args) - .subscribe(res => { - this.handleLoginResponse(args, this.egEvt.parse(res), isOpChange) - .then( - ok => resolve(ok), - notOk => reject(notOk) - ); - }); - }); - } - - handleLoginResponse( - args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): Promise { - - switch (evt.textcode) { - case 'SUCCESS': - this.handleLoginOk(args, evt, isOpChange); - return Promise.resolve(); - - case 'WORKSTATION_NOT_FOUND': - console.error(`No such workstation "${args.workstation}"`); - this.workstationState = EgAuthWsState.NOT_FOUND_SERVER; - delete args.workstation; - return this.login(args, isOpChange); - - default: - console.error(`Login returned unexpected event: ${evt}`); - return Promise.reject('login failed'); - } - } - - // Stash the login data - handleLoginOk(args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): void { - - if (isOpChange) { - this.egStore.setLoginSessionItem('eg.auth.token.oc', this.token()); - this.egStore.setLoginSessionItem('eg.auth.time.oc', this.authtime()); - this.opChangeUser = this.activeUser; - } - - this.activeUser = new EgAuthUser( - evt.payload.authtoken, - evt.payload.authtime, - args.workstation - ); - - this.egStore.setLoginSessionItem('eg.auth.token', this.token()); - this.egStore.setLoginSessionItem('eg.auth.time', this.authtime()); - } - - undoOpChange(): Promise { - if (this.opChangeUser) { - this.deleteSession(); - this.activeUser = this.opChangeUser; - this.opChangeUser = null; - this.egStore.removeLoginSessionItem('eg.auth.token.oc'); - this.egStore.removeLoginSessionItem('eg.auth.time.oc'); - this.egStore.setLoginSessionItem('eg.auth.token', this.token()); - this.egStore.setLoginSessionItem('eg.auth.time', this.authtime()); - } - return this.testAuthToken(); - } - - sessionPoll(): void { - // TODO - } - - // Resolves if login workstation matches a workstation known to this - // browser instance. - verifyWorkstation(): Promise { - return new Promise((resolve, reject) => { - - if (!this.user()) { - this.workstationState = EgAuthWsState.PENDING; - reject(); - return; - } - - if (!this.user().wsid()) { - this.workstationState = EgAuthWsState.NOT_USED; - reject(); - return; - } - - this.egStore.getItem('eg.workstation.all') - .then(workstations => { - if (!workstations) workstations = []; - - let ws = workstations.filter( - w => {return w.id == this.user().wsid()})[0]; - - if (ws) { - this.activeUser.workstation = ws.name; - this.workstationState = EgAuthWsState.VALID; - resolve(); - } else { - this.workstationState = EgAuthWsState.NOT_FOUND_LOCAL; - reject(); - } - }); - }); - } - - deleteSession(): void { - if (this.token()) { - this.egNet.request( - 'open-ils.auth', - 'open-ils.auth.session.delete', this.token()) - .subscribe(x => console.log('logged out')) - } - } - - logout(broadcast?: boolean) { - - if (broadcast) { - // TODO - //this.authChannel.postMessage({action : 'logout'}); - } - - this.deleteSession(); - this.egStore.clearLoginSessionItems(); - this.activeUser = null; - this.opChangeUser = null; - } -} diff --git a/Open-ILS/webby-src/src/app/core/auth.ts b/Open-ILS/webby-src/src/app/core/auth.ts new file mode 100644 index 0000000000..6d94d80169 --- /dev/null +++ b/Open-ILS/webby-src/src/app/core/auth.ts @@ -0,0 +1,239 @@ +/** + * + */ +import { Injectable, EventEmitter } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { EgNetService } from './net.service'; +import { EgEventService, EgEvent } from './event'; +import { EgIdlService, EgIdlObject } from './idl'; +import { EgStoreService } from './store'; + +// Models a login instance. +class EgAuthUser { + user: EgIdlObject; + workstation: string; // workstation name + token: string; + authtime: number; + + constructor(token: string, authtime: number, workstation?: string) { + this.token = token; + this.workstation = workstation; + this.authtime = authtime; + } +} + +// Params required for calling the login() method. +interface EgAuthLoginArgs { + username: string, + password: string, + type: string, + workstation?: string +} + +export enum EgAuthWsState { + PENDING, + NOT_USED, + NOT_FOUND_SERVER, + NOT_FOUND_LOCAL, + VALID +}; + +@Injectable() +export class EgAuthService { + + private activeUser: EgAuthUser; + + // opChangeUser refers to the user that has been superseded during + // an op-change event. This use will become the activeUser once + // again, when the op-change cycle has completed. + private opChangeUser: EgAuthUser; + + workstationState: EgAuthWsState = EgAuthWsState.PENDING; + + redirectUrl: string; + + constructor( + private egEvt: EgEventService, + private egNet: EgNetService, + private egStore: EgStoreService + ) {} + + // - Accessor functions alway refer to the active user. + + user(): EgIdlObject { + return this.activeUser.user + }; + + // Workstation name. + workstation(): string { + return this.activeUser.workstation; + }; + + token(): string { + return this.activeUser ? this.activeUser.token : null; + }; + + authtime(): Number { + return this.activeUser.authtime + }; + + // NOTE: EgNetService emits an event if the auth session has expired. + testAuthToken(): Promise { + + this.activeUser = new EgAuthUser( + this.egStore.getLoginSessionItem('eg.auth.token'), + this.egStore.getLoginSessionItem('eg.auth.time') + ); + + if (!this.token()) return Promise.reject('no authtoken'); + + return new Promise( (resolve, reject) => { + this.egNet.request( + 'open-ils.auth', + 'open-ils.auth.session.retrieve', this.token() + ).subscribe( + user => { + // EgNetService interceps NO_SESSION events. + // We can only get here if the session is valid. + this.activeUser.user = user; + this.sessionPoll(); + resolve(); + }, + err => { reject(); } + ); + }); + } + + checkWorkstation(): void { + // TODO: + // Emits event on invalid workstation. + } + + login(args: EgAuthLoginArgs, isOpChange?: boolean): Promise { + + return new Promise((resolve, reject) => { + this.egNet.request('open-ils.auth', 'open-ils.auth.login', args) + .subscribe(res => { + this.handleLoginResponse(args, this.egEvt.parse(res), isOpChange) + .then( + ok => resolve(ok), + notOk => reject(notOk) + ); + }); + }); + } + + handleLoginResponse( + args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): Promise { + + switch (evt.textcode) { + case 'SUCCESS': + this.handleLoginOk(args, evt, isOpChange); + return Promise.resolve(); + + case 'WORKSTATION_NOT_FOUND': + console.error(`No such workstation "${args.workstation}"`); + this.workstationState = EgAuthWsState.NOT_FOUND_SERVER; + delete args.workstation; + return this.login(args, isOpChange); + + default: + console.error(`Login returned unexpected event: ${evt}`); + return Promise.reject('login failed'); + } + } + + // Stash the login data + handleLoginOk(args: EgAuthLoginArgs, evt: EgEvent, isOpChange: boolean): void { + + if (isOpChange) { + this.egStore.setLoginSessionItem('eg.auth.token.oc', this.token()); + this.egStore.setLoginSessionItem('eg.auth.time.oc', this.authtime()); + this.opChangeUser = this.activeUser; + } + + this.activeUser = new EgAuthUser( + evt.payload.authtoken, + evt.payload.authtime, + args.workstation + ); + + this.egStore.setLoginSessionItem('eg.auth.token', this.token()); + this.egStore.setLoginSessionItem('eg.auth.time', this.authtime()); + } + + undoOpChange(): Promise { + if (this.opChangeUser) { + this.deleteSession(); + this.activeUser = this.opChangeUser; + this.opChangeUser = null; + this.egStore.removeLoginSessionItem('eg.auth.token.oc'); + this.egStore.removeLoginSessionItem('eg.auth.time.oc'); + this.egStore.setLoginSessionItem('eg.auth.token', this.token()); + this.egStore.setLoginSessionItem('eg.auth.time', this.authtime()); + } + return this.testAuthToken(); + } + + sessionPoll(): void { + // TODO + } + + // Resolves if login workstation matches a workstation known to this + // browser instance. + verifyWorkstation(): Promise { + return new Promise((resolve, reject) => { + + if (!this.user()) { + this.workstationState = EgAuthWsState.PENDING; + reject(); + return; + } + + if (!this.user().wsid()) { + this.workstationState = EgAuthWsState.NOT_USED; + reject(); + return; + } + + this.egStore.getItem('eg.workstation.all') + .then(workstations => { + if (!workstations) workstations = []; + + let ws = workstations.filter( + w => {return w.id == this.user().wsid()})[0]; + + if (ws) { + this.activeUser.workstation = ws.name; + this.workstationState = EgAuthWsState.VALID; + resolve(); + } else { + this.workstationState = EgAuthWsState.NOT_FOUND_LOCAL; + reject(); + } + }); + }); + } + + deleteSession(): void { + if (this.token()) { + this.egNet.request( + 'open-ils.auth', + 'open-ils.auth.session.delete', this.token()) + .subscribe(x => console.log('logged out')) + } + } + + logout(broadcast?: boolean) { + + if (broadcast) { + // TODO + //this.authChannel.postMessage({action : 'logout'}); + } + + this.deleteSession(); + this.egStore.clearLoginSessionItems(); + this.activeUser = null; + this.opChangeUser = null; + } +} diff --git a/Open-ILS/webby-src/src/app/core/event.service.ts b/Open-ILS/webby-src/src/app/core/event.service.ts deleted file mode 100644 index 3f6afc7d30..0000000000 --- a/Open-ILS/webby-src/src/app/core/event.service.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Injectable } from '@angular/core'; - -export class EgEvent { - code : Number; - textcode : String; - payload : any; - desc : String; - debug : String; - note : String; - servertime : String; - ilsperm : String; - ilspermloc : Number; - success : Boolean = false; - - toString(): String { - let s = `Event: ${this.code}:${this.textcode} -> ${this.desc}`; - if (this.ilsperm) - s += ` ${this.ilsperm}@${this.ilspermloc}`; - if (this.note) - s += `\n${this.note}`; - return s; - } -} - -@Injectable() -export class EgEventService { - - /** - * Returns an EgEvent if 'thing' is an event, null otherwise. - */ - parse(thing: any): EgEvent { - - // All events have a textcode - if (thing && typeof thing == 'object' && 'textcode' in thing) { - - let evt = new EgEvent(); - - ['textcode','payload','desc','note','servertime','ilsperm'] - .forEach(field => { evt[field] = thing[field]; }); - - evt.debug = thing.stacktrace; - evt.code = new Number(thing.code); - evt.ilspermloc = new Number(thing.ilspermloc); - evt.success = thing.textcode == 'SUCCESS'; - - return evt; - } - - return null; - } -} - - diff --git a/Open-ILS/webby-src/src/app/core/event.ts b/Open-ILS/webby-src/src/app/core/event.ts new file mode 100644 index 0000000000..3f6afc7d30 --- /dev/null +++ b/Open-ILS/webby-src/src/app/core/event.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; + +export class EgEvent { + code : Number; + textcode : String; + payload : any; + desc : String; + debug : String; + note : String; + servertime : String; + ilsperm : String; + ilspermloc : Number; + success : Boolean = false; + + toString(): String { + let s = `Event: ${this.code}:${this.textcode} -> ${this.desc}`; + if (this.ilsperm) + s += ` ${this.ilsperm}@${this.ilspermloc}`; + if (this.note) + s += `\n${this.note}`; + return s; + } +} + +@Injectable() +export class EgEventService { + + /** + * Returns an EgEvent if 'thing' is an event, null otherwise. + */ + parse(thing: any): EgEvent { + + // All events have a textcode + if (thing && typeof thing == 'object' && 'textcode' in thing) { + + let evt = new EgEvent(); + + ['textcode','payload','desc','note','servertime','ilsperm'] + .forEach(field => { evt[field] = thing[field]; }); + + evt.debug = thing.stacktrace; + evt.code = new Number(thing.code); + evt.ilspermloc = new Number(thing.ilspermloc); + evt.success = thing.textcode == 'SUCCESS'; + + return evt; + } + + return null; + } +} + + diff --git a/Open-ILS/webby-src/src/app/core/idl.service.ts b/Open-ILS/webby-src/src/app/core/idl.service.ts deleted file mode 100644 index e503ba74a4..0000000000 --- a/Open-ILS/webby-src/src/app/core/idl.service.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Injectable } from '@angular/core'; - -// Added globally by /IDL2js -declare var _preload_fieldmapper_IDL: Object; - -/** - * NOTE: To achieve full type strictness and avoid compile warnings, - * we would likely have to pre-compile the IDL down to a .ts file with all - * of the IDL class and field definitions. - */ - -/** - * Every IDL object class implements this interface. - */ -export interface EgIdlObject { - a: any[]; - classname: String; - _isfieldmapper: Boolean; - // Dynamically appended functions from the IDL. - [fields: string]: any; -} - -@Injectable() -export class EgIdlService { - - classes = {}; // IDL class metadata - constructors = {}; // IDL instance generators - - /** - * Create a new IDL object instance. - */ - create(cls: string, seed?:any[]): EgIdlObject { - if (this.constructors[cls]) - return new this.constructors[cls](seed); - throw new Error(`No such IDL class ${cls}`); - } - - parseIdl(): void { - let this_ = this; - this_.classes = _preload_fieldmapper_IDL; - - /** - * Creates the class constructor and getter/setter - * methods for each IDL class. - */ - let mkclass = (cls, fields) => { - this_.classes[cls].classname = cls; - - // This dance lets us encode each IDL object with the - // EgIdlObject interface. Useful for adding type restrictions - // where desired for functions, etc. - let generator:any = ((): EgIdlObject => { - - var x:any = function(seed) { - this.a = seed || []; - this.classname = cls; - this._isfieldmapper = true; - }; - - fields.forEach(function(field, idx) { - x.prototype[field.name] = function(n) { - if (arguments.length==1) this.a[idx] = n; - return this.a[idx]; - } - }); - - return x; - }); - - this_.constructors[cls] = generator(); - - // global class constructors required for JSON_v1.js - // TODO: polluting the window namespace w/ every IDL class - // is less than ideal. - window[cls] = this_.constructors[cls]; - } - - for (var cls in this_.classes) - mkclass(cls, this_.classes[cls].fields); - }; -} - diff --git a/Open-ILS/webby-src/src/app/core/idl.ts b/Open-ILS/webby-src/src/app/core/idl.ts new file mode 100644 index 0000000000..e503ba74a4 --- /dev/null +++ b/Open-ILS/webby-src/src/app/core/idl.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@angular/core'; + +// Added globally by /IDL2js +declare var _preload_fieldmapper_IDL: Object; + +/** + * NOTE: To achieve full type strictness and avoid compile warnings, + * we would likely have to pre-compile the IDL down to a .ts file with all + * of the IDL class and field definitions. + */ + +/** + * Every IDL object class implements this interface. + */ +export interface EgIdlObject { + a: any[]; + classname: String; + _isfieldmapper: Boolean; + // Dynamically appended functions from the IDL. + [fields: string]: any; +} + +@Injectable() +export class EgIdlService { + + classes = {}; // IDL class metadata + constructors = {}; // IDL instance generators + + /** + * Create a new IDL object instance. + */ + create(cls: string, seed?:any[]): EgIdlObject { + if (this.constructors[cls]) + return new this.constructors[cls](seed); + throw new Error(`No such IDL class ${cls}`); + } + + parseIdl(): void { + let this_ = this; + this_.classes = _preload_fieldmapper_IDL; + + /** + * Creates the class constructor and getter/setter + * methods for each IDL class. + */ + let mkclass = (cls, fields) => { + this_.classes[cls].classname = cls; + + // This dance lets us encode each IDL object with the + // EgIdlObject interface. Useful for adding type restrictions + // where desired for functions, etc. + let generator:any = ((): EgIdlObject => { + + var x:any = function(seed) { + this.a = seed || []; + this.classname = cls; + this._isfieldmapper = true; + }; + + fields.forEach(function(field, idx) { + x.prototype[field.name] = function(n) { + if (arguments.length==1) this.a[idx] = n; + return this.a[idx]; + } + }); + + return x; + }); + + this_.constructors[cls] = generator(); + + // global class constructors required for JSON_v1.js + // TODO: polluting the window namespace w/ every IDL class + // is less than ideal. + window[cls] = this_.constructors[cls]; + } + + for (var cls in this_.classes) + mkclass(cls, this_.classes[cls].fields); + }; +} + diff --git a/Open-ILS/webby-src/src/app/core/net.service.ts b/Open-ILS/webby-src/src/app/core/net.service.ts index b7b3648f27..0d98d17222 100644 --- a/Open-ILS/webby-src/src/app/core/net.service.ts +++ b/Open-ILS/webby-src/src/app/core/net.service.ts @@ -17,7 +17,7 @@ */ import { Injectable, EventEmitter } from '@angular/core'; import { Observable, Observer } from 'rxjs/Rx'; -import { EgEventService, EgEvent } from './event.service'; +import { EgEventService, EgEvent } from './event'; // Global vars from opensrf.js // These are availavble at runtime, but are not exported. diff --git a/Open-ILS/webby-src/src/app/core/org.service.ts b/Open-ILS/webby-src/src/app/core/org.service.ts deleted file mode 100644 index d641555243..0000000000 --- a/Open-ILS/webby-src/src/app/core/org.service.ts +++ /dev/null @@ -1,132 +0,0 @@ -import {Injectable} from '@angular/core'; -import {Observable} from 'rxjs/Rx'; -import {EgIdlObject, EgIdlService} from './idl.service'; -import {EgPcrudService} from './pcrud.service'; - -type EgOrgNodeOrId = number | EgIdlObject; - -@Injectable() -export class EgOrgService { - - private orgMap = {}; - private orgList: EgIdlObject[] = []; - private orgTree: EgIdlObject; // root node + children - - constructor( - private egPcrud: EgPcrudService - ) {} - - get(nodeOrId: EgOrgNodeOrId): EgIdlObject { - if (typeof nodeOrId == 'object') - return nodeOrId; - return this.orgMap[nodeOrId]; - }; - - list(): EgIdlObject[] { - return this.orgList; - }; - - tree(): EgIdlObject { - return this.orgTree; - } - - // get the root OU - root(): EgIdlObject { - return this.orgList[0]; - } - - // list of org_unit objects or IDs for ancestors + me - ancestors(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] { - let node = this.get(nodeOrId); - if (!node) return []; - let nodes = [node]; - while( (node = this.get(node.parent_ou()))) - nodes.push(node); - if (asId) - return nodes.map(function(n){return n.id()}); - return nodes; - }; - - // tests that a node can have users - canHaveUsers(nodeOrId): Boolean { - return this - .get(nodeOrId) - .ou_type() - .can_have_users() == 't'; - } - - // tests that a node can have volumes - canHaveVolumes(nodeOrId): Boolean { - return this - .get(nodeOrId) - .ou_type() - .can_have_vols() == 't'; - } - - // list of org_unit objects or IDs for me + descendants - descendants(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] { - let node = this.get(nodeOrId); - if (!node) return []; - let nodes = []; - function descend(n) { - nodes.push(n); - n.children().forEach(descend); - } - descend(node); - if (asId) - return nodes.map(function(n){return n.id()}); - return nodes; - } - - // list of org_unit objects or IDs for ancestors + me + descendants - fullPath(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] { - let list = this.ancestors(nodeOrId, false).concat( - this.descendants(nodeOrId, false).slice(1)); - if (asId) - return list.map(function(n){return n.id()}); - return list; - } - - sortTree(sortField?: string, node?: EgIdlObject): void { - if (!sortField) sortField = 'shortname'; - if (!node) node = this.orgTree; - node.children( - node.children.sort((a, b) => { - return a[sortField]() < b[sortField]() ? -1 : 1 - }) - ); - node.children.forEach(n => this.sortTree(n)); - } - - absorbTree(node?: EgIdlObject): void { - if (!node) { - node = this.orgTree; - this.orgMap = {}; - this.orgList = []; - } - this.orgMap[node.id()] = node; - this.orgList.push(node); - node.children().forEach(c => this.absorbTree(c)); - } - - /** - * Grabs all of the org units from the server, chops them up into - * various shapes, then returns an "all done" promise. - */ - fetchOrgs(): Promise { - - console.log('fetching..'); - return this.egPcrud.search('aou', {parent_ou : null}, - {flesh : -1, flesh_fields : {aou : ['children', 'ou_type']}}, - {anonymous : true} - ).toPromise().then(tree => { - // ingest tree, etc. - this.orgTree = tree; - this.absorbTree(); - console.log('TREE FETCHED: ' + tree); - }); - } - - // NOTE: see ./org-settings.service for settings - // TODO: ^-- -} diff --git a/Open-ILS/webby-src/src/app/core/org.ts b/Open-ILS/webby-src/src/app/core/org.ts new file mode 100644 index 0000000000..460b1e35d1 --- /dev/null +++ b/Open-ILS/webby-src/src/app/core/org.ts @@ -0,0 +1,132 @@ +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs/Rx'; +import {EgIdlObject, EgIdlService} from './idl'; +import {EgPcrudService} from './pcrud'; + +type EgOrgNodeOrId = number | EgIdlObject; + +@Injectable() +export class EgOrgService { + + private orgMap = {}; + private orgList: EgIdlObject[] = []; + private orgTree: EgIdlObject; // root node + children + + constructor( + private egPcrud: EgPcrudService + ) {} + + get(nodeOrId: EgOrgNodeOrId): EgIdlObject { + if (typeof nodeOrId == 'object') + return nodeOrId; + return this.orgMap[nodeOrId]; + }; + + list(): EgIdlObject[] { + return this.orgList; + }; + + tree(): EgIdlObject { + return this.orgTree; + } + + // get the root OU + root(): EgIdlObject { + return this.orgList[0]; + } + + // list of org_unit objects or IDs for ancestors + me + ancestors(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] { + let node = this.get(nodeOrId); + if (!node) return []; + let nodes = [node]; + while( (node = this.get(node.parent_ou()))) + nodes.push(node); + if (asId) + return nodes.map(function(n){return n.id()}); + return nodes; + }; + + // tests that a node can have users + canHaveUsers(nodeOrId): Boolean { + return this + .get(nodeOrId) + .ou_type() + .can_have_users() == 't'; + } + + // tests that a node can have volumes + canHaveVolumes(nodeOrId): Boolean { + return this + .get(nodeOrId) + .ou_type() + .can_have_vols() == 't'; + } + + // list of org_unit objects or IDs for me + descendants + descendants(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] { + let node = this.get(nodeOrId); + if (!node) return []; + let nodes = []; + function descend(n) { + nodes.push(n); + n.children().forEach(descend); + } + descend(node); + if (asId) + return nodes.map(function(n){return n.id()}); + return nodes; + } + + // list of org_unit objects or IDs for ancestors + me + descendants + fullPath(nodeOrId: EgOrgNodeOrId, asId: Boolean): EgIdlObject[] { + let list = this.ancestors(nodeOrId, false).concat( + this.descendants(nodeOrId, false).slice(1)); + if (asId) + return list.map(function(n){return n.id()}); + return list; + } + + sortTree(sortField?: string, node?: EgIdlObject): void { + if (!sortField) sortField = 'shortname'; + if (!node) node = this.orgTree; + node.children( + node.children.sort((a, b) => { + return a[sortField]() < b[sortField]() ? -1 : 1 + }) + ); + node.children.forEach(n => this.sortTree(n)); + } + + absorbTree(node?: EgIdlObject): void { + if (!node) { + node = this.orgTree; + this.orgMap = {}; + this.orgList = []; + } + this.orgMap[node.id()] = node; + this.orgList.push(node); + node.children().forEach(c => this.absorbTree(c)); + } + + /** + * Grabs all of the org units from the server, chops them up into + * various shapes, then returns an "all done" promise. + */ + fetchOrgs(): Promise { + + console.log('fetching..'); + return this.egPcrud.search('aou', {parent_ou : null}, + {flesh : -1, flesh_fields : {aou : ['children', 'ou_type']}}, + {anonymous : true} + ).toPromise().then(tree => { + // ingest tree, etc. + this.orgTree = tree; + this.absorbTree(); + console.log('TREE FETCHED: ' + tree); + }); + } + + // NOTE: see ./org-settings.service for settings + // TODO: ^-- +} diff --git a/Open-ILS/webby-src/src/app/core/pcrud.service.ts b/Open-ILS/webby-src/src/app/core/pcrud.service.ts deleted file mode 100644 index efe1d3e796..0000000000 --- a/Open-ILS/webby-src/src/app/core/pcrud.service.ts +++ /dev/null @@ -1,311 +0,0 @@ -import {Injectable} from '@angular/core'; -import {Observable, Observer} from 'rxjs/Rx'; -//import {toPromise} from 'rxjs/operators'; -import {EgIdlService, EgIdlObject} from './idl.service'; -import {EgNetService, EgNetRequest} from './net.service'; -import {EgAuthService} from './auth.service'; - -// Used for debugging. -declare var js2JSON: (jsThing:any) => string; -declare var OpenSRF: any; // creating sessions - -export interface EgPcrudReqOps { - authoritative?: boolean; - anonymous?: boolean; - idlist?: boolean; - atomic?: boolean; -} - -// For for documentation purposes. -type EgPcrudResponse = any; - -export class EgPcrudContext { - - static verboseLogging: boolean = true; // - static identGenerator: number = 0; // for debug logging - - private ident: number; - private authoritative: boolean; - private xactCloseMode: string; - private cudIdx: number; - private cudAction: string; - private cudLast: EgPcrudResponse; - private cudList: EgIdlObject[]; - - private egIdl: EgIdlService; - private egNet: EgNetService; - private egAuth: EgAuthService; - - // Tracks nested CUD actions - cudObserver: Observer; - - session: any; // OpenSRF.ClientSession - - constructor( // passed in by parent service -- not injected - egIdl: EgIdlService, - egNet: EgNetService, - egAuth: EgAuthService - ) { - this.egIdl = egIdl; - this.egNet = egNet; - this.egAuth = egAuth; - this.xactCloseMode = 'rollback'; - this.ident = EgPcrudContext.identGenerator++; - this.session = new OpenSRF.ClientSession('open-ils.pcrud'); - } - - toString(): string { - return '[PCRUDContext ' + this.ident + ']'; - } - - log(msg: string): void { - if (EgPcrudContext.verboseLogging) - console.debug(this + ': ' + msg); - } - - err(msg: string): void { - console.error(this + ': ' + msg); - } - - token(reqOps?: EgPcrudReqOps): string { - return (reqOps && reqOps.anonymous) ? - 'ANONYMOUS' : this.egAuth.token(); - } - - connect(): Promise { - this.log('connect'); - return new Promise( (resolve, reject) => { - this.session.connect({ - onconnect : () => { resolve(this); } - }); - }) - } - - disconnect(): void { - this.log('disconnect'); - this.session.disconnect(); - } - - retrieve(fmClass: string, pkey: Number | string, - pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { - if (!reqOps) reqOps = {}; - this.authoritative = reqOps.authoritative || false; - return this.dispatch( - `open-ils.pcrud.retrieve.${fmClass}`, - [this.token(reqOps), pkey, pcrudOps]); - } - - retrieveAll(fmClass: string, pcrudOps?: any, - reqOps?: EgPcrudReqOps): Observable { - let search = {}; - search[this.egIdl.classes[fmClass].pkey] = {'!=' : null}; - return this.search(fmClass, search, pcrudOps, reqOps); - } - - search(fmClass: string, search: any, - pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { - reqOps = reqOps || {}; - this.authoritative = reqOps.authoritative || false; - - let returnType = reqOps.idlist ? 'id_list' : 'search'; - let method = `open-ils.pcrud.${returnType}.${fmClass}`; - - if (reqOps.atomic) method += '.atomic'; - - return this.dispatch(method, [this.token(reqOps), search, pcrudOps]); - } - - create(list: EgIdlObject[]): Observable { - return this.cud('create', list) - } - update(list: EgIdlObject[]): Observable { - return this.cud('update', list) - } - remove(list: EgIdlObject[]): Observable { - return this.cud('delete', list) - } - autoApply(list: EgIdlObject[]): Observable { // RENAMED - return this.cud('auto', list) - } - - xactClose(): Observable { - return this.sendRequest( - 'open-ils.pcrud.transaction.' + this.xactCloseMode, - [this.token()] - ); - }; - - xactBegin(): Observable { - return this.sendRequest( - 'open-ils.pcrud.transaction.begin', [this.token()] - ); - }; - - private dispatch(method: string, params: any[]): Observable { - if (this.authoritative) { - return this.wrapXact(() => { - return this.sendRequest(method, params); - }); - } else { - return this.sendRequest(method, params) - } - }; - - - // => connect - // => xact_begin - // => action - // => xact_close(commit/rollback) - // => disconnect - wrapXact(mainFunc: () => Observable): Observable { - let this_ = this; - - return Observable.create(observer => { - - // 1. connect - this.connect() - - // 2. start the transaction - .then(() => {return this_.xactBegin().toPromise()}) - - // 3. execute the main body - .then(() => { - - mainFunc().subscribe( - res => observer.next(res), - err => observer.error(err), - () => { - this_.xactClose().toPromise().then(() => { - // 5. disconnect - this_.disconnect(); - // 6. all done - observer.complete(); - }); - } - ); - }) - }); - }; - - private sendRequest(method: string, - params: any[]): Observable { - - this.log(`sendRequest(${method})`); - - return this.egNet.requestCompiled( - new EgNetRequest( - 'open-ils.pcrud', method, params, this.session) - ); - } - - private cud(action: string, - list: EgIdlObject | EgIdlObject[]): Observable { - - this.log(`CUD(): ${action}`); - - this.cudIdx = 0; - this.cudAction = action; - this.xactCloseMode = 'commit'; - - if (!Array.isArray(list)) this.cudList = [list]; - - let this_ = this; - - return this.wrapXact(() => { - return Observable.create(observer => { - this_.cudObserver = observer; - this_.nextCudRequest(); - }); - }); - } - - /** - * Loops through the list of objects to update and sends - * them one at a time to the server for processing. Once - * all are done, the cudObserver is resolved. - */ - nextCudRequest(): void { - let this_ = this; - - if (this.cudIdx >= this.cudList.length) { - this.cudObserver.complete(); - return; - } - - let action = this.cudAction; - let fmObj = this.cudList[this.cudIdx++]; - - if (action == 'auto') { - if (fmObj.ischanged()) action = 'update'; - if (fmObj.isnew()) action = 'create'; - if (fmObj.isdeleted()) action = 'delete'; - - if (action == 'auto') { - // object does not need updating; move along - this.nextCudRequest(); - } - } - - this.sendRequest( - `open-ils.pcrud.${action}.${fmObj.classname}`, - [this.token(), fmObj] - ).subscribe( - res => this_.cudObserver.next(res), - err => this_.cudObserver.error(err), - () => this_.nextCudRequest() - ); - }; -} - -@Injectable() -export class EgPcrudService { - - constructor( - private egIdl: EgIdlService, - private egNet: EgNetService, - private egAuth: EgAuthService - ) {} - - // Pass-thru functions for one-off PCRUD calls - - connect(): Promise { - return this.newContext().connect(); - } - - newContext(): EgPcrudContext { - return new EgPcrudContext(this.egIdl, this.egNet, this.egAuth); - } - - retrieve(fmClass: string, pkey: Number | string, - pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { - return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps); - } - - retrieveAll(fmClass: string, pcrudOps?: any, - reqOps?: EgPcrudReqOps): Observable { - return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps); - } - - search(fmClass: string, search: any, - pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { - return this.newContext().search(fmClass, search, pcrudOps, reqOps); - } - - create(list: EgIdlObject[]): Observable { - return this.newContext().create(list); - } - - update(list: EgIdlObject[]): Observable { - return this.newContext().update(list); - } - - remove(list: EgIdlObject[]): Observable { - return this.newContext().remove(list); - } - - autoApply(list: EgIdlObject[]): Observable { - return this.newContext().autoApply(list); - } -} - - diff --git a/Open-ILS/webby-src/src/app/core/pcrud.ts b/Open-ILS/webby-src/src/app/core/pcrud.ts new file mode 100644 index 0000000000..42a93ef9fb --- /dev/null +++ b/Open-ILS/webby-src/src/app/core/pcrud.ts @@ -0,0 +1,311 @@ +import {Injectable} from '@angular/core'; +import {Observable, Observer} from 'rxjs/Rx'; +//import {toPromise} from 'rxjs/operators'; +import {EgIdlService, EgIdlObject} from './idl'; +import {EgNetService, EgNetRequest} from './net.service'; +import {EgAuthService} from './auth'; + +// Used for debugging. +declare var js2JSON: (jsThing:any) => string; +declare var OpenSRF: any; // creating sessions + +export interface EgPcrudReqOps { + authoritative?: boolean; + anonymous?: boolean; + idlist?: boolean; + atomic?: boolean; +} + +// For for documentation purposes. +type EgPcrudResponse = any; + +export class EgPcrudContext { + + static verboseLogging: boolean = true; // + static identGenerator: number = 0; // for debug logging + + private ident: number; + private authoritative: boolean; + private xactCloseMode: string; + private cudIdx: number; + private cudAction: string; + private cudLast: EgPcrudResponse; + private cudList: EgIdlObject[]; + + private egIdl: EgIdlService; + private egNet: EgNetService; + private egAuth: EgAuthService; + + // Tracks nested CUD actions + cudObserver: Observer; + + session: any; // OpenSRF.ClientSession + + constructor( // passed in by parent service -- not injected + egIdl: EgIdlService, + egNet: EgNetService, + egAuth: EgAuthService + ) { + this.egIdl = egIdl; + this.egNet = egNet; + this.egAuth = egAuth; + this.xactCloseMode = 'rollback'; + this.ident = EgPcrudContext.identGenerator++; + this.session = new OpenSRF.ClientSession('open-ils.pcrud'); + } + + toString(): string { + return '[PCRUDContext ' + this.ident + ']'; + } + + log(msg: string): void { + if (EgPcrudContext.verboseLogging) + console.debug(this + ': ' + msg); + } + + err(msg: string): void { + console.error(this + ': ' + msg); + } + + token(reqOps?: EgPcrudReqOps): string { + return (reqOps && reqOps.anonymous) ? + 'ANONYMOUS' : this.egAuth.token(); + } + + connect(): Promise { + this.log('connect'); + return new Promise( (resolve, reject) => { + this.session.connect({ + onconnect : () => { resolve(this); } + }); + }) + } + + disconnect(): void { + this.log('disconnect'); + this.session.disconnect(); + } + + retrieve(fmClass: string, pkey: Number | string, + pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { + if (!reqOps) reqOps = {}; + this.authoritative = reqOps.authoritative || false; + return this.dispatch( + `open-ils.pcrud.retrieve.${fmClass}`, + [this.token(reqOps), pkey, pcrudOps]); + } + + retrieveAll(fmClass: string, pcrudOps?: any, + reqOps?: EgPcrudReqOps): Observable { + let search = {}; + search[this.egIdl.classes[fmClass].pkey] = {'!=' : null}; + return this.search(fmClass, search, pcrudOps, reqOps); + } + + search(fmClass: string, search: any, + pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { + reqOps = reqOps || {}; + this.authoritative = reqOps.authoritative || false; + + let returnType = reqOps.idlist ? 'id_list' : 'search'; + let method = `open-ils.pcrud.${returnType}.${fmClass}`; + + if (reqOps.atomic) method += '.atomic'; + + return this.dispatch(method, [this.token(reqOps), search, pcrudOps]); + } + + create(list: EgIdlObject[]): Observable { + return this.cud('create', list) + } + update(list: EgIdlObject[]): Observable { + return this.cud('update', list) + } + remove(list: EgIdlObject[]): Observable { + return this.cud('delete', list) + } + autoApply(list: EgIdlObject[]): Observable { // RENAMED + return this.cud('auto', list) + } + + xactClose(): Observable { + return this.sendRequest( + 'open-ils.pcrud.transaction.' + this.xactCloseMode, + [this.token()] + ); + }; + + xactBegin(): Observable { + return this.sendRequest( + 'open-ils.pcrud.transaction.begin', [this.token()] + ); + }; + + private dispatch(method: string, params: any[]): Observable { + if (this.authoritative) { + return this.wrapXact(() => { + return this.sendRequest(method, params); + }); + } else { + return this.sendRequest(method, params) + } + }; + + + // => connect + // => xact_begin + // => action + // => xact_close(commit/rollback) + // => disconnect + wrapXact(mainFunc: () => Observable): Observable { + let this_ = this; + + return Observable.create(observer => { + + // 1. connect + this.connect() + + // 2. start the transaction + .then(() => {return this_.xactBegin().toPromise()}) + + // 3. execute the main body + .then(() => { + + mainFunc().subscribe( + res => observer.next(res), + err => observer.error(err), + () => { + this_.xactClose().toPromise().then(() => { + // 5. disconnect + this_.disconnect(); + // 6. all done + observer.complete(); + }); + } + ); + }) + }); + }; + + private sendRequest(method: string, + params: any[]): Observable { + + this.log(`sendRequest(${method})`); + + return this.egNet.requestCompiled( + new EgNetRequest( + 'open-ils.pcrud', method, params, this.session) + ); + } + + private cud(action: string, + list: EgIdlObject | EgIdlObject[]): Observable { + + this.log(`CUD(): ${action}`); + + this.cudIdx = 0; + this.cudAction = action; + this.xactCloseMode = 'commit'; + + if (!Array.isArray(list)) this.cudList = [list]; + + let this_ = this; + + return this.wrapXact(() => { + return Observable.create(observer => { + this_.cudObserver = observer; + this_.nextCudRequest(); + }); + }); + } + + /** + * Loops through the list of objects to update and sends + * them one at a time to the server for processing. Once + * all are done, the cudObserver is resolved. + */ + nextCudRequest(): void { + let this_ = this; + + if (this.cudIdx >= this.cudList.length) { + this.cudObserver.complete(); + return; + } + + let action = this.cudAction; + let fmObj = this.cudList[this.cudIdx++]; + + if (action == 'auto') { + if (fmObj.ischanged()) action = 'update'; + if (fmObj.isnew()) action = 'create'; + if (fmObj.isdeleted()) action = 'delete'; + + if (action == 'auto') { + // object does not need updating; move along + this.nextCudRequest(); + } + } + + this.sendRequest( + `open-ils.pcrud.${action}.${fmObj.classname}`, + [this.token(), fmObj] + ).subscribe( + res => this_.cudObserver.next(res), + err => this_.cudObserver.error(err), + () => this_.nextCudRequest() + ); + }; +} + +@Injectable() +export class EgPcrudService { + + constructor( + private egIdl: EgIdlService, + private egNet: EgNetService, + private egAuth: EgAuthService + ) {} + + // Pass-thru functions for one-off PCRUD calls + + connect(): Promise { + return this.newContext().connect(); + } + + newContext(): EgPcrudContext { + return new EgPcrudContext(this.egIdl, this.egNet, this.egAuth); + } + + retrieve(fmClass: string, pkey: Number | string, + pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { + return this.newContext().retrieve(fmClass, pkey, pcrudOps, reqOps); + } + + retrieveAll(fmClass: string, pcrudOps?: any, + reqOps?: EgPcrudReqOps): Observable { + return this.newContext().retrieveAll(fmClass, pcrudOps, reqOps); + } + + search(fmClass: string, search: any, + pcrudOps?: any, reqOps?: EgPcrudReqOps): Observable { + return this.newContext().search(fmClass, search, pcrudOps, reqOps); + } + + create(list: EgIdlObject[]): Observable { + return this.newContext().create(list); + } + + update(list: EgIdlObject[]): Observable { + return this.newContext().update(list); + } + + remove(list: EgIdlObject[]): Observable { + return this.newContext().remove(list); + } + + autoApply(list: EgIdlObject[]): Observable { + return this.newContext().autoApply(list); + } +} + + diff --git a/Open-ILS/webby-src/src/app/core/store.service.ts b/Open-ILS/webby-src/src/app/core/store.service.ts deleted file mode 100644 index f508f551e5..0000000000 --- a/Open-ILS/webby-src/src/app/core/store.service.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Store and retrieve data from various sources. - */ -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs/Rx'; -import { CookieService } from 'ngx-cookie'; - -@Injectable() -export class EgStoreService { - - // Base path for cookie-based storage. - // Useful for limiting cookies to subsections of the application. - loginSessionBasePath: string; - - // Set of keys whose values should disappear at logout. - loginSessionKeys: string[] = [ - 'eg.auth.token', - 'eg.auth.time', - 'eg.auth.token.oc', - 'eg.auth.time.oc' - ]; - - constructor(private cookieService: CookieService) {} - - private parseJson(valJson: string): any { - if (valJson == null || valJson == '') return null; - try { - return JSON.parse(valJson); - } catch(E) { - console.error(`Failure to parse JSON: ${E} => ${valJson}`); - return null; - } - } - - /** - * Add a an app-local login session key - */ - addLoginSessionKey(key: string): void { - this.loginSessionKeys.push(key); - } - - setItem(key: string, val: any, isJson?: Boolean): Promise { - // TODO: route keys appropriately - this.setLocalItem(key, val, false); - return Promise.resolve(); - } - - setLocalItem(key: string, val: any, isJson?: Boolean): void { - if (!isJson) val = JSON.stringify(val); - window.localStorage.setItem(key, val); - } - - setServerItem(key: string, val: any): Promise { - return Promise.resolve(); - } - - setSessionItem(key: string, val: any, isJson?: Boolean): void { - if (!isJson) val = JSON.stringify(val); - window.sessionStorage.setItem(key, val); - } - - setLoginSessionItem(key: string, val: any, isJson?:Boolean): void { - if (!isJson) val = JSON.stringify(val); - console.log(`storing ses item ${key} : ${val}`); - this.cookieService.put(key, val, {path : this.loginSessionBasePath}); - } - - getItem(key: string): Promise { - // TODO: route keys appropriately - return Promise.resolve(this.getLocalItem(key)); - } - - getLocalItem(key: string): any { - return this.parseJson(window.localStorage.getItem(key)); - } - - getServerItem(key: string): Promise { - return Promise.resolve(); - } - - getSessionItem(key: string): any { - return this.parseJson(window.sessionStorage.getItem(key)); - } - - getLoginSessionItem(key: string): any { - return this.parseJson(this.cookieService.get(key)); - } - - removeItem(key: string): Promise { - // TODO: route keys appropriately - return Promise.resolve(this.removeLocalItem(key)); - } - - removeLocalItem(key: string): void { - window.localStorage.removeItem(key); - } - - removeServerItem(key: string): Promise { - return Promise.resolve(); - } - - removeSessionItem(key: string): void { - window.sessionStorage.removeItem(key); - } - - removeLoginSessionItem(key: string): void { - this.cookieService.remove(key, {path : this.loginSessionBasePath}); - } - - clearLoginSessionItems(): void { - this.loginSessionKeys.forEach( - key => this.removeLoginSessionItem(key) - ); - } -} - diff --git a/Open-ILS/webby-src/src/app/core/store.ts b/Open-ILS/webby-src/src/app/core/store.ts new file mode 100644 index 0000000000..f508f551e5 --- /dev/null +++ b/Open-ILS/webby-src/src/app/core/store.ts @@ -0,0 +1,116 @@ +/** + * Store and retrieve data from various sources. + */ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs/Rx'; +import { CookieService } from 'ngx-cookie'; + +@Injectable() +export class EgStoreService { + + // Base path for cookie-based storage. + // Useful for limiting cookies to subsections of the application. + loginSessionBasePath: string; + + // Set of keys whose values should disappear at logout. + loginSessionKeys: string[] = [ + 'eg.auth.token', + 'eg.auth.time', + 'eg.auth.token.oc', + 'eg.auth.time.oc' + ]; + + constructor(private cookieService: CookieService) {} + + private parseJson(valJson: string): any { + if (valJson == null || valJson == '') return null; + try { + return JSON.parse(valJson); + } catch(E) { + console.error(`Failure to parse JSON: ${E} => ${valJson}`); + return null; + } + } + + /** + * Add a an app-local login session key + */ + addLoginSessionKey(key: string): void { + this.loginSessionKeys.push(key); + } + + setItem(key: string, val: any, isJson?: Boolean): Promise { + // TODO: route keys appropriately + this.setLocalItem(key, val, false); + return Promise.resolve(); + } + + setLocalItem(key: string, val: any, isJson?: Boolean): void { + if (!isJson) val = JSON.stringify(val); + window.localStorage.setItem(key, val); + } + + setServerItem(key: string, val: any): Promise { + return Promise.resolve(); + } + + setSessionItem(key: string, val: any, isJson?: Boolean): void { + if (!isJson) val = JSON.stringify(val); + window.sessionStorage.setItem(key, val); + } + + setLoginSessionItem(key: string, val: any, isJson?:Boolean): void { + if (!isJson) val = JSON.stringify(val); + console.log(`storing ses item ${key} : ${val}`); + this.cookieService.put(key, val, {path : this.loginSessionBasePath}); + } + + getItem(key: string): Promise { + // TODO: route keys appropriately + return Promise.resolve(this.getLocalItem(key)); + } + + getLocalItem(key: string): any { + return this.parseJson(window.localStorage.getItem(key)); + } + + getServerItem(key: string): Promise { + return Promise.resolve(); + } + + getSessionItem(key: string): any { + return this.parseJson(window.sessionStorage.getItem(key)); + } + + getLoginSessionItem(key: string): any { + return this.parseJson(this.cookieService.get(key)); + } + + removeItem(key: string): Promise { + // TODO: route keys appropriately + return Promise.resolve(this.removeLocalItem(key)); + } + + removeLocalItem(key: string): void { + window.localStorage.removeItem(key); + } + + removeServerItem(key: string): Promise { + return Promise.resolve(); + } + + removeSessionItem(key: string): void { + window.sessionStorage.removeItem(key); + } + + removeLoginSessionItem(key: string): void { + this.cookieService.remove(key, {path : this.loginSessionBasePath}); + } + + clearLoginSessionItems(): void { + this.loginSessionKeys.forEach( + key => this.removeLoginSessionItem(key) + ); + } +} + diff --git a/Open-ILS/webby-src/src/app/resolver.service.ts b/Open-ILS/webby-src/src/app/resolver.service.ts index 984e888ed1..c63b2d5ed6 100644 --- a/Open-ILS/webby-src/src/app/resolver.service.ts +++ b/Open-ILS/webby-src/src/app/resolver.service.ts @@ -1,8 +1,8 @@ import {Injectable} from '@angular/core'; import {Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot} from '@angular/router'; -import {EgIdlService} from '@eg/core/idl.service'; -import {EgOrgService} from '@eg/core/org.service'; +import {EgIdlService} from '@eg/core/idl'; +import {EgOrgService} from '@eg/core/org'; @Injectable() export class EgBaseResolver implements Resolve> { diff --git a/Open-ILS/webby-src/src/app/staff/admin/workstation/workstations.component.ts b/Open-ILS/webby-src/src/app/staff/admin/workstation/workstations.component.ts index 4e212cf090..231bcb4e4f 100644 --- a/Open-ILS/webby-src/src/app/staff/admin/workstation/workstations.component.ts +++ b/Open-ILS/webby-src/src/app/staff/admin/workstation/workstations.component.ts @@ -1,10 +1,10 @@ import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; -import {EgStoreService} from '@eg/core/store.service'; -import {EgIdlObject} from '@eg/core/idl.service'; +import {EgStoreService} from '@eg/core/store'; +import {EgIdlObject} from '@eg/core/idl'; import {EgNetService} from '@eg/core/net.service'; -import {EgAuthService} from '@eg/core/auth.service'; -import {EgOrgService} from '@eg/core/org.service'; +import {EgAuthService} from '@eg/core/auth'; +import {EgOrgService} from '@eg/core/org'; // Slim version of the WS that's stored in the cache. interface Workstation { @@ -21,7 +21,6 @@ export class EgWorkstationsComponent implements OnInit { selectedId: Number; workstations: Workstation[] = []; isRemoving: boolean = false; - newOwner: EgIdlObject; newName: String; @@ -29,12 +28,15 @@ export class EgWorkstationsComponent implements OnInit { private route: ActivatedRoute, private egNet: EgNetService, private egAuth: EgAuthService, - private egStore: EgStoreService + private egStore: EgStoreService, + private egOrg: EgOrgService ) {} ngOnInit() { this.egStore.getItem('eg.workstation.all') .then(res => this.workstations = res); + + this.newOwner = this.egOrg.root(); } selected(): Workstation { @@ -59,7 +61,8 @@ export class EgWorkstationsComponent implements OnInit { } registerWorkstation(): void { - console.log('registering ' + this.newName); + console.log(this.newOwner); + console.log('registering ' + this.newName + ' : ' + this.newOwner.shortname()); } } diff --git a/Open-ILS/webby-src/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts b/Open-ILS/webby-src/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts index 21bb111e67..3b2cfe1695 100644 --- a/Open-ILS/webby-src/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts +++ b/Open-ILS/webby-src/src/app/staff/circ/patron/bcsearch/bcsearch.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { EgNetService } from '@eg/core/net.service'; -import { EgAuthService } from '@eg/core/auth.service'; +import { EgAuthService } from '@eg/core/auth'; @Component({ templateUrl: 'bcsearch.component.html' diff --git a/Open-ILS/webby-src/src/app/staff/login.component.ts b/Open-ILS/webby-src/src/app/staff/login.component.ts index 599562d91a..3dd94940aa 100644 --- a/Open-ILS/webby-src/src/app/staff/login.component.ts +++ b/Open-ILS/webby-src/src/app/staff/login.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, Renderer } from '@angular/core'; import { Location } from '@angular/common'; import { Router } from '@angular/router'; -import { EgAuthService, EgAuthWsState } from '@eg/core/auth.service'; -import { EgStoreService } from '@eg/core/store.service'; // TODO: testing +import { EgAuthService, EgAuthWsState } from '@eg/core/auth'; +import { EgStoreService } from '@eg/core/store'; // TODO: testing @Component({ templateUrl : './login.component.html' diff --git a/Open-ILS/webby-src/src/app/staff/resolver.service.ts b/Open-ILS/webby-src/src/app/staff/resolver.service.ts index 83dba49a46..21eddec7ce 100644 --- a/Open-ILS/webby-src/src/app/staff/resolver.service.ts +++ b/Open-ILS/webby-src/src/app/staff/resolver.service.ts @@ -3,9 +3,9 @@ import { Location } from '@angular/common'; import { Observable, Observer } from 'rxjs/Rx'; import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; -import { EgStoreService } from '@eg/core/store.service'; +import { EgStoreService } from '@eg/core/store'; import { EgNetService } from '@eg/core/net.service'; -import { EgAuthService } from '@eg/core/auth.service'; +import { EgAuthService } from '@eg/core/auth'; /** * Apply configuration, etc. required by all staff components. diff --git a/Open-ILS/webby-src/src/app/staff/share/org-select.component.html b/Open-ILS/webby-src/src/app/staff/share/org-select.component.html index b8fb2d574e..50ec19bb39 100644 --- a/Open-ILS/webby-src/src/app/staff/share/org-select.component.html +++ b/Open-ILS/webby-src/src/app/staff/share/org-select.component.html @@ -1,4 +1,9 @@ - +
): Observable => { return text$ .debounceTime(100) .distinctUntilChanged() - .map(term => this.states.filter( - v => v.toLowerCase().indexOf(term.toLowerCase()) > -1) - ); + .map(term => { + return this.egOrg.list().filter(org => { + let sn = org.shortname().toLowerCase(); + return sn.indexOf(term.toLowerCase()) > -1 + }).map(org => { + // The browser won't collapse multiple spaces when + // using a space-ish unicode char instead of regular spaces. + let space = ' '; // U+2007 + let sn = org.shortname(); + for (var i = 0; i < org.ou_type().depth(); i++) { + sn = space + sn; + } + return sn; + }) + }); } } - diff --git a/Open-ILS/webby-src/src/app/staff/staff.component.ts b/Open-ILS/webby-src/src/app/staff/staff.component.ts index 6f7ba18616..836a92bbd1 100644 --- a/Open-ILS/webby-src/src/app/staff/staff.component.ts +++ b/Open-ILS/webby-src/src/app/staff/staff.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'; -import { EgAuthService, EgAuthWsState } from '@eg/core/auth.service'; +import { EgAuthService, EgAuthWsState } from '@eg/core/auth'; import { EgNetService } from '@eg/core/net.service'; @Component({