<eg-link-table-link i18n-label label="Item Tags"
routerLink="/staff/admin/local/asset/copy_tag"></eg-link-table-link>
<eg-link-table-link i18n-label label="Library Settings Editor"
- url="/eg/staff/admin/local/asset/org_unit_settings"></eg-link-table-link>
+ routerLink="/staff/admin/local/asset/org_unit_settings"></eg-link-table-link>
<eg-link-table-link i18n-label label="Non-Cataloged Types Editor"
routerLink="/staff/admin/local/config/non_cataloged_type"></eg-link-table-link>
<eg-link-table-link i18n-label label="Notifications / Action Triggers"
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" i18n>Edit Setting</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="row justify-content-center">
+ <div class="col">
+ <h5 i18n>{{entry.label}}</h5>
+ </div>
+ </div>
+ <div class="row justify-content-center">
+ <div class="col">
+ <span i18n>{{entry.description}}</span>
+ </div>
+ </div>
+ <div class="row mt-3">
+ <div class="col-md-6">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Context</div>
+ <eg-org-select [initialOrg]="entryContext"
+ (onChange)="entryContext = $event"></eg-org-select>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row mt-2">
+ <div class="col-md-6">
+ <ng-container [ngSwitch]="inputType()">
+
+ <ng-container *ngSwitchCase="'integer'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <input
+ class="form-control" type="number"
+ name="entryValue"
+ placeholder="Input a numerical value"
+ i18n-placeholder
+ [(ngModel)]="entryValue"/>
+ </div>
+ </div>
+ </ng-container>
+ <ng-container *ngSwitchCase="'currency'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <div class="input-group-text" i18n>$</div>
+ <input
+ class="form-control" type="number"
+ step="0.01"
+ name="entryValue"
+ placeholder="Input a monetary value"
+ i18n-placeholder
+ [(ngModel)]="entryValue"/>
+ </div>
+ </div>
+ </ng-container>
+ <ng-container *ngSwitchCase="'string'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <input
+ class="form-control" type="text"
+ name="entryValue"
+ placeholder="Input a value"
+ i18n-placeholder
+ [(ngModel)]="entryValue"/>
+ </div>
+ </div>
+ </ng-container>
+ <ng-container *ngSwitchCase="'interval'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <input
+ class="form-control" type="text"
+ name="entryValue"
+ placeholder="e.g. 1 day, 4 months"
+ i18n-placeholder
+ [(ngModel)]="entryValue"/>
+ </div>
+ </div>
+ </ng-container>
+ <ng-container *ngSwitchCase="'bool'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <select
+ class="custom-select" name="entryValue"
+ placeholder="True or False" i18n-placeholder
+ [(ngModel)]="entryValue">
+ <option value='true' i18n>True</option>
+ <option value='false' i18n>False</option>
+ </select>
+ </div>
+ </div>
+ </ng-container>
+ <ng-container *ngSwitchCase="'array'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <input
+ class="form-control" type="text"
+ name="entryValue"
+ placeholder="Input a comma-separated list..."
+ i18n-placeholder
+ [(ngModel)]="entryValue"/>
+ </div>
+ </div>
+ </ng-container>
+ <ng-container *ngSwitchCase="'link'">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Value</div>
+ <ng-container [ngSwitch]="entry.fmClass">
+ <ng-container *ngSwitchCase="'acpl'">
+ <eg-combobox placeholder="Select a value" [idlClass]="entry.fmClass" idlField="name" idlIncludeLibraryInLabel="owning_lib"
+ [asyncSupportsEmptyTermClick]="true" [displayTemplate]="fmClassLabel"
+ (onChange)="setInputValue($event ? $event.id : null)">
+ </eg-combobox>
+ </ng-container>
+ <ng-container *ngSwitchDefault>
+ <eg-combobox placeholder="Select a value" [idlClass]="entry.fmClass" idlField="name"
+ [asyncSupportsEmptyTermClick]="true" [displayTemplate]="fmClassLabel"
+ (onChange)="setInputValue($event ? $event.id : null)">
+ </eg-combobox>
+ </ng-container>
+ </ng-container><!-- fmClass ngSwitch -->
+ </div>
+ </div>
+ </ng-container>
+
+ </ng-container> <!-- input type ngSwitch -->
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-success"
+ (click)="delete()" i18n>Delete Setting</button>
+ <button type="button" class="btn btn-warning"
+ (click)="update()" i18n>Update Setting</button>
+ </div>
+</ng-template>
+
+<ng-template #fmClassLabel let-r="result" i18n>
+ {{r.label}}
+</ng-template>
\ No newline at end of file
--- /dev/null
+import {Component, Input, ViewChild, TemplateRef} from '@angular/core';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {OrgService} from '@eg/core/org.service';
+import {IdlObject} from '@eg/core/idl.service';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {OrgUnitSetting} from '@eg/staff/admin/local/org-unit-settings/org-unit-settings.component';
+
+@Component({
+ selector: 'eg-admin-edit-org-unit-setting-dialog',
+ templateUrl: './edit-org-unit-setting-dialog.component.html'
+})
+
+export class EditOuSettingDialogComponent extends DialogComponent {
+
+ // What OU Setting we're editing
+ entry: any = {};
+ entryValue: any;
+ entryContext: IdlObject;
+ linkedFieldOptions: IdlObject[];
+
+ constructor(
+ private auth: AuthService,
+ private net: NetService,
+ private org: OrgService,
+ private modal: NgbModal
+ ) {
+ super(modal);
+ if (!this.entry) {
+ this.entryValue = null;
+ this.entryContext = null;
+ this.linkedFieldOptions = null;
+ }
+ }
+
+ inputType() {
+ return this.entry.dataType;
+ }
+
+ setInputValue(inputValue) {
+ console.log("In Input value");
+ console.log(inputValue);
+ this.entryValue = inputValue;
+ }
+
+ getFieldClass() {
+ return this.entry.fm_class;
+ }
+
+ delete() {
+ this.close({setting: {[this.entry.name]: null}, context: this.entryContext});
+ }
+
+ update() {
+ this.close({setting: {[this.entry.name]: this.entryValue}, context: this.entryContext});
+ }
+}
+
+
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" i18n>History</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="row">
+ <div class="col-md-12">
+ <span i18n>{{entry.label}}</span>
+ </div>
+ </div>
+ <div class="mt-3">
+ <eg-grid #historyGrid [dataSource]="gridDataSource"
+ [disableSelect]="true" [disableMultiSelect]="true"
+ [sortable]="false">
+
+ <eg-grid-column path="id" [index]=true label="ID" i18n-label hidden></eg-grid-column>
+ <eg-grid-column path="date_applied" label="Date Changed" datatype="timestamp" i18n-label></eg-grid-column>
+ <eg-grid-column path="org.shortname" label="Location" i18n-label></eg-grid-column>
+ <eg-grid-column path="original_value_str" label="Original Value" i18n-label></eg-grid-column>
+ <eg-grid-column path="new_value_str" label="New Value" i18n-label></eg-grid-column>
+ <eg-grid-column i18n-label label="Revert?" name="revert"
+ [cellTemplate]="revertTemplate"></eg-grid-column>
+ </eg-grid>
+ </div>
+ </div>
+ <ng-template #revertTemplate let-log="row">
+ <span>
+ <a
+ (click)="revert(log)" class="pl-1"
+ [routerLink]="" i18n>
+ Revert
+ </a>
+ </span>
+ </ng-template>
+</ng-template>
\ No newline at end of file
--- /dev/null
+import {Component, Input, ViewChild, OnInit, TemplateRef} from '@angular/core';
+import {Observable, Observer, of} from 'rxjs';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {OrgService} from '@eg/core/org.service';
+import {Pager} from '@eg/share/util/pager';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridToolbarCheckboxComponent
+ } from '@eg/share/grid/grid-toolbar-checkbox.component';
+import {OrgUnitSetting} from '@eg/staff/admin/local/org-unit-settings/org-unit-settings.component';
+
+@Component({
+ selector: 'eg-admin-ou-setting-history-dialog',
+ templateUrl: './org-unit-setting-history-dialog.component.html'
+})
+
+export class OuSettingHistoryDialogComponent extends DialogComponent {
+
+ entry: any = {};
+ history: any[] = [];
+ gridDataSource: GridDataSource;
+ @ViewChild('historyGrid', { static:true }) historyGrid: GridComponent;
+
+
+ constructor(
+ private auth: AuthService,
+ private net: NetService,
+ private org: OrgService,
+ private modal: NgbModal
+ ) {
+ super(modal);
+ this.gridDataSource = new GridDataSource();
+ }
+
+ ngOnInit() {
+ this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+ return this.fetchHistory(pager);
+ };
+ }
+
+ fetchHistory(pager: Pager): Observable<any> {
+ return new Observable<any>(observer => {
+ this.gridDataSource.data = this.history;
+ observer.complete();
+ });
+ }
+
+ revert(log) {
+ if (log) {
+ var intTypes = ["integer", "currency", "link"];
+ if (intTypes.includes(this.entry.dataType)) {
+ log.new_value = parseInt(log.new_value);
+ } else {
+ log.new_value = log.new_value.replace(/^"(.*)"$/, '$1');
+ }
+ this.close({
+ setting: {[this.entry.name]: log.new_value},
+ context: this.org.get(log.org),
+ revert: true
+ });
+ this.gridDataSource.data = null;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title" *ngIf="isExport" i18n>Export</h4>
+ <h4 class="modal-title" *ngIf="!isExport" i18n>Import</h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <div class="row">
+ <div class="col-md-12">
+ <span *ngIf="isExport" i18n>
+ Copy this to your clipboard and save it to a file to export the settings.
+ </span>
+ <span *ngIf="!isExport" i18n>
+ Paste in your exported settings.
+ </span>
+ </div>
+ </div>
+ <div class="row mt-3">
+ <div class="col-md-12">
+ <textarea class="form-control" [(ngModel)]="jsonData" rows="4"></textarea>
+ </div>
+ </div>
+ <div class="row mt-3" *ngIf="!isExport">
+ <div class="col-md-3">
+ <button class="btn btn-outline-dark" i18n (click)="update()">Submit</button>
+ </div>
+ </div>
+ </div>
+</ng-template>
\ No newline at end of file
--- /dev/null
+import {Component, Input, ViewChild, TemplateRef} from '@angular/core';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {OrgUnitSetting} from '@eg/staff/admin/local/org-unit-settings/org-unit-settings.component';
+
+@Component({
+ selector: 'eg-admin-ou-setting-json-dialog',
+ templateUrl: './org-unit-setting-json-dialog.component.html'
+})
+
+export class OuSettingJsonDialogComponent extends DialogComponent {
+
+ isExport: boolean;
+ @Input() jsonData: string;
+
+ constructor(
+ private modal: NgbModal
+ ) {
+ super(modal);
+ }
+
+ update() {
+ this.close({
+ apply: true,
+ jsonData: this.jsonData
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+import {NgModule} from '@angular/core';\r
+import {RouterModule, Routes} from '@angular/router';\r
+import {OrgUnitSettingsComponent} from './org-unit-settings.component';\r
+\r
+const routes: Routes = [{\r
+ path: '',\r
+ component: OrgUnitSettingsComponent\r
+}];\r
+\r
+@NgModule({\r
+ imports: [RouterModule.forChild(routes)],\r
+ exports: [RouterModule]\r
+})\r
+\r
+export class OrgUnitSettingsRoutingModule {}
\ No newline at end of file
--- /dev/null
+<eg-title i18n-prefix prefix="Org Unit Settings Editor"></eg-title>
+<eg-staff-banner bannerText="Org Unit Settings Config" i18n-bannerText></eg-staff-banner>
+<!-- org unit selector -->
+
+<eg-admin-edit-org-unit-setting-dialog #editOuSettingDialog>
+</eg-admin-edit-org-unit-setting-dialog>
+
+<eg-admin-ou-setting-history-dialog #orgUnitSettingHistoryDialog>
+</eg-admin-ou-setting-history-dialog>
+
+<eg-admin-ou-setting-json-dialog #ouSettingJsonDialog>
+</eg-admin-ou-setting-json-dialog>
+
+<div class="row mt-3">
+ <div class="col-md-3">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <div class="input-group-text" i18n>Context Location</div>
+ <eg-org-select [initialOrg]="contextOrg"
+ (onChange)="contextOrgChanged($event)">
+ </eg-org-select>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-6">
+ <div class="input-group">
+ <input type="text"
+ class="form-control"
+ [(ngModel)]="filterString"
+ (blur)="applyFilter()"
+ />
+ <button class="btn btn-outline-dark mr-1" i18n>Filter</button>
+ <button class="btn btn-outline-dark mr-1" i18n
+ (click)="applyFilter(true)">Clear Filter</button>
+ </div>
+ </div>
+ <div class="col-md-3">
+ <div class="input-group">
+ <button class="btn btn-outline-dark mr-1"
+ (click)="showJsonDialog(true)" i18n>Export</button>
+ <button class="btn btn-outline-dark mr-1"
+ (click)="showJsonDialog(false)" i18n>Import</button>
+ </div>
+ </div>
+
+</div>
+<!-- Org Unit Settings Grid -->
+<div class='w-11 mt-3'>
+ <eg-grid #orgUnitSettingsGrid [dataSource]="gridDataSource"
+ [disableSelect]="true"
+ [sortable]="false" [showDeclaredFieldsOnly]="true"
+ persistKey="admin.actor.org_unit_settings">
+
+ <eg-grid-column i18n-label label="Edit" name="edit"
+ [cellTemplate]="editCellTemplate"></eg-grid-column>
+ <eg-grid-column i18n-label label="History" name="history"
+ [cellTemplate]="historyCellTemplate"></eg-grid-column>
+ <eg-grid-column path="grp" label="Group" i18n-label></eg-grid-column>
+ <eg-grid-column path="label" label="Setting" [index]="true" i18n-label></eg-grid-column>
+ <eg-grid-column path="context.shortname()" label="Context" i18n-label></eg-grid-column>
+ <eg-grid-column path="value_str" label="Value" i18n-label></eg-grid-column>
+
+ </eg-grid>
+</div>
+
+<ng-template #editCellTemplate let-entry="row">
+ <span>
+ <a
+ (click)="showEditSettingValueDialog(entry)" class="pl-1"
+ [routerLink]="" i18n>
+ Edit
+ </a>
+ </span>
+</ng-template>
+
+<ng-template #historyCellTemplate let-entry="row">
+ <span>
+ <a
+ (click)="showHistoryDialog(entry)" class="pl-1"
+ [routerLink]="" i18n>
+ History
+ </a>
+ </span>
+</ng-template>
\ No newline at end of file
--- /dev/null
+import {Component, OnInit, Input, ViewChild, ViewEncapsulation
+ } from '@angular/core';
+import {Router} from '@angular/router';
+import {Observable, Observer, of} from 'rxjs';
+import {map} from 'rxjs/operators';
+import {Pager} from '@eg/share/util/pager';
+import {IdlObject, IdlService} from '@eg/core/idl.service';
+import {OrgService} from '@eg/core/org.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridToolbarCheckboxComponent
+ } from '@eg/share/grid/grid-toolbar-checkbox.component';
+import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {ToastService} from '@eg/share/toast/toast.service';
+
+import {EditOuSettingDialogComponent
+ } from '@eg/staff/admin/local/org-unit-settings/edit-org-unit-setting-dialog.component';
+import {OuSettingHistoryDialogComponent
+ } from '@eg/staff/admin/local/org-unit-settings/org-unit-setting-history-dialog.component';
+import {OuSettingJsonDialogComponent
+ } from '@eg/staff/admin/local/org-unit-settings/org-unit-setting-json-dialog.component';
+
+export class OrgUnitSetting {
+ name: string;
+ label: string;
+ grp: string;
+ description: string;
+ value: any;
+ value_str: any;
+ dataType: string;
+ fmClass: string;
+ _idlOptions: IdlObject[];
+ _org_unit: IdlObject;
+ context: string;
+ view_perm: string;
+ _history: any[];
+}
+
+@Component({
+ templateUrl: './org-unit-settings.component.html'
+})
+
+export class OrgUnitSettingsComponent {
+
+ contextOrg: IdlObject;
+
+ initDone = false;
+ gridDataSource: GridDataSource;
+ gridTemplateContext: any;
+ prevFilter: string;
+ currentHistory: any[];
+ currentOptions: any[];
+ jsonFieldData: {};
+ @ViewChild('orgUnitSettingsGrid', { static:true }) orgUnitSettingsGrid: GridComponent;
+
+ @ViewChild('editOuSettingDialog', { static:true })
+ private editOuSettingDialog: EditOuSettingDialogComponent;
+ @ViewChild('orgUnitSettingHistoryDialog', { static:true })
+ private orgUnitSettingHistoryDialog: OuSettingHistoryDialogComponent;
+ @ViewChild('ouSettingJsonDialog', { static:true })
+ private ouSettingJsonDialog: OuSettingJsonDialogComponent;
+
+ refreshSettings: boolean;
+ renderFromPrefs: boolean;
+
+ settingTypeArr: any[];
+
+ @Input() filterString: string;
+
+ constructor(
+ private router: Router,
+ private org: OrgService,
+ private idl: IdlService,
+ private pcrud: PcrudService,
+ private auth: AuthService,
+ private store: ServerStoreService,
+ private localStore: StoreService,
+ private toast: ToastService,
+ private net: NetService,
+ ) {
+ this.gridDataSource = new GridDataSource();
+ this.refreshSettings = true;
+ this.renderFromPrefs = true;
+
+ this.contextOrg = this.org.get(this.auth.user().ws_ou());
+ }
+
+ ngOnInit() {
+ this.initDone = true;
+ this.settingTypeArr = [];
+ this.gridDataSource.getRows = (pager: Pager, sort: any[]) => {
+ return this.fetchSettingTypes(pager);
+ };
+ }
+
+ fetchSettingTypes(pager: Pager): Observable<any> {
+ return new Observable<any>(observer => {
+ this.pcrud.retrieveAll('coust', {flesh: 3, flesh_fields: {
+ 'coust': ['grp', 'view_perm']
+ }},
+ { authoritative: true }).subscribe(
+ settingTypes => this.allocateSettingTypes(settingTypes),
+ err => {},
+ () => {
+ this.refreshSettings = false;
+ this.mergeSettingValues().then(
+ ok => {
+ this.flattenSettings(observer);
+ }
+ );
+ }
+ );
+ });
+ }
+
+ mergeSettingValues(): Promise<any> {
+ const settingNames = this.settingTypeArr.map(setting => setting.name);
+ return new Promise((resolve, reject) => {
+ this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.ou_setting.ancestor_default.batch',
+ this.contextOrg.id(), settingNames, this.auth.token()
+ ).subscribe(
+ blob => {
+ let settingVals = Object.keys(blob).map(key => {
+ return {'name': key, 'setting': blob[key]}
+ });
+ settingVals.forEach(key => {
+ if (key.setting) {
+ let settingsObj = this.settingTypeArr.filter(
+ setting => setting.name == key.name
+ )[0];
+ settingsObj.value = key.setting.value;
+ settingsObj.value_str = settingsObj.value;
+ if (settingsObj.dataType == 'link' && (key.setting.value || key.setting.value == 0)) {
+ this.fetchLinkedField(settingsObj.fmClass, key.setting.value, settingsObj.value_str).then(res => {
+ settingsObj.value_str = res;
+ });
+ }
+ settingsObj._org_unit = this.org.get(key.setting.org);
+ settingsObj.context = settingsObj._org_unit.shortname();
+ }
+ });
+ resolve(this.settingTypeArr);
+ },
+ err => reject(err)
+ );
+ });
+ }
+
+ fetchLinkedField(fmClass, id, val) {
+ return new Promise((resolve, reject) => {
+ return this.pcrud.retrieve(fmClass, id).subscribe(linkedField => {
+ val = linkedField.name();
+ resolve(val);
+ });
+ });
+ }
+
+ fetchHistory(setting): Promise<any> {
+ let name = setting.name;
+ return new Promise((resolve, reject) => {
+ this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.org_unit.settings.history.retrieve',
+ this.auth.token(), name, this.contextOrg.id()
+ ).subscribe(res=> {
+ this.currentHistory = [];
+ if (!Array.isArray(res)) {
+ res = [res];
+ }
+ res.forEach(log => {
+ log.org = this.org.get(log.org);
+ log.new_value_str = log.new_value;
+ log.original_value_str = log.original_value;
+ if (setting.dataType == "link") {
+ if (log.new_value) {
+ this.fetchLinkedField(setting.fmClass, parseInt(log.new_value), log.new_value_str).then(val => {
+ log.new_value_str = val;
+ });
+ }
+ if (log.original_value) {
+ this.fetchLinkedField(setting.fmClass, parseInt(log.original_value), log.original_value_str).then(val => {
+ log.original_value_str = val;
+ });
+ }
+ }
+ if (log.new_value_str) log.new_value_str = log.new_value_str.replace(/^"(.*)"$/, '$1');
+ if (log.original_value_str) log.original_value_str = log.original_value_str.replace(/^"(.*)"$/, '$1');
+ });
+ this.currentHistory = res;
+ this.currentHistory.sort((a, b) => {
+ return a.date_applied < b.date_applied ? 1 : -1;
+ });
+
+ resolve(this.currentHistory);
+ }, err=>{reject(err);});
+ });
+ }
+
+ allocateSettingTypes(coust: IdlObject) {
+ let entry = new OrgUnitSetting();
+ entry.name = coust.name();
+ entry.label = coust.label();
+ entry.dataType = coust.datatype();
+ if (coust.fm_class()) entry.fmClass = coust.fm_class();
+ if (coust.description()) entry.description = coust.description();
+ // For some reason some setting types don't have a grp, should look into this...
+ if (coust.grp()) entry.grp = coust.grp().label();
+ if (coust.view_perm())
+ entry.view_perm = coust.view_perm().code();
+
+ this.settingTypeArr.push(entry);
+ }
+
+ flattenSettings(observer: Observer<any>) {
+ this.gridDataSource.data = this.settingTypeArr;
+ observer.complete();
+ }
+
+ contextOrgChanged(org: IdlObject) {
+ this.updateGrid(org);
+ }
+
+ applyFilter(clear?: boolean) {
+ if (clear) this.filterString = '';
+ this.updateGrid(this.contextOrg);
+ }
+
+ updateSetting(obj, entry) {
+ this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.org_unit.settings.update',
+ this.auth.token(), obj.context.id(), obj.setting
+ ).toPromise().then(res=> {
+ this.toast.success(entry.label + " Updated.");
+ if (!obj.setting[entry.name]) {
+ let settingsObj = this.settingTypeArr.filter(
+ setting => setting.name == entry.name
+ )[0];
+ settingsObj.value = null;
+ settingsObj._org_unit = null;
+ settingsObj.context = null;
+ }
+ this.mergeSettingValues();
+ },
+ err => {
+ this.toast.danger(entry.label + " failed to update: " + err.desc);
+ });
+ }
+
+ showEditSettingValueDialog(entry: OrgUnitSetting) {
+ this.editOuSettingDialog.entry = entry;
+ this.editOuSettingDialog.entryValue = entry.value;
+ this.editOuSettingDialog.entryContext = entry._org_unit || this.contextOrg;
+ this.editOuSettingDialog.open({size: 'lg'}).subscribe(
+ res => {
+ this.updateSetting(res, entry);
+ }
+ );
+ }
+
+ showHistoryDialog(entry: OrgUnitSetting) {
+ if (entry) {
+ this.fetchHistory(entry).then(
+ fetched => {
+ this.orgUnitSettingHistoryDialog.history = this.currentHistory;
+ this.orgUnitSettingHistoryDialog.gridDataSource.data = this.currentHistory;
+ this.orgUnitSettingHistoryDialog.entry = entry;
+ this.orgUnitSettingHistoryDialog.open({size: 'lg'}).subscribe(res => {
+ if (res.revert) {
+ this.updateSetting(res, entry);
+ }
+ });
+ }
+ )
+ }
+ }
+
+ showJsonDialog(isExport: boolean) {
+ this.ouSettingJsonDialog.isExport = isExport;
+ this.ouSettingJsonDialog.jsonData = "";
+ if (isExport) {
+ this.ouSettingJsonDialog.jsonData = "{";
+ this.gridDataSource.data.forEach(entry => {
+ this.ouSettingJsonDialog.jsonData +=
+ "\"" + entry.name + "\": {\"org\": \"" +
+ this.contextOrg.id() + "\", \"value\": ";
+ if (entry.value) {
+ this.ouSettingJsonDialog.jsonData += "\"" + entry.value + "\"";
+ } else {
+ this.ouSettingJsonDialog.jsonData += "null";
+ }
+ this.ouSettingJsonDialog.jsonData += "}";
+ if (this.gridDataSource.data.indexOf(entry) != (this.gridDataSource.data.length - 1))
+ this.ouSettingJsonDialog.jsonData += ",";
+ });
+ this.ouSettingJsonDialog.jsonData += "}";
+ }
+
+ this.ouSettingJsonDialog.open({size: 'lg'}).subscribe(res => {
+ if (res.apply && res.jsonData) {
+ let jsonSettings = JSON.parse(res.jsonData);
+ Object.entries(jsonSettings).forEach((fields) => {
+ let entry = this.settingTypeArr.find(x => x.name == fields[0]);
+ let obj = {setting: {}, context: {}};
+ let val = this.parseValType(fields[1]['value'], entry.dataType);
+ obj.setting[fields[0]] = val;
+ obj.context = this.org.get(fields[1]['org']);
+ this.updateSetting(obj, entry);
+ });
+ }
+ });
+ }
+
+ parseValType(value, dataType) {
+ if (dataType == "integer" || "currency" || "link") {
+ return Number(value);
+ } else if (dataType == "bool") {
+ return (value === 'true');
+ } else {
+ return value;
+ }
+ }
+
+ filterCoust() {
+ if (this.filterString != this.prevFilter) {
+ this.prevFilter = this.filterString;
+ if (this.filterString) {
+ this.gridDataSource.data = [];
+ let tempGrid = this.settingTypeArr;
+ tempGrid.forEach(row => {
+ let containsString =
+ row.name.includes(this.filterString) ||
+ row.label.includes(this.filterString) ||
+ (row.grp && row.grp.includes(this.filterString)) ||
+ (row.description && row.description.includes(this.filterString));
+ if (containsString) {
+ this.gridDataSource.data.push(row);
+ }
+ });
+ } else {
+ this.gridDataSource.data = this.settingTypeArr;
+ }
+ }
+ }
+
+ updateGrid(org) {
+ if (this.contextOrg != org) {
+ this.contextOrg = org;
+ this.refreshSettings = true;
+ }
+
+ if (this.filterString != this.prevFilter) {
+ this.refreshSettings = true;
+ }
+
+ if (this.refreshSettings) {
+ this.mergeSettingValues().then(
+ res => this.filterCoust()
+ );
+ }
+ }
+}
--- /dev/null
+import {NgModule} from '@angular/core';
+import {AdminCommonModule} from '@eg/staff/admin/common.module';
+import {TreeModule} from '@eg/share/tree/tree.module';
+import {OrgUnitSettingsComponent} from './org-unit-settings.component';
+import {EditOuSettingDialogComponent} from './edit-org-unit-setting-dialog.component';
+import {OuSettingHistoryDialogComponent} from './org-unit-setting-history-dialog.component';
+import {OrgUnitSettingsRoutingModule} from './org-unit-settings-routing.module';
+import {OuSettingJsonDialogComponent} from './org-unit-setting-json-dialog.component';
+
+@NgModule({
+ declarations: [
+ OrgUnitSettingsComponent,
+ EditOuSettingDialogComponent,
+ OuSettingHistoryDialogComponent,
+ OuSettingJsonDialogComponent
+ ],
+ imports: [
+ AdminCommonModule,
+ OrgUnitSettingsRoutingModule,
+ TreeModule
+ ],
+ exports: [
+ ],
+ providers: [
+ ]
+})
+
+export class OrgUnitSettingsModule {
+}
\ No newline at end of file
}]
}, {
path: 'config/standing_penalty',
- component: StandingPenaltyComponent
+ component: StandingPenaltyComponent,
+}, {
+ path: 'asset/org_unit_settings',
+ loadChildren: '@eg/staff/admin/local/org-unit-settings/org-unit-settings.module#OrgUnitSettingsModule'
}, {
path: 'config/ui_staff_portal_page_entry',
component: AdminStaffPortalPageComponent
return 1;
}
+__PACKAGE__->register_method(
+ method => "get_ou_setting_history",
+ api_name => "open-ils.actor.org_unit.settings.history.retrieve",
+ signature => {
+ desc => "Retrieves the history of an Org Unit Setting. The permission to retrieve " .
+ "an org unit setting's history is dependant on a specific permission specified " .
+ "in the view_perm column of the config.org_unit_setting_type " .
+ "table's row corresponding to the setting being changed." ,
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'Org Unit ID', type => 'number'},
+ {desc => 'Setting Type Name', type => 'string'}
+ ],
+ return => {desc => 'History IDL Object'}
+ }
+);
+
+sub get_ou_setting_history {
+ my( $self, $client, $auth, $setting, $orgid ) = @_;
+ my $e = new_editor(authtoken => $auth, xact => 1);
+ return $e->die_event unless $e->checkauth;
+
+ return $U->ou_ancestor_setting_log(
+ $orgid, $setting, $e, $auth
+ );
+
+}
__PACKAGE__->register_method(
method => "set_ou_settings",
my $coust = $e->retrieve_config_org_unit_setting_type([
$name, {flesh => 1, flesh_fields => {coust => ['view_perm']}}
]);
- if ($coust && $coust->view_perm) {
- # And you can't have permission if you don't have a valid session.
- return undef if not $e->checkauth;
- # And now that we know you MIGHT have permission, we check it.
- return undef if not $e->allowed($coust->view_perm->code, $orgid);
- }
+ return undef unless ou_ancestor_setting_perm_check($orgid, $coust, $auth)
}
my $query = {from => ['actor.org_unit_ancestor_setting', $name, $orgid]};
my $setting = $e->json_query($query)->[0];
return undef unless $setting;
return {org => $setting->{org_unit}, value => OpenSRF::Utils::JSON->JSON2perl($setting->{value})};
-}
+}
+
+# Returns the org id if the requestor has the permissions required
+# to view the ou setting.
+sub ou_ancestor_setting_perm_check {
+ my( $self, $orgid, $view_perm, $e, $auth ) = @_;
+ $e = $e || OpenILS::Utils::CStoreEditor->new(
+ (defined $auth) ? (authtoken => $auth) : ()
+ );
+
+ # And you can't have permission if you don't have a valid session.
+ return undef if not $e->checkauth;
+ # And now that we know you MIGHT have permission, we check it.
+ if ($view_perm) {
+ return undef unless $e->allowed($view_perm, $orgid);
+ }
+
+ return $orgid;
+}
+
+sub ou_ancestor_setting_log {
+ my ( $self, $orgid, $name, $e, $auth ) = @_;
+ $e = $e || OpenILS::Utils::CStoreEditor->new(
+ (defined $auth) ? (authtoken => $auth, xact => 1) : ()
+ );
+ my $coust;
+
+ if ($auth) {
+ $coust = $e->retrieve_config_org_unit_setting_type([
+ $name, {flesh => 1, flesh_fields => {coust => ['view_perm']}}
+ ]);
+ my $orgs = $self->get_org_ancestors($orgid);
+
+ my $qorg = $self->ou_ancestor_setting_perm_check(
+ $orgs,
+ $coust,
+ $e,
+ $auth
+ );
+ my $sort = { order_by => { coustl => 'date_applied DESC' } };
+ return $e->json_query({
+ from => 'coustl',
+ where => {
+ field_name => $name,
+ org => $qorg
+ },
+ $sort
+ });
+ };
+}
# This fetches a set of OU settings in one fell swoop,
# which can be significantly faster than invoking
'cwst', 'label'
)
), (
+ 'eg.grid.asset.ouSettings', 'gui', 'object',
+ oils_i18n_gettext(
+ 'eg.grid.asset.ouSettings',
+ 'Grid Config: asset.ouSettings',
+ 'cwst', 'label'
+), (
'eg.cat.record.summary.collapse', 'gui', 'bool',
oils_i18n_gettext(
'eg.cat.record.summary.collapse',
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.workstation_setting_type (name, grp, datatype, label)
+VALUES (
+ 'eg.grid.asset.ouSettings', 'gui', 'object',
+ oils_i18n_gettext(
+ 'eg.grid.asset.ouSettings',
+ 'Grid Config: asset.ouSettings',
+ 'cwst', 'label'
+ )
+);
+
+COMMIT;
\ No newline at end of file