}
return new Promise((resolve, reject) => {
- this.store.getItem('eg.workstation.all')
- .then(workstations => {
-
- if (workstations) {
- const ws = workstations.filter(
- w => Number(w.id) === Number(this.user().wsid()))[0];
-
- if (ws) {
- this.activeUser.workstation = ws.name;
- this.workstationState = AuthWsState.VALID;
- return resolve();
- }
+ const workstations =
+ this.store.getLocalItem('eg.workstation.all');
+
+ if (workstations) {
+ const ws = workstations.filter(
+ w => Number(w.id) === Number(this.user().wsid()))[0];
+
+ if (ws) {
+ this.activeUser.workstation = ws.name;
+ this.workstationState = AuthWsState.VALID;
+ return resolve();
}
+ }
- this.workstationState = AuthWsState.NOT_FOUND_LOCAL;
- reject();
- });
+ this.workstationState = AuthWsState.NOT_FOUND_LOCAL;
+ reject();
});
}
--- /dev/null
+/**
+ * Set and get server-stored settings.
+ */
+import {Injectable} from '@angular/core';
+import {AuthService} from './auth.service';
+import {NetService} from './net.service';
+
+// Settings summary objects returned by the API
+interface ServerSettingSummary {
+ name: string;
+ value: string;
+ has_org_setting: boolean;
+ has_user_setting: boolean;
+ has_workstation_setting: boolean;
+}
+
+@Injectable({providedIn: 'root'})
+export class ServerStoreService {
+
+ cache: {[key: string]: ServerSettingSummary};
+
+ constructor(
+ private net: NetService,
+ private auth: AuthService) {
+ this.cache = {};
+ }
+
+ setItem(key: string, value: any): Promise<any> {
+
+ if (!this.auth.token()) {
+ return Promise.reject('Auth required to apply settings');
+ }
+
+ const setting: any = {};
+ setting[key] = value;
+
+ return this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.settings.apply.user_or_ws',
+ this.auth.token(), setting)
+
+ .toPromise().then(appliedCount => {
+
+ if (Number(appliedCount) > 0) { // value applied
+ return this.cache[key] = value;
+ }
+
+ return Promise.reject(
+ `No user or workstation setting type exists for: "${key}".\n` +
+ 'Create a ws/user setting type or use setLocalItem() to ' +
+ 'store the value locally.'
+ );
+ });
+ }
+
+ // Returns a single setting value
+ getItem(key: string): Promise<any> {
+ return this.getItemBatch([key]).then(
+ settings => settings[key]
+ );
+ }
+
+ // Returns a set of key/value pairs for the requested settings
+ getItemBatch(keys: string[]): Promise<any> {
+
+ const values: any = {};
+ keys.forEach(key => {
+ if (this.cache[key]) {
+ values[key] = this.cache[key];
+ }
+ });
+
+ if (keys.length === Object.keys(values).length) {
+ // All values are cached already
+ return Promise.resolve(values);
+ }
+
+ if (!this.auth.token()) {
+ // Authtokens require for fetching server settings, but
+ // calls to retrieve settings could potentially occur
+ // before auth completes -- Ideally not, but just to be safe.
+ return Promise.resolve({});
+ }
+
+ // Server call required. Limit the settings to lookup to those
+ // we don't already have cached.
+ const serverKeys = [];
+ keys.forEach(key => {
+ if (!Object.keys(values).includes(key)) {
+ serverKeys.push(key);
+ }
+ });
+
+ return new Promise((resolve, reject) => {
+ this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.settings.retrieve',
+ serverKeys, this.auth.token()
+ ).subscribe(
+ summary => {
+ this.cache[summary.name] =
+ values[summary.name] = summary.value;
+ },
+ err => reject,
+ () => resolve(values)
+ );
+ });
+ }
+
+ removeItem(key: string): Promise<any> {
+ return this.setItem(key, null);
+ }
+}
+
/**
* Store and retrieve data from various sources.
+ *
+ * Data Types:
+ * 1. LocalItem: Stored in window.localStorage and persist indefinitely.
+ * 2. SessionItem: Stored in window.sessionStorage and persist until
+ * the end of the current browser tab/window. Data is only available
+ * to the tab/window where the data was set.
+ * 3. LoginItem: Stored as session cookies and persist until the browser
+ * is closed. These values are avalable to all browser windows/tabs.
*/
-import {Injectable, Injector} from '@angular/core';
+import {Injectable} from '@angular/core';
import {CookieService} from 'ngx-cookie';
-import {NetService} from './net.service';
-
-// Settings summary objects returned by the API
-export interface ServerSettingSummary {
- name: string;
- value: string;
- has_org_setting: boolean;
- has_user_setting: boolean;
- has_workstation_setting: boolean;
-}
@Injectable({providedIn: 'root'})
export class StoreService {
// Note cookies shared with /eg/staff must be stored at "/"
loginSessionBasePath = '/';
- // Loaded on demand to avoid circular ref in compiler.
- auth: any;
-
- cache: {[key: string]: ServerSettingSummary};
-
// Set of keys whose values should disappear at logout.
loginSessionKeys: string[] = [
'eg.auth.token',
];
constructor(
- private injector: Injector,
- private cookieService: CookieService,
- private net: NetService) {
- this.cache = {};
+ private cookieService: CookieService) {
}
private parseJson(valJson: string): any {
this.loginSessionKeys.push(key);
}
- setItem(key: string, val: any, isJson?: Boolean): Promise<void> {
- // TODO: route keys appropriately
- this.setLocalItem(key, val, isJson);
- return Promise.resolve();
- }
-
- setLocalItem(key: string, val: any, isJson?: Boolean): void {
+ setLocalItem(key: string, val: any, isJson?: boolean): void {
if (!isJson) {
val = JSON.stringify(val);
}
- // console.debug(`${key} ${val}`);
window.localStorage.setItem(key, val);
}
- setServerItem(key: string, val: any): Promise<void> {
- return Promise.resolve();
- }
-
- setSessionItem(key: string, val: any, isJson?: Boolean): void {
+ 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 {
+ setLoginSessionItem(key: string, val: any, isJson?: boolean): void {
if (!isJson) {
val = JSON.stringify(val);
}
{path : this.loginSessionBasePath, secure: true});
}
- getItem(key: string): Promise<any> {
- // 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<any> {
-
- if (this.cache[key]) {
- return Promise.resolve(this.cache[key].value);
- }
-
- if (!this.auth) {
- this.auth = this.injector.get('AuthService');
- }
-
- if (this.auth.token()) {
- return Promise.reject('auth token required for server settings');
- }
-
- return this.net.request(
- 'open-ils.actor',
- 'open-ils.actor.settings.retrieve',
- [key], this.auth.token()
- ).toPromise().then(
- (summary: ServerSettingSummary) => {
- this.cache[summary.name] = summary;
- return summary.value;
- }
- );
- }
-
getSessionItem(key: string): any {
return this.parseJson(window.sessionStorage.getItem(key));
}
return this.parseJson(this.cookieService.get(key));
}
- removeItem(key: string): Promise<any> {
- // TODO: route keys appropriately
- return Promise.resolve(this.removeLocalItem(key));
- }
-
removeLocalItem(key: string): void {
window.localStorage.removeItem(key);
}
- removeServerItem(key: string): Promise<void> {
- return Promise.resolve();
- }
-
removeSessionItem(key: string): void {
window.sessionStorage.removeItem(key);
}
<span class="ml-2" i18n>Manage Column Widths</span>
</a>
<a class="dropdown-item label-with-material-icon"
- (click)="saveColumns()">
+ (click)="saveGridConfig()">
<span class="material-icons">save</span>
- <span class="ml-2" i18n>Save Columns</span>
+ <span class="ml-2" i18n>Save Grid Settings</span>
</a>
<a class="dropdown-item label-with-material-icon"
(click)="gridContext.columnSet.reset()">
ngOnInit() {}
- saveColumns() {
+ saveGridConfig() {
// TODO: when server-side settings are supported, this operation
// may offer to save to user/workstation OR org unit settings
// depending on perms.
- this.gridContext.saveColumns().then(
+ this.gridContext.saveGridConfig().then(
// hide the with config after saving
ok => this.colWidthConfig.isVisible = false,
err => console.error(`Error saving columns: ${err}`)
import {Subscription} from 'rxjs/Subscription';
import {IdlService} from '@eg/core/idl.service';
import {OrgService} from '@eg/core/org.service';
-import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
import {FormatService} from '@eg/core/format.service';
import {GridContext, GridColumn, GridDataSource, GridRowFlairEntry} from './grid';
constructor(
private idl: IdlService,
private org: OrgService,
- private store: StoreService,
+ private store: ServerStoreService,
private format: FormatService
) {
this.context =
import {Subscription} from 'rxjs/Subscription';
import {IdlService, IdlObject} from '@eg/core/idl.service';
import {OrgService} from '@eg/core/org.service';
-import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
import {FormatService} from '@eg/core/format.service';
import {Pager} from '@eg/share/util/pager';
compileSaveObject(): GridColumnPersistConf[] {
// only store information about visible columns.
- const conf = this.displayColumns();
-
- // scrunch the data down to just the needed info
- return conf.map(col => {
+ // scrunch the data down to just the needed info.
+ return this.displayColumns().map(col => {
const c: GridColumnPersistConf = {name : col.name};
if (col.align !== 'left') { c.align = col.align; }
if (col.flex !== 2) { c.flex = Number(col.flex); }
// Services injected by our grid component
idl: IdlService;
org: OrgService;
- store: StoreService;
+ store: ServerStoreService;
format: FormatService;
constructor(
- idl: IdlService, org: OrgService,
- store: StoreService, format: FormatService) {
+ idl: IdlService,
+ org: OrgService,
+ store: ServerStoreService,
+ format: FormatService) {
this.idl = idl;
this.org = org;
// Load initial settings and data.
initData() {
- this.applyColumnsConfig()
+ this.applyGridConfig()
.then(ok => this.dataSource.requestPage(this.pager))
.then(ok => this.listenToPager());
}
this.ignorePager();
}
- applyColumnsConfig(): Promise<void> {
- return this.getColumnsConfig(this.persistKey)
- .then(conf => this.columnSet.applyColumnSettings(conf));
+ applyGridConfig(): Promise<void> {
+ return this.getGridConfig(this.persistKey)
+ .then(conf => {
+ let columns = [];
+ if (conf) {
+ columns = conf.columns;
+ if (conf.limit) {
+ this.pager.limit = conf.limit;
+ }
+ }
+
+ // This is called regardless of the presence of saved
+ // settings so defaults can be applied.
+ this.columnSet.applyColumnSettings(columns);
+ });
}
reload() {
});
}
-
- saveColumns(): Promise<any> {
+ saveGridConfig(): Promise<any> {
if (!this.persistKey) {
throw new Error('Grid persistKey required to save columns');
}
- const compiled: GridColumnPersistConf[] =
- this.columnSet.compileSaveObject();
- return this.store.setItem('eg.grid.' + this.persistKey, compiled);
- }
+ const conf = new GridPersistConf();
+ conf.version = 2;
+ conf.limit = this.pager.limit;
+ conf.columns = this.columnSet.compileSaveObject();
+ return this.store.setItem('eg.grid.' + this.persistKey, conf);
+ }
- // TODO: saveColumnsAsOrgSetting(...)
+ // TODO: saveGridConfigAsOrgSetting(...)
- getColumnsConfig(persistKey: string): Promise<GridColumnPersistConf[]> {
- if (!persistKey) { return Promise.resolve([]); }
+ getGridConfig(persistKey: string): Promise<GridPersistConf> {
+ if (!persistKey) { return Promise.resolve(null); }
return this.store.getItem('eg.grid.' + persistKey);
}
-
}
export class GridColumnPersistConf {
align?: string;
}
+export class GridPersistConf {
+ version: number;
+ limit: number;
+ columns: GridColumnPersistConf[];
+}
+
// Actions apply to specific rows
export class GridToolbarAction {
* workstation settings.
*/
import {Injectable, EventEmitter} from '@angular/core';
-import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
const AUDIO_BASE_URL = '/audio/notifications/';
@Injectable()
// map of requested audio path to resolved path
private urlCache: {[path: string]: string} = {};
- constructor(private store: StoreService) {}
+ constructor(private store: ServerStoreService) {}
play(path: string): void {
if (path) {
) {}
ngOnInit() {
- this.store.getItem('eg.workstation.all')
- .then(list => this.workstations = list || [])
- .then(noop => this.store.getItem('eg.workstation.default'))
- .then(defWs => {
- this.defaultName = defWs;
- this.selectedName = this.auth.workstation() || defWs;
- })
- .then(noop => {
- const rm = this.route.snapshot.paramMap.get('remove');
- if (rm) {
- this.removeSelected(this.removeWorkstation = rm);
- }
- });
+ this.workstations = this.store.getLocalItem('eg.workstation.all');
+ this.defaultName = this.store.getLocalItem('eg.workstation.default');
+ this.selectedName = this.auth.workstation() || this.defaultName;
+ const rm = this.route.snapshot.paramMap.get('remove');
+ if (rm) {
+ this.removeSelected(this.removeWorkstation = rm);
+ }
+ // TODO: use the org selector limitPerm option
this.perm.hasWorkPermAt(['REGISTER_WORKSTATION'], true)
.then(perms => {
// Disable org units that cannot have users and any
setDefault(): void {
if (this.selected()) {
this.defaultName = this.selected().name;
- this.store.setItem('eg.workstation.default', this.defaultName);
+ this.store.setLocalItem('eg.workstation.default', this.defaultName);
}
}
}
this.workstations = this.workstations.filter(w => w.name !== name);
- this.store.setItem('eg.workstation.all', this.workstations);
+ this.store.setLocalItem('eg.workstation.all', this.workstations);
if (this.defaultName === name) {
this.defaultName = null;
- this.store.removeItem('eg.workstation.default');
+ this.store.removeLocalItem('eg.workstation.default');
}
}
};
this.workstations.push(ws);
- this.store.setItem('eg.workstation.all', this.workstations)
- .then(ok => {
- this.newName = '';
- // when registering our first workstation, mark it as the
- // default and show it as selected in the ws selector.
- if (this.workstations.length === 1) {
- this.selectedName = ws.name;
- this.setDefault();
- }
- });
+ this.store.setLocalItem('eg.workstation.all', this.workstations)
+ this.newName = '';
+ // when registering our first workstation, mark it as the
+ // default and show it as selected in the ws selector.
+ if (this.workstations.length === 1) {
+ this.selectedName = ws.name;
+ this.setDefault();
+ }
}
}
import {Observer} from 'rxjs/Observer';
import {Router, Resolve, RouterStateSnapshot,
ActivatedRouteSnapshot} from '@angular/router';
-import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
import {NetService} from '@eg/core/net.service';
import {OrgService} from '@eg/core/org.service';
import {AuthService} from '@eg/core/auth.service';
constructor(
private router: Router,
- private store: StoreService,
+ private store: ServerStoreService,
private org: OrgService,
private net: NetService,
private auth: AuthService,
// Focus username
this.renderer.selectRootElement('#username').focus();
- this.store.getItem('eg.workstation.all')
- .then(list => this.workstations = list || [])
- .then(list => this.store.getItem('eg.workstation.default'))
- .then(defWs => this.args.workstation = defWs)
- .then(noOp => this.applyWorkstation());
+ this.workstations = this.store.getLocalItem('eg.workstation.all');
+ this.args.workstation =
+ this.store.getLocalItem('eg.workstation.default');
+ this.applyWorkstation();
}
applyWorkstation() {