js modernization and de-angularing
This commit is contained in:
parent
c9864ae7e9
commit
f3297326e8
491 changed files with 8283 additions and 40751 deletions
6
.mvn/wrapper/maven-wrapper.properties
vendored
6
.mvn/wrapper/maven-wrapper.properties
vendored
|
@ -6,7 +6,7 @@
|
|||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
|
@ -14,5 +14,5 @@
|
|||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
|
||||
|
|
|
@ -251,6 +251,7 @@
|
|||
</module>
|
||||
-->
|
||||
|
||||
<module name="UnnecessarySemicolonInEnumeration"/>
|
||||
|
||||
<!-- future enabled checks -->
|
||||
<!--
|
||||
|
|
12
client/angular/.gitignore
vendored
12
client/angular/.gitignore
vendored
|
@ -1,12 +0,0 @@
|
|||
# NPM
|
||||
/node_modules
|
||||
.node
|
||||
|
||||
# Generated Javascript files
|
||||
/src/**/*.js
|
||||
|
||||
# Build output
|
||||
/dist
|
||||
|
||||
# Generated log files
|
||||
*.log
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>m</title><path d="M16,43a6.89,6.89,0,1,1,6.89-6.89A6.9,6.9,0,0,1,16,43Zm0-11.09a4.19,4.19,0,1,0,4.19,4.19A4.2,4.2,0,0,0,16,31.91Z" fill="gray"/><path d="M36.22,43a6.89,6.89,0,1,1,6.89-6.89A6.9,6.9,0,0,1,36.22,43Zm0-11.09a4.19,4.19,0,1,0,4.19,4.19A4.2,4.2,0,0,0,36.22,31.91Z" fill="gray"/><path d="M56.09,43A6.89,6.89,0,1,1,63,36.11,6.9,6.9,0,0,1,56.09,43Zm0-11.09a4.19,4.19,0,1,0,4.19,4.19A4.2,4.2,0,0,0,56.09,31.91Z" fill="gray"/></svg>
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB |
|
@ -1,91 +0,0 @@
|
|||
{
|
||||
"name": "ng1-webpack",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"node": ">=6.2",
|
||||
"npm": ">=3.9"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --mode=development",
|
||||
"clean": "rimraf dist/",
|
||||
"test": "karma start test/karma.conf.js",
|
||||
"test-single-run": "karma start test/karma.conf.js --singleRun --no-auto-watch",
|
||||
"start": "webpack-dev-server --mode=development --port 4000 --history-api-fallback --colors --progress"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@microfocus/ias-icons": "1.1.3",
|
||||
"@microfocus/ng-ias": "1.0.1",
|
||||
"@microfocus/ux-ias": "1.1.3",
|
||||
"@uirouter/angularjs": "1.0.30",
|
||||
"angular": "1.8.3",
|
||||
"angular-aria": "1.8.3",
|
||||
"angular-sanitize": "1.8.3",
|
||||
"angular-translate": "2.19.0",
|
||||
"core-js": "3.27.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/angular": "^1.8.3",
|
||||
"@types/angular-mocks": "1.5.11",
|
||||
"@types/angular-translate": "2.15.2",
|
||||
"@types/angular-ui-router": "1.1.40",
|
||||
"@types/jasmine": "2.8.9",
|
||||
"@types/node": "9.4.7",
|
||||
"angular-mocks": "1.6.9",
|
||||
"autoprefixer": "8.1.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.3.2",
|
||||
"file-loader": "1.1.11",
|
||||
"html-loader": "0.5.5",
|
||||
"html-webpack-plugin": "3.0.6",
|
||||
"ignore-loader": "0.1.2",
|
||||
"imports-loader": "0.8.0",
|
||||
"jasmine": "3.2.0",
|
||||
"jasmine-core": "3.2.1",
|
||||
"jshint": "^2.13.4",
|
||||
"jshint-loader": "0.8.4",
|
||||
"json-loader": "0.5.7",
|
||||
"karma": "^6.3.20",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-jasmine": "1.1.2",
|
||||
"karma-jasmine-html-reporter": "1.3.1",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-spec-reporter": "0.0.32",
|
||||
"karma-webpack": "5.0.0",
|
||||
"lodash": ">=4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"ngtemplate-loader": "2.0.1",
|
||||
"node-sass": "^7.0.0",
|
||||
"postcss-loader": "2.1.1",
|
||||
"raw-loader": "0.5.1",
|
||||
"rimraf": "2.6.2",
|
||||
"sass-loader": "6.0.7",
|
||||
"serialize-javascript": "4.0.0",
|
||||
"string-replace-loader": "2.1.1",
|
||||
"style-loader": "0.20.3",
|
||||
"trim-newlines": ">=3.0.1",
|
||||
"ts-loader": "4.0.1",
|
||||
"ts-mockito": "2.3.1",
|
||||
"tslint": "5.9.1",
|
||||
"tslint-loader": "3.6.0",
|
||||
"typescript": "4.9.5",
|
||||
"uglifyjs-webpack-plugin": "2.2.0",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "^4.46.0",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-dev-server": "4.11.1",
|
||||
"webpack-merge": "4.1.2",
|
||||
"write-file-webpack-plugin": "4.2.0"
|
||||
},
|
||||
"overrides": {
|
||||
"jshint-loader": {
|
||||
"lodash": ">=4.17.21"
|
||||
},
|
||||
"rcloader": {
|
||||
"lodash": ">=4.17.21"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import * as angular from 'angular';
|
||||
import { IAugmentedJQuery, IAttributes} from 'angular';
|
||||
|
||||
export interface IContentTemplateFunction {
|
||||
($element: IAugmentedJQuery, $attrs?: IAttributes): string;
|
||||
}
|
||||
|
||||
export function Component(options: {
|
||||
bindings?: any,
|
||||
bindToController?: boolean,
|
||||
controllerAs?: string,
|
||||
template?: (string | any[] | IContentTemplateFunction),
|
||||
templateUrl?: string,
|
||||
transclude?: boolean,
|
||||
stylesheetUrl?: string
|
||||
}) {
|
||||
return (controller: Function) => angular.extend(options, { controller: controller });
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog autogen-change-password-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_RandomPasswords' | translate"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="'Display_PasswordGeneration' | translate"></p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr ng-repeat="i in [0,2,4,6,8,10,12,14,16,18]">
|
||||
<td ng-repeat="j in [i, i+1]">
|
||||
<div ng-bind="$ctrl.passwordSuggestions[j]" ng-click="$ctrl.onChoosePasswordSuggestion(j)">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="$ctrl.populatePasswordSuggestions()"
|
||||
ng-disabled="$ctrl.fetchingRandoms">{{ 'Button_More' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="cancel()">{{ 'Button_Cancel' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="cancel()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,35 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
ias-dialog {
|
||||
&.autogen-change-password-dialog {
|
||||
table {
|
||||
td {
|
||||
min-width: 160px;
|
||||
height: 15px;
|
||||
|
||||
div {
|
||||
cursor: pointer;
|
||||
font-family: monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHelpDeskService, IRandomPasswordResponse, ISuccessResponse} from '../../services/helpdesk.service';
|
||||
import {IPromise, IQService} from 'angular';
|
||||
import {IChangePasswordSuccess} from './success-change-password.controller';
|
||||
|
||||
const RANDOM_MAPPING_SIZE = 20;
|
||||
|
||||
require('./autogen-change-password.component.scss');
|
||||
|
||||
export default class AutogenChangePasswordController {
|
||||
fetchingRandoms: boolean;
|
||||
passwordSuggestions: string[];
|
||||
|
||||
static $inject = [ '$q', 'HelpDeskService', 'IasDialogService', 'personUserKey' ];
|
||||
constructor(private $q: IQService,
|
||||
private HelpDeskService: IHelpDeskService,
|
||||
private IasDialogService: any,
|
||||
private personUserKey: string) {
|
||||
this.passwordSuggestions = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
this.passwordSuggestions.push('');
|
||||
}
|
||||
this.populatePasswordSuggestions();
|
||||
}
|
||||
|
||||
generateRandomMapping(): number[] {
|
||||
let map: number[] = [];
|
||||
for (let i = 0; i < RANDOM_MAPPING_SIZE; i++) {
|
||||
map.push(i);
|
||||
}
|
||||
let randomComparatorFunction = () => 0.5 - Math.random();
|
||||
map.sort(randomComparatorFunction);
|
||||
map.sort(randomComparatorFunction);
|
||||
return map;
|
||||
}
|
||||
|
||||
onChoosePasswordSuggestion(index: number) {
|
||||
let chosenPassword = this.passwordSuggestions[index];
|
||||
this.HelpDeskService.setPassword(this.personUserKey, false, chosenPassword)
|
||||
.then((result: ISuccessResponse) => {
|
||||
// Send the password and success message to the parent element via the close() method.
|
||||
let data: IChangePasswordSuccess = { password: chosenPassword, successMessage: result.successMessage };
|
||||
this.IasDialogService.close(data);
|
||||
});
|
||||
}
|
||||
|
||||
passwordSuggestionFactory(index: number): any {
|
||||
return () => {
|
||||
return this.HelpDeskService.getRandomPassword(this.personUserKey).then(
|
||||
(result: IRandomPasswordResponse) => {
|
||||
this.passwordSuggestions[index] = result.password;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
populatePasswordSuggestions() {
|
||||
this.fetchingRandoms = true;
|
||||
let ordering = this.generateRandomMapping();
|
||||
let promiseChain: IPromise<any> = this.$q.when();
|
||||
ordering.forEach((index: number) => {
|
||||
promiseChain = promiseChain.then(this.passwordSuggestionFactory(index));
|
||||
});
|
||||
promiseChain.then(() => {
|
||||
this.fetchingRandoms = false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog random-change-password-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_ChangePassword' | translate"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="'Display_SetRandomPasswordPrompt' | translate"></p>
|
||||
</div>
|
||||
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="$ctrl.confirmSetRandomPassword()">{{ 'Button_OK' | translate }}</ias-button>
|
||||
<ias-button ng-click="cancel()">{{ 'Button_Cancel' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="cancel()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHelpDeskService, ISuccessResponse} from '../../services/helpdesk.service';
|
||||
import {IChangePasswordSuccess} from './success-change-password.controller';
|
||||
|
||||
export default class RandomChangePasswordController {
|
||||
|
||||
static $inject = [
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'personUserKey',
|
||||
'translateFilter'
|
||||
];
|
||||
constructor(private HelpDeskService: IHelpDeskService,
|
||||
private IasDialogService: any,
|
||||
private personUserKey: string,
|
||||
private translateFilter: (id: string) => string) {
|
||||
}
|
||||
|
||||
confirmSetRandomPassword() {
|
||||
this.HelpDeskService.setPassword(this.personUserKey, true)
|
||||
.then((result: ISuccessResponse) => {
|
||||
// Send the password and success message to the parent element via the close() method.
|
||||
let chosenPassword = '[' + this.translateFilter('Display_Random') + ']';
|
||||
let data: IChangePasswordSuccess = { password: chosenPassword, successMessage: result.successMessage };
|
||||
this.IasDialogService.close(data);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog success-change-password-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_ChangePassword' | translate"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="$ctrl.successMessage"></p>
|
||||
<div ng-if="$ctrl.displayNewPassword">
|
||||
<span ng-bind="'Field_NewPassword' | translate"></span>
|
||||
<ias-button ng-click="$ctrl.togglePasswordMasked()" ng-if="$ctrl.maskPasswords">
|
||||
{{ 'Button_Show' | translate }}
|
||||
</ias-button>
|
||||
<input ng-model="$ctrl.password" ng-hide="$ctrl.passwordMasked" readonly type="text" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="cancel()">{{ 'Button_OK' | translate }}</ias-button>
|
||||
<ias-button ng-click="$ctrl.clearAnswers()"
|
||||
ng-if="$ctrl.clearResponsesSetting==='ask'">{{ 'Button_ClearResponses' | translate }}
|
||||
</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="cancel()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHelpDeskService } from '../../services/helpdesk.service';
|
||||
import {IQService} from 'angular';
|
||||
import {IHelpDeskConfigService, PASSWORD_UI_MODES} from '../../services/helpdesk-config.service';
|
||||
|
||||
export interface IChangePasswordSuccess {
|
||||
password: string;
|
||||
successMessage: string;
|
||||
}
|
||||
|
||||
export default class SuccessChangePasswordController {
|
||||
clearResponsesSetting: string;
|
||||
maskPasswords: boolean;
|
||||
password: string;
|
||||
passwordMasked: boolean;
|
||||
successMessage: string;
|
||||
displayNewPassword: boolean;
|
||||
|
||||
static $inject = [
|
||||
'$q',
|
||||
'changePasswordSuccessData',
|
||||
'ConfigService',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'personUserKey',
|
||||
'translateFilter'
|
||||
];
|
||||
constructor(private $q: IQService,
|
||||
changePasswordSuccessData: IChangePasswordSuccess,
|
||||
private configService: IHelpDeskConfigService,
|
||||
private HelpDeskService: IHelpDeskService,
|
||||
private IasDialogService: any,
|
||||
private personUserKey: string,
|
||||
private translateFilter: (id: string) => string) {
|
||||
this.password = changePasswordSuccessData.password;
|
||||
this.successMessage = changePasswordSuccessData.successMessage;
|
||||
|
||||
let promise = this.$q.all([
|
||||
this.configService.getClearResponsesSetting(),
|
||||
this.configService.maskPasswordsEnabled(),
|
||||
this.configService.getPasswordUiMode()
|
||||
]);
|
||||
promise.then((result) => {
|
||||
this.clearResponsesSetting = result[0];
|
||||
this.maskPasswords = result[1];
|
||||
this.passwordMasked = this.maskPasswords;
|
||||
|
||||
// If it's random, don't display the new password
|
||||
this.displayNewPassword = (result[2] !== PASSWORD_UI_MODES.RANDOM);
|
||||
});
|
||||
}
|
||||
|
||||
clearAnswers() {
|
||||
this.IasDialogService.close();
|
||||
}
|
||||
|
||||
togglePasswordMasked() {
|
||||
this.passwordMasked = !this.passwordMasked;
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog type-change-password-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_ChangePassword' | translate"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="$ctrl.message"></p>
|
||||
|
||||
<table ng-model-options="{ debounce: $ctrl.inputDebounce }">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<input autofocus ng-model="$ctrl.password1" ng-hide="$ctrl.passwordMasked" type="text">
|
||||
<input autofocus ng-model="$ctrl.password1" ng-show="$ctrl.passwordMasked" type="password">
|
||||
</td>
|
||||
<td>
|
||||
<div class="strength-meter"
|
||||
ng-attr-title="{{ 'Display_StrengthMeter' | translate }}"
|
||||
ng-if="$ctrl.showStrengthMeter">
|
||||
<ias-icon class="strength-base" icon="strength5"></ias-icon>
|
||||
<ias-icon class="strength" icon="strength1" ng-show="$ctrl.strength===1"></ias-icon>
|
||||
<ias-icon class="strength" icon="strength2" ng-show="$ctrl.strength===2"></ias-icon>
|
||||
<ias-icon class="strength" icon="strength3" ng-show="$ctrl.strength===3"></ias-icon>
|
||||
<ias-icon class="strength" icon="strength4" ng-show="$ctrl.strength===4"></ias-icon>
|
||||
<ias-icon class="strength" icon="strength5" ng-show="$ctrl.strength===5"></ias-icon>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input ng-model="$ctrl.password2" ng-hide="$ctrl.passwordMasked" type="text">
|
||||
<input ng-model="$ctrl.password2" ng-show="$ctrl.passwordMasked" type="password">
|
||||
<div id="password-icon"
|
||||
ng-attr-title="{{ 'Button_Show' | translate }}"
|
||||
ng-if="$ctrl.maskPasswords">
|
||||
<a ng-bind="'Button_Show' | translate"
|
||||
ng-click="$ctrl.togglePasswordMasked()"
|
||||
tabindex="0"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<ias-icon icon="status_ok_thin"
|
||||
ng-attr-title="{{ 'Display_MatchCondition' | translate }}"
|
||||
ng-show="$ctrl.matchStatus==='MATCH'"></ias-icon>
|
||||
<ias-icon icon="message_error_thick"
|
||||
ng-attr-title="{{ 'Display_MatchCondition' | translate }}"
|
||||
ng-show="$ctrl.matchStatus==='NO_MATCH'"></ias-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="$ctrl.chooseTypedPassword()"
|
||||
ng-disabled="!$ctrl.passwordAcceptable">{{ 'Button_ChangePassword' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="$ctrl.onClickRandomPasswords()"
|
||||
ng-if="$ctrl.passwordUiMode === 'both'">{{ 'Title_RandomPasswords' | translate }}
|
||||
</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="cancel()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,105 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
.ias-dialog {
|
||||
&.type-change-password-dialog {
|
||||
.ias-dialog-content {
|
||||
table {
|
||||
border: none;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: auto;
|
||||
|
||||
input {
|
||||
margin: 7px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ias-icon-message_error_thick {
|
||||
color: #e50000;
|
||||
}
|
||||
|
||||
.ias-icon-status_ok_thin {
|
||||
color: #37c26a;
|
||||
}
|
||||
|
||||
.strength-meter {
|
||||
position: relative;
|
||||
|
||||
.ias-icon {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.strength {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
&.ias-icon-strength1 {
|
||||
color: #e50000;
|
||||
}
|
||||
|
||||
&.ias-icon-strength2 {
|
||||
color: #f17e12;
|
||||
}
|
||||
|
||||
&.ias-icon-strength3 {
|
||||
color: #ffd92d;
|
||||
}
|
||||
|
||||
&.ias-icon-strength4 {
|
||||
color: #01a9e7;
|
||||
}
|
||||
|
||||
&.ias-icon-strength5 {
|
||||
color: #37c26a;
|
||||
}
|
||||
}
|
||||
|
||||
.strength-base {
|
||||
color: #dae1e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
.ias-dialog {
|
||||
&.type-change-password-dialog {
|
||||
.ias-dialog-content {
|
||||
table {
|
||||
.strength-meter {
|
||||
.strength {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHelpDeskService, ISuccessResponse} from '../../services/helpdesk.service';
|
||||
import {ILogService, IQService, IScope, ITimeoutService, IWindowService} from 'angular';
|
||||
import {IHelpDeskConfigService} from '../../services/helpdesk-config.service';
|
||||
import {IChangePasswordSuccess} from './success-change-password.controller';
|
||||
import {IPasswordService, IValidatePasswordData} from '../../services/password.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
|
||||
require('./type-change-password.component.scss');
|
||||
|
||||
const EMPTY_MATCH_STATUS = 'EMPTY';
|
||||
const IN_PROGRESS_MESSAGE_WAIT_MS = 5;
|
||||
|
||||
export default class TypeChangePasswordController {
|
||||
inputDebounce: number;
|
||||
maskPasswords: boolean;
|
||||
matchStatus: string;
|
||||
message: string;
|
||||
password1: string;
|
||||
password2: string;
|
||||
passwordAcceptable: boolean;
|
||||
passwordMasked: boolean;
|
||||
passwordSuggestions: string[];
|
||||
passwordUiMode: string;
|
||||
pendingValidation: boolean;
|
||||
showStrengthMeter: boolean;
|
||||
strength: number;
|
||||
|
||||
static $inject = [
|
||||
'$log',
|
||||
'$q',
|
||||
'$scope',
|
||||
'$timeout',
|
||||
'ConfigService',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'PasswordService',
|
||||
'personUserKey',
|
||||
'PwmService',
|
||||
'translateFilter'
|
||||
];
|
||||
constructor(private $log: ILogService,
|
||||
private $q: IQService,
|
||||
private $scope: IScope,
|
||||
private $timeout: ITimeoutService,
|
||||
private configService: IHelpDeskConfigService,
|
||||
private HelpDeskService: IHelpDeskService,
|
||||
private IasDialogService: any,
|
||||
private passwordService: IPasswordService,
|
||||
private personUserKey: string,
|
||||
private pwmService: IPwmService,
|
||||
private translateFilter: (id: string) => string) {
|
||||
this.inputDebounce = this.pwmService.ajaxTypingWait;
|
||||
this.matchStatus = EMPTY_MATCH_STATUS;
|
||||
this.message = translateFilter('Display_PasswordPrompt');
|
||||
this.password1 = '';
|
||||
this.password2 = '';
|
||||
this.passwordAcceptable = false;
|
||||
this.passwordSuggestions = [];
|
||||
for (let i = 0; i < 20; i++) {
|
||||
this.passwordSuggestions.push('');
|
||||
}
|
||||
this.pendingValidation = false;
|
||||
this.showStrengthMeter = HelpDeskService.showStrengthMeter;
|
||||
this.strength = 0;
|
||||
|
||||
let promise = this.$q.all([
|
||||
this.configService.getPasswordUiMode(),
|
||||
this.configService.maskPasswordsEnabled()
|
||||
]);
|
||||
promise.then((result) => {
|
||||
this.passwordUiMode = result[0];
|
||||
this.maskPasswords = result[1];
|
||||
this.passwordMasked = this.maskPasswords;
|
||||
});
|
||||
|
||||
// Update dialog whenever a password field changes
|
||||
this.$scope.$watch('$ctrl.password1', (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
if (this.password2.length) {
|
||||
this.password2 = '';
|
||||
}
|
||||
|
||||
this.updateDialog();
|
||||
}
|
||||
});
|
||||
|
||||
this.$scope.$watch('$ctrl.password2', (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
this.updateDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
chooseTypedPassword() {
|
||||
if (!this.passwordAcceptable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.HelpDeskService.setPassword(this.personUserKey, false, this.password1)
|
||||
.then((result: ISuccessResponse) => {
|
||||
// Send the password and success message to the parent element via the close() method.
|
||||
let data: IChangePasswordSuccess = { password: this.password1, successMessage: result.successMessage };
|
||||
this.IasDialogService.close(data);
|
||||
});
|
||||
}
|
||||
|
||||
// Use the autogenPasswords property to signify to the parent element that the operator clicked "Random Passwords"
|
||||
onClickRandomPasswords() {
|
||||
this.IasDialogService.close({ autogenPasswords: true });
|
||||
}
|
||||
|
||||
togglePasswordMasked() {
|
||||
this.passwordMasked = !this.passwordMasked;
|
||||
}
|
||||
|
||||
updateDialog() {
|
||||
// Since user may continue typing, don't process request if another is already in progress
|
||||
if (this.pendingValidation) {
|
||||
return;
|
||||
}
|
||||
this.pendingValidation = true;
|
||||
|
||||
this.passwordService.validatePassword(this.password1, this.password2, this.personUserKey)
|
||||
.then(
|
||||
(data: IValidatePasswordData) => {
|
||||
this.pendingValidation = false;
|
||||
if (data.version !== 2) {
|
||||
throw new Error('[ unexpected version string from server ]');
|
||||
}
|
||||
|
||||
this.passwordAcceptable = data.passed && data.match === 'MATCH';
|
||||
this.matchStatus = data.match;
|
||||
this.message = data.message;
|
||||
|
||||
if (!this.password1) {
|
||||
this.strength = 0;
|
||||
}
|
||||
if (data.strength < 20) {
|
||||
this.strength = 1;
|
||||
}
|
||||
else if (data.strength < 45) {
|
||||
this.strength = 2;
|
||||
}
|
||||
else if (data.strength < 70) {
|
||||
this.strength = 3;
|
||||
}
|
||||
else if (data.strength < 100) {
|
||||
this.strength = 4;
|
||||
}
|
||||
else {
|
||||
this.strength = 5;
|
||||
}
|
||||
},
|
||||
(result: any) => {
|
||||
this.pendingValidation = false;
|
||||
this.$log.error(result);
|
||||
this.message = this.translateFilter('Display_CommunicationError');
|
||||
}
|
||||
);
|
||||
|
||||
this.$timeout(() => {
|
||||
if (this.pendingValidation) {
|
||||
this.message = this.translateFilter('Display_CheckingPassword');
|
||||
}
|
||||
}, IN_PROGRESS_MESSAGE_WAIT_MS);
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
{
|
||||
"Button_AddSearchAttribute": "Add Search Attribute",
|
||||
"Button_Attributes": "User Data",
|
||||
"Button_Cancel": "Cancel",
|
||||
"Button_ChangePassword": "Change Password",
|
||||
"Button_ClearResponses": "Clear Answers",
|
||||
"Button_Close": "Close",
|
||||
"Button_CloseWindow": "Close Window",
|
||||
"Button_Confirm": "Confirm",
|
||||
"Button_Delete": "Delete",
|
||||
"Button_SendEmail": "Send Email",
|
||||
"Button_Export": "Export",
|
||||
"Button_Email": "Email",
|
||||
"Button_ExportOrgChart": "Export Organizational Chart",
|
||||
"Button_EmailTeam": "Email Team Members",
|
||||
"Button_GoBack": "Go Back",
|
||||
"Button_HelpdeskClearOtpSecret": "Clear OTP Secret",
|
||||
"Button_More": "More",
|
||||
"Button_OK": "OK",
|
||||
"Button_OTP": "OTP",
|
||||
"Button_TokenVerification": "Token Verification",
|
||||
"Button_SendToken": "Send Token",
|
||||
"Button_Remove": "Remove",
|
||||
"Button_Show": "Show",
|
||||
"Button_SMS": "SMS",
|
||||
"Button_Unlock": "Unlock",
|
||||
"Button_Verifications": "Verifications",
|
||||
"Button_Verify": "Verify",
|
||||
"Confirm": "Are you sure you wish to proceed?",
|
||||
"Confirm_DeleteUser": "Are you sure you wish to proceed? If you continue, the selected user will be deleted permanently. This can not be undone.",
|
||||
"Display_CaptchaRefresh": "Refresh",
|
||||
"Display_CheckingPassword": "Checking Password....",
|
||||
"Display_HelpdeskOtpValidation": "Instruct the user to load their mobile authentication app and share the current pass code.",
|
||||
"Display_InvalidVerification": "Viewing details only available after a user has been successfully verified",
|
||||
"Display_MatchCondition": "Match Condition",
|
||||
"Display_NoResponses": "User does not have responses",
|
||||
"Display_PasswordGeneration": "The following passwords have been randomly generated for you.",
|
||||
"Display_PasswordPrompt": "Please type your new password",
|
||||
"Display_PleaseWait": "Loading...",
|
||||
"Display_Random": "Random",
|
||||
"Display_SearchResultsExceeded": "Search results exceeded maximum search size",
|
||||
"Display_SearchResultsNone": "No results",
|
||||
"Display_SearchAttrsUnique": "Search attributes must be unique",
|
||||
"Display_SelectAttribute": "Select attribute...",
|
||||
"Display_SetRandomPasswordPrompt": "Set a new random password for this user?",
|
||||
"Display_StrengthMeter": "Password Strength",
|
||||
"Display_TokenDestination": "Token Destination",
|
||||
"Display_ViewDetails": "View Details",
|
||||
"Display_EmailPrefix": "Email - ",
|
||||
"Display_SmsPrefix": "SMS - ",
|
||||
"Display_PeopleSearch": "Please type your search query below. You may search for a person based on name, email address or telephone number.",
|
||||
"Field_DateTime": "Date/Time",
|
||||
"Field_Display": "Display",
|
||||
"Field_LdapProfile": "LDAP Profile",
|
||||
"Field_Method": "Method",
|
||||
"Field_NewPassword": "New Password",
|
||||
"Field_Policy": "Policy",
|
||||
"Field_Profile": "Profile",
|
||||
"Field_Username": "User Name",
|
||||
"Long_Title_VerificationSend": "Before this user can be selected, the user's identity must be verified. Please select a verification method.",
|
||||
"Instructions_ExportOrgChart1": "Click the Export button to begin download of the organizational chart data. After download is complete, you can import the data into a program like Visio so it can be formatted into the desired output.",
|
||||
"Instructions_ExportOrgChart2": "Choose the export level depth, and press the Export button below.",
|
||||
"Instructions_EmailTeam1": "The email list is generated based off of organizational data starting at this point. When you click the Send Email button, your default email program should automatically open, with the list of email addresses pre-filled.",
|
||||
"Instructions_EmailTeam2": "Choose the team level depth, and press the Email button below.",
|
||||
"Placeholder_Search": "Search",
|
||||
"Title_AdvancedSearch": "Advanced Search",
|
||||
"Title_ChangePassword": "Change Password",
|
||||
"Title_DirectReports": "Direct Report(s)",
|
||||
"Title_Helpdesk": "Help Desk",
|
||||
"Title_HelpDeskCard": "Help Desk Cards",
|
||||
"Title_HelpDeskTable": "Help Desk Table",
|
||||
"Title_Management": "Management",
|
||||
"Title_Organization": "Organization",
|
||||
"Title_OrgChart": "Organizational Chart",
|
||||
"Title_Print": "Print",
|
||||
"Title_PasswordPolicy": "Password Policy",
|
||||
"Title_PeopleSearch": "People Search",
|
||||
"Title_PeopleSearchCard": "People Search Cards",
|
||||
"Title_PeopleSearchTable": "People Search Table",
|
||||
"Title_RandomPasswords": "Random Passwords",
|
||||
"Title_RecentVerifications": "Recent Verifications",
|
||||
"Title_SecurityResponses": "Security Responses",
|
||||
"Title_Status": "Status",
|
||||
"Title_Success": "Success",
|
||||
"Title_UserEventHistory": "Password History",
|
||||
"Title_ValidateCode": "Validate Code",
|
||||
"Title_VerificationSend": "Select verification method",
|
||||
"Title_ExportOrgChart": "Export Organizational Chart",
|
||||
"Title_EmailOrgChart": "Email Team Members",
|
||||
"Label_ExportLevelDepth": "Export Level Depth",
|
||||
"Label_EmailLevelDepth": "Team Level Depth",
|
||||
"Label_EmailTeamMembersFound": "Email Addresses Found",
|
||||
"Label_StartingFrom": "starting from {{personName}}"
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
|
||||
<title>PWM Development</title>
|
||||
|
||||
<link rel="stylesheet" href="vendor/ux-ias/ias-icons.css">
|
||||
<link rel="stylesheet" href="vendor/ux-ias/ux-ias.css">
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body ng-cloak>
|
||||
<ui-view>Loading...</ui-view>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
export interface IPerson {
|
||||
// Common properties
|
||||
userKey?: string;
|
||||
numDirectReports?: number;
|
||||
|
||||
// Autocomplete properties (via Search)
|
||||
_displayName?: string;
|
||||
|
||||
// Details properties (not available in search)
|
||||
detail?: any;
|
||||
displayNames?: string[];
|
||||
photoURL?: string;
|
||||
links?: any[];
|
||||
|
||||
// Search properties (not available in details)
|
||||
givenName?: string;
|
||||
mail?: string;
|
||||
sn?: string;
|
||||
telephoneNumber?: string;
|
||||
title?: string;
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
|
||||
import {ICompileService, IScope, ITemplateCacheService, element} from 'angular';
|
||||
|
||||
declare const PWM_GLOBAL: any;
|
||||
declare const PWM_MAIN: any;
|
||||
|
||||
const PWM_CHANGEPW = window['PWM_CHANGEPW'];
|
||||
const PW_SUGGESTIONS_TEMPLATE = require("./password-suggestions.html");
|
||||
require("./password-suggestions.scss");
|
||||
|
||||
export default class ChangePasswordController {
|
||||
static $inject = ["$scope", "$compile", "$templateCache"];
|
||||
constructor(
|
||||
private $scope: IScope | any,
|
||||
private $compile: ICompileService,
|
||||
private $templateCache: ITemplateCacheService
|
||||
) {
|
||||
}
|
||||
|
||||
getString(key: string) {
|
||||
return PWM_MAIN.showString(key);
|
||||
}
|
||||
|
||||
doRandomGeneration() {
|
||||
PWM_MAIN.showDialog({
|
||||
title: PWM_MAIN.showString('Title_RandomPasswords'),
|
||||
dialogClass: 'narrow',
|
||||
text: "",
|
||||
showOk: false,
|
||||
showClose: true,
|
||||
loadFunction: () => {
|
||||
this.populateDialog()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
populateDialog() {
|
||||
this.$scope.$ctrl = this;
|
||||
const passwordSuggestionsElement: JQuery = this.$compile(this.$templateCache.get(PW_SUGGESTIONS_TEMPLATE) as string)(this.$scope);
|
||||
|
||||
var myElement = element( document.querySelector( '#dialogPopup .dialogBody, #html5Dialog .dialogBody' ) );
|
||||
myElement.replaceWith(passwordSuggestionsElement);
|
||||
|
||||
this.$scope.$applyAsync();
|
||||
|
||||
PWM_CHANGEPW.beginFetchRandoms({});
|
||||
}
|
||||
|
||||
onChoosePassword(event) {
|
||||
PWM_CHANGEPW.copyToPasswordFields(event.target.textContent);
|
||||
}
|
||||
|
||||
onMoreRandomsButtonClick() {
|
||||
PWM_CHANGEPW.beginFetchRandoms({});
|
||||
}
|
||||
|
||||
onCancelRandomsButtonClick() {
|
||||
PWM_MAIN.closeWaitDialog('dialogPopup');
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
|
||||
import { module } from 'angular';
|
||||
import ChangePasswordController from './changepassword.controller';
|
||||
|
||||
module("changepassword.module", [])
|
||||
.controller("ChangePasswordController", ChangePasswordController);
|
|
@ -1,56 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="dialogBody narrow">
|
||||
{{$ctrl.getString('Display_PasswordGeneration')}}
|
||||
<br><br>
|
||||
<table class="noborder">
|
||||
<tbody>
|
||||
|
||||
<tr class="noborder" ng-repeat="i in [0,2,4,6,8,10,12,14,16,18]">
|
||||
<td class="noborder" ng-repeat="j in [i, i+1]" style="padding-bottom: 5px;" width="20%">
|
||||
<div style="visibility: visible;" class="link-randomPasswordValue" href="#" id="randomGen{{j}}"
|
||||
ng-click="$ctrl.onChoosePassword($event)">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br><br>
|
||||
<table class="noborder">
|
||||
<tbody>
|
||||
<tr class="noborder">
|
||||
<td class="noborder">
|
||||
<button class="btn" id="moreRandomsButton" ng-click="$ctrl.onMoreRandomsButtonClick()">
|
||||
<span class="btn-icon pwm-icon pwm-icon-refresh"></span>
|
||||
{{$ctrl.getString('Button_More')}}
|
||||
</button>
|
||||
</td>
|
||||
<td class="noborder" style="text-align:right;">
|
||||
<button class="btn" id="cancelRandomsButton" ng-click="$ctrl.onCancelRandomsButtonClick()">
|
||||
<span class="btn-icon pwm-icon pwm-icon-times"></span>
|
||||
{{$ctrl.getString('Button_Cancel')}}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {IWindowService} from 'angular';
|
||||
|
||||
// Date filter - converts ISO8601 date to human-readable format
|
||||
export default ['$window',
|
||||
function($window: IWindowService): (isoDate: string) => string {
|
||||
return (isoDate: string): string => {
|
||||
let date = new Date(isoDate);
|
||||
if ($window['PWM_MAIN']) {
|
||||
return $window['PWM_MAIN'].TimestampHandler.formatDate(date);
|
||||
}
|
||||
else {
|
||||
return date.toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog" ng-switch="status">
|
||||
<div class="ias-dialog-container" ng-switch-default>
|
||||
<div class="WaitDialogBlank"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-container" ng-switch-when="confirm">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="title"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="text"></p>
|
||||
<p ng-bind="secondaryText" ng-if="!!secondaryText"></p>
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="confirm()">{{ 'Button_OK' | translate }}</ias-button>
|
||||
<ias-button ng-click="close()">{{ 'Button_Cancel' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-container" ng-switch-when="success">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="title"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="text"></p>
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="close()">{{ 'Button_OK' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,195 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-header">
|
||||
<h2 id="page-content-title" translate="Title_Helpdesk">Help Desk</h2>
|
||||
<span class="ias-fill"></span>
|
||||
<div class="help-desk-icons">
|
||||
<ias-button id="helpdesk-refresh-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.refresh()" ng-attr-title="{{ 'Display_CaptchaRefresh' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="reload_refresh_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="helpdesk-go-back-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.gotoSearch()" ng-attr-title="{{ 'Button_GoBack' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="close_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="secondary-header">
|
||||
<div class="help-desk-person-card">
|
||||
<person-card person="$ctrl.personCard" show-image="$ctrl.photosEnabled"></person-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-desk-content">
|
||||
<div class="help-desk-buttons">
|
||||
<ias-button ng-click="$ctrl.changePassword()"
|
||||
ng-disabled="$ctrl.buttonDisabled('changePassword')"
|
||||
ng-if="$ctrl.buttonVisible('changePassword')">{{ 'Button_ChangePassword' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="$ctrl.unlockUser()"
|
||||
ng-disabled="$ctrl.buttonDisabled('unlock')"
|
||||
ng-if="$ctrl.buttonVisible('unlock')">{{ 'Button_Unlock' | translate }}
|
||||
</ias-button>
|
||||
|
||||
<ias-button ng-click="$ctrl.clearResponses()"
|
||||
ng-if="$ctrl.buttonVisible('clearResponses') && !$ctrl.buttonDisabled('clearResponses')">
|
||||
{{ 'Button_ClearResponses' | translate }}
|
||||
</ias-button>
|
||||
<ias-button disabled
|
||||
ng-attr-title="{{ 'Display_NoResponses' | translate }}"
|
||||
ng-if="$ctrl.buttonVisible('clearResponses') && $ctrl.buttonDisabled('clearResponses')">
|
||||
{{ 'Button_ClearResponses' | translate }}
|
||||
</ias-button>
|
||||
|
||||
<ias-button ng-click="$ctrl.clearOtpSecret()"
|
||||
ng-disabled="$ctrl.buttonDisabled('clearOtpSecret')"
|
||||
ng-if="$ctrl.buttonVisible('clearOtpSecret')">{{ 'Button_HelpdeskClearOtpSecret' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="$ctrl.verifyUser()"
|
||||
ng-disabled="$ctrl.buttonDisabled('verification')"
|
||||
ng-if="$ctrl.buttonVisible('verification')">{{ 'Button_Verify' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="$ctrl.deleteUser()"
|
||||
ng-disabled="$ctrl.buttonDisabled('deleteUser')"
|
||||
ng-if="$ctrl.buttonVisible('deleteUser')">{{ 'Button_Delete' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="$ctrl.clickCustomButton(button)"
|
||||
ng-repeat="(label, button) in $ctrl.customButtons"
|
||||
ng-attr-title="{{button.description}}">{{ button.name }}
|
||||
</ias-button>
|
||||
</div>
|
||||
|
||||
<div class="tabset-container">
|
||||
<div class="ias-tabset">
|
||||
<div class="ias-tab"
|
||||
ias-toggle-active="ias-active"
|
||||
ias-toggle="profileTab">
|
||||
{{'Field_Profile' | translate}}
|
||||
</div>
|
||||
<div class="ias-tab"
|
||||
ias-toggle-active="ias-active"
|
||||
ias-toggle="statusTab"
|
||||
ng-if="$ctrl.person.statusData && $ctrl.person.statusData.length">
|
||||
{{'Title_Status' | translate}}
|
||||
</div>
|
||||
<div class="ias-tab"
|
||||
ias-toggle-active="ias-active"
|
||||
ias-toggle="historyTab"
|
||||
ng-if="$ctrl.person.userHistory && $ctrl.person.userHistory.length">
|
||||
{{'Title_UserEventHistory' | translate}}
|
||||
</div>
|
||||
<div class="ias-tab"
|
||||
ias-toggle-active="ias-active"
|
||||
ias-toggle="passwordTab">
|
||||
{{'Title_PasswordPolicy' | translate}}
|
||||
</div>
|
||||
<div class="ias-tab"
|
||||
ias-toggle-active="ias-active"
|
||||
ias-toggle="securityTab"
|
||||
ng-if="$ctrl.person.helpdeskResponses && $ctrl.person.helpdeskResponses.length">
|
||||
{{'Title_SecurityResponses' | translate}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-panes">
|
||||
<ias-tab-pane toggle-group="detailsTabGroup" name="profileTab">
|
||||
<table class="details-table">
|
||||
<tbody>
|
||||
<tr ng-repeat="item in $ctrl.person.profileData">
|
||||
<td ng-bind="item.label"></td>
|
||||
<td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
|
||||
<td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
|
||||
<td ng-if="item.type==='multiString'">
|
||||
<div ng-bind="value" ng-repeat="value in item.values"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ias-tab-pane>
|
||||
<ias-tab-pane toggle-group="detailsTabGroup" name="statusTab">
|
||||
<table class="details-table">
|
||||
<tbody>
|
||||
<tr ng-repeat="item in $ctrl.person.statusData">
|
||||
<td ng-bind="item.label"></td>
|
||||
<td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
|
||||
<td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
|
||||
<td ng-if="item.type==='multiString'">
|
||||
<div ng-bind="value" ng-repeat="value in item.values"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ias-tab-pane>
|
||||
<ias-tab-pane toggle-group="detailsTabGroup" name="historyTab" ng-if="!!$ctrl.person.userHistory">
|
||||
<table class="details-table">
|
||||
<tbody>
|
||||
<tr ng-repeat="item in $ctrl.person.userHistory">
|
||||
<td ng-bind="item.timestamp | dateFilter"></td>
|
||||
<td ng-bind="item.label"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ias-tab-pane>
|
||||
<ias-tab-pane toggle-group="detailsTabGroup" name="passwordTab">
|
||||
<table class="details-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td ng-bind="'Field_Policy' | translate"></td>
|
||||
<td ng-bind="$ctrl.person.passwordPolicyDN"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td ng-bind="'Field_Profile' | translate"></td>
|
||||
<td ng-bind="$ctrl.person.passwordPolicyID"></td>
|
||||
</tr>
|
||||
<tr class="bottom-border" ng-if="!!$ctrl.person.passwordRequirements">
|
||||
<td ng-bind="'Field_Display' | translate"></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="item in $ctrl.person.passwordRequirements" ng-bind-html="item"></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="(key, item) in $ctrl.person.passwordPolicyRules">
|
||||
<td ng-bind="key"></td>
|
||||
<td ng-bind="item"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ias-tab-pane>
|
||||
<ias-tab-pane toggle-group="detailsTabGroup" name="securityTab">
|
||||
<table class="details-table">
|
||||
<tbody>
|
||||
<tr ng-repeat="item in $ctrl.person.helpdeskResponses">
|
||||
<td ng-bind="item.label"></td>
|
||||
<td ng-bind="item.value | dateFilter" ng-if="item.type==='timestamp'"></td>
|
||||
<td ng-bind="item.value" ng-if="item.type==='string' || item.type==='number'"></td>
|
||||
<td ng-if="item.type==='multiString'">
|
||||
<div ng-bind="value" ng-repeat="value in item.values"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ias-tab-pane>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,263 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
help-desk-detail {
|
||||
display: block;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
|
||||
#page-content-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
> .ias-header {
|
||||
> .help-desk-icons {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
> .help-desk-content {
|
||||
display: flex;
|
||||
flex-flow: row-reverse wrap;
|
||||
justify-content: flex-end;
|
||||
|
||||
> .help-desk-buttons,
|
||||
> .tabset-container {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
> .help-desk-buttons {
|
||||
> .ias-button {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
min-width: 170px;
|
||||
width: 100%;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .tabset-container {
|
||||
width: 100%;
|
||||
|
||||
> .tab-panes {
|
||||
> .ias-tab-pane {
|
||||
table {
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.details-table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
|
||||
tr {
|
||||
height: 25px;
|
||||
|
||||
td {
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
height: 19px;
|
||||
text-align: left;
|
||||
|
||||
&:first-child {
|
||||
color: #949494;
|
||||
text-align: right;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding: 3px 15px;
|
||||
}
|
||||
|
||||
> ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 850px) {
|
||||
help-desk-detail {
|
||||
> .help-desk-content {
|
||||
> .help-desk-buttons {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
> .tabset-container {
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
|
||||
> .tab-panes {
|
||||
> .ias-tab-pane {
|
||||
table {
|
||||
max-width: 800px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1050px) {
|
||||
help-desk-detail {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
> .ias-header,
|
||||
> .secondary-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
> .help-desk-content {
|
||||
flex: 1 1px;
|
||||
flex-wrap: nowrap;
|
||||
height: 100%;
|
||||
margin-top: 15px;
|
||||
overflow: auto;
|
||||
|
||||
> .help-desk-buttons,
|
||||
> .tabset-container {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
> .help-desk-buttons {
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding-right: 17px;
|
||||
}
|
||||
|
||||
> .tabset-container {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
> .ias-tabset {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab-panes {
|
||||
flex: 1 1px;
|
||||
overflow: auto;
|
||||
|
||||
> .ias-tab-pane {
|
||||
table {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
help-desk-detail {
|
||||
> .ias-header {
|
||||
> .help-desk-icons {
|
||||
margin-left: 0;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.details-table {
|
||||
tr {
|
||||
td {
|
||||
text-align: right;
|
||||
|
||||
&:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 850px) {
|
||||
help-desk-detail {
|
||||
> .help-desk-content {
|
||||
> .help-desk-buttons {
|
||||
margin-left: 0;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1050px) {
|
||||
help-desk-detail {
|
||||
> .help-desk-content {
|
||||
> .help-desk-buttons {
|
||||
padding-left: 17px;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike IE, Edge, and Firefox, Chrome and Safari do not need extra space for the button scrollbar when the buttons
|
||||
// extend below the bottom of the page
|
||||
@media (min-width: 1050px) {
|
||||
[data-browsertype=webkit] {
|
||||
help-desk-detail {
|
||||
> .help-desk-content {
|
||||
> .help-desk-buttons {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&[dir="rtl"] {
|
||||
help-desk-detail {
|
||||
> .help-desk-content {
|
||||
> .help-desk-buttons {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ias-tab-pane.ias-open {
|
||||
display: block; // Can remove once https://github.com/MicroFocus/ux-ias/issues/18 (2nd comment) is resolved
|
||||
}
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {Component} from '../../component';
|
||||
import {IHelpDeskService, ISuccessResponse} from '../../services/helpdesk.service';
|
||||
import {IScope, ui} from 'angular';
|
||||
import {noop} from 'angular';
|
||||
import {IActionButton, IHelpDeskConfigService, PASSWORD_UI_MODES} from '../../services/helpdesk-config.service';
|
||||
import {IPerson} from '../../models/person.model';
|
||||
import {IChangePasswordSuccess} from '../../components/changepassword/success-change-password.controller';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
|
||||
let autogenChangePasswordTemplateUrl =
|
||||
require('../../components/changepassword/autogen-change-password.component.html');
|
||||
let helpdeskDetailDialogTemplateUrl = require('./helpdesk-detail-dialog.template.html');
|
||||
let randomChangePasswordTemplateUrl = require('../../components/changepassword/random-change-password.component.html');
|
||||
let successChangePasswordTemplateUrl =
|
||||
require('../../components/changepassword/success-change-password.component.html');
|
||||
let typeChangePasswordTemplateUrl = require('../../components/changepassword/type-change-password.component.html');
|
||||
let verificationsDialogTemplateUrl = require('./verifications-dialog.template.html');
|
||||
|
||||
const STATUS_WAIT = 'wait';
|
||||
const STATUS_CONFIRM = 'confirm';
|
||||
const STATUS_SUCCESS = 'success';
|
||||
const PROFILE_TAB_NAME = 'profileTab';
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./helpdesk-detail.component.scss'),
|
||||
templateUrl: require('./helpdesk-detail.component.html')
|
||||
})
|
||||
export default class HelpDeskDetailComponent {
|
||||
customButtons: {[key: string]: IActionButton};
|
||||
person: any;
|
||||
personCard: IPerson;
|
||||
photosEnabled: boolean;
|
||||
searchViewLocalStorageKey: string;
|
||||
|
||||
static $inject = [
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'ConfigService',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'IasToggleService',
|
||||
'LocalStorageService'
|
||||
];
|
||||
|
||||
constructor(private $state: ui.IStateService,
|
||||
private $stateParams: ui.IStateParamsService,
|
||||
private configService: IHelpDeskConfigService,
|
||||
private helpDeskService: IHelpDeskService,
|
||||
private IasDialogService: any,
|
||||
private toggleService: { showComponent: (componentName: string) => null },
|
||||
private localStorageService: LocalStorageService) {
|
||||
this.searchViewLocalStorageKey = this.localStorageService.keys.HELPDESK_SEARCH_VIEW;
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
this.configService.getCustomButtons().then((customButtons: {[key: string]: IActionButton}) => {
|
||||
this.customButtons = customButtons;
|
||||
});
|
||||
|
||||
this.configService.photosEnabled().then((photosEnabled: boolean) => {
|
||||
this.photosEnabled = photosEnabled;
|
||||
});
|
||||
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
buttonDisabled(buttonName: string): boolean {
|
||||
if (!this.person || !this.person.enabledButtons) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this.person.enabledButtons.indexOf(buttonName) === -1);
|
||||
}
|
||||
|
||||
buttonVisible(buttonName: string): boolean {
|
||||
if (!this.person || !this.person.visibleButtons) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this.person.visibleButtons.indexOf(buttonName) !== -1);
|
||||
}
|
||||
|
||||
changePassword(): void {
|
||||
this.configService.getPasswordUiMode()
|
||||
.then((passwordUiMode) => {
|
||||
if (passwordUiMode) {
|
||||
if (passwordUiMode === PASSWORD_UI_MODES.TYPE) {
|
||||
this.changePasswordType();
|
||||
}
|
||||
else if (passwordUiMode === PASSWORD_UI_MODES.AUTOGEN) {
|
||||
this.changePasswordAutogen();
|
||||
}
|
||||
else if (passwordUiMode === PASSWORD_UI_MODES.BOTH) {
|
||||
this.changePasswordType();
|
||||
}
|
||||
else if (passwordUiMode === PASSWORD_UI_MODES.RANDOM) {
|
||||
this.changePasswordRandom();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Error('Unable to retrieve a valid password UI mode.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
changePasswordAutogen() {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'AutogenChangePasswordController as $ctrl',
|
||||
templateUrl: autogenChangePasswordTemplateUrl,
|
||||
locals: {
|
||||
personUserKey: this.getUserKey()
|
||||
}
|
||||
})
|
||||
// If the password was changed, the promise resolves. IasDialogService passes the data intact.
|
||||
.then(this.changePasswordSuccess.bind(this), noop);
|
||||
}
|
||||
|
||||
changePasswordClearResponses() {
|
||||
let userKey = this.getUserKey();
|
||||
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: [
|
||||
'$scope',
|
||||
'HelpDeskService',
|
||||
'translateFilter',
|
||||
($scope: IScope | any,
|
||||
helpDeskService: IHelpDeskService,
|
||||
translateFilter: (id: string) => string) => {
|
||||
$scope.status = STATUS_WAIT;
|
||||
$scope.title = translateFilter('Button_ClearResponses');
|
||||
helpDeskService.clearResponses(userKey).then((data: ISuccessResponse) => {
|
||||
// TODO - error dialog?
|
||||
$scope.status = STATUS_SUCCESS;
|
||||
$scope.text = data.successMessage;
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
],
|
||||
templateUrl: helpdeskDetailDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
changePasswordRandom() {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'RandomChangePasswordController as $ctrl',
|
||||
templateUrl: randomChangePasswordTemplateUrl,
|
||||
locals: {
|
||||
personUserKey: this.getUserKey()
|
||||
}
|
||||
})
|
||||
// If the password was changed, the promise resolves. IasDialogService passes the data intact.
|
||||
.then(this.changePasswordSuccess.bind(this), noop);
|
||||
}
|
||||
|
||||
changePasswordSuccess(data: IChangePasswordSuccess) {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'SuccessChangePasswordController as $ctrl',
|
||||
templateUrl: successChangePasswordTemplateUrl,
|
||||
locals: {
|
||||
changePasswordSuccessData: data,
|
||||
personUserKey: this.getUserKey()
|
||||
}
|
||||
})
|
||||
.then(this.changePasswordClearResponses.bind(this), this.refresh.bind(this));
|
||||
}
|
||||
|
||||
changePasswordType() {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'TypeChangePasswordController as $ctrl',
|
||||
templateUrl: typeChangePasswordTemplateUrl,
|
||||
locals: {
|
||||
personUserKey: this.getUserKey()
|
||||
}
|
||||
})
|
||||
// If the operator clicked "Random Passwords" or the password was changed, the promise resolves.
|
||||
.then((data: IChangePasswordSuccess & { autogenPasswords: boolean }) => {
|
||||
// If the operator clicked "Random Passwords", data.autogenPasswords will be true
|
||||
if (data.autogenPasswords) {
|
||||
this.changePasswordAutogen();
|
||||
}
|
||||
else {
|
||||
this.changePasswordSuccess(data); // IasDialogService passes the data intact.
|
||||
}
|
||||
}, noop);
|
||||
}
|
||||
|
||||
clearOtpSecret(): void {
|
||||
if (this.buttonDisabled('clearOtpSecret')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userKey = this.getUserKey();
|
||||
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: [
|
||||
'$scope',
|
||||
'HelpDeskService',
|
||||
'translateFilter',
|
||||
($scope: IScope | any,
|
||||
helpDeskService: IHelpDeskService,
|
||||
translateFilter: (id: string) => string) => {
|
||||
$scope.status = STATUS_CONFIRM;
|
||||
$scope.title = translateFilter('Button_HelpdeskClearOtpSecret');
|
||||
$scope.text = translateFilter('Confirm');
|
||||
$scope.confirm = () => {
|
||||
$scope.status = STATUS_WAIT;
|
||||
helpDeskService.clearOtpSecret(userKey).then((data: ISuccessResponse) => {
|
||||
// TODO - error dialog?
|
||||
$scope.status = STATUS_SUCCESS;
|
||||
$scope.text = data.successMessage;
|
||||
this.refresh();
|
||||
});
|
||||
};
|
||||
}
|
||||
],
|
||||
templateUrl: helpdeskDetailDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
clearResponses(): void {
|
||||
if (this.buttonDisabled('clearResponses')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userKey = this.getUserKey();
|
||||
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: [
|
||||
'$scope',
|
||||
'HelpDeskService',
|
||||
'translateFilter',
|
||||
($scope: IScope | any,
|
||||
helpDeskService: IHelpDeskService,
|
||||
translateFilter: (id: string) => string) => {
|
||||
$scope.status = STATUS_CONFIRM;
|
||||
$scope.title = translateFilter('Button_ClearResponses');
|
||||
$scope.text = translateFilter('Confirm');
|
||||
$scope.confirm = () => {
|
||||
$scope.status = STATUS_WAIT;
|
||||
helpDeskService.clearResponses(userKey).then((data: ISuccessResponse) => {
|
||||
// TODO - error dialog?
|
||||
$scope.status = STATUS_SUCCESS;
|
||||
$scope.text = data.successMessage;
|
||||
this.refresh();
|
||||
});
|
||||
};
|
||||
}
|
||||
],
|
||||
templateUrl: helpdeskDetailDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
clickCustomButton(button: IActionButton): void {
|
||||
// Custom buttons are never disabled
|
||||
|
||||
let userKey = this.getUserKey();
|
||||
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: [
|
||||
'$scope',
|
||||
'HelpDeskService',
|
||||
'translateFilter',
|
||||
($scope: IScope | any,
|
||||
helpDeskService: IHelpDeskService,
|
||||
translateFilter: (id: string) => string) => {
|
||||
$scope.status = STATUS_CONFIRM;
|
||||
$scope.title = translateFilter('Button_Confirm') + ' ' + button.name;
|
||||
$scope.text = button.description;
|
||||
$scope.secondaryText = translateFilter('Confirm');
|
||||
$scope.confirm = () => {
|
||||
$scope.status = STATUS_WAIT;
|
||||
helpDeskService.customAction(button.name, userKey).then((data: ISuccessResponse) => {
|
||||
// TODO - error dialog? (note that this error dialog is slightly different)
|
||||
$scope.status = STATUS_SUCCESS;
|
||||
$scope.title = translateFilter('Title_Success');
|
||||
$scope.secondaryText = null;
|
||||
$scope.text = data.successMessage;
|
||||
this.refresh();
|
||||
});
|
||||
};
|
||||
}
|
||||
],
|
||||
templateUrl: helpdeskDetailDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
deleteUser(): void {
|
||||
if (this.buttonDisabled('deleteUser')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userKey = this.getUserKey();
|
||||
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: [
|
||||
'$scope',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'translateFilter',
|
||||
($scope: IScope | any,
|
||||
helpDeskService: IHelpDeskService,
|
||||
IasDialogService: any,
|
||||
translateFilter: (id: string) => string) => {
|
||||
$scope.status = STATUS_CONFIRM;
|
||||
$scope.title = translateFilter('Button_Confirm');
|
||||
$scope.text = translateFilter('Confirm_DeleteUser');
|
||||
$scope.confirm = () => {
|
||||
$scope.status = STATUS_WAIT;
|
||||
helpDeskService.deleteUser(userKey).then((data: ISuccessResponse) => {
|
||||
// TODO - error dialog?
|
||||
$scope.status = STATUS_SUCCESS;
|
||||
$scope.title = translateFilter('Title_Success');
|
||||
$scope.text = data.successMessage;
|
||||
$scope.close = () => {
|
||||
IasDialogService.close();
|
||||
this.gotoSearch();
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
],
|
||||
templateUrl: helpdeskDetailDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
getUserKey(): string {
|
||||
return this.$stateParams['personId'];
|
||||
}
|
||||
|
||||
gotoSearch(): void {
|
||||
let view = this.localStorageService.getItem(this.searchViewLocalStorageKey);
|
||||
if (view) {
|
||||
this.$state.go(view);
|
||||
}
|
||||
else {
|
||||
this.$state.go('search.cards');
|
||||
}
|
||||
}
|
||||
|
||||
initialize(): void {
|
||||
const personId = this.getUserKey();
|
||||
|
||||
this.helpDeskService.getPersonCard(personId).then((personCard: IPerson) => {
|
||||
this.personCard = personCard;
|
||||
});
|
||||
|
||||
this.toggleService.showComponent(PROFILE_TAB_NAME);
|
||||
|
||||
this.helpDeskService
|
||||
.getPerson(personId)
|
||||
.then((person: any) => {
|
||||
this.person = person;
|
||||
}, this.gotoSearch.bind(this));
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this.person = null;
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
unlockUser(): void {
|
||||
if (this.buttonDisabled('unlock')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let userKey = this.getUserKey();
|
||||
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: [
|
||||
'$scope',
|
||||
'HelpDeskService',
|
||||
'translateFilter',
|
||||
($scope: IScope | any,
|
||||
helpDeskService: IHelpDeskService,
|
||||
translateFilter: (id: string) => string) => {
|
||||
$scope.status = STATUS_CONFIRM;
|
||||
$scope.title = translateFilter('Button_Unlock');
|
||||
$scope.text = translateFilter('Confirm');
|
||||
$scope.confirm = () => {
|
||||
$scope.status = STATUS_WAIT;
|
||||
helpDeskService.unlockIntruder(userKey).then((data: ISuccessResponse) => {
|
||||
// TODO - error dialog?
|
||||
$scope.status = STATUS_SUCCESS;
|
||||
$scope.text = data.successMessage;
|
||||
this.refresh();
|
||||
});
|
||||
};
|
||||
}
|
||||
],
|
||||
templateUrl: helpdeskDetailDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
verifyUser(): void {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'VerificationsDialogController as $ctrl',
|
||||
templateUrl: verificationsDialogTemplateUrl,
|
||||
locals: {
|
||||
personUserKey: this.getUserKey(),
|
||||
showRequiredOnly: false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,371 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import SearchResult from '../../models/search-result.model';
|
||||
import {isArray, isString, IPromise, IQService, IScope, ITimeoutService} from 'angular';
|
||||
import {IPerson} from '../../models/person.model';
|
||||
import {IHelpDeskConfigService} from '../../services/helpdesk-config.service';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import {IHelpDeskService} from '../../services/helpdesk.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import {IAdvancedSearchConfig, IAdvancedSearchQuery, IAttributeMetadata} from '../../services/base-config.service';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
|
||||
let verificationsDialogTemplateUrl = require('./verifications-dialog.template.html');
|
||||
let recentVerificationsDialogTemplateUrl = require('./recent-verifications-dialog.template.html');
|
||||
|
||||
export default abstract class HelpDeskSearchBaseComponent {
|
||||
advancedSearch = false;
|
||||
advancedSearchTags = {};
|
||||
advancedSearchEnabled: boolean;
|
||||
advancedSearchMaxRows: number;
|
||||
columnConfiguration: any;
|
||||
errorMessage: string;
|
||||
inputDebounce: number;
|
||||
protected pendingRequests: IPromise<any>[] = [];
|
||||
photosEnabled: boolean;
|
||||
query: string;
|
||||
queries: IAdvancedSearchQuery[];
|
||||
searchMessage: string;
|
||||
searchResult: SearchResult;
|
||||
searchTextLocalStorageKey: string;
|
||||
searchViewLocalStorageKey: string;
|
||||
verificationsEnabled: boolean;
|
||||
|
||||
constructor(protected $q: IQService,
|
||||
protected $scope: IScope,
|
||||
protected $state: angular.ui.IStateService,
|
||||
protected $stateParams: angular.ui.IStateParamsService,
|
||||
protected $timeout: ITimeoutService,
|
||||
protected $translate: angular.translate.ITranslateService,
|
||||
protected configService: IHelpDeskConfigService,
|
||||
protected helpDeskService: IHelpDeskService,
|
||||
protected IasDialogService: any,
|
||||
protected localStorageService: LocalStorageService,
|
||||
protected promiseService: PromiseService,
|
||||
protected pwmService: IPwmService,
|
||||
protected commonSearchService: CommonSearchService) {
|
||||
this.searchTextLocalStorageKey = this.localStorageService.keys.HELPDESK_SEARCH_TEXT;
|
||||
this.searchViewLocalStorageKey = this.localStorageService.keys.HELPDESK_SEARCH_VIEW;
|
||||
|
||||
this.inputDebounce = this.pwmService.ajaxTypingWait;
|
||||
}
|
||||
|
||||
protected initialize(): IPromise<void> {
|
||||
return this.$q.all(
|
||||
[
|
||||
this.configService.verificationsEnabled().then((verificationsEnabled: boolean) => {
|
||||
this.verificationsEnabled = verificationsEnabled;
|
||||
}),
|
||||
this.configService.advancedSearchConfig().then((advancedSearchConfig: IAdvancedSearchConfig) => {
|
||||
this.advancedSearchEnabled = advancedSearchConfig.enabled;
|
||||
this.advancedSearchMaxRows = advancedSearchConfig.maxRows;
|
||||
|
||||
for (let advancedSearchTag of advancedSearchConfig.attributes) {
|
||||
this.advancedSearchTags[advancedSearchTag.attribute] = advancedSearchTag;
|
||||
}
|
||||
})
|
||||
]
|
||||
).then(result => {
|
||||
const searchQuery = this.getSearchQuery();
|
||||
if (searchQuery) {
|
||||
// A search query has been passed in, disregard the current search state
|
||||
this.query = searchQuery;
|
||||
this.advancedSearch = false;
|
||||
this.storeSearchText();
|
||||
this.commonSearchService.setHdAdvancedSearchActive(this.advancedSearch);
|
||||
this.commonSearchService.setHdAdvSearchQueries([]);
|
||||
} else {
|
||||
this.query = this.getSearchText();
|
||||
this.advancedSearch = this.commonSearchService.isHdAdvancedSearchActive();
|
||||
this.queries = this.commonSearchService.getHdAdvSearchQueries();
|
||||
if (this.queries.length === 0) {
|
||||
this.addSearchTag();
|
||||
}
|
||||
}
|
||||
|
||||
// Once <ias-search-box> from ng-ias allows the autofocus attribute, we can remove this code
|
||||
this.$timeout(() => {
|
||||
document.getElementsByTagName('input')[0].focus();
|
||||
});
|
||||
|
||||
this.$scope.$watch('$ctrl.query', (newValue: string, oldValue: string) => {
|
||||
this.onSearchTextChange(newValue, oldValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getMessage(): string {
|
||||
return this.errorMessage || this.searchMessage;
|
||||
}
|
||||
|
||||
private getSearchQuery(): string {
|
||||
let param: string = this.$stateParams['query'];
|
||||
// If multiple query parameters are defined, use the first one
|
||||
if (isArray(param)) {
|
||||
param = param[0].trim();
|
||||
}
|
||||
else if (isString(param)) {
|
||||
param = param.trim();
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
private getSearchText(): string {
|
||||
return this.localStorageService.getItem(this.searchTextLocalStorageKey);
|
||||
}
|
||||
|
||||
abstract fetchData(): void;
|
||||
|
||||
protected clearSearch(): void {
|
||||
this.query = null;
|
||||
this.queries = [];
|
||||
this.searchResult = null;
|
||||
this.clearErrorMessage();
|
||||
this.clearSearchMessage();
|
||||
this.abortPendingRequests();
|
||||
}
|
||||
|
||||
protected fetchSearchData(): IPromise<void | SearchResult> {
|
||||
this.abortPendingRequests();
|
||||
this.searchResult = null;
|
||||
let promise;
|
||||
|
||||
if (this.advancedSearch) {
|
||||
if (!this.queries || (this.queries.length === 1 && !this.queries[0].key)) {
|
||||
this.clearSearch();
|
||||
return null;
|
||||
}
|
||||
|
||||
const keys = new Set();
|
||||
for (let searchQuery of this.queries) {
|
||||
keys.add(searchQuery.key);
|
||||
}
|
||||
|
||||
const duplicateSearchAttrsFound = keys.size < this.queries.length;
|
||||
if (duplicateSearchAttrsFound) {
|
||||
this.$translate('Display_SearchAttrsUnique')
|
||||
.then((translation: string) => {
|
||||
this.searchMessage = translation;
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
promise = this.helpDeskService.advancedSearch(this.queries);
|
||||
}
|
||||
else {
|
||||
if (!this.query) {
|
||||
this.clearSearch();
|
||||
return null;
|
||||
}
|
||||
|
||||
promise = this.helpDeskService.search(this.query);
|
||||
}
|
||||
|
||||
this.pendingRequests.push(promise);
|
||||
|
||||
return promise
|
||||
.then(
|
||||
function(searchResult: SearchResult) {
|
||||
this.clearErrorMessage();
|
||||
this.clearSearchMessage();
|
||||
|
||||
// Aborted request
|
||||
if (!searchResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Too many results returned
|
||||
if (searchResult.sizeExceeded) {
|
||||
this.setSearchMessage('Display_SearchResultsExceeded');
|
||||
}
|
||||
|
||||
// No results returned. Not an else if statement so that the more important message is presented
|
||||
if (!searchResult.people.length) {
|
||||
this.setSearchMessage('Display_SearchResultsNone');
|
||||
}
|
||||
|
||||
return searchResult;
|
||||
}.bind(this),
|
||||
function(error) {
|
||||
this.setErrorMessage(error);
|
||||
this.clearSearchMessage();
|
||||
}.bind(this))
|
||||
.finally(function() {
|
||||
this.removePendingRequest(promise);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
private gotoState(state: string): void {
|
||||
this.$state.go(state);
|
||||
}
|
||||
|
||||
private initiateSearch() {
|
||||
this.clearSearchMessage();
|
||||
this.clearErrorMessage();
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private onSearchTextChange(newValue: string, oldValue: string): void {
|
||||
if (newValue === oldValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.storeSearchText();
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
protected abortPendingRequests() {
|
||||
for (let index = 0; index < this.pendingRequests.length; index++) {
|
||||
let pendingRequest = this.pendingRequests[index];
|
||||
this.promiseService.abort(pendingRequest);
|
||||
}
|
||||
|
||||
this.pendingRequests = [];
|
||||
}
|
||||
|
||||
protected setErrorMessage(message: string) {
|
||||
this.errorMessage = message;
|
||||
}
|
||||
|
||||
protected clearErrorMessage() {
|
||||
this.errorMessage = null;
|
||||
}
|
||||
|
||||
// If message is a string it will be translated. If it is a promise it will assign the string from the resolved
|
||||
// promise
|
||||
protected setSearchMessage(translationKey: string) {
|
||||
if (!translationKey) {
|
||||
this.clearSearchMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
this.$translate(translationKey.toString())
|
||||
.then((translation: string) => {
|
||||
self.searchMessage = translation;
|
||||
});
|
||||
}
|
||||
|
||||
protected clearSearchMessage(): void {
|
||||
this.searchMessage = null;
|
||||
}
|
||||
|
||||
protected removePendingRequest(promise: IPromise<any>) {
|
||||
let index = this.pendingRequests.indexOf(promise);
|
||||
|
||||
if (index > -1) {
|
||||
this.pendingRequests.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private onAdvancedSearchAttributeChanged(query: IAdvancedSearchQuery) {
|
||||
// Make sure we set the default value if the type is select
|
||||
const attributeMetadata: IAttributeMetadata = this.advancedSearchTags[query.key];
|
||||
if (attributeMetadata.type == 'select') {
|
||||
query.value = this.commonSearchService.getDefaultValue(attributeMetadata);
|
||||
}
|
||||
|
||||
this.commonSearchService.setHdAdvSearchQueries(this.queries);
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
private onAdvancedSearchAttributeValueChanged() {
|
||||
this.commonSearchService.setHdAdvSearchQueries(this.queries);
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
private onAdvancedSearchValueChanged() {
|
||||
this.commonSearchService.setHdAdvSearchQueries(this.queries);
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
removeSearchTag(tagIndex: number): void {
|
||||
this.queries.splice(tagIndex, 1);
|
||||
this.commonSearchService.setHdAdvSearchQueries(this.queries);
|
||||
|
||||
if (this.queries.length > 0) {
|
||||
this.initiateSearch();
|
||||
}
|
||||
else {
|
||||
this.clearSearch();
|
||||
this.advancedSearch = false;
|
||||
this.commonSearchService.setHdAdvancedSearchActive(this.advancedSearch);
|
||||
}
|
||||
}
|
||||
|
||||
addSearchTag(): void {
|
||||
const firstTagName = Object.keys(this.advancedSearchTags)[0];
|
||||
const attributeMetaData: IAttributeMetadata = this.advancedSearchTags[firstTagName];
|
||||
|
||||
const query: IAdvancedSearchQuery = {
|
||||
key: attributeMetaData.attribute,
|
||||
value: this.commonSearchService.getDefaultValue(attributeMetaData),
|
||||
};
|
||||
|
||||
this.queries.push(query);
|
||||
}
|
||||
|
||||
protected selectPerson(person: IPerson): void {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'VerificationsDialogController as $ctrl',
|
||||
templateUrl: verificationsDialogTemplateUrl,
|
||||
locals: {
|
||||
personUserKey: person.userKey,
|
||||
showRequiredOnly: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected showVerifications(): void {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'RecentVerificationsDialogController as $ctrl',
|
||||
templateUrl: recentVerificationsDialogTemplateUrl
|
||||
});
|
||||
}
|
||||
|
||||
protected storeSearchText(): void {
|
||||
this.localStorageService.setItem(this.searchTextLocalStorageKey, this.query || '');
|
||||
}
|
||||
|
||||
enableAdvancedSearch(): void {
|
||||
this.clearSearch();
|
||||
this.addSearchTag();
|
||||
this.advancedSearch = true;
|
||||
this.commonSearchService.setHdAdvancedSearchActive(this.advancedSearch);
|
||||
}
|
||||
|
||||
protected toggleView(state: string): void {
|
||||
this.storeSearchView(state);
|
||||
this.storeSearchText();
|
||||
this.gotoState(state);
|
||||
}
|
||||
|
||||
private storeSearchView(state: string) {
|
||||
this.localStorageService.setItem(this.searchViewLocalStorageKey, state);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
|
||||
<div class="peoplesearch-header">
|
||||
<div class="basic-search-container" ng-if="!$ctrl.advancedSearch">
|
||||
<h2 id="page-content-title" ng-if="!$ctrl.advancedSearch" translate="Title_Helpdesk">Help Desk</h2>
|
||||
<ias-search-box id="input" ng-model="$ctrl.query"
|
||||
ng-model-options="{debounce: $ctrl.inputDebounce}"
|
||||
placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
|
||||
</ias-search-box>
|
||||
|
||||
<ias-button id="advanced-search-icon" class="ias-icon-button" ng-click="$ctrl.enableAdvancedSearch()"
|
||||
ng-if="$ctrl.advancedSearchEnabled"
|
||||
ng-attr-title="{{ 'Title_AdvancedSearch' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="search_advanced"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<div class="advanced-search-container" ng-if="$ctrl.advancedSearch">
|
||||
<div class="attribute-row" ng-repeat="query in $ctrl.queries">
|
||||
<select ng-model="query.key" ng-change="$ctrl.onAdvancedSearchAttributeChanged(query)">
|
||||
<option ng-repeat="tag in $ctrl.advancedSearchTags" ng-attr-value="{{tag.attribute}}">{{tag.label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Show a drop-down if the attribute type is 'select'-->
|
||||
<select ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type === 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value">
|
||||
<option ng-attr-value="{{name}}"
|
||||
ng-repeat="(name, label) in $ctrl.advancedSearchTags[query.key].options">{{label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Otherwise, just show a regular input field-->
|
||||
<input ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type !== 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value"
|
||||
autocomplete="off" ng-model-options="{debounce: $ctrl.inputDebounce}">
|
||||
|
||||
<ias-button class="ias-icon-button" ng-click="$ctrl.removeSearchTag($index)"
|
||||
ng-attr-title="{{ 'Button_Remove' | translate }}">
|
||||
<ias-icon icon="close_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<ias-button id="add-attribute-row" class="ias-icon-button" ng-click="$ctrl.addSearchTag()"
|
||||
ng-if="$ctrl.queries.length < $ctrl.advancedSearchMaxRows"
|
||||
ng-attr-title="{{ 'Button_AddSearchAttribute' | translate }}">
|
||||
<ias-icon icon="new_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<span class="ias-fill"></span>
|
||||
<div class="ias-header">
|
||||
<ias-button id="view-tile-icon" class="ias-icon-button ias-selected"
|
||||
ng-disabled="true"
|
||||
ng-attr-title="{{ 'Title_HelpDeskCard' | translate }}">
|
||||
<ias-icon icon="view_tile_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="view-list-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.gotoTableView()"
|
||||
ng-attr-title="{{ 'Title_HelpDeskTable' | translate }}">
|
||||
<ias-icon icon="view_list_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-info-container">
|
||||
<div class="search-info" ng-class="{'loading': !$ctrl.getMessage()}"
|
||||
ng-if="$ctrl.loading || $ctrl.searchMessage || $ctrl.errorMessage"
|
||||
ng-bind="$ctrl.getMessage() || ('Display_PleaseWait' | translate)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="people-search-component-content">
|
||||
<div class="ias-grid">
|
||||
<person-card person="person"
|
||||
show-image="$ctrl.photosEnabled"
|
||||
ng-repeat="person in $ctrl.searchResult.people | orderBy:'displayNames[0]'"
|
||||
ng-click="$ctrl.selectPerson(person)">
|
||||
</person-card>
|
||||
</div>
|
||||
</div>
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {Component} from '../../component';
|
||||
import {IQService, IScope, ITimeoutService} from 'angular';
|
||||
import {IHelpDeskConfigService} from '../../services/helpdesk-config.service';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import HelpDeskSearchBaseComponent from './helpdesk-search-base.component';
|
||||
import SearchResult from '../../models/search-result.model';
|
||||
import {IPerson} from '../../models/person.model';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import {IHelpDeskService} from '../../services/helpdesk.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./helpdesk-search.component.scss'),
|
||||
templateUrl: require('./helpdesk-search-cards.component.html')
|
||||
})
|
||||
export default class HelpDeskSearchCardsComponent extends HelpDeskSearchBaseComponent {
|
||||
static $inject = [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'$translate',
|
||||
'ConfigService',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'LocalStorageService',
|
||||
'PromiseService',
|
||||
'PwmService',
|
||||
'CommonSearchService'
|
||||
];
|
||||
constructor($q: IQService,
|
||||
$scope: IScope,
|
||||
$state: angular.ui.IStateService,
|
||||
$stateParams: angular.ui.IStateParamsService,
|
||||
$timeout: ITimeoutService,
|
||||
$translate: angular.translate.ITranslateService,
|
||||
configService: IHelpDeskConfigService,
|
||||
helpDeskService: IHelpDeskService,
|
||||
IasDialogService: any,
|
||||
localStorageService: LocalStorageService,
|
||||
promiseService: PromiseService,
|
||||
pwmService: IPwmService,
|
||||
commonSearchService: CommonSearchService) {
|
||||
super($q, $scope, $state, $stateParams, $timeout, $translate, configService, helpDeskService, IasDialogService,
|
||||
localStorageService, promiseService, pwmService, commonSearchService);
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.initialize().then(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
|
||||
this.configService.photosEnabled().then((photosEnabled: boolean) => {
|
||||
this.photosEnabled = photosEnabled;
|
||||
});
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
let searchResult = this.fetchSearchData();
|
||||
if (searchResult) {
|
||||
searchResult.then(this.onSearchResult.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
gotoTableView(): void {
|
||||
this.toggleView('search.table');
|
||||
}
|
||||
|
||||
private onSearchResult(searchResult: SearchResult): void {
|
||||
// Aborted request
|
||||
if (!searchResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchResult = new SearchResult({
|
||||
sizeExceeded: searchResult.sizeExceeded,
|
||||
searchResults: []
|
||||
});
|
||||
|
||||
this.pendingRequests = searchResult.people.map(
|
||||
(person: IPerson) => {
|
||||
// Store this promise because it is abortable
|
||||
let promise = this.helpDeskService.getPersonCard(person.userKey);
|
||||
|
||||
promise
|
||||
.then(function(person: IPerson) {
|
||||
// Aborted request
|
||||
if (!person) {
|
||||
return;
|
||||
}
|
||||
|
||||
// searchResult may be overwritten by ESC->[LETTER] typed in after a search
|
||||
// has started but before all calls to helpdeskService.getPersonCard have resolved
|
||||
if (this.searchResult) {
|
||||
this.searchResult.people.push(person);
|
||||
}
|
||||
}.bind(this),
|
||||
(error) => {
|
||||
this.setErrorMessage(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.removePendingRequest(promise);
|
||||
});
|
||||
|
||||
return promise;
|
||||
},
|
||||
this
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="peoplesearch-header">
|
||||
<div class="basic-search-container" ng-if="!$ctrl.advancedSearch">
|
||||
<h2 id="page-content-title" ng-if="!$ctrl.advancedSearch" translate="Title_Helpdesk">Help Desk</h2>
|
||||
<ias-search-box id="input" ng-model="$ctrl.query"
|
||||
ng-model-options="{debounce: $ctrl.inputDebounce}"
|
||||
placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
|
||||
</ias-search-box>
|
||||
|
||||
<ias-button id="advanced-search-icon" class="ias-icon-button" ng-click="$ctrl.enableAdvancedSearch()"
|
||||
ng-if="$ctrl.advancedSearchEnabled"
|
||||
ng-attr-title="{{ 'Title_AdvancedSearch' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="search_advanced"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<div class="advanced-search-container" ng-if="$ctrl.advancedSearch">
|
||||
<div class="attribute-row" ng-repeat="query in $ctrl.queries">
|
||||
<select ng-model="query.key" ng-change="$ctrl.onAdvancedSearchAttributeChanged(query)">
|
||||
<option ng-repeat="tag in $ctrl.advancedSearchTags" ng-attr-value="{{tag.attribute}}">{{tag.label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Show a drop-down if the attribute type is 'select'-->
|
||||
<select ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type === 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value">
|
||||
<option ng-attr-value="{{name}}"
|
||||
ng-repeat="(name, label) in $ctrl.advancedSearchTags[query.key].options">{{label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Otherwise, just show a regular input field-->
|
||||
<input ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type !== 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value"
|
||||
autocomplete="off" ng-model-options="{debounce: $ctrl.inputDebounce}">
|
||||
|
||||
<ias-button class="ias-icon-button" ng-click="$ctrl.removeSearchTag($index)"
|
||||
ng-attr-title="{{ 'Button_Remove' | translate }}">
|
||||
<ias-icon icon="close_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<ias-button id="add-attribute-row" class="ias-icon-button" ng-click="$ctrl.addSearchTag()"
|
||||
ng-if="$ctrl.queries.length < $ctrl.advancedSearchMaxRows"
|
||||
ng-attr-title="{{ 'Button_AddSearchAttribute' | translate }}">
|
||||
<ias-icon icon="new_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<span class="ias-fill"></span>
|
||||
<div class="ias-header">
|
||||
<ias-button id="view-tile-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.gotoCardsView()"
|
||||
ng-attr-title="{{ 'Title_HelpDeskCard' | translate }}">
|
||||
<ias-icon icon="view_tile_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="view-list-icon" class="ias-icon-button ias-selected"
|
||||
ng-disabled="true"
|
||||
ng-attr-title="{{ 'Title_HelpDeskTable' | translate }}">
|
||||
<ias-icon icon="view_list_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<div class="icon-divider vertical"></div>
|
||||
<ias-button class="ias-icon-button table-configuration-menu-toggle" ias-toggle="menu1"
|
||||
ng-attr-title="{{ 'Title_Settings' | translate }}">
|
||||
<ias-icon icon="configure_thick"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-menu name="menu1" ias-align="end end" class="ias-styles-root">
|
||||
<div class="ias-input-container">
|
||||
<div class="checkbox-button" ng-repeat="(key, value) in $ctrl.columnConfiguration">
|
||||
<input type="checkbox" ng-model="value.visible" aria-label="Toggle column visibility" />
|
||||
<ias-button class="toggle-column-btn" ng-click="value.visible = !value.visible; $event.stopImmediatePropagation();">{{value.label}}</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</ias-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-info-container">
|
||||
<div class="search-info" ng-class="{'loading': !$ctrl.getMessage()}"
|
||||
ng-if="$ctrl.loading || $ctrl.searchMessage || $ctrl.errorMessage"
|
||||
ng-bind="$ctrl.getMessage() || ('Display_PleaseWait' | translate)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="people-search-component-content">
|
||||
<table class="ias-table" ias-sort="$ctrl.sort" ng-show="$ctrl.searchResult.people.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible"
|
||||
ias-sort-on="key">{{value.label}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="person in $ctrl.searchResult.people | orderBy:$ctrl.sort"
|
||||
ng-click="$ctrl.selectPerson(person)">
|
||||
<td ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible">
|
||||
<span ng-bind="person[key]"></span>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IQService, IScope, ITimeoutService} from 'angular';
|
||||
import HelpDeskSearchBaseComponent from './helpdesk-search-base.component';
|
||||
import {Component} from '../../component';
|
||||
import SearchResult from '../../models/search-result.model';
|
||||
import {IHelpDeskConfigService} from '../../services/helpdesk-config.service';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import {IHelpDeskService} from '../../services/helpdesk.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./helpdesk-search.component.scss'),
|
||||
templateUrl: require('./helpdesk-search-table.component.html')
|
||||
})
|
||||
export default class HelpDeskSearchTableComponent extends HelpDeskSearchBaseComponent {
|
||||
columnConfiguration: any;
|
||||
|
||||
static $inject = [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'$translate',
|
||||
'ConfigService',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'LocalStorageService',
|
||||
'PromiseService',
|
||||
'PwmService',
|
||||
'CommonSearchService'
|
||||
];
|
||||
constructor($q: IQService,
|
||||
$scope: IScope,
|
||||
$state: angular.ui.IStateService,
|
||||
$stateParams: angular.ui.IStateParamsService,
|
||||
$timeout: ITimeoutService,
|
||||
$translate: angular.translate.ITranslateService,
|
||||
configService: IHelpDeskConfigService,
|
||||
helpDeskService: IHelpDeskService,
|
||||
IasDialogService: any,
|
||||
localStorageService: LocalStorageService,
|
||||
promiseService: PromiseService,
|
||||
pwmService: IPwmService,
|
||||
commonSearchService: CommonSearchService) {
|
||||
super($q, $scope, $state, $stateParams, $timeout, $translate, configService, helpDeskService, IasDialogService,
|
||||
localStorageService, promiseService, pwmService, commonSearchService);
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.initialize().then(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
|
||||
// The table columns are dynamic and configured via a service
|
||||
this.configService.getColumnConfig().then(
|
||||
(columnConfiguration: any) => {
|
||||
this.columnConfiguration = Object.keys(columnConfiguration).reduce(
|
||||
function(accumulator, columnId) {
|
||||
accumulator[columnId] = {
|
||||
label: columnConfiguration[columnId],
|
||||
visible: true
|
||||
};
|
||||
|
||||
return accumulator;
|
||||
},
|
||||
{});
|
||||
},
|
||||
(error) => {
|
||||
this.setErrorMessage(error);
|
||||
});
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
let searchResult = this.fetchSearchData();
|
||||
if (searchResult) {
|
||||
searchResult.then(this.onSearchResult.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
gotoCardsView(): void {
|
||||
this.toggleView('search.cards');
|
||||
}
|
||||
|
||||
private onSearchResult(searchResult: SearchResult): void {
|
||||
this.searchResult = searchResult;
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
help-desk-search-cards,
|
||||
help-desk-search-table {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
|
||||
#page-content-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.verifications-button {
|
||||
margin: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
> .people-search-component-content {
|
||||
flex: 1 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.helpdesk-search-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.basic-search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
> * + * {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-search-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 15px;
|
||||
|
||||
> * + * {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
& + div {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aligned-input {
|
||||
margin-top: 15px;
|
||||
|
||||
> * {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ias-button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-gif-25 {
|
||||
background-image: url('../../../images/icons/wait_25.gif');
|
||||
display: inline-block;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
help-desk-search-cards,
|
||||
help-desk-search-table {
|
||||
.ias-search {
|
||||
margin-left: 10px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.verifications-button {
|
||||
margin: 5px 0 5px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.aligned-input {
|
||||
.ias-button {
|
||||
margin-left: 5px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ias-input-container > .checkbox-button > .ias-button.toggle-column-btn {
|
||||
&:focus, &:hover {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// These need to be at the top so imported components can override the default styling
|
||||
require('../../styles.scss');
|
||||
require('../peoplesearch/peoplesearch.scss');
|
||||
|
||||
import 'angular-aria';
|
||||
|
||||
import {IComponentOptions, module} from 'angular';
|
||||
import DateFilter from './date.filters';
|
||||
import HelpDeskDetailComponent from './helpdesk-detail.component';
|
||||
import HelpDeskSearchTableComponent from './helpdesk-search-table.component';
|
||||
import HelpDeskSearchCardsComponent from './helpdesk-search-cards.component';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import ObjectService from '../../services/object.service';
|
||||
import PersonCardDirective from '../peoplesearch/person-card.component';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import RecentVerificationsDialogController from './recent-verifications-dialog.controller';
|
||||
import uxModule from '../../ux/ux.module';
|
||||
import VerificationsDialogController from './verifications-dialog.controller';
|
||||
import AutogenChangePasswordController from '../../components/changepassword/autogen-change-password.controller';
|
||||
import RandomChangePasswordController from '../../components/changepassword/random-change-password.controller';
|
||||
import SuccessChangePasswordController from '../../components/changepassword/success-change-password.controller';
|
||||
import TypeChangePasswordController from '../../components/changepassword/type-change-password.controller';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
const moduleName = 'help-desk';
|
||||
|
||||
module(moduleName, [
|
||||
'ngAria',
|
||||
'ngSanitize',
|
||||
uxModule
|
||||
])
|
||||
|
||||
.component('helpDeskSearchCards', HelpDeskSearchCardsComponent as IComponentOptions)
|
||||
.component('helpDeskSearchTable', HelpDeskSearchTableComponent as IComponentOptions)
|
||||
.component('helpDeskDetail', HelpDeskDetailComponent as IComponentOptions)
|
||||
.directive('personCard', PersonCardDirective)
|
||||
.controller('AutogenChangePasswordController', AutogenChangePasswordController)
|
||||
.controller('RandomChangePasswordController', RandomChangePasswordController)
|
||||
.controller('RecentVerificationsDialogController', RecentVerificationsDialogController)
|
||||
.controller('SuccessChangePasswordController', SuccessChangePasswordController)
|
||||
.controller('TypeChangePasswordController', TypeChangePasswordController)
|
||||
.controller('VerificationsDialogController', VerificationsDialogController)
|
||||
.filter('dateFilter', DateFilter)
|
||||
.service('ObjectService', ObjectService)
|
||||
.service('PromiseService', PromiseService)
|
||||
.service('LocalStorageService', LocalStorageService)
|
||||
.service('CommonSearchService', CommonSearchService);
|
||||
|
||||
export default moduleName;
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'angular';
|
||||
import 'angular-translate';
|
||||
import '@microfocus/ng-ias/dist/ng-ias';
|
||||
|
||||
import { bootstrap, module } from 'angular';
|
||||
import helpDeskModule from './helpdesk.module';
|
||||
import routes from './routes';
|
||||
import uiRouter from '@uirouter/angularjs';
|
||||
import PeopleService from '../../services/people.service.dev';
|
||||
import HelpDeskConfigService from '../../services/helpdesk-config.service.dev';
|
||||
import HelpDeskService from '../../services/helpdesk.service.dev';
|
||||
import PasswordService from '../../services/password.service.dev';
|
||||
import PwmService from '../../services/pwm.service.dev';
|
||||
|
||||
|
||||
module('app', [
|
||||
uiRouter,
|
||||
helpDeskModule,
|
||||
'pascalprecht.translate',
|
||||
'ng-ias'
|
||||
])
|
||||
.config(['$translateProvider', ($translateProvider: angular.translate.ITranslateProvider) => {
|
||||
$translateProvider.translations('en', require('../../i18n/translations_en.json'));
|
||||
$translateProvider.useSanitizeValueStrategy('escapeParameters');
|
||||
$translateProvider.preferredLanguage('en');
|
||||
}])
|
||||
.config(routes)
|
||||
.service('HelpDeskService', HelpDeskService)
|
||||
.service('PasswordService', PasswordService)
|
||||
.service('PeopleService', PeopleService)
|
||||
.service('PwmService', PwmService)
|
||||
.service('ConfigService', HelpDeskConfigService);
|
||||
|
||||
// Attach to the page document
|
||||
bootstrap(document, ['app']);
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'angular';
|
||||
import 'angular-translate';
|
||||
import 'angular-sanitize';
|
||||
import '@microfocus/ng-ias/dist/ng-ias';
|
||||
|
||||
// Add a polyfill for Set() for IE11, since it's used in peoplesearch-base.component.ts
|
||||
import 'core-js/es/set';
|
||||
|
||||
import { bootstrap, module } from 'angular';
|
||||
import helpDeskModule from './helpdesk.module';
|
||||
import PeopleService from '../../services/people.service';
|
||||
import PwmService from '../../services/pwm.service';
|
||||
import routes from './routes';
|
||||
import TranslationsLoaderFactory from '../../services/translations-loader.factory';
|
||||
import uiRouter from '@uirouter/angularjs';
|
||||
import HelpDeskConfigService from '../../services/helpdesk-config.service';
|
||||
import HelpDeskService from '../../services/helpdesk.service';
|
||||
import PasswordService from '../../services/password.service';
|
||||
|
||||
|
||||
module('app', [
|
||||
uiRouter,
|
||||
helpDeskModule,
|
||||
'pascalprecht.translate',
|
||||
'ng-ias'
|
||||
])
|
||||
.config(routes)
|
||||
.config([
|
||||
'$translateProvider',
|
||||
($translateProvider: angular.translate.ITranslateProvider) => {
|
||||
$translateProvider
|
||||
.translations('fallback', require('../../i18n/translations_en.json'))
|
||||
.useLoader('translationsLoader')
|
||||
.useSanitizeValueStrategy('escapeParameters')
|
||||
.preferredLanguage('en')
|
||||
.fallbackLanguage('fallback')
|
||||
.forceAsyncReload(true);
|
||||
}])
|
||||
.service('HelpDeskService', HelpDeskService)
|
||||
.service('PasswordService', PasswordService)
|
||||
.service('PeopleService', PeopleService)
|
||||
.service('PwmService', PwmService)
|
||||
.service('ConfigService', HelpDeskConfigService)
|
||||
.factory('translationsLoader', TranslationsLoaderFactory);
|
||||
|
||||
// Attach to the page document, wait for PWM to load first
|
||||
window['PWM_GLOBAL'].startupFunctions.push(() => {
|
||||
bootstrap(document, ['app'], { strictDi: true });
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHelpDeskService, IRecentVerifications} from '../../services/helpdesk.service';
|
||||
|
||||
export default class RecentVerificationsDialogController {
|
||||
recentVerifications: IRecentVerifications;
|
||||
|
||||
static $inject = [ 'HelpDeskService' ];
|
||||
constructor(helpDeskService: IHelpDeskService) {
|
||||
helpDeskService.getRecentVerifications()
|
||||
.then((recentVerifications) => {
|
||||
this.recentVerifications = recentVerifications;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_RecentVerifications' | translate"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="'Display_SearchResultsNone' | translate"
|
||||
ng-if="!$ctrl.recentVerifications.length"></p>
|
||||
<table class="ias-table" ng-if="!!$ctrl.recentVerifications.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-bind="'Field_LdapProfile' | translate"></th>
|
||||
<th ng-bind="'Field_Username' | translate"></th>
|
||||
<th ng-bind="'Field_DateTime' | translate"></th>
|
||||
<th ng-bind="'Field_Method' | translate"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="record in $ctrl.recentVerifications">
|
||||
<td ng-bind="record.profile"></td>
|
||||
<td ng-bind="record.username"></td>
|
||||
<td ng-bind="record.timestamp | dateFilter"></td>
|
||||
<td ng-bind="record.method"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div ng-repeat="method in $ctrl.availableVerificationMethods">
|
||||
<ias-button ng-click="$ctrl.selectVerificationMethod(method.name)">
|
||||
{{ method.label | translate }}
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="close()">{{ 'Button_OK' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
|
||||
export default [
|
||||
'$stateProvider',
|
||||
'$urlRouterProvider',
|
||||
(
|
||||
$stateProvider: angular.ui.IStateProvider,
|
||||
$urlRouterProvider: angular.ui.IUrlRouterProvider
|
||||
) => {
|
||||
$urlRouterProvider.otherwise(
|
||||
($injector: angular.auto.IInjectorService, $location: angular.ILocationService) => {
|
||||
let $state: angular.ui.IStateService = <angular.ui.IStateService>$injector.get('$state');
|
||||
let localStorageService: LocalStorageService =
|
||||
<LocalStorageService>$injector.get('LocalStorageService');
|
||||
|
||||
let storedView = localStorageService.getItem(localStorageService.keys.HELPDESK_SEARCH_VIEW);
|
||||
|
||||
if (storedView) {
|
||||
$state.go(storedView);
|
||||
}
|
||||
else {
|
||||
$location.url('search/cards');
|
||||
}
|
||||
});
|
||||
|
||||
$stateProvider.state('search', {
|
||||
url: '/search?query',
|
||||
abstract: true,
|
||||
template: '<div class="help-desk-search-component"><ui-view/></div>',
|
||||
});
|
||||
$stateProvider.state('search.cards', { url: '/cards', component: 'helpDeskSearchCards' });
|
||||
$stateProvider.state('search.table', { url: '/table', component: 'helpDeskSearchTable' });
|
||||
$stateProvider.state('details', { url: '/details/{personId}', component: 'helpDeskDetail' });
|
||||
}];
|
|
@ -1,216 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
require('./verifications-dialog.component.scss');
|
||||
|
||||
import {ui, ITimeoutService} from 'angular';
|
||||
import {
|
||||
IHelpDeskConfigService, IVerificationMap, TOKEN_CHOICE, VERIFICATION_METHOD_LABELS,
|
||||
VERIFICATION_METHOD_NAMES
|
||||
} from '../../services/helpdesk-config.service';
|
||||
import {
|
||||
IHelpDeskService,
|
||||
IVerificationOptions,
|
||||
IVerificationStatus,
|
||||
IVerificationTokenResponse
|
||||
} from '../../services/helpdesk.service';
|
||||
import ObjectService from '../../services/object.service';
|
||||
|
||||
const STATUS_FAILED = 'failed';
|
||||
const STATUS_NONE = 'none';
|
||||
const STATUS_PASSED = 'passed';
|
||||
const STATUS_SELECT = 'select';
|
||||
const STATUS_VERIFY = 'verify';
|
||||
const STATUS_WAIT = 'wait';
|
||||
|
||||
export default class VerificationsDialogController {
|
||||
verificationOptions: IVerificationOptions;
|
||||
tokenDestinationID: string;
|
||||
sendingVerificationToken = false;
|
||||
verificationTokenSent = false;
|
||||
|
||||
availableVerificationMethods: IVerificationMap;
|
||||
formData: any = {};
|
||||
inputs: { name: string, label: string }[];
|
||||
isDetailsView: boolean;
|
||||
status: string;
|
||||
tokenData: string;
|
||||
viewDetailsEnabled: boolean;
|
||||
verificationMethod: string;
|
||||
verificationStatus: string;
|
||||
|
||||
static $inject = [
|
||||
'$state',
|
||||
'$timeout',
|
||||
'ConfigService',
|
||||
'HelpDeskService',
|
||||
'IasDialogService',
|
||||
'ObjectService',
|
||||
'personUserKey',
|
||||
'showRequiredOnly'
|
||||
];
|
||||
constructor(private $state: ui.IStateService,
|
||||
private $timeout: ITimeoutService,
|
||||
private configService: IHelpDeskConfigService,
|
||||
private helpDeskService: IHelpDeskService,
|
||||
private IasDialogService: any,
|
||||
private objectService: ObjectService,
|
||||
private personUserKey: string,
|
||||
private showRequiredOnly: boolean) {
|
||||
|
||||
this.isDetailsView = (this.$state.current.name === 'details');
|
||||
this.status = STATUS_WAIT;
|
||||
this.verificationStatus = STATUS_NONE;
|
||||
this.viewDetailsEnabled = false;
|
||||
|
||||
this.helpDeskService
|
||||
.checkVerification(this.personUserKey)
|
||||
.then((response: IVerificationStatus) => {
|
||||
this.verificationOptions = response.verificationOptions;
|
||||
|
||||
if (!this.isDetailsView && response.passed) {
|
||||
// If we're not on the details page already, and verifications have been passed, then just go right
|
||||
// to the details page:
|
||||
this.gotoDetailsPage();
|
||||
}
|
||||
else {
|
||||
this.status = STATUS_SELECT;
|
||||
this.determineAvailableVerificationMethods();
|
||||
}
|
||||
})
|
||||
.catch((reason: any) => {
|
||||
alert(reason);
|
||||
|
||||
this.status = STATUS_NONE;
|
||||
this.verificationStatus = STATUS_NONE;
|
||||
this.IasDialogService.close();
|
||||
});
|
||||
}
|
||||
|
||||
determineAvailableVerificationMethods() {
|
||||
this.availableVerificationMethods = [];
|
||||
|
||||
const methodNames: string[] = this.showRequiredOnly ?
|
||||
this.verificationOptions.verificationMethods.required :
|
||||
this.verificationOptions.verificationMethods.optional;
|
||||
|
||||
if (methodNames) {
|
||||
for (let methodName of methodNames) {
|
||||
this.availableVerificationMethods.push({
|
||||
name: methodName,
|
||||
label: VERIFICATION_METHOD_LABELS[methodName]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clickOkButton() {
|
||||
if (this.verificationStatus === STATUS_PASSED) {
|
||||
this.IasDialogService.close();
|
||||
}
|
||||
}
|
||||
|
||||
private gotoDetailsPage() {
|
||||
this.$timeout(() => {
|
||||
this.IasDialogService.close();
|
||||
this.$state.go('details', {personId: this.personUserKey});
|
||||
});
|
||||
}
|
||||
|
||||
selectVerificationMethod(method: string) {
|
||||
this.verificationMethod = method;
|
||||
|
||||
if (method === VERIFICATION_METHOD_NAMES.ATTRIBUTES) {
|
||||
this.configService.getVerificationAttributes()
|
||||
.then((response) => {
|
||||
this.status = STATUS_VERIFY;
|
||||
this.inputs = response;
|
||||
|
||||
// Need to initialize the formData values to empty strings, otherwise null values will be sent to
|
||||
// the server
|
||||
for (let i = 0; i < this.inputs.length; i++) {
|
||||
this.formData[this.inputs[i].name] = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (method === VERIFICATION_METHOD_NAMES.TOKEN) {
|
||||
this.status = STATUS_VERIFY;
|
||||
|
||||
try {
|
||||
// Select the first destination in the list as default.
|
||||
this.tokenDestinationID = this.verificationOptions.tokenDestinations[0].id;
|
||||
} catch (error) {}
|
||||
}
|
||||
else if (method === VERIFICATION_METHOD_NAMES.OTP) {
|
||||
this.status = STATUS_VERIFY;
|
||||
}
|
||||
}
|
||||
|
||||
sendVerificationData() {
|
||||
this.verificationStatus = STATUS_WAIT;
|
||||
let data = {};
|
||||
this.objectService.assign(data, this.formData);
|
||||
if (this.tokenData) {
|
||||
this.objectService.assign(data, {
|
||||
tokenData: this.tokenData
|
||||
});
|
||||
}
|
||||
this.helpDeskService.validateVerificationData(this.personUserKey, data, this.verificationMethod)
|
||||
.then((response) => {
|
||||
if (response.passed) {
|
||||
this.verificationStatus = STATUS_PASSED;
|
||||
}
|
||||
else {
|
||||
this.verificationStatus = STATUS_FAILED;
|
||||
}
|
||||
})
|
||||
.catch((reason) => {
|
||||
this.verificationStatus = STATUS_FAILED;
|
||||
});
|
||||
}
|
||||
|
||||
onTokenDestinationChanged() {
|
||||
this.verificationTokenSent = false;
|
||||
}
|
||||
|
||||
sendVerificationTokenToDestination() {
|
||||
this.sendingVerificationToken = true;
|
||||
this.verificationTokenSent = false;
|
||||
|
||||
this.helpDeskService.sendVerificationToken(this.personUserKey, this.tokenDestinationID)
|
||||
.then((response) => {
|
||||
this.verificationTokenSent = true;
|
||||
this.tokenData = (response as any).data.tokenData;
|
||||
})
|
||||
.catch((reason) => {
|
||||
this.verificationTokenSent = false;
|
||||
alert(reason);
|
||||
})
|
||||
.finally(() => {
|
||||
this.sendingVerificationToken = false;
|
||||
});
|
||||
}
|
||||
|
||||
viewDetails() {
|
||||
if (this.verificationStatus === STATUS_PASSED) {
|
||||
this.gotoDetailsPage();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog" ng-switch="$ctrl.status">
|
||||
<div class="ias-dialog-container" ng-switch-when="select">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_VerificationSend' | translate"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="'Long_Title_VerificationSend' | translate"></p>
|
||||
|
||||
<ias-button ng-click="$ctrl.selectVerificationMethod(method.name)"
|
||||
ng-repeat="method in $ctrl.availableVerificationMethods"
|
||||
style="margin-right: 3px;">
|
||||
{{ method.label | translate }}
|
||||
</ias-button>
|
||||
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="close()">{{ 'Button_Cancel' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-container grow-with-content" ng-switch-when="verify">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_ValidateCode' | translate"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-dialog-content" ng-switch="$ctrl.verificationMethod">
|
||||
<form>
|
||||
<div ng-switch-when="ATTRIBUTES">
|
||||
<div class="ias-input-container" ng-repeat="input in $ctrl.inputs">
|
||||
<label ng-attr-for="{{input.name}}" ng-bind="input.label"></label>
|
||||
<input autofocus
|
||||
autocomplete="off"
|
||||
ng-attr-id="{{input.name}}"
|
||||
type="text"
|
||||
ng-model="$ctrl.formData[input.name]">
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="TOKEN" ng-switch-when-separator="|">
|
||||
<label ng-bind="'Display_TokenDestination' | translate"></label>
|
||||
<div class="ias-input-container token-destination-submitter">
|
||||
<select id="verification-token-destination" ng-model="$ctrl.tokenDestinationID"
|
||||
ng-change="$ctrl.onTokenDestinationChanged()">
|
||||
<option ng-repeat="tokenDestination in $ctrl.verificationOptions.tokenDestinations"
|
||||
ng-attr-value="{{tokenDestination.id}}">
|
||||
{{ tokenDestination.type == 'sms'?'Display_SmsPrefix':'Display_EmailPrefix' | translate}}
|
||||
{{tokenDestination.display}}
|
||||
</option>
|
||||
</select>
|
||||
<ias-button ng-click="$ctrl.sendVerificationTokenToDestination()"
|
||||
ng-disabled="$ctrl.sendingVerificationToken"
|
||||
title="{{ 'Button_SendToken' | translate }}">
|
||||
{{ 'Button_SendToken' | translate }}
|
||||
</ias-button>
|
||||
<div class="loading-gif-25" ng-if="$ctrl.sendingVerificationToken"></div>
|
||||
<ias-icon icon="status_ok_thick" style="color:#37c26a;" class="ias-success" ng-if="$ctrl.verificationTokenSent"></ias-icon>
|
||||
</div>
|
||||
<div class="ias-input-container">
|
||||
<label>Token</label>
|
||||
<input autofocus autocomplete="off" id="token" type="text" ng-model="$ctrl.formData.code">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div ng-switch-when="OTP">
|
||||
<p ng-bind="'Display_HelpdeskOtpValidation' | translate"></p>
|
||||
<div class="ias-input-container">
|
||||
<label>Code</label>
|
||||
<input autofocus autocomplete="off" id="code" type="text" ng-model="$ctrl.formData.code">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aligned-input">
|
||||
<ias-button ng-click="$ctrl.sendVerificationData()" ng-disabled="$ctrl.verificationStatus==='passed'">
|
||||
{{ 'Button_Verify' | translate }}
|
||||
</ias-button>
|
||||
<div class="loading-gif-25" ng-if="$ctrl.verificationStatus==='wait'"></div>
|
||||
<ias-icon icon="status_ok_thick" style="color:#37c26a;" class="ias-success" ng-if="$ctrl.verificationStatus==='passed'"></ias-icon>
|
||||
<ias-icon icon="status_error_thick" style="color:#e50000;" class="ias-error" ng-if="$ctrl.verificationStatus==='failed'"></ias-icon>
|
||||
</div>
|
||||
<p ng-bind="'Display_InvalidVerification' | translate" ng-if="$ctrl.verificationStatus==='failed'"></p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-bind="'Display_ViewDetails' | translate"
|
||||
ng-disabled="$ctrl.verificationStatus!=='passed'"
|
||||
ng-click="$ctrl.viewDetails()"
|
||||
ng-if="!$ctrl.isDetailsView"></ias-button>
|
||||
<ias-button ng-disabled="$ctrl.verificationStatus!=='passed'"
|
||||
ng-click="$ctrl.clickOkButton()"
|
||||
ng-if="$ctrl.isDetailsView">
|
||||
{{ 'Button_OK' | translate }}
|
||||
</ias-button>
|
||||
<ias-button ng-click="close()">{{ 'Button_Cancel' | translate }}</ias-button>
|
||||
</div>
|
||||
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'angular';
|
||||
import 'angular-translate';
|
||||
import '@microfocus/ng-ias/dist/ng-ias';
|
||||
|
||||
import { bootstrap, module } from 'angular';
|
||||
import ConfigService from '../../services/peoplesearch-config.service.dev';
|
||||
import peopleSearchModule from './peoplesearch.module';
|
||||
import PeopleService from '../../services/people.service.dev';
|
||||
import PwmService from '../../services/pwm.service.dev';
|
||||
import routes from '../../routes';
|
||||
import routeErrorHandler from '../../route-error-handler';
|
||||
import uiRouter from '@uirouter/angularjs';
|
||||
|
||||
|
||||
module('app', [
|
||||
uiRouter,
|
||||
peopleSearchModule,
|
||||
'pascalprecht.translate',
|
||||
'ng-ias'
|
||||
])
|
||||
|
||||
.config(routes)
|
||||
.config(['$translateProvider', ($translateProvider: angular.translate.ITranslateProvider) => {
|
||||
$translateProvider.translations('en', require('../../i18n/translations_en.json'));
|
||||
$translateProvider.useSanitizeValueStrategy('escapeParameters');
|
||||
$translateProvider.preferredLanguage('en');
|
||||
}])
|
||||
.run(routeErrorHandler)
|
||||
.service('PeopleService', PeopleService)
|
||||
.service('PwmService', PwmService)
|
||||
.service('ConfigService', ConfigService);
|
||||
|
||||
// Attach to the page document
|
||||
bootstrap(document, ['app']);
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import 'angular';
|
||||
import 'angular-translate';
|
||||
import '@microfocus/ng-ias/dist/ng-ias';
|
||||
|
||||
// Add a polyfill for Set() for IE11, since it's used in peoplesearch-base.component.ts
|
||||
import 'core-js/es/set';
|
||||
|
||||
import { bootstrap, module } from 'angular';
|
||||
import ConfigService from '../../services/peoplesearch-config.service';
|
||||
import peopleSearchModule from './peoplesearch.module';
|
||||
import PeopleService from '../../services/people.service';
|
||||
import PwmService from '../../services/pwm.service';
|
||||
import routes from '../../routes';
|
||||
import routeErrorHandler from '../../route-error-handler';
|
||||
import TranslationsLoaderFactory from '../../services/translations-loader.factory';
|
||||
import uiRouter from '@uirouter/angularjs';
|
||||
|
||||
module('app', [
|
||||
uiRouter,
|
||||
peopleSearchModule,
|
||||
'pascalprecht.translate',
|
||||
'ng-ias'
|
||||
])
|
||||
|
||||
.config(routes)
|
||||
.config([
|
||||
'$translateProvider',
|
||||
($translateProvider: angular.translate.ITranslateProvider) => {
|
||||
$translateProvider
|
||||
.translations('fallback', require('../../i18n/translations_en.json'))
|
||||
.useLoader('translationsLoader')
|
||||
.useSanitizeValueStrategy('escapeParameters')
|
||||
.preferredLanguage('en')
|
||||
.fallbackLanguage('fallback')
|
||||
.forceAsyncReload(true);
|
||||
}])
|
||||
.config([
|
||||
'$locationProvider', ($locationProvider: angular.ILocationProvider) => {
|
||||
$locationProvider.hashPrefix('');
|
||||
}])
|
||||
.run(routeErrorHandler)
|
||||
.service('PeopleService', PeopleService)
|
||||
.service('PwmService', PwmService)
|
||||
.service('ConfigService', ConfigService)
|
||||
.factory('translationsLoader', TranslationsLoaderFactory);
|
||||
|
||||
// Attach to the page document, wait for PWM to load first
|
||||
window['PWM_GLOBAL'].startupFunctions.push(() => {
|
||||
bootstrap(document, ['app'], { strictDi: true });
|
||||
});
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_EmailOrgChart' | translate"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="'Instructions_EmailTeam1' | translate"></p>
|
||||
<div ng-if="$ctrl.maxDepth > 1" class="ias-input-container">
|
||||
<label for="depth" ng-bind="'Label_EmailLevelDepth' | translate"></label>
|
||||
<select id="depth" ng-model="$ctrl.depth" ng-change="$ctrl.depthChanged()">
|
||||
<option ng-repeat="x in [].constructor($ctrl.maxDepth) track by $index">{{$index+1}}</option>
|
||||
</select>
|
||||
{{ 'Label_StartingFrom' | translate: { personName: $ctrl.personName } }}
|
||||
</div>
|
||||
<div id="teamMembersContainer" class="ias-input-container">
|
||||
<label for="teamMembers" ng-bind="'Label_EmailTeamMembersFound' | translate"></label>
|
||||
<textarea id="teamMembers" ng-model="$ctrl.teamEmailList"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-disabled="$ctrl.fetchingTeamMembers" ng-click="$ctrl.emailOrgChart()">{{ 'Button_SendEmail' | translate }}</ias-button>
|
||||
</div>
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,32 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
#teamMembersContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 150px;
|
||||
|
||||
> textarea {
|
||||
align-self: stretch;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as angular from 'angular';
|
||||
import {IPeopleService} from '../../services/people.service';
|
||||
|
||||
require('./orgchart-email.component.scss');
|
||||
|
||||
export default class OrgchartEmailController {
|
||||
depth = '1';
|
||||
fetchingTeamMembers = false;
|
||||
teamEmailList: string;
|
||||
|
||||
static $inject = [
|
||||
'$window',
|
||||
'IasDialogService',
|
||||
'translateFilter',
|
||||
'peopleService',
|
||||
'maxDepth',
|
||||
'personName',
|
||||
'userKey'
|
||||
];
|
||||
constructor(private $window: angular.IWindowService,
|
||||
private IasDialogService: any,
|
||||
private translateFilter: (id: string) => string,
|
||||
private peopleService: IPeopleService,
|
||||
private maxDepth: number,
|
||||
private personName: string,
|
||||
private userKey: string) {
|
||||
|
||||
this.fetchEmailList();
|
||||
}
|
||||
|
||||
emailOrgChart() {
|
||||
this.$window.location.href = `mailto:${this.teamEmailList}`;
|
||||
this.IasDialogService.close();
|
||||
}
|
||||
|
||||
depthChanged() {
|
||||
this.fetchEmailList();
|
||||
}
|
||||
|
||||
fetchEmailList() {
|
||||
this.fetchingTeamMembers = true;
|
||||
|
||||
this.peopleService.getTeamEmails(this.userKey, +this.depth)
|
||||
.then((teamEmails: string[]) => {
|
||||
this.teamEmailList = teamEmails.toString();
|
||||
})
|
||||
.finally(() => {
|
||||
this.fetchingTeamMembers = false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-label">
|
||||
<div class="ias-title" ng-bind="'Title_ExportOrgChart' | translate"></div>
|
||||
</div>
|
||||
<div class="ias-dialog-content">
|
||||
<p ng-bind="'Instructions_ExportOrgChart1' | translate"></p>
|
||||
<label ng-if="$ctrl.maxDepth > 1" for="depth" ng-bind="'Label_ExportLevelDepth' | translate"></label>
|
||||
<div ng-if="$ctrl.maxDepth > 1">
|
||||
<select id="depth" ng-model="$ctrl.depth">
|
||||
<option ng-repeat="x in [].constructor($ctrl.maxDepth) track by $index">{{$index+1}}</option>
|
||||
</select>
|
||||
{{ 'Label_StartingFrom' | translate: { personName: $ctrl.personName } }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ias-actions">
|
||||
<ias-button ng-click="$ctrl.exportOrgChart()">{{ 'Button_Export' | translate }}</ias-button>
|
||||
</div>
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
id="close-icon"
|
||||
ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
|
||||
ng-click="close()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {IPwmService} from '../../services/pwm.service';
|
||||
|
||||
require('./orgchart-export.component.scss');
|
||||
|
||||
export default class OrgchartExportController {
|
||||
depth = '1';
|
||||
|
||||
static $inject = [
|
||||
'$window',
|
||||
'IasDialogService',
|
||||
'translateFilter',
|
||||
'PwmService',
|
||||
'maxDepth',
|
||||
'personName',
|
||||
'userKey'
|
||||
];
|
||||
constructor(private $window: angular.IWindowService,
|
||||
private IasDialogService: any,
|
||||
private translateFilter: (id: string) => string,
|
||||
private pwmService: IPwmService,
|
||||
private maxDepth: number,
|
||||
private personName: string,
|
||||
private userKey: string) {
|
||||
}
|
||||
|
||||
exportOrgChart() {
|
||||
this.$window.location.href = this.pwmService.getServerUrl('exportOrgChart', {
|
||||
depth: this.depth,
|
||||
userKey: this.userKey
|
||||
});
|
||||
|
||||
this.IasDialogService.close();
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-header">
|
||||
<h2 id="page-content-title" translate="Title_Organization">Organization</h2>
|
||||
<ias-autocomplete placeholder="{{'Placeholder_Search' | translate}}"
|
||||
ias-debounce="$ctrl.inputDebounce"
|
||||
ias-items="person in $ctrl.autoCompleteSearch($query)"
|
||||
ias-on-item-selected="$ctrl.onAutoCompleteItemSelected($item)"
|
||||
ias-search-text="$ctrl.query">
|
||||
<template><span ng-bind="person._displayName" class="single-line"></span></template>
|
||||
</ias-autocomplete>
|
||||
|
||||
<span class="ias-fill"></span>
|
||||
|
||||
<ias-button id="view-tile-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.gotoSearchState('search.cards')"
|
||||
ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="view_tile_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="view-list-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.gotoSearchState('search.table')"
|
||||
ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="view_list_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<div class="icon-divider vertical"></div>
|
||||
<ias-button id="orgchart-icon" class="ias-icon-button ias-selected" ng-disabled="true"
|
||||
ng-attr-title="{{ 'Title_OrgChart' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="orgchart_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="print-icon" class="ias-icon-button"
|
||||
ng-click="$ctrl.printOrgChart()" ng-if="$ctrl.printEnabled"
|
||||
ng-attr-title="{{ 'Title_Print' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="printer"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
|
||||
|
||||
<org-chart person="$ctrl.person"
|
||||
direct-reports="$ctrl.directReports"
|
||||
show-images="$ctrl.photosEnabled"
|
||||
assistant="$ctrl.assistant"
|
||||
management-chain="$ctrl.managementChain">
|
||||
</org-chart>
|
||||
|
||||
<ui-view></ui-view>
|
|
@ -1,36 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
org-chart-search {
|
||||
#page-content-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
|
||||
> org-chart {
|
||||
flex: 1 1;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { Component } from '../../component';
|
||||
import { IPeopleSearchConfigService } from '../../services/peoplesearch-config.service';
|
||||
import { IPeopleService } from '../../services/people.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import {isArray, isString, IPromise, IQService, IScope, ITimeoutService, IWindowService} from 'angular';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import IOrgChartData from '../../models/orgchart-data.model';
|
||||
import { IPerson } from '../../models/person.model';
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./orgchart-search.component.scss'),
|
||||
templateUrl: require('./orgchart-search.component.html')
|
||||
})
|
||||
export default class OrgChartSearchComponent {
|
||||
directReports: IPerson[];
|
||||
inputDebounce: number;
|
||||
managementChain: IPerson[];
|
||||
assistant: IPerson;
|
||||
person: IPerson;
|
||||
photosEnabled: boolean;
|
||||
managementChainLimit: number;
|
||||
query: string;
|
||||
searchTextLocalStorageKey: string;
|
||||
printEnabled: boolean;
|
||||
|
||||
static $inject = [
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'$window',
|
||||
'ConfigService',
|
||||
'LocalStorageService',
|
||||
'PeopleService',
|
||||
'PwmService'
|
||||
];
|
||||
constructor(private $state: angular.ui.IStateService,
|
||||
private $stateParams: angular.ui.IStateParamsService,
|
||||
private $timeout: ITimeoutService,
|
||||
private $window: IWindowService,
|
||||
private configService: IPeopleSearchConfigService,
|
||||
private localStorageService: LocalStorageService,
|
||||
private peopleService: IPeopleService,
|
||||
private pwmService: IPwmService) {
|
||||
this.searchTextLocalStorageKey = this.localStorageService.keys.SEARCH_TEXT;
|
||||
this.inputDebounce = this.pwmService.ajaxTypingWait;
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
const self = this;
|
||||
|
||||
this.configService.photosEnabled().then(
|
||||
(photosEnabled: boolean) => {
|
||||
this.photosEnabled = photosEnabled;
|
||||
});
|
||||
|
||||
this.configService.getOrgChartMaxParents().then(
|
||||
(orgChartMaxParents: number) => {
|
||||
this.managementChainLimit = orgChartMaxParents;
|
||||
});
|
||||
|
||||
this.configService.printingEnabled().then(
|
||||
(printingEnabled: boolean) => {
|
||||
this.printEnabled = printingEnabled;
|
||||
});
|
||||
|
||||
this.query = this.getSearchText();
|
||||
|
||||
let personId: string = this.$stateParams['personId'];
|
||||
|
||||
this.fetchOrgChartData(personId)
|
||||
.then((orgChartData: IOrgChartData) => {
|
||||
if (!orgChartData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Override personId in case it was undefined
|
||||
personId = orgChartData.self.userKey;
|
||||
|
||||
if (orgChartData.assistant) {
|
||||
self.assistant = orgChartData.assistant;
|
||||
}
|
||||
|
||||
self.peopleService.getPerson(personId)
|
||||
.then((person: IPerson) => {
|
||||
self.person = person;
|
||||
},
|
||||
(error) => {
|
||||
// TODO: handle error
|
||||
});
|
||||
|
||||
self.peopleService.getManagementChain(personId, self.managementChainLimit)
|
||||
.then((managementChain: IPerson[]) => {
|
||||
self.managementChain = managementChain;
|
||||
},
|
||||
(error) => {
|
||||
// TODO: handle error
|
||||
});
|
||||
|
||||
self.peopleService.getDirectReports(personId)
|
||||
.then((directReports: IPerson[]) => {
|
||||
self.directReports = directReports;
|
||||
},
|
||||
(error) => {
|
||||
// TODO: handle error
|
||||
});
|
||||
},
|
||||
(error) => {
|
||||
// TODO: handle error
|
||||
});
|
||||
|
||||
// Once <ias-search-box> from ng-ias allows the autofocus attribute, we can remove this code
|
||||
this.$timeout(() => {
|
||||
document.getElementsByTagName('input')[0].focus();
|
||||
});
|
||||
}
|
||||
|
||||
autoCompleteSearch(query: string): IPromise<IPerson[]> {
|
||||
this.storeSearchText(query);
|
||||
return this.peopleService.autoComplete(query);
|
||||
}
|
||||
|
||||
gotoSearchState(state: string) {
|
||||
this.$state.go(state, { query: this.query });
|
||||
}
|
||||
|
||||
onAutoCompleteItemSelected(person: IPerson): void {
|
||||
this.storeSearchText(null);
|
||||
this.$state.go('orgchart.search', { personId: person.userKey, query: null });
|
||||
}
|
||||
|
||||
private fetchOrgChartData(personId): IPromise<IOrgChartData> {
|
||||
return this.peopleService.getOrgChartData(personId, true);
|
||||
}
|
||||
|
||||
private getSearchText(): string {
|
||||
let param: string = this.$stateParams['query'];
|
||||
// If multiple query parameters are defined, use the first one
|
||||
if (isArray(param)) {
|
||||
param = param[0].trim();
|
||||
}
|
||||
else if (isString(param)) {
|
||||
param = param.trim();
|
||||
}
|
||||
|
||||
return param || this.localStorageService.getItem(this.searchTextLocalStorageKey);
|
||||
}
|
||||
|
||||
protected storeSearchText(query): void {
|
||||
this.localStorageService.setItem(this.searchTextLocalStorageKey, query || '');
|
||||
}
|
||||
|
||||
private printOrgChart(): void {
|
||||
this.$window.print();
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="org-chart-section managers"
|
||||
ng-class="{ 'overflow': $ctrl.showingOverflow() }"
|
||||
ng-if="$ctrl.hasManagementChain()">
|
||||
<h3 translate="Title_Management">Management</h3>
|
||||
<div>
|
||||
<div class="manager"
|
||||
ng-repeat="manager in $ctrl.getManagementChain() track by $id(manager.displayNames)">
|
||||
<div class="org-chart-connector"></div>
|
||||
<person-card person="manager"
|
||||
show-image="$ctrl.showImages || ($ctrl.showingOverflow() && $last)"
|
||||
size="{{ $ctrl.getManagerCardSize() }}"
|
||||
show-direct-report-count="false"
|
||||
ng-click="$ctrl.selectPerson(manager.userKey)">
|
||||
</person-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="org-chart-section self">
|
||||
<person-card person="$ctrl.person"
|
||||
direct-reports="$ctrl.directReports"
|
||||
ng-click="$ctrl.onClickPerson()"
|
||||
class="self"
|
||||
size="large"
|
||||
show-direct-report-count="$ctrl.showDirectReports"
|
||||
show-image="$ctrl.showImages"></person-card>
|
||||
<div class="assistant" ng-if="$ctrl.assistant">
|
||||
<div class="org-chart-connector dashed"></div>
|
||||
<person-card person="$ctrl.assistant"
|
||||
size="small"
|
||||
show-direct-report-count="false"
|
||||
show-image="$ctrl.showImages"
|
||||
ng-click="$ctrl.selectPerson($ctrl.assistant.userKey)"></person-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="org-chart-section direct-reports" ng-if="$ctrl.hasDirectReports()">
|
||||
<h3 translate="Title_DirectReports">Direct Reports</h3>
|
||||
<div class="org-chart-connector"></div>
|
||||
|
||||
<div class="ias-grid">
|
||||
<person-card person="directReport"
|
||||
show-direct-report-count="$ctrl.showDirectReports"
|
||||
show-image="$ctrl.showImages"
|
||||
ng-repeat="directReport in $ctrl.directReports | orderBy:'displayNames[0]'"
|
||||
ng-click="$ctrl.selectPerson(directReport.userKey)">
|
||||
</person-card>
|
||||
</div>
|
||||
</div>
|
|
@ -1,425 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
$org-chart-connector-color: #808080;
|
||||
$org-chart-secondary-connector-color: #dae1e1;
|
||||
$org-chart-text-color: #808080;
|
||||
|
||||
$manager-connector-height: 16px;
|
||||
|
||||
.ias-styles-root {
|
||||
.reports {
|
||||
background-color: #dae1e1;
|
||||
border-radius: 2px;
|
||||
color: #434c50;
|
||||
font-size: 14px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
text-align: center;
|
||||
top: 3px;
|
||||
min-width: 35px;
|
||||
}
|
||||
|
||||
.assistant,
|
||||
.manager {
|
||||
.ias-tile {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
display: block;
|
||||
height: 96px;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
width: 120px;
|
||||
|
||||
> .ias-avatar {
|
||||
border: 3px solid #808080;
|
||||
border-radius: 100%;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
|
||||
&:hover {
|
||||
//border-color: $person-card-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
> .reports {
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
> .ias-tile-content {
|
||||
background-color: white;
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
:nth-child(n + 3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reports {
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.self {
|
||||
&.ias-tile {
|
||||
background-color: #ffffff;
|
||||
border: 3px solid #808080;
|
||||
border-radius: 3px;
|
||||
height: auto;
|
||||
min-height: 96px;
|
||||
//width: 346px;
|
||||
max-width: 100%;
|
||||
|
||||
> .ias-avatar {
|
||||
flex: 0 0 65px;
|
||||
height: 65px;
|
||||
width: 65px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
> .ias-tile-content {
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// (XS) Default display
|
||||
org-chart {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
||||
.assistant {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// (L) Wide enough to show main person offset to right and display managers horizontally
|
||||
&.large {
|
||||
> .org-chart-section {
|
||||
text-align: left;
|
||||
|
||||
> .ias-tile {
|
||||
&[size="large"] {
|
||||
margin: 0 0 0 128px;
|
||||
}
|
||||
}
|
||||
|
||||
.org-chart-connector {
|
||||
left: 172px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.managers {
|
||||
margin-left: 0;
|
||||
min-height: 128px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
> h3 {
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
.org-chart-connector {
|
||||
left: 42px;
|
||||
}
|
||||
|
||||
.manager {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
margin-left: 0;
|
||||
margin-bottom: 32px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 115px;
|
||||
|
||||
> .org-chart-connector {
|
||||
bottom: initial;
|
||||
top: 56px;
|
||||
left: 57px;
|
||||
height: 72px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
> .org-chart-connector {
|
||||
background-color: $org-chart-secondary-connector-color;
|
||||
bottom: initial;
|
||||
height: 3px;
|
||||
left: -37px;
|
||||
top: 26px;
|
||||
width: 69px;
|
||||
}
|
||||
|
||||
.ias-tile {
|
||||
> .ias-avatar {
|
||||
background-color: $org-chart-secondary-connector-color;
|
||||
|
||||
&:not(:hover) {
|
||||
border-color: $org-chart-secondary-connector-color;
|
||||
}
|
||||
}
|
||||
> .ias-tile-content {
|
||||
> .details {
|
||||
> :first-child {
|
||||
color: $org-chart-connector-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.self {
|
||||
display: inline-flex;
|
||||
|
||||
> .assistant {
|
||||
display: inline-block;
|
||||
margin-left: 33px;
|
||||
position: relative;
|
||||
|
||||
.ias-tile {
|
||||
> .ias-avatar {
|
||||
background-color: $org-chart-secondary-connector-color;
|
||||
|
||||
&:not(:hover) {
|
||||
border-color: $org-chart-secondary-connector-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .org-chart-connector {
|
||||
background-color: transparent;
|
||||
border-top: 3px dashed $org-chart-secondary-connector-color;
|
||||
height: 0;
|
||||
left: -37px;
|
||||
top: 26px;
|
||||
width: 69px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .org-chart-section {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
&.direct-reports {
|
||||
> .org-chart-connector {
|
||||
height: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
&.managers {
|
||||
min-height: 98px;
|
||||
|
||||
&.overflow {
|
||||
.manager {
|
||||
&:last-child {
|
||||
> .ias-tile {
|
||||
> .reports {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> .ias-tile-content {
|
||||
> .avatar {
|
||||
//background-image: url('../../images/icons/m_circle-horz-menu_thin.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.manager {
|
||||
margin-bottom: $manager-connector-height;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&.empty-manager {
|
||||
> .ias-tile {
|
||||
cursor: initial;
|
||||
|
||||
> .ias-tile-content {
|
||||
> .avatar {
|
||||
background: $org-chart-secondary-connector-color;
|
||||
border-color: $org-chart-secondary-connector-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .org-chart-connector {
|
||||
background-color: $org-chart-secondary-connector-color;
|
||||
}
|
||||
}
|
||||
|
||||
> .ias-tile {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.org-chart-connector {
|
||||
bottom: -$manager-connector-height;
|
||||
height: $manager-connector-height;
|
||||
top: initial;
|
||||
}
|
||||
}
|
||||
|
||||
> h3 {
|
||||
color: $org-chart-text-color;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 14px;
|
||||
margin: 0;
|
||||
padding: 15px 0 5px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
> .ias-tile {
|
||||
&[size="large"] {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
> .ias-grid {
|
||||
border-top: 3px solid $org-chart-connector-color;
|
||||
min-height: 90px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.org-chart-connector {
|
||||
background-color: $org-chart-connector-color;
|
||||
left: 0;
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
.assistant,
|
||||
.manager {
|
||||
.reports {
|
||||
left: 20px;
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// (XS) Default display
|
||||
org-chart {
|
||||
> .org-chart-section {
|
||||
> h3 {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
// (L) Wide enough to show main person offset to right and display managers horizontally
|
||||
&.large {
|
||||
> .org-chart-section {
|
||||
text-align: right;
|
||||
|
||||
> .ias-tile {
|
||||
&[size="large"] {
|
||||
margin: 0 128px 0 0;
|
||||
}
|
||||
|
||||
.reports {
|
||||
left: 3px;
|
||||
right: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.org-chart-connector {
|
||||
left: initial;
|
||||
right: 172px;
|
||||
}
|
||||
|
||||
&.managers {
|
||||
margin-left: auto;
|
||||
|
||||
> h3 {
|
||||
left: initial;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.org-chart-connector {
|
||||
left: initial;
|
||||
right: 42px;
|
||||
}
|
||||
|
||||
.manager {
|
||||
margin-left: 5px;
|
||||
margin-right: 0;
|
||||
text-align: right;
|
||||
|
||||
&:first-child {
|
||||
margin-right: 115px;
|
||||
|
||||
> .org-chart-connector {
|
||||
left: initial;
|
||||
right: 57px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
> .org-chart-connector {
|
||||
left: initial;
|
||||
right: -37px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.self {
|
||||
> .assistant {
|
||||
margin-left: 0;
|
||||
margin-right: 33px;
|
||||
|
||||
> .org-chart-connector {
|
||||
left: auto;
|
||||
right: -37px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
describe('testing OrgChartComponent', () => {
|
||||
beforeEach(() => {
|
||||
});
|
||||
|
||||
it('should pass', () => {
|
||||
expect('foo').toEqual('foo');
|
||||
});
|
||||
|
||||
it('should fail', () => {
|
||||
expect('foo').not.toEqual('bar');
|
||||
});
|
||||
});
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { Component } from '../../component';
|
||||
import { element, IAugmentedJQuery, IFilterService, IScope, IWindowService } from 'angular';
|
||||
import ElementSizeService from '../../ux/element-size.service';
|
||||
import { IPerson } from '../../models/person.model';
|
||||
import {IPeopleSearchConfigService} from '../../services/peoplesearch-config.service';
|
||||
|
||||
export enum OrgChartSize {
|
||||
ExtraSmall = 0,
|
||||
Small = 365,
|
||||
Large = 631
|
||||
}
|
||||
|
||||
@Component({
|
||||
bindings: {
|
||||
directReports: '<',
|
||||
managementChain: '<',
|
||||
assistant: '<',
|
||||
person: '<',
|
||||
showImages: '<'
|
||||
},
|
||||
stylesheetUrl: require('./orgchart.component.scss'),
|
||||
templateUrl: require('./orgchart.component.html')
|
||||
})
|
||||
export default class OrgChartComponent {
|
||||
directReports: IPerson[];
|
||||
elementWidth: number;
|
||||
isLargeLayout: boolean;
|
||||
managementChain: IPerson[];
|
||||
person: IPerson;
|
||||
showDirectReports: boolean;
|
||||
assistant: IPerson;
|
||||
|
||||
private elementSize: OrgChartSize = OrgChartSize.ExtraSmall;
|
||||
private maxVisibleManagers: number;
|
||||
private visibleManagers: IPerson[];
|
||||
|
||||
static $inject = [ '$element', '$filter', '$scope', '$state', '$window', 'ConfigService', 'MfElementSizeService' ];
|
||||
constructor(
|
||||
private $element: IAugmentedJQuery,
|
||||
private $filter: IFilterService,
|
||||
private $scope: IScope,
|
||||
private $state: angular.ui.IStateService,
|
||||
private $window: IWindowService,
|
||||
private configService: IPeopleSearchConfigService,
|
||||
private elementSizeService: ElementSizeService) {
|
||||
}
|
||||
|
||||
$onDestroy(): void {
|
||||
// TODO: remove $window click listener
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
this.configService.orgChartShowChildCount().then(
|
||||
(showChildCount: boolean) => {
|
||||
this.showDirectReports = showChildCount;
|
||||
});
|
||||
|
||||
// OrgChartComponent has different functionality at different widths. On element resize, we
|
||||
// want to update the state of the component and trigger a $digest
|
||||
this.elementSizeService
|
||||
.watchWidth(this.$element, OrgChartSize)
|
||||
.onResize(this.onResize.bind(this));
|
||||
|
||||
// In large displays managers are displayed in a row. Any time this property changes, we want
|
||||
// to force our manager list to be recalculated in this.getManagementChain() so it returns the correct
|
||||
// result at all element widths
|
||||
this.$scope.$watch('$ctrl.maxVisibleManagers', () => {
|
||||
this.resetManagerList();
|
||||
});
|
||||
}
|
||||
|
||||
getManagerCardSize(): string {
|
||||
return this.isLargeLayout ? 'small' : 'normal';
|
||||
}
|
||||
|
||||
getManagementChain(): IPerson[] {
|
||||
// Display managers in a row
|
||||
if (this.isLargeLayout) {
|
||||
// All managers can fit on screen
|
||||
if (this.maxVisibleManagers >= this.managementChain.length) {
|
||||
return this.managementChain;
|
||||
}
|
||||
|
||||
// Not all managers can fit on screen
|
||||
if (!this.visibleManagers) {
|
||||
// Show a blank manager as last manager in the chain in place of
|
||||
// the last visible manager. Blank manager links to the new last visible manager.
|
||||
this.visibleManagers = this.managementChain.slice(0, this.maxVisibleManagers - 1);
|
||||
const lastManager = this.managementChain[this.maxVisibleManagers - 2];
|
||||
|
||||
this.visibleManagers.push(<IPerson>({
|
||||
userKey: lastManager.userKey,
|
||||
photoURL: null,
|
||||
displayNames: []
|
||||
}));
|
||||
}
|
||||
|
||||
return this.visibleManagers;
|
||||
}
|
||||
|
||||
// All managers can fit on screen in a column. Order is reversed for ease of rendering and style
|
||||
return [].concat(this.managementChain).reverse();
|
||||
}
|
||||
|
||||
hasDirectReports(): boolean {
|
||||
return this.directReports && !!this.directReports.length;
|
||||
}
|
||||
|
||||
hasManagementChain(): boolean {
|
||||
return this.managementChain && !!this.managementChain.length;
|
||||
}
|
||||
|
||||
onClickPerson(): void {
|
||||
if (this.person) {
|
||||
this.$state.go('orgchart.search.details', { personId: this.person.userKey });
|
||||
}
|
||||
}
|
||||
|
||||
selectPerson(userKey: string): void {
|
||||
this.$state.go('orgchart.search', { personId: userKey });
|
||||
}
|
||||
|
||||
showingOverflow(): boolean {
|
||||
return this.visibleManagers &&
|
||||
this.managementChain &&
|
||||
this.visibleManagers.length < this.managementChain.length;
|
||||
}
|
||||
|
||||
private onResize(newValue: number): void {
|
||||
this.isLargeLayout = (newValue >= OrgChartSize.Large);
|
||||
if (!this.isLargeLayout) {
|
||||
this.resetManagerList();
|
||||
}
|
||||
this.maxVisibleManagers = Math.floor(
|
||||
(newValue - 115 /* left margin */) / 125 /* card width + right margin */);
|
||||
}
|
||||
|
||||
// Remove all displayed managers so the list is updated on element resize
|
||||
private resetManagerList(): void {
|
||||
this.visibleManagers = null;
|
||||
}
|
||||
}
|
|
@ -1,362 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as angular from 'angular';
|
||||
import {isArray, isString, IPromise, IQService, IScope, ITimeoutService} from 'angular';
|
||||
import { IPeopleSearchConfigService } from '../../services/peoplesearch-config.service';
|
||||
import { IPeopleService } from '../../services/people.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import { IPerson } from '../../models/person.model';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import SearchResult from '../../models/search-result.model';
|
||||
import {IAdvancedSearchConfig, IAdvancedSearchQuery, IAttributeMetadata} from '../../services/base-config.service';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
abstract class PeopleSearchBaseComponent {
|
||||
advancedSearch = false;
|
||||
advancedSearchTags = {};
|
||||
advancedSearchEnabled: boolean;
|
||||
advancedSearchMaxRows: number;
|
||||
errorMessage: string;
|
||||
inputDebounce: number;
|
||||
orgChartEnabled: boolean;
|
||||
protected pendingRequests: IPromise<any>[] = [];
|
||||
searchMessage: string;
|
||||
searchResult: SearchResult;
|
||||
query: string;
|
||||
queries: IAdvancedSearchQuery[];
|
||||
searchTextLocalStorageKey: string;
|
||||
searchViewLocalStorageKey: string;
|
||||
|
||||
constructor(protected $q: IQService,
|
||||
protected $scope: IScope,
|
||||
protected $state: angular.ui.IStateService,
|
||||
protected $stateParams: angular.ui.IStateParamsService,
|
||||
protected $timeout: ITimeoutService,
|
||||
protected $translate: angular.translate.ITranslateService,
|
||||
protected configService: IPeopleSearchConfigService,
|
||||
protected localStorageService: LocalStorageService,
|
||||
protected peopleService: IPeopleService,
|
||||
protected promiseService: PromiseService,
|
||||
protected pwmService: IPwmService,
|
||||
protected commonSearchService: CommonSearchService) {
|
||||
this.searchTextLocalStorageKey = this.localStorageService.keys.SEARCH_TEXT;
|
||||
this.searchViewLocalStorageKey = this.localStorageService.keys.SEARCH_VIEW;
|
||||
|
||||
this.inputDebounce = this.pwmService.ajaxTypingWait;
|
||||
}
|
||||
|
||||
getMessage(): string {
|
||||
return this.errorMessage || this.searchMessage;
|
||||
}
|
||||
|
||||
gotoOrgchart(): void {
|
||||
this.gotoState('orgchart.index');
|
||||
}
|
||||
|
||||
private gotoState(state: string): void {
|
||||
this.$state.go(state);
|
||||
}
|
||||
|
||||
private initiateSearch() {
|
||||
this.clearSearchMessage();
|
||||
this.clearErrorMessage();
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
private onAdvancedSearchAttributeChanged(query: IAdvancedSearchQuery) {
|
||||
// Make sure we set the default value if the type is select
|
||||
const attributeMetadata: IAttributeMetadata = this.advancedSearchTags[query.key];
|
||||
if (attributeMetadata.type == 'select') {
|
||||
query.value = this.commonSearchService.getDefaultValue(attributeMetadata);
|
||||
}
|
||||
|
||||
this.commonSearchService.setPsAdvSearchQueries(this.queries);
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
private onAdvancedSearchAttributeValueChanged() {
|
||||
this.commonSearchService.setPsAdvSearchQueries(this.queries);
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
private onAdvancedSearchValueChanged() {
|
||||
this.commonSearchService.setPsAdvSearchQueries(this.queries);
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
private onSearchTextChange(newValue: string, oldValue: string): void {
|
||||
if (newValue === oldValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.storeSearchText();
|
||||
this.initiateSearch();
|
||||
}
|
||||
|
||||
removeSearchTag(tagIndex: number): void {
|
||||
this.queries.splice(tagIndex, 1);
|
||||
this.commonSearchService.setPsAdvSearchQueries(this.queries);
|
||||
|
||||
if (this.queries.length > 0) {
|
||||
this.initiateSearch();
|
||||
}
|
||||
else {
|
||||
this.clearSearch();
|
||||
this.advancedSearch = false;
|
||||
this.commonSearchService.setPsAdvancedSearchActive(this.advancedSearch);
|
||||
}
|
||||
}
|
||||
|
||||
addSearchTag(): void {
|
||||
const firstTagName = Object.keys(this.advancedSearchTags)[0];
|
||||
const attributeMetaData: IAttributeMetadata = this.advancedSearchTags[firstTagName];
|
||||
|
||||
const query: IAdvancedSearchQuery = {
|
||||
key: attributeMetaData.attribute,
|
||||
value: this.commonSearchService.getDefaultValue(attributeMetaData),
|
||||
};
|
||||
|
||||
this.queries.push(query);
|
||||
}
|
||||
|
||||
selectPerson(person: IPerson): void {
|
||||
this.$state.go('.details', { personId: person.userKey, query: this.query });
|
||||
}
|
||||
|
||||
// We are still loading if there are pending requests but no search results have come back yet
|
||||
get loading(): boolean {
|
||||
return !!this.pendingRequests.length && !this.searchResult;
|
||||
}
|
||||
|
||||
protected abortPendingRequests() {
|
||||
for (let index = 0; index < this.pendingRequests.length; index++) {
|
||||
let pendingRequest = this.pendingRequests[index];
|
||||
this.promiseService.abort(pendingRequest);
|
||||
}
|
||||
|
||||
this.pendingRequests = [];
|
||||
}
|
||||
|
||||
protected removePendingRequest(promise: IPromise<any>) {
|
||||
let index = this.pendingRequests.indexOf(promise);
|
||||
|
||||
if (index > -1) {
|
||||
this.pendingRequests.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected setErrorMessage(message: string) {
|
||||
this.errorMessage = message;
|
||||
}
|
||||
|
||||
protected clearErrorMessage() {
|
||||
this.errorMessage = null;
|
||||
}
|
||||
|
||||
// If message is a string it will be translated. If it is a promise it will assign the string from the resolved
|
||||
// promise
|
||||
protected setSearchMessage(translationKey: string) {
|
||||
if (!translationKey) {
|
||||
this.clearSearchMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
this.$translate(translationKey.toString())
|
||||
.then((translation: string) => {
|
||||
self.searchMessage = translation;
|
||||
});
|
||||
}
|
||||
|
||||
protected clearSearch(): void {
|
||||
this.query = null;
|
||||
this.queries = [];
|
||||
this.searchResult = null;
|
||||
this.clearErrorMessage();
|
||||
this.clearSearchMessage();
|
||||
this.abortPendingRequests();
|
||||
}
|
||||
|
||||
protected clearSearchMessage(): void {
|
||||
this.searchMessage = null;
|
||||
}
|
||||
|
||||
abstract fetchData(): void;
|
||||
|
||||
protected fetchSearchData(): IPromise<void | SearchResult> {
|
||||
this.abortPendingRequests();
|
||||
this.searchResult = null;
|
||||
|
||||
const self = this;
|
||||
let promise;
|
||||
|
||||
if (this.advancedSearch) {
|
||||
if (!this.queries || (this.queries.length === 1 && !this.queries[0].key)) {
|
||||
this.clearSearch();
|
||||
return null;
|
||||
}
|
||||
|
||||
const keys = new Set();
|
||||
for (let searchQuery of this.queries) {
|
||||
keys.add(searchQuery.key);
|
||||
}
|
||||
|
||||
const duplicateSearchAttrsFound = keys.size < this.queries.length;
|
||||
if (duplicateSearchAttrsFound) {
|
||||
this.$translate('Display_SearchAttrsUnique')
|
||||
.then((translation: string) => {
|
||||
this.searchMessage = translation;
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
promise = this.peopleService.advancedSearch(this.queries);
|
||||
}
|
||||
else {
|
||||
if (!this.query) {
|
||||
this.clearSearch();
|
||||
return null;
|
||||
}
|
||||
|
||||
promise = this.peopleService.search(this.query);
|
||||
}
|
||||
|
||||
this.pendingRequests.push(promise);
|
||||
|
||||
return promise
|
||||
.then(
|
||||
(searchResult: SearchResult) => {
|
||||
self.clearErrorMessage();
|
||||
self.clearSearchMessage();
|
||||
|
||||
// Aborted request
|
||||
if (!searchResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Too many results returned
|
||||
if (searchResult.sizeExceeded) {
|
||||
self.setSearchMessage('Display_SearchResultsExceeded');
|
||||
}
|
||||
|
||||
// No results returned. Not an else if statement so that the more important message is presented
|
||||
if (!searchResult.people.length) {
|
||||
self.setSearchMessage('Display_SearchResultsNone');
|
||||
}
|
||||
|
||||
return searchResult;
|
||||
},
|
||||
(error) => {
|
||||
self.setErrorMessage(error);
|
||||
self.clearSearchMessage();
|
||||
})
|
||||
.finally(() => {
|
||||
self.removePendingRequest(promise);
|
||||
});
|
||||
}
|
||||
|
||||
protected initialize(): IPromise<void> {
|
||||
return this.$q.all(
|
||||
[
|
||||
// Determine whether org-chart should appear
|
||||
this.configService.orgChartEnabled().then((orgChartEnabled: boolean) => {
|
||||
this.orgChartEnabled = orgChartEnabled;
|
||||
}),
|
||||
this.configService.advancedSearchConfig().then((advancedSearchConfig: IAdvancedSearchConfig) => {
|
||||
this.advancedSearchEnabled = advancedSearchConfig.enabled;
|
||||
this.advancedSearchMaxRows = advancedSearchConfig.maxRows;
|
||||
|
||||
for (let advancedSearchTag of advancedSearchConfig.attributes) {
|
||||
this.advancedSearchTags[advancedSearchTag.attribute] = advancedSearchTag;
|
||||
}
|
||||
})
|
||||
]
|
||||
).then(result => {
|
||||
const searchQuery = this.getSearchQuery();
|
||||
if (searchQuery) {
|
||||
// A search query has been passed in, disregard the current search state
|
||||
this.query = searchQuery;
|
||||
this.advancedSearch = false;
|
||||
this.storeSearchText();
|
||||
this.commonSearchService.setPsAdvancedSearchActive(this.advancedSearch);
|
||||
this.commonSearchService.setPsAdvSearchQueries([]);
|
||||
} else {
|
||||
this.query = this.getSearchText();
|
||||
this.advancedSearch = this.commonSearchService.isPsAdvancedSearchActive();
|
||||
this.queries = this.commonSearchService.getPsAdvSearchQueries();
|
||||
if (this.queries.length === 0) {
|
||||
this.addSearchTag();
|
||||
}
|
||||
}
|
||||
|
||||
// Once <ias-search-box> from ng-ias allows the autofocus attribute, we can remove this code
|
||||
this.$timeout(() => {
|
||||
document.getElementsByTagName('input')[0].focus();
|
||||
});
|
||||
|
||||
this.$scope.$watch('$ctrl.query', (newValue: string, oldValue: string) => {
|
||||
this.onSearchTextChange(newValue, oldValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getSearchQuery(): string {
|
||||
let param: string = this.$stateParams['query'];
|
||||
// If multiple query parameters are defined, use the first one
|
||||
if (isArray(param)) {
|
||||
param = param[0].trim();
|
||||
}
|
||||
else if (isString(param)) {
|
||||
param = param.trim();
|
||||
}
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
private getSearchText(): string {
|
||||
return this.localStorageService.getItem(this.searchTextLocalStorageKey);
|
||||
}
|
||||
|
||||
protected storeSearchText(): void {
|
||||
this.localStorageService.setItem(this.searchTextLocalStorageKey, this.query || '');
|
||||
}
|
||||
|
||||
enableAdvancedSearch(): void {
|
||||
this.clearSearch();
|
||||
this.addSearchTag();
|
||||
this.advancedSearch = true;
|
||||
this.commonSearchService.setPsAdvancedSearchActive(this.advancedSearch);
|
||||
}
|
||||
|
||||
protected toggleView(state: string): void {
|
||||
this.storeSearchView(state);
|
||||
this.storeSearchText();
|
||||
this.gotoState(state);
|
||||
}
|
||||
|
||||
private storeSearchView(state: string) {
|
||||
this.localStorageService.setItem(this.searchViewLocalStorageKey, state);
|
||||
}
|
||||
}
|
||||
|
||||
export default PeopleSearchBaseComponent;
|
|
@ -1,100 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="peoplesearch-header">
|
||||
<div class="basic-search-container" ng-if="!$ctrl.advancedSearch">
|
||||
<h2 id="page-content-title" translate="Title_PeopleSearch">People Search</h2>
|
||||
<ias-search-box id="input" ng-model="$ctrl.query" title="{{'Display_PeopleSearch' | translate}}"
|
||||
ng-model-options="{debounce: $ctrl.inputDebounce}"
|
||||
placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
|
||||
</ias-search-box>
|
||||
|
||||
<ias-button id="advanced-search-icon" class="ias-icon-button" ng-click="$ctrl.enableAdvancedSearch()"
|
||||
ng-if="$ctrl.advancedSearchEnabled"
|
||||
ng-attr-title="{{ 'Title_AdvancedSearch' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="search_advanced"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<div class="advanced-search-container" ng-if="$ctrl.advancedSearch">
|
||||
<div class="attribute-row" ng-repeat="query in $ctrl.queries">
|
||||
<select ng-model="query.key" ng-change="$ctrl.onAdvancedSearchAttributeChanged(query)">
|
||||
<option ng-repeat="tag in $ctrl.advancedSearchTags" ng-attr-value="{{tag.attribute}}">{{tag.label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Show a drop-down if the attribute type is 'select'-->
|
||||
<select ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type === 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value">
|
||||
<option ng-attr-value="{{name}}"
|
||||
ng-repeat="(name, label) in $ctrl.advancedSearchTags[query.key].options">{{label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Otherwise, just show a regular input field-->
|
||||
<input ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type !== 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value"
|
||||
autocomplete="off" ng-model-options="{debounce: $ctrl.inputDebounce}">
|
||||
|
||||
<ias-button class="ias-icon-button" ng-click="$ctrl.removeSearchTag($index)"
|
||||
ng-attr-title="{{ 'Button_Remove' | translate }}">
|
||||
<ias-icon icon="close_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<ias-button id="add-attribute-row" class="ias-icon-button" ng-click="$ctrl.addSearchTag()"
|
||||
ng-if="$ctrl.queries.length < $ctrl.advancedSearchMaxRows"
|
||||
ng-attr-title="{{ 'Button_AddSearchAttribute' | translate }}">
|
||||
<ias-icon icon="new_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<span class="ias-fill"></span>
|
||||
<div class="ias-header">
|
||||
<ias-button id="view-tile-icon" class="ias-icon-button ias-selected" ng-disabled="true"
|
||||
ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}">
|
||||
<ias-icon icon="view_tile_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="view-list-icon" class="ias-icon-button" ng-click="$ctrl.gotoTableView()"
|
||||
ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}">
|
||||
<ias-icon icon="view_list_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<div class="icon-divider vertical" ng-if="$ctrl.orgChartEnabled"></div>
|
||||
<ias-button id="orgchart-icon" class="ias-icon-button" ng-click="$ctrl.gotoOrgchart()" ng-if="$ctrl.orgChartEnabled"
|
||||
ng-attr-title="{{ 'Title_OrgChart' | translate }}">
|
||||
<ias-icon icon="orgchart_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-info-container">
|
||||
<div class="search-info" ng-class="{'loading': !$ctrl.getMessage()}"
|
||||
ng-if="$ctrl.loading || $ctrl.searchMessage || $ctrl.errorMessage"
|
||||
ng-bind="$ctrl.getMessage() || ('Display_PleaseWait' | translate)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="people-search-component-content">
|
||||
<div class="ias-grid">
|
||||
<person-card person="person"
|
||||
show-image="$ctrl.photosEnabled"
|
||||
ng-repeat="person in $ctrl.searchResult.people | orderBy:'displayNames[0]'"
|
||||
ng-click="$ctrl.selectPerson(person)">
|
||||
</person-card>
|
||||
</div>
|
||||
|
||||
<ui-view></ui-view>
|
||||
</div>
|
|
@ -1,93 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
people-search-cards {
|
||||
#page-content-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
|
||||
// At medium size, cards are centered and no longer take up 100% width
|
||||
&.medium {
|
||||
> .people-search-component-content {
|
||||
> .person-card-list {
|
||||
> person-card {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
width: 272px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At large size, cards fit next to each other
|
||||
&.large {
|
||||
> .people-search-component-content {
|
||||
> .person-card-list {
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
|
||||
> person-card {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .people-search-component-content {
|
||||
flex: 1 1;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
|
||||
> .person-card-list {
|
||||
> person-card {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
people-search-cards {
|
||||
&.large {
|
||||
> .people-search-component-content {
|
||||
.person-card-list {
|
||||
text-align: right;
|
||||
|
||||
> person-card {
|
||||
margin-right: auto;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { Component } from '../../component';
|
||||
import ElementSizeService from '../../ux/element-size.service';
|
||||
import IPeopleSearchConfigService from '../../services/peoplesearch-config.service';
|
||||
import IPeopleService from '../../services/people.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import {isString, IAugmentedJQuery, IQService, IScope, ITimeoutService} from 'angular';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import PeopleSearchBaseComponent from './peoplesearch-base.component';
|
||||
import { IPerson } from '../../models/person.model';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import SearchResult from '../../models/search-result.model';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
export enum PeopleSearchCardsSize {
|
||||
Small = 0,
|
||||
Medium = 365,
|
||||
Large = 450
|
||||
}
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./peoplesearch-cards.component.scss'),
|
||||
templateUrl: require('./peoplesearch-cards.component.html')
|
||||
})
|
||||
export default class PeopleSearchCardsComponent extends PeopleSearchBaseComponent {
|
||||
photosEnabled: boolean;
|
||||
|
||||
static $inject = [
|
||||
'$element',
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'$translate',
|
||||
'ConfigService',
|
||||
'LocalStorageService',
|
||||
'MfElementSizeService',
|
||||
'PeopleService',
|
||||
'PromiseService',
|
||||
'PwmService',
|
||||
'CommonSearchService'
|
||||
];
|
||||
constructor(private $element: IAugmentedJQuery,
|
||||
$q: IQService,
|
||||
$scope: IScope,
|
||||
$state: angular.ui.IStateService,
|
||||
$stateParams: angular.ui.IStateParamsService,
|
||||
$timeout: ITimeoutService,
|
||||
$translate: angular.translate.ITranslateService,
|
||||
configService: IPeopleSearchConfigService,
|
||||
localStorageService: LocalStorageService,
|
||||
private elementSizeService: ElementSizeService,
|
||||
peopleService: IPeopleService,
|
||||
promiseService: PromiseService,
|
||||
pwmService: IPwmService,
|
||||
commonSearchService: CommonSearchService) {
|
||||
super($q,
|
||||
$scope,
|
||||
$state,
|
||||
$stateParams,
|
||||
$timeout,
|
||||
$translate,
|
||||
configService,
|
||||
localStorageService,
|
||||
peopleService,
|
||||
promiseService,
|
||||
pwmService,
|
||||
commonSearchService);
|
||||
}
|
||||
|
||||
$onDestroy(): void {
|
||||
// TODO: remove $window click listener
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
this.initialize().then(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
|
||||
this.configService.photosEnabled().then((photosEnabled: boolean) => {
|
||||
this.photosEnabled = photosEnabled;
|
||||
});
|
||||
|
||||
this.elementSizeService.watchWidth(this.$element, PeopleSearchCardsSize);
|
||||
}
|
||||
|
||||
gotoTableView() {
|
||||
this.toggleView('search.table');
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
let searchResultPromise = this.fetchSearchData();
|
||||
if (searchResultPromise) {
|
||||
|
||||
searchResultPromise.then(this.onSearchResult.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
private onSearchResult(searchResult: SearchResult): void {
|
||||
// Aborted request
|
||||
if (!searchResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchResult = new SearchResult({
|
||||
sizeExceeded: searchResult.sizeExceeded,
|
||||
searchResults: []
|
||||
});
|
||||
|
||||
let self = this;
|
||||
|
||||
this.pendingRequests = searchResult.people.map(
|
||||
(person: IPerson) => {
|
||||
// Store this promise because it is abortable
|
||||
let promise = this.peopleService.getPerson(person.userKey);
|
||||
|
||||
promise
|
||||
.then((person: IPerson) => {
|
||||
// Aborted request
|
||||
if (!person) {
|
||||
return;
|
||||
}
|
||||
|
||||
// searchResult may be overwritten by ESC->[LETTER] typed in after a search
|
||||
// has started but before all calls to peopleService.getPerson have resolved
|
||||
if (self.searchResult) {
|
||||
self.searchResult.people.push(person);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
self.setErrorMessage(error);
|
||||
})
|
||||
.finally(() => {
|
||||
self.removePendingRequest(promise);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="peoplesearch-header">
|
||||
<div class="basic-search-container" ng-if="!$ctrl.advancedSearch">
|
||||
<h2 id="page-content-title" translate="Title_PeopleSearch">People Search</h2>
|
||||
<ias-search-box id="input" ng-model="$ctrl.query"
|
||||
ng-model-options="{debounce: $ctrl.inputDebounce}"
|
||||
placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
|
||||
</ias-search-box>
|
||||
|
||||
<ias-button id="advanced-search-icon" class="ias-icon-button" ng-click="$ctrl.enableAdvancedSearch()"
|
||||
ng-if="$ctrl.advancedSearchEnabled"
|
||||
ng-attr-title="{{ 'Title_AdvancedSearch' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="search_advanced"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<div class="advanced-search-container" ng-if="$ctrl.advancedSearch">
|
||||
<div class="attribute-row" ng-repeat="query in $ctrl.queries">
|
||||
<select ng-model="query.key" ng-change="$ctrl.onAdvancedSearchAttributeChanged(query)">
|
||||
<option ng-repeat="tag in $ctrl.advancedSearchTags" ng-attr-value="{{tag.attribute}}">{{tag.label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Show a drop-down if the attribute type is 'select'-->
|
||||
<select ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type === 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value">
|
||||
<option ng-attr-value="{{name}}"
|
||||
ng-repeat="(name, label) in $ctrl.advancedSearchTags[query.key].options">{{label}}</option>
|
||||
</select>
|
||||
|
||||
<!--Otherwise, just show a regular input field-->
|
||||
<input ng-model="query.value" ng-if="$ctrl.advancedSearchTags[query.key].type !== 'select'"
|
||||
ng-change="$ctrl.onAdvancedSearchValueChanged($event)" class="attribute-value"
|
||||
autocomplete="off" ng-model-options="{debounce: $ctrl.inputDebounce}">
|
||||
|
||||
<ias-button class="ias-icon-button" ng-click="$ctrl.removeSearchTag($index)"
|
||||
ng-attr-title="{{ 'Button_Remove' | translate }}">
|
||||
<ias-icon icon="close_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<ias-button id="add-attribute-row" class="ias-icon-button" ng-click="$ctrl.addSearchTag()"
|
||||
ng-if="$ctrl.queries.length < $ctrl.advancedSearchMaxRows"
|
||||
ng-attr-title="{{ 'Button_AddSearchAttribute' | translate }}">
|
||||
<ias-icon icon="new_thin"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<span class="ias-fill"></span>
|
||||
<div class="ias-header">
|
||||
<ias-button id="view-title-button" class="ias-icon-button"
|
||||
ng-click="$ctrl.gotoCardsView()"
|
||||
ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="view_tile_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button id="view-list-button" class="ias-icon-button ias-selected" ng-disabled="true"
|
||||
ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="view_list_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<div class="icon-divider vertical"></div>
|
||||
<ias-button id="view-orgchart-button" class="ias-icon-button" ng-click="$ctrl.gotoOrgchart()"
|
||||
ng-if="$ctrl.orgChartEnabled"
|
||||
ng-attr-title="{{ 'Title_OrgChart' | translate }}">
|
||||
<ias-icon class="ias-selected" icon="orgchart_thin"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button class="ias-icon-button table-configuration-menu-toggle" ias-toggle="menu1"
|
||||
ng-attr-title="{{ 'Title_Settings' | translate }}">
|
||||
<ias-icon icon="configure_thick"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-menu name="menu1" ias-align="end end" class="ias-styles-root">
|
||||
<div class="ias-input-container">
|
||||
<div class="checkbox-button" ng-repeat="(key, value) in $ctrl.columnConfiguration">
|
||||
<input type="checkbox" ng-model="value.visible" aria-label="Toggle column visibility" />
|
||||
<ias-button class="toggle-column-btn" ng-click="value.visible = !value.visible; $event.stopImmediatePropagation();">{{value.label}}</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
</ias-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-info-container">
|
||||
<div class="search-info" ng-class="{'loading': !$ctrl.getMessage()}"
|
||||
ng-if="$ctrl.loading || $ctrl.searchMessage || $ctrl.errorMessage"
|
||||
ng-bind="$ctrl.getMessage() || ('Display_PleaseWait' | translate)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="people-search-component-content">
|
||||
<table class="ias-table" ias-sort="$ctrl.sort" ng-show="$ctrl.searchResult.people.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible"
|
||||
ias-sort-on="key">{{value.label}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="person in $ctrl.searchResult.people | orderBy:$ctrl.sort"
|
||||
ng-click="$ctrl.selectPerson(person)">
|
||||
<td ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible">
|
||||
<span ng-bind="person[key]"></span>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ui-view></ui-view>
|
||||
</div>
|
|
@ -1,51 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
people-search-table {
|
||||
#page-content-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
height: 100%;
|
||||
|
||||
> .people-search-component-content {
|
||||
flex: 1 1;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
.table-configuration-menu-toggle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ias-input-container > .checkbox-button > .ias-button.toggle-column-btn {
|
||||
&:focus, &:hover {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { Component } from '../../component';
|
||||
import { IPeopleSearchConfigService } from '../../services/peoplesearch-config.service';
|
||||
import IPeopleService from '../../services/people.service';
|
||||
import IPwmService from '../../services/pwm.service';
|
||||
import {IQService, IScope, ITimeoutService} from 'angular';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import PeopleSearchBaseComponent from './peoplesearch-base.component';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import SearchResult from '../../models/search-result.model';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./peoplesearch-table.component.scss'),
|
||||
templateUrl: require('./peoplesearch-table.component.html')
|
||||
})
|
||||
export default class PeopleSearchTableComponent extends PeopleSearchBaseComponent {
|
||||
columnConfiguration: any;
|
||||
|
||||
static $inject = [
|
||||
'$q',
|
||||
'$scope',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'$translate',
|
||||
'ConfigService',
|
||||
'LocalStorageService',
|
||||
'PeopleService',
|
||||
'PromiseService',
|
||||
'PwmService',
|
||||
'CommonSearchService'
|
||||
];
|
||||
constructor($q: IQService,
|
||||
$scope: IScope,
|
||||
$state: angular.ui.IStateService,
|
||||
$stateParams: angular.ui.IStateParamsService,
|
||||
$timeout: ITimeoutService,
|
||||
$translate: angular.translate.ITranslateService,
|
||||
configService: IPeopleSearchConfigService,
|
||||
localStorageService: LocalStorageService,
|
||||
peopleService: IPeopleService,
|
||||
promiseService: PromiseService,
|
||||
pwmService: IPwmService,
|
||||
commonSearchService: CommonSearchService) {
|
||||
super($q,
|
||||
$scope,
|
||||
$state,
|
||||
$stateParams,
|
||||
$timeout,
|
||||
$translate,
|
||||
configService,
|
||||
localStorageService,
|
||||
peopleService,
|
||||
promiseService,
|
||||
pwmService,
|
||||
commonSearchService);
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
this.initialize().then(() => {
|
||||
this.fetchData();
|
||||
});
|
||||
|
||||
let self = this;
|
||||
|
||||
// The table columns are dynamic and configured via a service
|
||||
this.configService.getColumnConfig().then(
|
||||
(columnConfiguration: any) => {
|
||||
self.columnConfiguration = Object.keys(columnConfiguration).reduce(
|
||||
function(accumulator, columnId) {
|
||||
accumulator[columnId] = {
|
||||
label: columnConfiguration[columnId],
|
||||
visible: true
|
||||
};
|
||||
|
||||
return accumulator;
|
||||
},
|
||||
{});
|
||||
},
|
||||
(error) => {
|
||||
self.setErrorMessage(error);
|
||||
});
|
||||
}
|
||||
|
||||
gotoCardsView() {
|
||||
this.toggleView('search.cards');
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
let searchResult = this.fetchSearchData();
|
||||
if (searchResult) {
|
||||
searchResult.then(this.onSearchResult.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
private onSearchResult(searchResult: SearchResult): void {
|
||||
this.searchResult = searchResult;
|
||||
}
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// These need to be at the top so imported components can override the default styling
|
||||
require('../../styles.scss');
|
||||
require('./peoplesearch.scss');
|
||||
|
||||
import 'angular-aria';
|
||||
|
||||
import {IComponentOptions, module} from 'angular';
|
||||
import { HighlightFilter } from './string.filters';
|
||||
import { FullNameFilter } from './person.filters';
|
||||
import OrgChartComponent from './orgchart.component';
|
||||
import OrgChartSearchComponent from './orgchart-search.component';
|
||||
import PeopleSearchTableComponent from './peoplesearch-table.component';
|
||||
import PeopleSearchCardsComponent from './peoplesearch-cards.component';
|
||||
import PersonCardDirective from './person-card.component';
|
||||
import PersonDetailsDialogComponent from './person-details-dialog.component';
|
||||
import LocalStorageService from '../../services/local-storage.service';
|
||||
import PromiseService from '../../services/promise.service';
|
||||
import uxModule from '../../ux/ux.module';
|
||||
import CommonSearchService from '../../services/common-search.service';
|
||||
import OrgchartExportController from './orgchart-export.controller';
|
||||
import OrgchartEmailController from './orgchart-email.controller';
|
||||
|
||||
const moduleName = 'people-search';
|
||||
|
||||
module(moduleName, [
|
||||
'ngAria',
|
||||
'pascalprecht.translate',
|
||||
uxModule
|
||||
])
|
||||
.filter('fullName', FullNameFilter)
|
||||
.filter('highlight', HighlightFilter)
|
||||
.component('orgChart', OrgChartComponent as IComponentOptions)
|
||||
.component('orgChartSearch', OrgChartSearchComponent as IComponentOptions)
|
||||
.directive('personCard', PersonCardDirective)
|
||||
.component('peopleSearchTable', PeopleSearchTableComponent as IComponentOptions)
|
||||
.component('peopleSearchCards', PeopleSearchCardsComponent as IComponentOptions)
|
||||
.component('personDetailsDialogComponent', PersonDetailsDialogComponent as IComponentOptions)
|
||||
.controller('OrgchartExportController', OrgchartExportController)
|
||||
.controller('OrgchartEmailController', OrgchartEmailController)
|
||||
.service('PromiseService', PromiseService)
|
||||
.service('LocalStorageService', LocalStorageService)
|
||||
.service('CommonSearchService', CommonSearchService);
|
||||
|
||||
export default moduleName;
|
|
@ -1,142 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
.help-desk-search-component,
|
||||
.people-search-component {
|
||||
height: 100%;
|
||||
|
||||
> ui-view {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.peoplesearch-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
.basic-search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
|
||||
> * + * {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-search-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 15px;
|
||||
|
||||
> * + * {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
& + div {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
select.attribute-value {
|
||||
min-width: 210px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-info-container {
|
||||
text-align: left;
|
||||
|
||||
.search-info {
|
||||
background-color: #fff6ce;
|
||||
border: 1px solid #dae1e1;
|
||||
border-radius: 3px;
|
||||
color: #808080;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
margin: 0 auto 10px;
|
||||
padding: 5px;
|
||||
|
||||
&.loading {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #01a9e7;
|
||||
}
|
||||
|
||||
.ias-avatar {
|
||||
background: transparent url('../../../images/user.png') no-repeat center center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.ias-header {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.checkbox-button {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
.icon-divider {
|
||||
&.vertical {
|
||||
background-color: rgba(#808080, .5);
|
||||
height: 25px;
|
||||
margin: 0 5px;
|
||||
width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.single-line {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.ias-dialog {
|
||||
.ias-dialog-container {
|
||||
max-width: 100%;
|
||||
width: 375px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
.ias-search {
|
||||
> .ias-icon-button {
|
||||
right: auto; // Can remove once https://github.com/MicroFocus/ux-ias/issues/18 is fixed
|
||||
}
|
||||
}
|
||||
|
||||
.help-desk-search-component,
|
||||
.people-search-component {
|
||||
.search-info-container {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="ias-tile ias-medium" ng-attr-id="{{'displayName-' + $ctrl.person.displayNames[0] }}" ng-switch="$ctrl.size">
|
||||
<div class="ias-avatar" ng-if="$ctrl.isSmall() || $ctrl.showImage"
|
||||
ng-style="$ctrl.getAvatarStyle()"
|
||||
aria-label="User avatar"></div>
|
||||
|
||||
<div class="reports"
|
||||
ng-if="$ctrl.numDirectReportsVisible"
|
||||
ng-bind="$ctrl.person.numDirectReports"
|
||||
ng-attr-title="{{$ctrl.person.numDirectReports}} {{ 'Title_DirectReports' | translate }}"></div>
|
||||
|
||||
<div class="ias-tile-content" ng-switch-when="small">
|
||||
<h3 class="single-line" ng-bind="$ctrl.person.displayNames[0]"
|
||||
ng-attr-title="{{$ctrl.person.displayNames[0]}}"></h3>
|
||||
<div class="single-line" ng-bind="$ctrl.person.displayNames[1]"
|
||||
ng-attr-title="{{$ctrl.person.displayNames[1]}}"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-tile-content" ng-class="{'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-when="large">
|
||||
<h3 class="single-line" ng-bind="$ctrl.person.displayNames[0]"
|
||||
ng-attr-title="{{$ctrl.person.displayNames[0]}}"></h3>
|
||||
<div class="single-line" ng-bind="displayName" ng-attr-title="{{displayName}}"
|
||||
ng-repeat="displayName in $ctrl.person.displayNames.slice(1, 8) track by $index"></div>
|
||||
</div>
|
||||
|
||||
<div class="ias-tile-content" ng-class="{'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-default>
|
||||
<h3 class="single-line" ng-bind="$ctrl.person.displayNames[0]"
|
||||
ng-attr-title="{{$ctrl.person.displayNames[0]}}"></h3>
|
||||
<div class="single-line" ng-bind="displayName" ng-attr-title="{{displayName}}"
|
||||
ng-repeat="displayName in $ctrl.person.displayNames.slice(1, 4) track by $index"></div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IAugmentedJQuery} from 'angular';
|
||||
import { IPerson } from '../../models/person.model';
|
||||
import { IPeopleService } from '../../services/people.service';
|
||||
|
||||
const templateUrl = require('./person-card.component.html');
|
||||
|
||||
class PersonCardController {
|
||||
private details: any[]; // For large style cards
|
||||
private disableFocus: boolean;
|
||||
private person: IPerson;
|
||||
private directReports: IPerson[];
|
||||
private size: string;
|
||||
private showDirectReportCount: boolean;
|
||||
private showImage: boolean;
|
||||
|
||||
static $inject = ['$element', 'PeopleService'];
|
||||
constructor(private $element: IAugmentedJQuery, private peopleService: IPeopleService) {
|
||||
this.details = [];
|
||||
this.size = 'medium';
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
if (!this.disableFocus) {
|
||||
this.$element[0].tabIndex = 0;
|
||||
this.$element.on('keydown', this.onKeyDown.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
$onChanges(): void {
|
||||
if (this.person) {
|
||||
this.setDisplayData();
|
||||
|
||||
if (this.showDirectReportCount) {
|
||||
this.peopleService.getNumberOfDirectReports(this.person.userKey)
|
||||
.then(
|
||||
(numDirectReports) => {
|
||||
this.person.numDirectReports = numDirectReports;
|
||||
},
|
||||
(error) => {
|
||||
// TODO: handle error. NOOP is fine for now because it won't try to display the result
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$onDestroy(): void {
|
||||
this.$element.off('keydown', this.onKeyDown.bind(this));
|
||||
}
|
||||
|
||||
getAvatarStyle(): any {
|
||||
if (!this.showImage) {
|
||||
return { 'background-image': 'url()' };
|
||||
}
|
||||
|
||||
if (this.person && this.person.photoURL) {
|
||||
return { 'background-image': 'url(' + this.person.photoURL + ')' };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
isSmall(): boolean {
|
||||
return this.size === 'small';
|
||||
}
|
||||
|
||||
get numDirectReportsVisible(): boolean {
|
||||
return this.showDirectReportCount && this.person && !!this.person.numDirectReports;
|
||||
}
|
||||
|
||||
private onKeyDown(event: KeyboardEvent): void {
|
||||
if (event.keyCode === 13 || event.keyCode === 32) { // 13 = Enter, 32 = Space
|
||||
this.$element.triggerHandler('click');
|
||||
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
|
||||
private setDisplayData(): void {
|
||||
if (this.person.detail) {
|
||||
this.details = Object
|
||||
.keys(this.person.detail)
|
||||
.map((key: string) => {
|
||||
return this.person.detail[key];
|
||||
});
|
||||
}
|
||||
|
||||
if (this.directReports) {
|
||||
this.person.numDirectReports = this.directReports.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function PersonCardDirectiveFactory() {
|
||||
return {
|
||||
bindToController: true,
|
||||
controller: PersonCardController,
|
||||
controllerAs: '$ctrl',
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
directReports: '<',
|
||||
disableFocus: '<',
|
||||
person: '<',
|
||||
showImage: '<',
|
||||
size: '@',
|
||||
showDirectReportCount: '<'
|
||||
},
|
||||
templateUrl: templateUrl
|
||||
};
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
|
||||
<!--
|
||||
~ Password Management Servlets (PWM)
|
||||
~ http://www.pwm-project.org
|
||||
~
|
||||
~ Copyright (c) 2006-2009 Novell, Inc.
|
||||
~ Copyright (c) 2009-2021 The PWM Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<div class="ias-styles-root">
|
||||
<div class="ias-dialog person-details-dialog">
|
||||
<div class="ias-dialog-container">
|
||||
<div class="ias-dialog-content">
|
||||
<div class="person-details-dialog-header">
|
||||
<div class="ias-avatar" ng-if="$ctrl.photosEnabled" ng-style="$ctrl.getAvatarStyle()" alt="User image"></div>
|
||||
|
||||
<div class="ias-header">
|
||||
<h2 ng-bind="$ctrl.person.displayNames[0]"></h2>
|
||||
<span class="ias-fill"></span>
|
||||
<ias-button class="ias-icon-button" ng-click="$ctrl.closeDialog()">
|
||||
<ias-icon icon="close_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
<div ng-bind="$ctrl.person.displayNames[1]"></div>
|
||||
<div ng-bind="$ctrl.person.displayNames[2]"></div>
|
||||
<div ng-bind="$ctrl.person.displayNames[3]"></div>
|
||||
<div class="person-dialog-actions">
|
||||
<ias-button type="button" class="ias-icon-text-button"
|
||||
ng-click="$ctrl.gotoOrgChart()" ng-if="$ctrl.orgChartEnabled">
|
||||
<ias-icon icon="orgchart_thin" id="orgchart-button"></ias-icon>
|
||||
<span translate="Title_OrgChart">Organizational Chart</span>
|
||||
</ias-button>
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
ng-attr-title="{{ 'Button_ExportOrgChart' | translate }}"
|
||||
ng-click="$ctrl.beginExport()" ng-if="$ctrl.exportEnabled">
|
||||
<ias-icon icon="download_thick"></ias-icon>
|
||||
</ias-button>
|
||||
<ias-button class="ias-icon-button ias-dialog-cancel-button"
|
||||
ng-attr-title="{{ 'Button_EmailTeam' | translate }}"
|
||||
ng-click="$ctrl.beginEmail()" ng-if="$ctrl.emailTeamEnabled">
|
||||
<ias-icon icon="email_thick"></ias-icon>
|
||||
</ias-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="person-details-content">
|
||||
<table class="details-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li ng-repeat="reference in $ctrl.person.links">
|
||||
<a ng-href="{{reference.link}}"><span ng-bind="reference.name"></span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="(key, detail) in $ctrl.person.detail">
|
||||
<td ng-bind="detail.label"></td>
|
||||
<td ng-switch="detail.type">
|
||||
<div class="detail-container" ng-switch-when="userDN">
|
||||
<ul>
|
||||
<li ng-repeat="user in detail.userReferences">
|
||||
<a ng-href="{{$ctrl.getPersonDetailsUrl(user.userKey)}}"
|
||||
ng-bind="user.displayName"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="detail-container" ng-switch-default>
|
||||
<ul>
|
||||
<li ng-repeat="value in detail.values">
|
||||
<a ng-href="mailto:{{value}}"
|
||||
ng-bind="value"
|
||||
ng-if="detail.type === 'email'"></a>
|
||||
<a ng-href="tel:{{value}}"
|
||||
ng-bind="value"
|
||||
ng-if="detail.type === 'tel'"></a>
|
||||
<span ng-bind="value"
|
||||
ng-if="detail.type !== 'email' && detail.type !== 'tel'"></span>
|
||||
|
||||
<a ui-sref="search.table({ query: value })"
|
||||
class="details-table-search-link"
|
||||
ng-if="detail.searchable"
|
||||
ng-attr-title="{{('Placeholder_Search' | translate) + ' \'' + value + '\''}}">
|
||||
<ias-icon icon="search_thick"></ias-icon>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,135 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.ias-styles-root {
|
||||
.person-dialog-actions {
|
||||
clear: both;
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
|
||||
> .ias-icon-text-button + .ias-icon-button {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
> .ias-icon-button + .ias-icon-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.person-details-dialog {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
|
||||
.ias-dialog-container {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
top: auto;
|
||||
transform: none;
|
||||
|
||||
> .ias-dialog-content {
|
||||
display: grid;
|
||||
grid-template-rows: max-content 1fr;
|
||||
flex-grow: 1;
|
||||
|
||||
> .person-details-content {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ias-avatar {
|
||||
float: left;
|
||||
height: 100px;
|
||||
margin-bottom: 15px;
|
||||
margin-right: 15px;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.person-details-dialog-header {
|
||||
background-color: #eef2f2;
|
||||
color: #434c50;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
[data-browserType=ie] .person-details-dialog-header > .ias-header > h2 {
|
||||
width: 185px;
|
||||
}
|
||||
|
||||
.person-details-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.details-table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
|
||||
tr {
|
||||
height: 25px;
|
||||
|
||||
td {
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
height: 19px;
|
||||
text-align: left;
|
||||
|
||||
&:first-child {
|
||||
color: #949494;
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
padding: 3px 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding: 3px 15px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.details-table-search-link {
|
||||
display: inline-block;
|
||||
font-size: 25px;
|
||||
vertical-align: middle;
|
||||
|
||||
.ias-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { Component } from '../../component';
|
||||
import {IPeopleSearchConfigService, IPersonDetailsConfig} from '../../services/peoplesearch-config.service';
|
||||
import { IPeopleService } from '../../services/people.service';
|
||||
import {IAugmentedJQuery, ITimeoutService, noop} from 'angular';
|
||||
import { IPerson } from '../../models/person.model';
|
||||
import {IChangePasswordSuccess} from '../../components/changepassword/success-change-password.controller';
|
||||
|
||||
let orgchartExportTemplateUrl = require('./orgchart-export.component.html');
|
||||
let orgchartEmailTemplateUrl = require('./orgchart-email.component.html');
|
||||
|
||||
@Component({
|
||||
stylesheetUrl: require('./person-details-dialog.component.scss'),
|
||||
templateUrl: require('./person-details-dialog.component.html')
|
||||
})
|
||||
export default class PersonDetailsDialogComponent {
|
||||
person: IPerson;
|
||||
photosEnabled: boolean;
|
||||
orgChartEnabled: boolean;
|
||||
exportEnabled: boolean;
|
||||
emailTeamEnabled: boolean;
|
||||
maxExportDepth: number;
|
||||
maxEmailDepth: number;
|
||||
|
||||
static $inject = [
|
||||
'$element',
|
||||
'$state',
|
||||
'$stateParams',
|
||||
'$timeout',
|
||||
'ConfigService',
|
||||
'PeopleService',
|
||||
'IasDialogService'
|
||||
];
|
||||
|
||||
constructor(private $element: IAugmentedJQuery,
|
||||
private $state: angular.ui.IStateService,
|
||||
private $stateParams: angular.ui.IStateParamsService,
|
||||
private $timeout: ITimeoutService,
|
||||
private configService: IPeopleSearchConfigService,
|
||||
private peopleService: IPeopleService,
|
||||
private IasDialogService: any) {
|
||||
}
|
||||
|
||||
$onInit(): void {
|
||||
const personId = this.$stateParams['personId'];
|
||||
|
||||
this.configService.orgChartEnabled().then((orgChartEnabled: boolean) => {
|
||||
this.orgChartEnabled = orgChartEnabled;
|
||||
});
|
||||
|
||||
this.configService.photosEnabled().then((photosEnabled: boolean) => {
|
||||
this.photosEnabled = photosEnabled;
|
||||
});
|
||||
|
||||
this.configService.personDetailsConfig().then((personDetailsConfig: IPersonDetailsConfig) => {
|
||||
this.photosEnabled = personDetailsConfig.photosEnabled;
|
||||
this.orgChartEnabled = personDetailsConfig.orgChartEnabled;
|
||||
this.exportEnabled = personDetailsConfig.exportEnabled;
|
||||
this.emailTeamEnabled = personDetailsConfig.emailTeamEnabled;
|
||||
this.maxExportDepth = personDetailsConfig.maxExportDepth;
|
||||
this.maxEmailDepth = personDetailsConfig.maxEmailDepth;
|
||||
});
|
||||
|
||||
this.peopleService
|
||||
.getPerson(personId)
|
||||
.then(
|
||||
(person: IPerson) => {
|
||||
this.person = person;
|
||||
},
|
||||
(error) => {
|
||||
// TODO: Handle error. NOOP for now will not assign person
|
||||
});
|
||||
}
|
||||
|
||||
$postLink() {
|
||||
const self = this;
|
||||
this.$timeout(() => {
|
||||
self.$element.find('button')[0].focus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
closeDialog(): void {
|
||||
this.$state.go('^', { query: this.$stateParams['query'] });
|
||||
}
|
||||
|
||||
getAvatarStyle(): any {
|
||||
if (!this.person || !this.person.photoURL || !this.photosEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { 'background-image': 'url(' + this.person.photoURL + ')' };
|
||||
}
|
||||
|
||||
gotoOrgChart(): void {
|
||||
this.$state.go('orgchart.search', { personId: this.person.userKey });
|
||||
}
|
||||
|
||||
getPersonDetailsUrl(personId: string): string {
|
||||
return this.$state.href('.', { personId: personId }, { inherit: true, });
|
||||
}
|
||||
|
||||
searchText(text: string): void {
|
||||
this.$state.go('search.table', { query: text });
|
||||
}
|
||||
|
||||
beginExport() {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'OrgchartExportController as $ctrl',
|
||||
templateUrl: orgchartExportTemplateUrl,
|
||||
locals: {
|
||||
peopleService: this.peopleService,
|
||||
maxDepth: this.maxExportDepth,
|
||||
personName: this.person.displayNames[0],
|
||||
userKey: this.person.userKey
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beginEmail() {
|
||||
this.IasDialogService
|
||||
.open({
|
||||
controller: 'OrgchartEmailController as $ctrl',
|
||||
templateUrl: orgchartEmailTemplateUrl,
|
||||
locals: {
|
||||
peopleService: this.peopleService,
|
||||
maxDepth: this.maxEmailDepth,
|
||||
personName: this.person.displayNames[0],
|
||||
userKey: this.person.userKey
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
export function HighlightFilter() {
|
||||
return (input: string, searchText: string): string => {
|
||||
if (!input || !searchText || !searchText.length) {
|
||||
return input;
|
||||
}
|
||||
|
||||
const searchTextRegExp = new RegExp(searchText, 'gi');
|
||||
|
||||
return input.replace(searchTextRegExp, function (match) {
|
||||
return `<span class="highlight">${match}</span>`;
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { IAngularEvent, IRootScopeService } from 'angular';
|
||||
import { IStateService } from 'angular-ui-router';
|
||||
|
||||
export default [
|
||||
'$transitions',
|
||||
'$state',
|
||||
($transitions, $state: IStateService) => {
|
||||
$transitions.onError({}, (transition) => {
|
||||
if (transition._error === 'OrgChart disabled') {
|
||||
$state.go('search.cards');
|
||||
}
|
||||
});
|
||||
}
|
||||
];
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { IPeopleSearchConfigService } from './services/peoplesearch-config.service';
|
||||
import { IQService } from 'angular';
|
||||
import LocalStorageService from './services/local-storage.service';
|
||||
|
||||
export default [
|
||||
'$stateProvider',
|
||||
'$urlRouterProvider',
|
||||
(
|
||||
$stateProvider: angular.ui.IStateProvider,
|
||||
$urlRouterProvider: angular.ui.IUrlRouterProvider
|
||||
) => {
|
||||
$urlRouterProvider.otherwise(
|
||||
($injector: angular.auto.IInjectorService, $location: angular.ILocationService) => {
|
||||
let $state: angular.ui.IStateService = <angular.ui.IStateService>$injector.get('$state');
|
||||
let localStorageService: LocalStorageService =
|
||||
<LocalStorageService>$injector.get('LocalStorageService');
|
||||
|
||||
let storedView = localStorageService.getItem(localStorageService.keys.SEARCH_VIEW);
|
||||
|
||||
if (storedView) {
|
||||
$state.go(storedView);
|
||||
}
|
||||
else {
|
||||
$location.url('search/cards');
|
||||
}
|
||||
});
|
||||
|
||||
$stateProvider.state('search', {
|
||||
url: '/search?query',
|
||||
abstract: true,
|
||||
template: '<div class="people-search-component"><ui-view/></div>',
|
||||
});
|
||||
$stateProvider.state('search.table', { url: '/table', component: 'peopleSearchTable' });
|
||||
$stateProvider.state('search.cards', { url: '/cards', component: 'peopleSearchCards' });
|
||||
$stateProvider.state('search.table.details', {
|
||||
url: '/details/{personId}',
|
||||
component: 'personDetailsDialogComponent'
|
||||
});
|
||||
$stateProvider.state('search.cards.details', {
|
||||
url: '/details/{personId}',
|
||||
component: 'personDetailsDialogComponent'
|
||||
});
|
||||
$stateProvider.state('orgchart', { url: '/orgchart?query',
|
||||
abstract: true,
|
||||
template: '<ui-view/>',
|
||||
resolve: {
|
||||
enabled: [
|
||||
'$q',
|
||||
'ConfigService',
|
||||
($q: IQService, configService: IPeopleSearchConfigService) => {
|
||||
let deferred = $q.defer();
|
||||
|
||||
configService
|
||||
.orgChartEnabled()
|
||||
.then((orgChartEnabled: boolean) => {
|
||||
if (!orgChartEnabled) {
|
||||
deferred.reject('OrgChart disabled');
|
||||
}
|
||||
else {
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}]
|
||||
}
|
||||
});
|
||||
$stateProvider.state('orgchart.index', { url: '', component: 'orgChartSearch' });
|
||||
$stateProvider.state('orgchart.search', { url: '/{personId}', component: 'orgChartSearch' });
|
||||
$stateProvider.state('orgchart.search.details', { url: '/details', component: 'personDetailsDialogComponent' });
|
||||
}];
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IConfigService} from './base-config.service';
|
||||
import {IPromise, IQService} from 'angular';
|
||||
|
||||
export abstract class ConfigBaseService implements IConfigService {
|
||||
abstract getColumnConfig(): IPromise<any>;
|
||||
|
||||
constructor(protected $q: IQService) {
|
||||
}
|
||||
|
||||
getValue(key: string): IPromise<any> {
|
||||
return null;
|
||||
}
|
||||
|
||||
photosEnabled(): IPromise<boolean> {
|
||||
return this.$q.resolve(true);
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {IHttpService, ILogService, IPromise, IQService} from 'angular';
|
||||
import {IPwmService} from './pwm.service';
|
||||
|
||||
const COLUMN_CONFIG = 'searchColumns';
|
||||
export const PHOTO_ENABLED = 'enablePhoto';
|
||||
const PRINTING_ENABLED = 'enableOrgChartPrinting';
|
||||
|
||||
export const ADVANCED_SEARCH_ENABLED = 'enableAdvancedSearch';
|
||||
export const ADVANCED_SEARCH_MAX_ATTRIBUTES = 'maxAdvancedSearchAttributes';
|
||||
export const ADVANCED_SEARCH_ATTRIBUTES = 'advancedSearchAttributes';
|
||||
|
||||
export interface IConfigService {
|
||||
getColumnConfig(): IPromise<any>;
|
||||
getValue(key: string): IPromise<any>;
|
||||
photosEnabled(): IPromise<boolean>;
|
||||
printingEnabled(): IPromise<boolean>;
|
||||
}
|
||||
|
||||
export interface IAttributeMetadata {
|
||||
attribute: string;
|
||||
label: string;
|
||||
type: string;
|
||||
options: any;
|
||||
}
|
||||
|
||||
export interface IAdvancedSearchConfig {
|
||||
enabled: boolean;
|
||||
maxRows: number;
|
||||
attributes: IAttributeMetadata[];
|
||||
}
|
||||
|
||||
export interface IAdvancedSearchQuery {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export abstract class ConfigBaseService implements IConfigService {
|
||||
|
||||
constructor(protected $http: IHttpService,
|
||||
protected $log: ILogService,
|
||||
protected $q: IQService,
|
||||
protected pwmService: IPwmService) {
|
||||
}
|
||||
|
||||
getColumnConfig(): IPromise<any> {
|
||||
return this.getValue(COLUMN_CONFIG);
|
||||
}
|
||||
|
||||
private getEndpointValue(endpoint: string, key: string): IPromise<any> {
|
||||
return this.$http
|
||||
.get(endpoint, { cache: true })
|
||||
.then((response) => {
|
||||
if (response.data['error']) {
|
||||
return this.handlePwmError(response);
|
||||
}
|
||||
|
||||
return response.data['data'][key];
|
||||
}, this.handleHttpError);
|
||||
}
|
||||
|
||||
getValue(key: string): IPromise<any> {
|
||||
let endpoint: string = this.pwmService.getServerUrl('clientData');
|
||||
return this.getEndpointValue(endpoint, key);
|
||||
}
|
||||
|
||||
private handleHttpError(error): void {
|
||||
this.$log.error(error);
|
||||
}
|
||||
|
||||
private handlePwmError(response): IPromise<any> {
|
||||
const errorMessage = `${response.data['errorCode']}: ${response.data['errorMessage']}`;
|
||||
this.$log.error(errorMessage);
|
||||
|
||||
return this.$q.reject(response.data['errorMessage']);
|
||||
}
|
||||
|
||||
photosEnabled(): IPromise<boolean> {
|
||||
return this.getValue(PHOTO_ENABLED)
|
||||
.then(null, () => { return true; }); // On error use default
|
||||
}
|
||||
|
||||
printingEnabled(): IPromise<boolean> {
|
||||
return this.getValue(PRINTING_ENABLED)
|
||||
.then(null, () => { return true; }); // On error use default
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHttpService, ILogService, IQService, IWindowService, module} from 'angular';
|
||||
import LocalStorageService from './local-storage.service';
|
||||
import CommonSearchService from './common-search.service';
|
||||
import {anyNumber, anyString, deepEqual, instance, mock, strictEqual, verify, when} from 'ts-mockito';
|
||||
import {IAdvancedSearchQuery} from './base-config.service';
|
||||
|
||||
describe('In common-search.service.test.ts', () => {
|
||||
beforeEach(() => {
|
||||
module('app', []);
|
||||
});
|
||||
|
||||
// Define some angular objects we'll grab from angular-mocks
|
||||
let $log: ILogService;
|
||||
|
||||
beforeEach(inject((_$http_, _$log_, _$q_, _$window_) => {
|
||||
$log = _$log_;
|
||||
$log.info('This is an info message');
|
||||
}));
|
||||
|
||||
it('Pulls search queries from local storage, or empty array if undefined or bad data', (done: DoneFn) => {
|
||||
let mockLocalStorageService = mock(LocalStorageService);
|
||||
when(mockLocalStorageService.getItem('undefinedScenario')).thenReturn(undefined);
|
||||
when(mockLocalStorageService.getItem('bogusScenario')).thenReturn('bogus');
|
||||
when(mockLocalStorageService.getItem('notArrayScenario')).thenReturn('{"key":"foo","value":"bar"}');
|
||||
when(mockLocalStorageService.getItem('goodScenario')).thenReturn(JSON.stringify([
|
||||
{
|
||||
key: 'foo',
|
||||
value: 'bar'
|
||||
}
|
||||
]));
|
||||
|
||||
const commonSearchService = new CommonSearchService(instance(mockLocalStorageService));
|
||||
|
||||
expect(commonSearchService.getAdvSearchQueries('undefinedScenario')).toEqual([]);
|
||||
expect(commonSearchService.getAdvSearchQueries('bogusScenario')).toEqual([]);
|
||||
expect(commonSearchService.getAdvSearchQueries('notArrayScenario')).toEqual([]);
|
||||
expect(commonSearchService.getAdvSearchQueries('goodScenario')).toContain({
|
||||
key: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('Stores search queries into local storage, or does nothing if bad data', (done: DoneFn) => {
|
||||
let mockLocalStorageService = mock(LocalStorageService);
|
||||
|
||||
const queries: IAdvancedSearchQuery[] = [
|
||||
{key: 'foo', value: 'one'},
|
||||
{key: 'bar', value: 'two'},
|
||||
{key: 'baz', value: 'three'}
|
||||
];
|
||||
|
||||
const commonSearchService = new CommonSearchService(instance(mockLocalStorageService));
|
||||
|
||||
commonSearchService.setAdvSearchQueries('nullData', null);
|
||||
commonSearchService.setAdvSearchQueries('undefinedData', undefined);
|
||||
commonSearchService.setAdvSearchQueries('emptyArray', []);
|
||||
commonSearchService.setAdvSearchQueries('lotsOfData', queries);
|
||||
|
||||
verify(mockLocalStorageService.removeItem('nullData')).called();
|
||||
verify(mockLocalStorageService.removeItem('undefinedData')).called();
|
||||
verify(mockLocalStorageService.setItem('emptyArray', '[]')).called();
|
||||
verify(mockLocalStorageService.setItem('emptyArray', anyString())).called();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('Pulls advanced search active state from local storage, or false if undefined or bad data', (done: DoneFn) => {
|
||||
let mockLocalStorageService = mock(LocalStorageService);
|
||||
when(mockLocalStorageService.getItem('undefinedScenario')).thenReturn(undefined);
|
||||
when(mockLocalStorageService.getItem('bogusScenario')).thenReturn('bogus');
|
||||
when(mockLocalStorageService.getItem('invalidScenario')).thenReturn('{}');
|
||||
when(mockLocalStorageService.getItem('falseScenario')).thenReturn(JSON.stringify(false));
|
||||
when(mockLocalStorageService.getItem('trueScenario')).thenReturn(JSON.stringify(true));
|
||||
|
||||
const commonSearchService = new CommonSearchService(instance(mockLocalStorageService));
|
||||
|
||||
expect(commonSearchService.isAdvSearchActive('undefinedScenario')).toEqual(false);
|
||||
expect(commonSearchService.isAdvSearchActive('bogusScenario')).toEqual(false);
|
||||
expect(commonSearchService.isAdvSearchActive('invalidScenario')).toEqual(false);
|
||||
expect(commonSearchService.isAdvSearchActive('falseScenario')).toEqual(false);
|
||||
expect(commonSearchService.isAdvSearchActive('trueScenario')).toEqual(true);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('Stores the advanced search active state to local storage', (done: DoneFn) => {
|
||||
let mockLocalStorageService = mock(LocalStorageService);
|
||||
|
||||
const commonSearchService = new CommonSearchService(instance(mockLocalStorageService));
|
||||
|
||||
commonSearchService.setAdvSearchActive('nullData', null);
|
||||
commonSearchService.setAdvSearchActive('undefinedData', undefined);
|
||||
commonSearchService.setAdvSearchActive('trueScenario', true);
|
||||
commonSearchService.setAdvSearchActive('falseScenario', false);
|
||||
|
||||
verify(mockLocalStorageService.removeItem('nullData')).called();
|
||||
verify(mockLocalStorageService.removeItem('undefinedData')).called();
|
||||
verify(mockLocalStorageService.setItem('trueScenario', 'true')).called();
|
||||
verify(mockLocalStorageService.setItem('falseScenario', 'false')).called();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import LocalStorageService from './local-storage.service';
|
||||
import {IAdvancedSearchQuery, IAttributeMetadata} from './base-config.service';
|
||||
|
||||
const PS_ADV_SEARCH_ACTIVE = 'psAdvancedSearchActive';
|
||||
const PS_ADV_SEARCH_QUERIES = 'psAdvancedSearchQueries';
|
||||
const HD_ADV_SEARCH_ACTIVE = 'hdAdvancedSearchActive';
|
||||
const HD_ADV_SEARCH_QUERIES = 'hdAdvancedSearchQueries';
|
||||
|
||||
export default class CommonSearchService {
|
||||
static $inject = ['LocalStorageService'];
|
||||
constructor(private localStorageService: LocalStorageService) {
|
||||
}
|
||||
|
||||
getDefaultValue(attributeMetaData: IAttributeMetadata) {
|
||||
if (attributeMetaData) {
|
||||
if (attributeMetaData.type === 'select') {
|
||||
const keys: string[] = Object.keys(attributeMetaData.options);
|
||||
if (keys && keys.length > 0) {
|
||||
return keys[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
isPsAdvancedSearchActive(): boolean {
|
||||
return this.isAdvSearchActive(PS_ADV_SEARCH_ACTIVE);
|
||||
}
|
||||
|
||||
setPsAdvancedSearchActive(active: boolean): void {
|
||||
this.setAdvSearchActive(PS_ADV_SEARCH_ACTIVE, active);
|
||||
}
|
||||
|
||||
getPsAdvSearchQueries(): IAdvancedSearchQuery[] {
|
||||
return this.getAdvSearchQueries(PS_ADV_SEARCH_QUERIES);
|
||||
}
|
||||
|
||||
setPsAdvSearchQueries(queries: IAdvancedSearchQuery[]) {
|
||||
this.setAdvSearchQueries(PS_ADV_SEARCH_QUERIES, queries);
|
||||
}
|
||||
|
||||
isHdAdvancedSearchActive(): boolean {
|
||||
return this.isAdvSearchActive(HD_ADV_SEARCH_ACTIVE);
|
||||
}
|
||||
|
||||
setHdAdvancedSearchActive(active: boolean): void {
|
||||
this.setAdvSearchActive(HD_ADV_SEARCH_ACTIVE, active);
|
||||
}
|
||||
|
||||
getHdAdvSearchQueries(): IAdvancedSearchQuery[] {
|
||||
return this.getAdvSearchQueries(HD_ADV_SEARCH_QUERIES);
|
||||
}
|
||||
|
||||
setHdAdvSearchQueries(queries: IAdvancedSearchQuery[]) {
|
||||
this.setAdvSearchQueries(HD_ADV_SEARCH_QUERIES, queries);
|
||||
}
|
||||
|
||||
isAdvSearchActive(storageName: string): boolean {
|
||||
if (storageName) {
|
||||
const storageValue = this.localStorageService.getItem(storageName);
|
||||
if (storageValue) {
|
||||
return (storageValue === 'true');
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setAdvSearchActive(storageName: string, active: boolean): void {
|
||||
if (storageName) {
|
||||
// Make sure active is a boolean first
|
||||
if (typeof(active) === typeof(true)) {
|
||||
this.localStorageService.setItem(storageName, JSON.stringify(active));
|
||||
} else {
|
||||
// If we were given undefine or null data, then just remove the named item from local storage
|
||||
this.localStorageService.removeItem(storageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAdvSearchQueries(storageName: string): IAdvancedSearchQuery[] {
|
||||
if (storageName) {
|
||||
const storageValue = this.localStorageService.getItem(storageName);
|
||||
if (storageValue) {
|
||||
try {
|
||||
const parsedValue = JSON.parse(storageValue);
|
||||
if (Array.isArray(parsedValue)) {
|
||||
return parsedValue;
|
||||
}
|
||||
} catch (error) {
|
||||
// Unparseable, an empty array will be returned below
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
setAdvSearchQueries(storageName: string, queries: IAdvancedSearchQuery[]) {
|
||||
if (storageName) {
|
||||
if (queries) {
|
||||
this.localStorageService.setItem(storageName, JSON.stringify(queries));
|
||||
} else {
|
||||
// If we were given undefine or null data, then just remove the named item from local storage
|
||||
this.localStorageService.removeItem(storageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { IPromise, IQService } from 'angular';
|
||||
import {ConfigBaseService} from './base-config.service.dev';
|
||||
import {IConfigService} from './base-config.service';
|
||||
import {
|
||||
IActionButton,
|
||||
IHelpDeskConfigService, IVerificationMap, PASSWORD_UI_MODES, TOKEN_CHOICE, VERIFICATION_METHOD_LABELS,
|
||||
VERIFICATION_METHOD_NAMES
|
||||
} from './helpdesk-config.service';
|
||||
|
||||
export default class HelpDeskConfigService extends ConfigBaseService implements IConfigService, IHelpDeskConfigService {
|
||||
static $inject = [ '$q' ];
|
||||
constructor($q: IQService) {
|
||||
super($q);
|
||||
}
|
||||
|
||||
getClearResponsesSetting(): IPromise<string> {
|
||||
return this.$q.resolve('ask');
|
||||
}
|
||||
|
||||
getColumnConfig(): IPromise<any> {
|
||||
return this.$q.resolve({
|
||||
givenName: 'First Name',
|
||||
sn: 'Last Name',
|
||||
title: 'Title',
|
||||
mail: 'Email',
|
||||
telephoneNumber: 'Telephone',
|
||||
workforceId: 'Workforce ID'
|
||||
});
|
||||
}
|
||||
|
||||
getCustomButtons(): IPromise<{[key: string]: IActionButton}> {
|
||||
return this.$q.resolve({
|
||||
'Clone User': {
|
||||
name: 'Clone User',
|
||||
description: 'Clones the current user'
|
||||
},
|
||||
'Merge User': {
|
||||
name: 'Merge User',
|
||||
description: 'Merges the current user with another user'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getPasswordUiMode(): IPromise<string> {
|
||||
return this.$q.resolve(PASSWORD_UI_MODES.BOTH);
|
||||
}
|
||||
|
||||
getTokenSendMethod(): IPromise<string> {
|
||||
return this.$q.resolve(TOKEN_CHOICE);
|
||||
}
|
||||
|
||||
getVerificationAttributes(): IPromise<IVerificationMap> {
|
||||
return this.$q.resolve([
|
||||
{ name: 'workforceID', label: 'Workforce ID' },
|
||||
{ name: 'mail', label: 'Email Address' }
|
||||
]);
|
||||
}
|
||||
|
||||
getVerificationMethods(): IPromise<IVerificationMap> {
|
||||
return this.$q.resolve([
|
||||
{ name: VERIFICATION_METHOD_NAMES.ATTRIBUTES, label: VERIFICATION_METHOD_LABELS.ATTRIBUTES },
|
||||
{ name: VERIFICATION_METHOD_NAMES.SMS, label: VERIFICATION_METHOD_LABELS.SMS }
|
||||
]);
|
||||
}
|
||||
|
||||
maskPasswordsEnabled(): IPromise<boolean> {
|
||||
return this.$q.resolve(true);
|
||||
}
|
||||
|
||||
verificationsEnabled(): IPromise<boolean> {
|
||||
return this.$q.resolve(true);
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
|
||||
export const helpdeskProcessAction_clientData = {
|
||||
"error": false,
|
||||
"errorCode": 0,
|
||||
"data": {
|
||||
"searchColumns": {
|
||||
"cn": "Username",
|
||||
"givenName": "First Name",
|
||||
"sn": "Last Name",
|
||||
"mail": "Email",
|
||||
"workforceID": "Workforce ID"
|
||||
},
|
||||
"maskPasswords": false,
|
||||
"clearResponses": "ask",
|
||||
"pwUiMode": "autogen",
|
||||
"tokenSendMethod": "EMAILONLY",
|
||||
"actions": {},
|
||||
"verificationMethods": {
|
||||
"optional": [
|
||||
"TOKEN",
|
||||
"OTP"
|
||||
],
|
||||
"required": [
|
||||
"ATTRIBUTES",
|
||||
"OTP"
|
||||
]
|
||||
},
|
||||
"verificationForm": [
|
||||
{
|
||||
"name": "workforceID",
|
||||
"label": "workforceID"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {IHttpBackendService, IHttpService, ILogService, IQService, IWindowService, module} from 'angular';
|
||||
import ObjectService from './object.service';
|
||||
import {default as PwmService, IPwmService} from './pwm.service';
|
||||
import LocalStorageService from './local-storage.service';
|
||||
import HelpDeskConfigService, {IVerificationMap} from './helpdesk-config.service';
|
||||
|
||||
import {helpdeskProcessAction_clientData} from './helpdesk-config.service.test-data';
|
||||
|
||||
describe('In helpdesk-config.service.test.ts', () => {
|
||||
beforeEach(() => {
|
||||
module('app', []);
|
||||
});
|
||||
|
||||
let localStorageService: LocalStorageService;
|
||||
let objectService: ObjectService;
|
||||
let pwmService: IPwmService;
|
||||
let helpDeskConfigService: HelpDeskConfigService;
|
||||
let $httpBackend: IHttpBackendService;
|
||||
|
||||
beforeEach(inject((
|
||||
$http: IHttpService,
|
||||
$log: ILogService,
|
||||
$q: IQService,
|
||||
$window: IWindowService,
|
||||
_$httpBackend_: IHttpBackendService
|
||||
) => {
|
||||
localStorageService = new LocalStorageService($log, $window);
|
||||
objectService = new ObjectService();
|
||||
pwmService = new PwmService($http, $log, $q, $window);
|
||||
$httpBackend = _$httpBackend_;
|
||||
helpDeskConfigService = new HelpDeskConfigService($http, $log, $q, pwmService as PwmService);
|
||||
}));
|
||||
|
||||
it('getVerificationMethods returns only the required verification methods', (done: DoneFn) => {
|
||||
$httpBackend.whenGET( '/context.html?processAction=clientData').respond(helpdeskProcessAction_clientData);
|
||||
|
||||
helpDeskConfigService.getVerificationMethods()
|
||||
.then((verifications: IVerificationMap) => {
|
||||
expect(verifications.length).toBe(2);
|
||||
|
||||
expect(verifications).toContain({name: 'ATTRIBUTES', label: 'Button_Attributes'});
|
||||
expect(verifications).toContain({name: 'OTP', label: 'Button_OTP'});
|
||||
|
||||
done();
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
done.fail(error);
|
||||
});
|
||||
|
||||
// This causes the $http service to finally resolve the response:
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
// helpDeskConfigService should return both required and optional, because we passed in: {includeOptional: true}
|
||||
it('getVerificationMethods returns both required and optional verification methods', (done: DoneFn) => {
|
||||
$httpBackend.whenGET( '/context.html?processAction=clientData').respond(helpdeskProcessAction_clientData);
|
||||
|
||||
helpDeskConfigService.getVerificationMethods({includeOptional: true})
|
||||
.then((verifications: IVerificationMap) => {
|
||||
expect(verifications.length).toBe(3);
|
||||
|
||||
expect(verifications).toContain({name: 'ATTRIBUTES', label: 'Button_Attributes'});
|
||||
expect(verifications).toContain({name: 'EMAIL', label: 'Button_Email'});
|
||||
expect(verifications).toContain({name: 'OTP', label: 'Button_OTP'});
|
||||
|
||||
done();
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
done.fail(error);
|
||||
});
|
||||
|
||||
// This causes the $http service to finally resolve the response:
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { IHttpService, ILogService, IPromise, IQService } from 'angular';
|
||||
import IPwmService from './pwm.service';
|
||||
import PwmService from './pwm.service';
|
||||
import {ConfigBaseService,
|
||||
IAdvancedSearchConfig,
|
||||
IConfigService,
|
||||
ADVANCED_SEARCH_ENABLED,
|
||||
ADVANCED_SEARCH_MAX_ATTRIBUTES,
|
||||
ADVANCED_SEARCH_ATTRIBUTES
|
||||
} from './base-config.service';
|
||||
|
||||
const CLEAR_RESPONSES_CONFIG = 'clearResponses';
|
||||
const CUSTOM_BUTTON_CONFIG = 'actions';
|
||||
const MASK_PASSWORDS_CONFIG = 'maskPasswords';
|
||||
const PASSWORD_UI_MODE_CONFIG = 'pwUiMode';
|
||||
const TOKEN_SEND_METHOD_CONFIG = 'tokenSendMethod';
|
||||
const TOKEN_VERIFICATION_METHOD = 'TOKEN';
|
||||
const TOKEN_SMS_ONLY = 'SMSONLY';
|
||||
const TOKEN_EMAIL_ONLY = 'EMAILONLY';
|
||||
const VERIFICATION_FORM_CONFIG = 'verificationForm';
|
||||
const VERIFICATION_METHODS_CONFIG = 'verificationMethods';
|
||||
export const TOKEN_CHOICE = 'CHOICE_SMS_EMAIL';
|
||||
|
||||
export const PASSWORD_UI_MODES = {
|
||||
NONE: 'none',
|
||||
AUTOGEN: 'autogen',
|
||||
RANDOM: 'random',
|
||||
TYPE: 'type',
|
||||
BOTH: 'both'
|
||||
};
|
||||
|
||||
export const VERIFICATION_METHOD_NAMES = {
|
||||
ATTRIBUTES: 'ATTRIBUTES',
|
||||
TOKEN: 'TOKEN',
|
||||
OTP: 'OTP'
|
||||
};
|
||||
|
||||
export const VERIFICATION_METHOD_LABELS = {
|
||||
ATTRIBUTES: 'Button_Attributes',
|
||||
TOKEN: 'Button_TokenVerification',
|
||||
OTP: 'Button_OTP'
|
||||
};
|
||||
|
||||
interface IVerificationResponse {
|
||||
optional: string[];
|
||||
required: string[];
|
||||
}
|
||||
|
||||
export type IVerificationMap = {name: string, label: string}[];
|
||||
|
||||
export interface IActionButton {
|
||||
description: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IHelpDeskConfigService extends IConfigService {
|
||||
getClearResponsesSetting(): IPromise<string>;
|
||||
getCustomButtons(): IPromise<{[key: string]: IActionButton}>;
|
||||
getPasswordUiMode(): IPromise<string>;
|
||||
getTokenSendMethod(): IPromise<string>;
|
||||
getVerificationAttributes(): IPromise<IVerificationMap>;
|
||||
maskPasswordsEnabled(): IPromise<boolean>;
|
||||
verificationsEnabled(): IPromise<boolean>;
|
||||
advancedSearchConfig(): IPromise<IAdvancedSearchConfig>;
|
||||
}
|
||||
|
||||
export default class HelpDeskConfigService extends ConfigBaseService implements IConfigService, IHelpDeskConfigService {
|
||||
|
||||
static $inject = ['$http', '$log', '$q', 'PwmService' ];
|
||||
constructor($http: IHttpService, $log: ILogService, $q: IQService, pwmService: IPwmService) {
|
||||
super($http, $log, $q, pwmService);
|
||||
}
|
||||
|
||||
getClearResponsesSetting(): IPromise<string> {
|
||||
return this.getValue(CLEAR_RESPONSES_CONFIG);
|
||||
}
|
||||
|
||||
getCustomButtons(): IPromise<{[key: string]: IActionButton}> {
|
||||
return this.getValue(CUSTOM_BUTTON_CONFIG);
|
||||
}
|
||||
|
||||
getPasswordUiMode(): IPromise<string> {
|
||||
return this.getValue(PASSWORD_UI_MODE_CONFIG);
|
||||
}
|
||||
|
||||
getTokenSendMethod(): IPromise<string> {
|
||||
return this.getValue(TOKEN_SEND_METHOD_CONFIG);
|
||||
}
|
||||
|
||||
getVerificationAttributes(): IPromise<IVerificationMap> {
|
||||
return this.getValue(VERIFICATION_FORM_CONFIG);
|
||||
}
|
||||
|
||||
maskPasswordsEnabled(): IPromise<boolean> {
|
||||
return this.getValue(MASK_PASSWORDS_CONFIG);
|
||||
}
|
||||
|
||||
verificationsEnabled(): IPromise<boolean> {
|
||||
return this.getValue(VERIFICATION_METHODS_CONFIG)
|
||||
.then((result: IVerificationResponse) => {
|
||||
return !!result.required.length;
|
||||
});
|
||||
}
|
||||
|
||||
advancedSearchConfig(): IPromise<IAdvancedSearchConfig> {
|
||||
return this.$q.all([
|
||||
this.getValue(ADVANCED_SEARCH_ENABLED),
|
||||
this.getValue(ADVANCED_SEARCH_MAX_ATTRIBUTES),
|
||||
this.getValue(ADVANCED_SEARCH_ATTRIBUTES)
|
||||
]).then((result) => {
|
||||
return {
|
||||
enabled: result[0],
|
||||
maxRows: result[1],
|
||||
attributes: result[2]
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,547 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
IHelpDeskService, IRandomPasswordResponse, IRecentVerifications, ISuccessResponse, IVerificationStatus,
|
||||
IVerificationTokenResponse
|
||||
} from './helpdesk.service';
|
||||
import {IPromise, IQService, ITimeoutService, IWindowService} from 'angular';
|
||||
import {IPerson} from '../models/person.model';
|
||||
import SearchResult from '../models/search-result.model';
|
||||
|
||||
const peopleData = require('./people.data.json');
|
||||
|
||||
const MAX_RESULTS = 10;
|
||||
const SIMULATED_RESPONSE_TIME = 300;
|
||||
|
||||
export default class HelpDeskService implements IHelpDeskService {
|
||||
private people: IPerson[];
|
||||
|
||||
static $inject = [ '$q', '$timeout', '$window' ];
|
||||
constructor(private $q: IQService, private $timeout: ITimeoutService, private $window: IWindowService) {
|
||||
this.people = peopleData.map((person) => <IPerson>(person));
|
||||
}
|
||||
|
||||
checkVerification(userKey: string): IPromise<IVerificationStatus> {
|
||||
return this.simulateResponse({ passed: false });
|
||||
}
|
||||
|
||||
clearOtpSecret(userKey: string): IPromise<ISuccessResponse> {
|
||||
return this.simulateResponse({ successMessage: 'OTP Secret successfully cleared.' });
|
||||
}
|
||||
|
||||
clearResponses(userKey: string): IPromise<ISuccessResponse> {
|
||||
return this.simulateResponse({ successMessage: 'Security answers successfully cleared.' });
|
||||
}
|
||||
|
||||
customAction(actionName: string, userKey: string): IPromise<ISuccessResponse> {
|
||||
if (actionName === 'Clone User') {
|
||||
return this.simulateResponse({ successMessage: 'User successfully cloned.' });
|
||||
}
|
||||
else if (actionName === 'Merge User') {
|
||||
return this.simulateResponse({ successMessage: 'User successfully merged.' });
|
||||
}
|
||||
else {
|
||||
this.$q.reject('Error! Action name doesn\'t exist.');
|
||||
}
|
||||
}
|
||||
|
||||
deleteUser(userKey: string): IPromise<ISuccessResponse> {
|
||||
return this.simulateResponse({ successMessage: 'User successfully deleted.' });
|
||||
}
|
||||
|
||||
getPerson(userKey: string): IPromise<any> {
|
||||
return this.simulateResponse({
|
||||
'userDisplayName': 'Andrew Astin - aastin - aastin@ad.utopia.netiq.com',
|
||||
'userHistory': [
|
||||
{
|
||||
'timestamp': '2017-12-13T18:36:50Z',
|
||||
'label': 'Help Desk Set Password'
|
||||
},
|
||||
{
|
||||
'timestamp': '2017-12-13T18:47:11Z',
|
||||
'label': 'Help Desk Set Password'
|
||||
},
|
||||
{
|
||||
'timestamp': '2017-12-13T18:50:35Z',
|
||||
'label': 'Setup Password Responses'
|
||||
},
|
||||
{
|
||||
'timestamp': '2017-12-13T19:19:33Z',
|
||||
'label': 'Help Desk Set Password'
|
||||
},
|
||||
{
|
||||
'timestamp': '2017-12-13T19:23:50Z',
|
||||
'label': 'Change Password'
|
||||
},
|
||||
{
|
||||
'timestamp': '2017-12-13T20:47:57Z',
|
||||
'label': 'Clear Responses'
|
||||
},
|
||||
{
|
||||
'timestamp': '2017-12-13T20:48:38Z',
|
||||
'label': 'Setup Password Responses'
|
||||
}
|
||||
],
|
||||
'passwordPolicyRules': {
|
||||
'Policy Enabled': 'True',
|
||||
'Minimum Length': '2',
|
||||
'Maximum Length': '64',
|
||||
'Minimum Upper Case': '0',
|
||||
'Maximum Upper Case': '0',
|
||||
'Minimum Lower Case': '0',
|
||||
'Maximum Lower Case': '0',
|
||||
'Allow Numeric': 'True',
|
||||
'Minimum Numeric': '0',
|
||||
'Maximum Numeric': '0',
|
||||
'Minimum Unique': '0',
|
||||
'Allow First Character Numeric': 'True',
|
||||
'Allow Last Character Numeric': 'True',
|
||||
'Allow Special': 'True',
|
||||
'Minimum Special': '0',
|
||||
'Maximum Special': '0',
|
||||
'Allow First Character Special': 'True',
|
||||
'Allow Last Character Special': 'True',
|
||||
'Maximum Repeat': '0',
|
||||
'Maximum Sequential Repeat': '0',
|
||||
'Change Message': '',
|
||||
'Expiration Interval': '0',
|
||||
'Minimum Lifetime': '0',
|
||||
'Case Sensitive': 'True',
|
||||
'Unique Required': 'False',
|
||||
'Disallowed Values': 'password\ntest',
|
||||
'Disallowed Attributes': 'givenName\ncn\nsn',
|
||||
'Disallow Current': 'True',
|
||||
'Maximum AD Complexity Violations': '2',
|
||||
'Regular Expression Match': '',
|
||||
'Regular Expression No Match': '',
|
||||
'Minimum Alpha': '0',
|
||||
'Maximum Alpha': '0',
|
||||
'Minimum Non-Alpha': '0',
|
||||
'Maximum Non-Alpha': '0',
|
||||
'Enable Word List': 'True',
|
||||
'Minimum Strength': '0',
|
||||
'Maximum Consecutive': '0',
|
||||
'Character Groups Minimum Required': '0',
|
||||
'Character Group Values': '.*[0-9]\n.*[^A-Za-z0-9]\n.*[A-Z]\n.*[a-z]',
|
||||
'Rule_AllowMacroInRegExSetting': 'True'
|
||||
},
|
||||
'passwordRequirements': [
|
||||
'Password is case sensitive.',
|
||||
'Must be at least 2 characters long.',
|
||||
'Must not include any of the following values: password test',
|
||||
'Must not include part of your name or user name.',
|
||||
'Must not include a common word or commonly used sequence of characters.'
|
||||
],
|
||||
'passwordPolicyDN': 'cn=Policy,cn=Password Policies,cn=Security',
|
||||
'passwordPolicyID': 'n/a',
|
||||
'statusData': [
|
||||
{
|
||||
'key': 'Field_Username',
|
||||
'type': 'string',
|
||||
'label': 'User Name',
|
||||
'value': 'aastin'
|
||||
},
|
||||
{
|
||||
'key': 'Field_UserDN',
|
||||
'type': 'string',
|
||||
'label': 'User DN',
|
||||
'value': 'cn=aastin,ou=users,o=novell'
|
||||
},
|
||||
{
|
||||
'key': 'Field_UserEmail',
|
||||
'type': 'string',
|
||||
'label': 'Email',
|
||||
'value': 'aastin@ad.utopia.netiq.com'
|
||||
},
|
||||
{
|
||||
'key': 'Field_UserSMS',
|
||||
'type': 'string',
|
||||
'label': 'SMS',
|
||||
'value': 'n/a'
|
||||
},
|
||||
{
|
||||
'key': 'Field_AccountEnabled',
|
||||
'type': 'string',
|
||||
'label': 'Account Enabled',
|
||||
'value': 'True'
|
||||
},
|
||||
{
|
||||
'key': 'Field_AccountExpired',
|
||||
'type': 'string',
|
||||
'label': 'Account Expired',
|
||||
'value': 'False'
|
||||
},
|
||||
{
|
||||
'key': 'Field_UserGUID',
|
||||
'type': 'string',
|
||||
'label': 'User GUID',
|
||||
'value': 'ae95c9790234624d9848ae95c9790234'
|
||||
},
|
||||
{
|
||||
'key': 'Field_AccountExpirationTime',
|
||||
'type': 'timestamp',
|
||||
'label': 'Account Expiration Time',
|
||||
'value': 'n/a'
|
||||
},
|
||||
{
|
||||
'key': 'Field_LastLoginTime',
|
||||
'type': 'timestamp',
|
||||
'label': 'Last Login Time',
|
||||
'value': '2017-12-13T20:48:33Z'
|
||||
},
|
||||
{
|
||||
'key': 'Field_LastLoginTimeDelta',
|
||||
'type': 'string',
|
||||
'label': 'Last Login Time Delta',
|
||||
'value': '4 days, 22 hours, 49 minutes, 3 seconds'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordExpired',
|
||||
'type': 'string',
|
||||
'label': 'Password Expired',
|
||||
'value': 'False'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordPreExpired',
|
||||
'type': 'string',
|
||||
'label': 'Password Pre-Expired',
|
||||
'value': 'False'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordWithinWarningPeriod',
|
||||
'type': 'string',
|
||||
'label': 'Within Warning Period',
|
||||
'value': 'False'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordSetTime',
|
||||
'type': 'timestamp',
|
||||
'label': 'Password Set Time',
|
||||
'value': '2017-12-13T19:23:49Z'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordSetTimeDelta',
|
||||
'type': 'string',
|
||||
'label': 'Password Set Time Delta',
|
||||
'value': '5 days, 13 minutes, 47 seconds'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordExpirationTime',
|
||||
'type': 'timestamp',
|
||||
'label': 'Password Expiration Time',
|
||||
'value': 'n/a'
|
||||
},
|
||||
{
|
||||
'key': 'Field_PasswordLocked',
|
||||
'type': 'string',
|
||||
'label': 'Password Locked (Intruder Detect)',
|
||||
'value': 'False'
|
||||
},
|
||||
{
|
||||
'key': 'Field_ResponsesStored',
|
||||
'type': 'string',
|
||||
'label': 'Responses Stored',
|
||||
'value': 'True'
|
||||
},
|
||||
{
|
||||
'key': 'Field_ResponsesNeeded',
|
||||
'type': 'string',
|
||||
'label': 'Response Updates are Needed',
|
||||
'value': 'False'
|
||||
},
|
||||
{
|
||||
'key': 'Field_ResponsesTimestamp',
|
||||
'type': 'timestamp',
|
||||
'label': 'Stored Responses Timestamp',
|
||||
'value': '2017-12-13T20:48:37Z'
|
||||
},
|
||||
{
|
||||
'key': 'Field_ResponsesTimestamp',
|
||||
'type': 'timestamp',
|
||||
'label': 'Stored Responses Timestamp',
|
||||
'value': '2017-12-13T20:48:37Z'
|
||||
}
|
||||
],
|
||||
'profileData': [
|
||||
{
|
||||
'key': 'cn',
|
||||
'type': 'string',
|
||||
'label': 'CN',
|
||||
'value': 'aastin'
|
||||
},
|
||||
{
|
||||
'key': 'givenName',
|
||||
'type': 'string',
|
||||
'label': 'First Name',
|
||||
'value': 'Andrew'
|
||||
},
|
||||
{
|
||||
'key': 'sn',
|
||||
'type': 'string',
|
||||
'label': 'Last Name',
|
||||
'value': 'Astin'
|
||||
},
|
||||
{
|
||||
'key': 'mail',
|
||||
'type': 'string',
|
||||
'label': 'Email Address',
|
||||
'value': 'aastin@ad.utopia.netiq.com'
|
||||
},
|
||||
{
|
||||
'key': 'telephoneNumber',
|
||||
'type': 'multiString',
|
||||
'label': 'Telephone Number',
|
||||
'values': [
|
||||
'801-802-0259'
|
||||
]
|
||||
},
|
||||
{
|
||||
'key': 'title',
|
||||
'type': 'string',
|
||||
'label': 'Title',
|
||||
'value': 'Identity Administrator'
|
||||
},
|
||||
{
|
||||
'key': 'ou',
|
||||
'type': 'string',
|
||||
'label': 'Department',
|
||||
'value': 'Information Technology'
|
||||
},
|
||||
{
|
||||
'key': 'businessCategory',
|
||||
'type': 'string',
|
||||
'label': 'Business Category',
|
||||
'value': 'Identity'
|
||||
},
|
||||
{
|
||||
'key': 'company',
|
||||
'type': 'string',
|
||||
'label': 'Company',
|
||||
'value': 'Utopia Corp'
|
||||
},
|
||||
{
|
||||
'key': 'street',
|
||||
'type': 'string',
|
||||
'label': 'Street',
|
||||
'value': '50 Upper 5th'
|
||||
},
|
||||
{
|
||||
'key': 'physicalDeliveryOfficeName',
|
||||
'type': 'string',
|
||||
'label': 'City',
|
||||
'value': 'New York City'
|
||||
},
|
||||
{
|
||||
'key': 'st',
|
||||
'type': 'string',
|
||||
'label': 'State',
|
||||
'value': 'New York'
|
||||
},
|
||||
{
|
||||
'key': 'l',
|
||||
'type': 'string',
|
||||
'label': 'Location',
|
||||
'value': 'Manhattan'
|
||||
},
|
||||
{
|
||||
'key': 'employeeStatus',
|
||||
'type': 'string',
|
||||
'label': 'Employee Status',
|
||||
'value': 'A'
|
||||
},
|
||||
{
|
||||
'key': 'workforceID',
|
||||
'type': 'string',
|
||||
'label': 'Workforce ID',
|
||||
'value': 'E000259'
|
||||
}
|
||||
],
|
||||
'helpdeskResponses': [
|
||||
{
|
||||
'key': 'item_1',
|
||||
'type': 'string',
|
||||
'label': 'In what year were you born?',
|
||||
'value': '1987'
|
||||
},
|
||||
{
|
||||
'key': 'item_2',
|
||||
'type': 'string',
|
||||
'label': 'What is your favorite type of weather?',
|
||||
'value': 'Clear sky with funny-shaped clouds'
|
||||
},
|
||||
{
|
||||
'key': 'item_3',
|
||||
'type': 'string',
|
||||
'label': 'What is your favorite song?',
|
||||
'value': 'Rudolph the Red-Nosed Reindeer'
|
||||
}
|
||||
],
|
||||
'visibleButtons': [
|
||||
'refresh',
|
||||
'back',
|
||||
'changePassword',
|
||||
'unlock',
|
||||
'clearResponses',
|
||||
'clearOtpSecret',
|
||||
'verification',
|
||||
'deleteUser'
|
||||
],
|
||||
'enabledButtons': [
|
||||
'refresh',
|
||||
'back',
|
||||
'changePassword',
|
||||
'unlock',
|
||||
'clearResponses',
|
||||
'clearOtpSecret',
|
||||
'verification',
|
||||
'deleteUser'
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
getPersonCard(userKey: string): IPromise<any> {
|
||||
let self = this;
|
||||
|
||||
let deferred = this.$q.defer();
|
||||
let deferredAbort = this.$q.defer();
|
||||
|
||||
let timeoutPromise = this.$timeout(() => {
|
||||
const person = this.findPerson(userKey);
|
||||
|
||||
if (person) {
|
||||
deferred.resolve(person);
|
||||
}
|
||||
else {
|
||||
deferred.reject(`Person with id: "${userKey}" not found.`);
|
||||
}
|
||||
}, SIMULATED_RESPONSE_TIME);
|
||||
|
||||
// To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
|
||||
deferred.promise['_httpTimeout'] = deferredAbort;
|
||||
deferredAbort.promise.then(() => {
|
||||
self.$timeout.cancel(timeoutPromise);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
getRandomPassword(userKey: string): IPromise<IRandomPasswordResponse> {
|
||||
let randomNumber = Math.floor(Math.random() * Math.floor(100));
|
||||
let passwordSuggestion = 'suggestion' + randomNumber;
|
||||
return this.simulateResponse({ password: passwordSuggestion });
|
||||
}
|
||||
|
||||
getRecentVerifications(): IPromise<IRecentVerifications> {
|
||||
return this.simulateResponse([
|
||||
{
|
||||
timestamp: '2017-12-06T23:19:07Z',
|
||||
profile: 'default',
|
||||
username: 'aastin',
|
||||
method: 'Personal Data'
|
||||
},
|
||||
{
|
||||
timestamp: '2017-12-03T22:11:07Z',
|
||||
profile: 'default',
|
||||
username: 'bjroach',
|
||||
method: 'Personal Data'
|
||||
},
|
||||
{
|
||||
timestamp: '2017-12-02T13:09:07Z',
|
||||
profile: 'default',
|
||||
username: 'rrhoads',
|
||||
method: 'Personal Data'
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
search(query: string): IPromise<SearchResult> {
|
||||
let people = this.people.filter((person: IPerson) => {
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
return person._displayName.toLowerCase().indexOf(query.toLowerCase()) >= 0;
|
||||
});
|
||||
|
||||
const sizeExceeded = (people.length > MAX_RESULTS);
|
||||
if (sizeExceeded) {
|
||||
people = people.slice(MAX_RESULTS);
|
||||
}
|
||||
|
||||
let result = new SearchResult({sizeExceeded: sizeExceeded, searchResults: people});
|
||||
return this.simulateResponse(result);
|
||||
}
|
||||
|
||||
sendVerificationToken(userKey: string, choice: string): IPromise<IVerificationTokenResponse> {
|
||||
return this.simulateResponse({ destination: 'bcarrolj@paypal.com' });
|
||||
}
|
||||
|
||||
setPassword(userKey: string, random: boolean, password?: string): IPromise<ISuccessResponse> {
|
||||
if (random) {
|
||||
return this.simulateResponse({ successMessage: 'Random password successfully set!' });
|
||||
}
|
||||
else {
|
||||
return this.simulateResponse({ successMessage: 'Password successfully set!' });
|
||||
}
|
||||
}
|
||||
|
||||
private simulateResponse<T>(data: T): IPromise<T> {
|
||||
let self = this;
|
||||
|
||||
let deferred = this.$q.defer();
|
||||
let deferredAbort = this.$q.defer();
|
||||
|
||||
let timeoutPromise = this.$timeout(() => {
|
||||
deferred.resolve(data);
|
||||
}, SIMULATED_RESPONSE_TIME);
|
||||
|
||||
// To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
|
||||
deferred.promise['_httpTimeout'] = deferredAbort;
|
||||
deferredAbort.promise.then(() => {
|
||||
self.$timeout.cancel(timeoutPromise);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise as IPromise<T>;
|
||||
}
|
||||
|
||||
unlockIntruder(userKey: string): IPromise<ISuccessResponse> {
|
||||
return this.simulateResponse({ successMessage: 'Unlock successful.' });
|
||||
}
|
||||
|
||||
validateVerificationData(userKey: string, data: any, method: string): IPromise<IVerificationStatus> {
|
||||
return this.simulateResponse({ passed: true });
|
||||
}
|
||||
|
||||
private findPerson(id: string): IPerson {
|
||||
const people = this.people.filter((person: IPerson) => person.userKey == id);
|
||||
|
||||
if (people.length) {
|
||||
return people[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
get showStrengthMeter(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
|
||||
export const getRecentVerifications_response = {
|
||||
"error": false,
|
||||
"errorCode": 0,
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"timestamp": "2018-02-22T15:14:39Z",
|
||||
"profile": "default",
|
||||
"username": "bjenner",
|
||||
"method": "Personal Data"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import HelpDeskService, {IRecentVerifications} from './helpdesk.service';
|
||||
import {IHttpBackendService, IHttpService, ILogService, IQService, IWindowService, module} from 'angular';
|
||||
import ObjectService from './object.service';
|
||||
import {default as PwmService, IPwmService} from './pwm.service';
|
||||
import LocalStorageService from './local-storage.service';
|
||||
import {getRecentVerifications_response} from './helpdesk.service.test-data';
|
||||
|
||||
describe('In helpdesk.service.test.ts', () => {
|
||||
beforeEach(() => {
|
||||
module('app', []);
|
||||
});
|
||||
|
||||
let localStorageService: LocalStorageService;
|
||||
let objectService: ObjectService;
|
||||
let pwmService: IPwmService;
|
||||
let helpDeskService: HelpDeskService;
|
||||
let $httpBackend: IHttpBackendService;
|
||||
let $window: IWindowService;
|
||||
|
||||
beforeEach(inject((
|
||||
$http: IHttpService,
|
||||
$log: ILogService,
|
||||
$q: IQService,
|
||||
_$window_: IWindowService,
|
||||
_$httpBackend_: IHttpBackendService
|
||||
) => {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$window = _$window_;
|
||||
|
||||
localStorageService = new LocalStorageService($log, $window);
|
||||
objectService = new ObjectService();
|
||||
pwmService = new PwmService($http, $log, $q, $window);
|
||||
helpDeskService = new HelpDeskService($log, $q, localStorageService, objectService, pwmService, $window);
|
||||
}));
|
||||
|
||||
it('getRecentVerifications returns the right record data', (done: DoneFn) => {
|
||||
(pwmService as PwmService).PWM_GLOBAL = { pwmFormID: 'fake-pwm-form-id' };
|
||||
|
||||
$httpBackend.whenPOST( '/context.html?processAction=showVerifications&pwmFormID=fake-pwm-form-id')
|
||||
.respond(getRecentVerifications_response);
|
||||
|
||||
helpDeskService.getRecentVerifications()
|
||||
.then((recentVerifications: IRecentVerifications) => {
|
||||
expect(recentVerifications.length).toBe(1);
|
||||
|
||||
expect(recentVerifications[0].username).toBe('bjenner');
|
||||
expect(recentVerifications[0].profile).toBe('default');
|
||||
expect(recentVerifications[0].timestamp).toBe('2018-02-22T15:14:39Z');
|
||||
expect(recentVerifications[0].method).toBe('Personal Data');
|
||||
|
||||
done();
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
done.fail(error);
|
||||
});
|
||||
|
||||
// This causes the $http service to finally resolve the response:
|
||||
$httpBackend.flush();
|
||||
});
|
||||
});
|
|
@ -1,363 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IPwmService} from './pwm.service';
|
||||
import {ILogService, IPromise, IQService, IWindowService} from 'angular';
|
||||
import LocalStorageService from './local-storage.service';
|
||||
import ObjectService from './object.service';
|
||||
import SearchResult from '../models/search-result.model';
|
||||
import {IQuery} from './people.service';
|
||||
|
||||
const VERIFICATION_PROCESS_ACTIONS = {
|
||||
ATTRIBUTES: 'validateAttributes',
|
||||
TOKEN: 'verifyVerificationToken',
|
||||
OTP: 'validateOtpCode'
|
||||
};
|
||||
|
||||
const DEFAULT_SHOW_STRENGTH_METER = false;
|
||||
|
||||
export interface IHelpDeskService {
|
||||
checkVerification(userKey: string): IPromise<IVerificationStatus>;
|
||||
clearOtpSecret(userKey: string): IPromise<ISuccessResponse>;
|
||||
clearResponses(userKey: string): IPromise<ISuccessResponse>;
|
||||
customAction(actionName: string, userKey: string): IPromise<ISuccessResponse>;
|
||||
deleteUser(userKey: string): IPromise<ISuccessResponse>;
|
||||
getPerson(userKey: string): IPromise<any>;
|
||||
getPersonCard(userKey: string): IPromise<any>;
|
||||
getRandomPassword(userKey: string): IPromise<IRandomPasswordResponse>;
|
||||
getRecentVerifications(): IPromise<IRecentVerifications>;
|
||||
search(query: string): IPromise<SearchResult>;
|
||||
advancedSearch(queries: IQuery[]): IPromise<SearchResult>;
|
||||
sendVerificationToken(userKey: string, choice: string): IPromise<IVerificationTokenResponse>;
|
||||
setPassword(userKey: string, random: boolean, password?: string): IPromise<ISuccessResponse>;
|
||||
unlockIntruder(userKey: string): IPromise<ISuccessResponse>;
|
||||
validateVerificationData(userKey: string, formData: any, method: any): IPromise<IVerificationStatus>;
|
||||
showStrengthMeter: boolean;
|
||||
}
|
||||
|
||||
export type IRecentVerifications = IRecentVerification[];
|
||||
|
||||
type IRecentVerification = {
|
||||
profile: string,
|
||||
username: string,
|
||||
timestamp: string,
|
||||
method: string
|
||||
};
|
||||
|
||||
export interface IRandomPasswordResponse {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ISuccessResponse {
|
||||
successMessage: string;
|
||||
}
|
||||
|
||||
interface IValidationStatus extends IVerificationStatus {
|
||||
verificationState: string;
|
||||
}
|
||||
|
||||
export interface IVerificationOptions {
|
||||
verificationMethods: {
|
||||
optional: string[];
|
||||
required: string[];
|
||||
};
|
||||
verificationForm: [{
|
||||
name: string;
|
||||
label: string;
|
||||
}];
|
||||
tokenDestinations: [{
|
||||
id: string;
|
||||
display: string;
|
||||
type: string;
|
||||
}];
|
||||
}
|
||||
|
||||
export interface IVerificationStatus {
|
||||
passed: boolean;
|
||||
verificationOptions: IVerificationOptions;
|
||||
}
|
||||
|
||||
export interface IVerificationTokenResponse {
|
||||
destination: string;
|
||||
}
|
||||
|
||||
export default class HelpDeskService implements IHelpDeskService {
|
||||
PWM_GLOBAL: any;
|
||||
|
||||
static $inject = [ '$log', '$q', 'LocalStorageService', 'ObjectService', 'PwmService', '$window' ];
|
||||
constructor(private $log: ILogService,
|
||||
private $q: IQService,
|
||||
private localStorageService: LocalStorageService,
|
||||
private objectService: ObjectService,
|
||||
private pwmService: IPwmService,
|
||||
$window: IWindowService) {
|
||||
if ($window['PWM_GLOBAL']) {
|
||||
this.PWM_GLOBAL = $window['PWM_GLOBAL'];
|
||||
}
|
||||
else {
|
||||
this.$log.warn('PWM_GLOBAL is not defined on window');
|
||||
}
|
||||
}
|
||||
|
||||
checkVerification(userKey: string): IPromise<IVerificationStatus> {
|
||||
let url: string = this.pwmService.getServerUrl('checkVerification');
|
||||
let data = {
|
||||
userKey: userKey,
|
||||
verificationState: undefined
|
||||
};
|
||||
|
||||
const verificationState = this.localStorageService.getItem(this.localStorageService.keys.VERIFICATION_STATE);
|
||||
if (verificationState != null) {
|
||||
data.verificationState = verificationState;
|
||||
}
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: any) => {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
|
||||
clearOtpSecret(userKey: string): IPromise<ISuccessResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('clearOtpSecret');
|
||||
let data: any = { userKey: userKey };
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: ISuccessResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
clearResponses(userKey: string): IPromise<ISuccessResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('clearResponses');
|
||||
url += `&userKey=${userKey}`;
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, {})
|
||||
.then((result: ISuccessResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
customAction(actionName: string, userKey: string): IPromise<ISuccessResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('executeAction');
|
||||
url += `&name=${actionName}`;
|
||||
let data: any = { userKey: userKey };
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: ISuccessResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
deleteUser(userKey: string): IPromise<ISuccessResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('deleteUser');
|
||||
url += `&userKey=${userKey}`;
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, {})
|
||||
.then((result: ISuccessResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
getPerson(userKey: string): IPromise<any> {
|
||||
let url: string = this.pwmService.getServerUrl('detail');
|
||||
url += `&userKey=${userKey}`;
|
||||
|
||||
const verificationState = this.localStorageService.getItem(this.localStorageService.keys.VERIFICATION_STATE);
|
||||
if (verificationState != null) {
|
||||
url += `&verificationState=${verificationState}`;
|
||||
}
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, {})
|
||||
.then((result: any) => {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
|
||||
getPersonCard(userKey: string): IPromise<any> {
|
||||
let url = this.pwmService.getServerUrl('card');
|
||||
url += `&userKey=${userKey}`;
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, {})
|
||||
.then((result: any) => {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
|
||||
getRandomPassword(userKey: string): IPromise<IRandomPasswordResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('randomPassword');
|
||||
let data = {
|
||||
username: userKey,
|
||||
strength: 0
|
||||
};
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: { data: IRandomPasswordResponse }) => {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
|
||||
getRecentVerifications(): IPromise<IRecentVerifications> {
|
||||
let url: string = this.pwmService.getServerUrl('showVerifications');
|
||||
const data = {
|
||||
verificationState: undefined
|
||||
};
|
||||
|
||||
const verificationState = this.localStorageService.getItem(this.localStorageService.keys.VERIFICATION_STATE);
|
||||
if (verificationState != null) {
|
||||
data.verificationState = verificationState;
|
||||
}
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: any) => {
|
||||
return result.data.records;
|
||||
});
|
||||
}
|
||||
|
||||
search(query: string): IPromise<SearchResult> {
|
||||
let formID: string = encodeURIComponent('&pwmFormID=' + this.PWM_GLOBAL['pwmFormID']);
|
||||
let url: string = this.pwmService.getServerUrl('search')
|
||||
+ '&pwmFormID=' + this.PWM_GLOBAL['pwmFormID'];
|
||||
|
||||
let data = {
|
||||
mode: 'simple',
|
||||
username: query,
|
||||
pwmFormID: formID
|
||||
};
|
||||
return this.pwmService
|
||||
.httpRequest(url, {
|
||||
data: data,
|
||||
preventCache: true
|
||||
})
|
||||
.then((result: any) => {
|
||||
let receivedData: any = result.data;
|
||||
let searchResult: SearchResult = new SearchResult(receivedData);
|
||||
return searchResult;
|
||||
});
|
||||
}
|
||||
|
||||
advancedSearch(queries: IQuery[]): IPromise<SearchResult> {
|
||||
let formID: string = encodeURIComponent('&pwmFormID=' + this.PWM_GLOBAL['pwmFormID']);
|
||||
let url: string = this.pwmService.getServerUrl('search')
|
||||
+ '&pwmFormID=' + this.PWM_GLOBAL['pwmFormID'];
|
||||
|
||||
let data = {
|
||||
mode: 'advanced',
|
||||
pwmFormID: formID,
|
||||
searchValues: queries
|
||||
};
|
||||
return this.pwmService
|
||||
.httpRequest(url, {
|
||||
data: data,
|
||||
preventCache: true
|
||||
})
|
||||
.then((result: any) => {
|
||||
let receivedData: any = result.data;
|
||||
let searchResult: SearchResult = new SearchResult(receivedData);
|
||||
return searchResult;
|
||||
});
|
||||
}
|
||||
|
||||
sendVerificationToken(userKey: string, destinationID: string): IPromise<IVerificationTokenResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('sendVerificationToken');
|
||||
let data: any = {
|
||||
userKey: userKey,
|
||||
id: destinationID
|
||||
};
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: IVerificationTokenResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
setPassword(userKey: string, random: boolean, password?: string): IPromise<ISuccessResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('setPassword');
|
||||
let data: any = { username: userKey };
|
||||
if (random) {
|
||||
data.random = true;
|
||||
}
|
||||
else {
|
||||
data.password = password;
|
||||
}
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: ISuccessResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
unlockIntruder(userKey: string): IPromise<ISuccessResponse> {
|
||||
let url: string = this.pwmService.getServerUrl('unlockIntruder');
|
||||
url += `&userKey=${userKey}`;
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, {})
|
||||
.then((result: ISuccessResponse) => {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
validateVerificationData(userKey: string, data: any, method: string): IPromise<IVerificationStatus> {
|
||||
let processAction = VERIFICATION_PROCESS_ACTIONS[method];
|
||||
let url: string = this.pwmService.getServerUrl(processAction);
|
||||
let content = {
|
||||
userKey: userKey,
|
||||
verificationState: undefined
|
||||
};
|
||||
|
||||
const verificationState = this.localStorageService.getItem(this.localStorageService.keys.VERIFICATION_STATE);
|
||||
if (verificationState != null) {
|
||||
content.verificationState = verificationState;
|
||||
}
|
||||
|
||||
this.objectService.assign(data, content);
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, { data: data })
|
||||
.then((result: any) => {
|
||||
const validationStatus: IValidationStatus = result.data;
|
||||
|
||||
this.localStorageService.setItem(
|
||||
this.localStorageService.keys.VERIFICATION_STATE,
|
||||
validationStatus.verificationState
|
||||
);
|
||||
return validationStatus;
|
||||
});
|
||||
}
|
||||
|
||||
get showStrengthMeter(): boolean {
|
||||
if (this.PWM_GLOBAL) {
|
||||
return this.PWM_GLOBAL['setting-showStrengthMeter'] || DEFAULT_SHOW_STRENGTH_METER;
|
||||
}
|
||||
|
||||
return DEFAULT_SHOW_STRENGTH_METER;
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { ILogService, IWindowService } from 'angular';
|
||||
|
||||
const PWM_PREFIX = 'PWM_';
|
||||
const KEYS = {
|
||||
SEARCH_TEXT: 'searchText',
|
||||
HELPDESK_SEARCH_TEXT: 'helpdeskSearchText',
|
||||
SEARCH_VIEW: 'searchView',
|
||||
HELPDESK_SEARCH_VIEW: 'helpdeskSearchView',
|
||||
VERIFICATION_STATE: 'verificationState'
|
||||
};
|
||||
|
||||
export default class LocalStorageService {
|
||||
keys: any = KEYS;
|
||||
private localStorageEnabled = true;
|
||||
|
||||
static $inject = [ '$log', '$window' ];
|
||||
constructor($log: ILogService, private $window: IWindowService) {
|
||||
if (!$window.sessionStorage.getItem) {
|
||||
this.localStorageEnabled = false;
|
||||
$log.info('Local Storage API not enabled. Using NOOP implementation.');
|
||||
}
|
||||
}
|
||||
|
||||
getItem(key: string): any {
|
||||
if (this.localStorageEnabled) {
|
||||
return this.$window.sessionStorage[this.prepKey(key)];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
setItem(key: string, value: any): void {
|
||||
if (this.localStorageEnabled && value) {
|
||||
this.$window.sessionStorage[this.prepKey(key)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
removeItem(key: string): any {
|
||||
if (this.localStorageEnabled) {
|
||||
return this.$window.sessionStorage.removeItem(this.prepKey(key));
|
||||
}
|
||||
}
|
||||
|
||||
private prepKey(key: string) {
|
||||
return PWM_PREFIX + key;
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export default class ObjectService {
|
||||
// ES5 implementation of Object.assign
|
||||
// Source from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
assign(target, varArgs) { // .length of function is 2
|
||||
'use strict';
|
||||
if (target == null) { // TypeError if undefined or null
|
||||
throw new TypeError('Cannot convert undefined or null to object');
|
||||
}
|
||||
|
||||
const to = Object(target);
|
||||
|
||||
for (let index = 1; index < arguments.length; index++) {
|
||||
const nextSource = arguments[index];
|
||||
|
||||
if (nextSource != null) { // Skip over if undefined or null
|
||||
for (let nextKey in nextSource) {
|
||||
// Avoid bugs when hasOwnProperty is shadowed
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const SIMULATED_RESPONSE_TIME = 100;
|
||||
const STRENGTH_PER_PASSWORD_CHARACTER = 10;
|
||||
const MAX_STRENGTH = 100;
|
||||
const STRENGTH_REQUIRED = 40;
|
||||
|
||||
import {IPasswordService, IValidatePasswordData} from './password.service';
|
||||
import {IPromise, IQService, ITimeoutService} from 'angular';
|
||||
|
||||
export default class PasswordService implements IPasswordService {
|
||||
|
||||
static $inject = ['$q', '$timeout'];
|
||||
constructor(private $q: IQService, private $timeout: ITimeoutService) {
|
||||
}
|
||||
|
||||
validatePassword(password1: string, password2: string, userKey: string): IPromise<IValidatePasswordData> {
|
||||
let strength = Math.min((password1.length * STRENGTH_PER_PASSWORD_CHARACTER), MAX_STRENGTH);
|
||||
let match = (password1 === password2);
|
||||
let message: string = null;
|
||||
let passed = (strength >= STRENGTH_REQUIRED);
|
||||
|
||||
if (!password1) {
|
||||
message = 'Please type your new password';
|
||||
}
|
||||
if (!passed) {
|
||||
message = 'New password is too short';
|
||||
}
|
||||
else if (!password2) {
|
||||
message = 'Password meets requirements, please type confirmation password';
|
||||
}
|
||||
else if (!match) {
|
||||
message = 'Passwords do not match';
|
||||
}
|
||||
else {
|
||||
message = 'New password accepted, please click change password';
|
||||
}
|
||||
|
||||
let matchStatus: string = null;
|
||||
if (!password1) {
|
||||
matchStatus = 'EMPTY';
|
||||
}
|
||||
else {
|
||||
matchStatus = match ? 'MATCH' : 'NO_MATCH';
|
||||
}
|
||||
|
||||
let data = {
|
||||
version: 2,
|
||||
strength: strength,
|
||||
match: matchStatus,
|
||||
message: message,
|
||||
passed: passed,
|
||||
errorCode: 0
|
||||
};
|
||||
|
||||
let self = this;
|
||||
|
||||
let deferred = this.$q.defer();
|
||||
let deferredAbort = this.$q.defer();
|
||||
|
||||
let timeoutPromise = this.$timeout(() => {
|
||||
deferred.resolve(data);
|
||||
}, SIMULATED_RESPONSE_TIME);
|
||||
|
||||
// To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
|
||||
deferred.promise['_httpTimeout'] = deferredAbort;
|
||||
deferredAbort.promise.then(() => {
|
||||
self.$timeout.cancel(timeoutPromise);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise as IPromise<IValidatePasswordData>;
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {ILogService, IPromise, IQService, ITimeoutService, IWindowService} from 'angular';
|
||||
import {IPwmService} from './pwm.service';
|
||||
|
||||
export interface IPasswordService {
|
||||
validatePassword(password1: string, password2: string, userKey: string): IPromise<IValidatePasswordData>;
|
||||
}
|
||||
|
||||
export interface IValidatePasswordData {
|
||||
version: number;
|
||||
strength: number;
|
||||
match: string;
|
||||
message: string;
|
||||
passed: boolean;
|
||||
errorCode: number;
|
||||
}
|
||||
|
||||
export default class PasswordService implements IPasswordService {
|
||||
PWM_MAIN: any;
|
||||
|
||||
static $inject = ['$log', '$q', '$timeout', '$window', 'PwmService', 'translateFilter'];
|
||||
|
||||
constructor(private $log: ILogService,
|
||||
private $q: IQService,
|
||||
private $timeout: ITimeoutService,
|
||||
private $window: IWindowService,
|
||||
private pwmService: IPwmService,
|
||||
private translateFilter: (id: string) => string) {
|
||||
if ($window['PWM_MAIN']) {
|
||||
this.PWM_MAIN = $window['PWM_MAIN'];
|
||||
}
|
||||
else {
|
||||
this.$log.warn('PWM_MAIN is not defined on window');
|
||||
}
|
||||
}
|
||||
|
||||
validatePassword(password1: string, password2: string, userKey: string): IPromise<IValidatePasswordData> {
|
||||
let data = {
|
||||
password1: password1,
|
||||
password2: password2,
|
||||
username: userKey
|
||||
};
|
||||
let url: string = this.pwmService.getServerUrl('checkPassword');
|
||||
|
||||
return this.pwmService
|
||||
.httpRequest(url, {data: data})
|
||||
.then((result: { data: IValidatePasswordData }) => {
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,260 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPromise, IQService, ITimeoutService } from 'angular';
|
||||
import { IPerson } from '../models/person.model';
|
||||
import {IPeopleService, IQuery} from './people.service';
|
||||
import IOrgChartData from '../models/orgchart-data.model';
|
||||
import SearchResult from '../models/search-result.model';
|
||||
|
||||
const peopleData = require('./people.data.json');
|
||||
|
||||
const MAX_RESULTS = 10;
|
||||
const SIMULATED_RESPONSE_TIME = 0;
|
||||
|
||||
export default class PeopleService implements IPeopleService {
|
||||
private people: IPerson[];
|
||||
|
||||
static $inject = ['$q', '$timeout' ];
|
||||
constructor(private $q: IQService, private $timeout: ITimeoutService) {
|
||||
this.people = peopleData.map((person) => <IPerson>(person));
|
||||
|
||||
// Create directReports detail (instead of managing this in people.data.json
|
||||
this.people.forEach((person: IPerson) => {
|
||||
const directReports = this.findDirectReports(person.userKey);
|
||||
|
||||
if (!directReports.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
person.detail.directReports = {
|
||||
name: 'directReports',
|
||||
label: 'Direct Reports',
|
||||
type: 'userDN',
|
||||
userReferences: directReports
|
||||
.map((directReport: IPerson) => {
|
||||
return {
|
||||
userKey: directReport.userKey,
|
||||
displayName: directReport._displayName
|
||||
};
|
||||
})
|
||||
};
|
||||
}, this);
|
||||
}
|
||||
|
||||
advancedSearch(queries: IQuery[]): IPromise<SearchResult> {
|
||||
let self = this;
|
||||
|
||||
let deferred = this.$q.defer();
|
||||
let deferredAbort = this.$q.defer();
|
||||
|
||||
let timeoutPromise = this.$timeout(() => {
|
||||
|
||||
let people = this.getAdvancedSearchResults(queries);
|
||||
const sizeExceeded = (people.length > MAX_RESULTS);
|
||||
if (sizeExceeded) {
|
||||
people = people.slice(MAX_RESULTS);
|
||||
}
|
||||
|
||||
deferred.resolve(new SearchResult({sizeExceeded: sizeExceeded, searchResults: people}));
|
||||
}, SIMULATED_RESPONSE_TIME * 6);
|
||||
|
||||
// To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
|
||||
deferred.promise['_httpTimeout'] = deferredAbort;
|
||||
deferredAbort.promise.then(() => {
|
||||
self.$timeout.cancel(timeoutPromise);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise as IPromise<SearchResult>;
|
||||
}
|
||||
|
||||
autoComplete(query: string): IPromise<IPerson[]> {
|
||||
return this.search(query)
|
||||
.then((searchResult: SearchResult) => {
|
||||
let people = searchResult.people;
|
||||
// Alphabetize results by _displayName
|
||||
people = people.sort((person1, person2) => person1._displayName.localeCompare(person2._displayName));
|
||||
|
||||
if (people && people.length > 10) {
|
||||
return people.slice(0, 10);
|
||||
}
|
||||
|
||||
return people;
|
||||
});
|
||||
}
|
||||
|
||||
getDirectReports(id: string): angular.IPromise<IPerson[]> {
|
||||
const people = this.findDirectReports(id);
|
||||
|
||||
return this.$q.resolve(people);
|
||||
}
|
||||
|
||||
getManagementChain(id: string, managementChainLimit): IPromise<IPerson[]> {
|
||||
let person = this.findPerson(id);
|
||||
|
||||
if (person) {
|
||||
const managementChain: IPerson[] = [];
|
||||
|
||||
while (person = this.findManager(person)) {
|
||||
managementChain.push(person);
|
||||
}
|
||||
|
||||
return this.$q.resolve(managementChain);
|
||||
}
|
||||
|
||||
return this.$q.reject(`Person with id: "${id}" not found.`);
|
||||
}
|
||||
|
||||
getOrgChartData(personId: string): angular.IPromise<IOrgChartData> {
|
||||
if (!personId) {
|
||||
personId = '9';
|
||||
}
|
||||
|
||||
const self = this.findPerson(personId);
|
||||
|
||||
const orgChartData: IOrgChartData = {
|
||||
manager: this.findManager(self),
|
||||
children: this.findDirectReports(personId),
|
||||
self: self,
|
||||
assistant: this.findAssistant(self)
|
||||
};
|
||||
|
||||
return this.$q.resolve(orgChartData);
|
||||
}
|
||||
|
||||
getNumberOfDirectReports(personId: string): IPromise<number> {
|
||||
return this.getDirectReports(personId)
|
||||
.then((directReports: IPerson[]) => {
|
||||
return directReports.length;
|
||||
});
|
||||
}
|
||||
|
||||
getPerson(id: string): IPromise<IPerson> {
|
||||
let self = this;
|
||||
|
||||
let deferred = this.$q.defer();
|
||||
let deferredAbort = this.$q.defer();
|
||||
|
||||
let timeoutPromise = this.$timeout(() => {
|
||||
const person = this.findPerson(id);
|
||||
|
||||
if (person) {
|
||||
deferred.resolve(person);
|
||||
}
|
||||
else {
|
||||
deferred.reject(`Person with id: "${id}" not found.`);
|
||||
}
|
||||
}, SIMULATED_RESPONSE_TIME);
|
||||
|
||||
// To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
|
||||
deferred.promise['_httpTimeout'] = deferredAbort;
|
||||
deferredAbort.promise.then(() => {
|
||||
self.$timeout.cancel(timeoutPromise);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
search(query: string): angular.IPromise<SearchResult> {
|
||||
let self = this;
|
||||
|
||||
let deferred = this.$q.defer();
|
||||
let deferredAbort = this.$q.defer();
|
||||
|
||||
let timeoutPromise = this.$timeout(() => {
|
||||
let people = this.people.filter((person: IPerson) => {
|
||||
if (!query) {
|
||||
return false;
|
||||
}
|
||||
return person._displayName.toLowerCase().indexOf(query.toLowerCase()) >= 0;
|
||||
});
|
||||
|
||||
const sizeExceeded = (people.length > MAX_RESULTS);
|
||||
if (sizeExceeded) {
|
||||
people = people.slice(MAX_RESULTS);
|
||||
}
|
||||
|
||||
deferred.resolve(new SearchResult({sizeExceeded: sizeExceeded, searchResults: people}));
|
||||
}, SIMULATED_RESPONSE_TIME * 6);
|
||||
|
||||
// To simulate an abortable promise, edit SIMULATED_RESPONSE_TIME
|
||||
deferred.promise['_httpTimeout'] = deferredAbort;
|
||||
deferredAbort.promise.then(() => {
|
||||
self.$timeout.cancel(timeoutPromise);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
return deferred.promise as IPromise<SearchResult>;
|
||||
}
|
||||
|
||||
private findDirectReports(id: string): IPerson[] {
|
||||
return this.people.filter((person: IPerson) => person.detail['manager']['userReferences'][0].userKey == id);
|
||||
}
|
||||
|
||||
private findAssistant(person: IPerson): IPerson {
|
||||
if (!('assistant' in person.detail)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.findPerson(person.detail['assistant']['userReferences'][0].userKey);
|
||||
}
|
||||
|
||||
private findManager(person: IPerson): IPerson {
|
||||
return this.findPerson(person.detail['manager']['userReferences'][0].userKey);
|
||||
}
|
||||
|
||||
private findPerson(id: string): IPerson {
|
||||
const people = this.people.filter((person: IPerson) => person.userKey == id);
|
||||
|
||||
if (people.length) {
|
||||
return people[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private getAdvancedSearchResults(queries: IQuery[]): IPerson[] {
|
||||
let people = queries.length ? this.people : [];
|
||||
|
||||
queries.forEach((query: IQuery) => {
|
||||
people = people.filter((person: IPerson) => {
|
||||
if (!query.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let property = person[query.name];
|
||||
|
||||
if (!property) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof property === 'object' || typeof property === 'number') {
|
||||
property = JSON.stringify(property);
|
||||
}
|
||||
|
||||
return property.toLowerCase().indexOf(query.value.toLowerCase()) >= 0;
|
||||
});
|
||||
});
|
||||
|
||||
return people;
|
||||
}
|
||||
}
|
|
@ -1,291 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IDeferred, IHttpService, ILogService, IPromise, IQService, IWindowService} from 'angular';
|
||||
import {IPerson} from '../models/person.model';
|
||||
import IPwmService from './pwm.service';
|
||||
import IOrgChartData from '../models/orgchart-data.model';
|
||||
import SearchResult from '../models/search-result.model';
|
||||
import {IPeopleSearchConfigService} from './peoplesearch-config.service';
|
||||
|
||||
export interface IQuery {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface IPeopleService {
|
||||
advancedSearch(queries: IQuery[]): IPromise<SearchResult>;
|
||||
|
||||
autoComplete(query: string): IPromise<IPerson[]>;
|
||||
|
||||
getDirectReports(personId: string): IPromise<IPerson[]>;
|
||||
|
||||
getNumberOfDirectReports(personId: string): IPromise<number>;
|
||||
|
||||
getManagementChain(id: string, managementChainLimit): IPromise<IPerson[]>;
|
||||
|
||||
getOrgChartData(personId: string, skipChildren: boolean): IPromise<IOrgChartData>;
|
||||
|
||||
getPerson(id: string): IPromise<IPerson>;
|
||||
|
||||
getTeamEmails(id: string, depth: number): IPromise<string[]>;
|
||||
|
||||
search(query: string): IPromise<SearchResult>;
|
||||
}
|
||||
|
||||
export default class PeopleService implements IPeopleService {
|
||||
PWM_GLOBAL: any;
|
||||
|
||||
static $inject = ['$http', '$log', '$q', 'ConfigService', 'PwmService', '$window'];
|
||||
|
||||
constructor(private $http: IHttpService,
|
||||
private $log: ILogService,
|
||||
private $q: IQService,
|
||||
private configService: IPeopleSearchConfigService,
|
||||
private pwmService: IPwmService,
|
||||
$window: IWindowService) {
|
||||
if ($window['PWM_GLOBAL']) {
|
||||
this.PWM_GLOBAL = $window['PWM_GLOBAL'];
|
||||
}
|
||||
else {
|
||||
this.$log.warn('PWM_GLOBAL is not defined on window');
|
||||
}
|
||||
}
|
||||
|
||||
advancedSearch(queries: IQuery[]): IPromise<SearchResult> {
|
||||
// Deferred object used for aborting requests. See promise.service.ts for more information
|
||||
let httpTimeout = this.$q.defer();
|
||||
|
||||
let formID: string = encodeURIComponent('&pwmFormID=' + this.PWM_GLOBAL['pwmFormID']);
|
||||
let url: string = this.pwmService.getServerUrl('search')
|
||||
+ '&pwmFormID=' + this.PWM_GLOBAL['pwmFormID'];
|
||||
let request = this.$http
|
||||
.post(url, {
|
||||
mode: 'advanced',
|
||||
pwmFormID: formID,
|
||||
searchValues: queries
|
||||
}, {
|
||||
cache: true,
|
||||
timeout: httpTimeout.promise,
|
||||
headers: {'Content-Type': 'multipart/form-data'},
|
||||
});
|
||||
|
||||
let promise = request.then(
|
||||
(response) => {
|
||||
if (response.data['error']) {
|
||||
return this.handlePwmError(response);
|
||||
}
|
||||
|
||||
let receivedData: any = response.data['data'];
|
||||
let searchResult: SearchResult = new SearchResult(receivedData);
|
||||
|
||||
return searchResult;
|
||||
},
|
||||
this.handleHttpError.bind(this));
|
||||
|
||||
promise['_httpTimeout'] = httpTimeout;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
autoComplete(query: string): IPromise<IPerson[]> {
|
||||
return this.search(query, {'includeDisplayName': true})
|
||||
.then((searchResult: SearchResult) => {
|
||||
let people = searchResult.people;
|
||||
|
||||
if (people && people.length > 10) {
|
||||
return people.slice(0, 10);
|
||||
}
|
||||
|
||||
return people;
|
||||
});
|
||||
}
|
||||
|
||||
getDirectReports(id: string): IPromise<IPerson[]> {
|
||||
return this.getOrgChartData(id, false).then((orgChartData: IOrgChartData) => {
|
||||
let people: IPerson[] = [];
|
||||
|
||||
for (let directReport of orgChartData.children) {
|
||||
let person: IPerson = <IPerson>(directReport);
|
||||
people.push(person);
|
||||
}
|
||||
|
||||
return people;
|
||||
});
|
||||
}
|
||||
|
||||
getNumberOfDirectReports(id: string): IPromise<number> {
|
||||
return this.getDirectReports(id).then((people: IPerson[]) => {
|
||||
return people.length;
|
||||
});
|
||||
}
|
||||
|
||||
getManagementChain(id: string, managementChainLimit): IPromise<IPerson[]> {
|
||||
let people: IPerson[] = [];
|
||||
return this.getManagerRecursive(id, people, managementChainLimit);
|
||||
}
|
||||
|
||||
private getManagerRecursive(id: string, people: IPerson[], managementChainLimit): IPromise<IPerson[]> {
|
||||
return this.getOrgChartData(id, true)
|
||||
.then((orgChartData: IOrgChartData) => {
|
||||
if (orgChartData.manager && people.length < managementChainLimit) {
|
||||
people.push(orgChartData.manager);
|
||||
|
||||
return this.getManagerRecursive(orgChartData.manager.userKey, people, managementChainLimit);
|
||||
}
|
||||
|
||||
return people;
|
||||
});
|
||||
}
|
||||
|
||||
getOrgChartData(personId: string, noChildren: boolean): angular.IPromise<IOrgChartData> {
|
||||
return this.$http
|
||||
.get(this.pwmService.getServerUrl('orgChartData'), {
|
||||
cache: true,
|
||||
params: {
|
||||
userKey: personId,
|
||||
noChildren: noChildren
|
||||
}
|
||||
})
|
||||
.then(
|
||||
(response) => {
|
||||
if (response.data['error']) {
|
||||
return this.handlePwmError(response);
|
||||
}
|
||||
|
||||
let responseData = response.data['data'];
|
||||
|
||||
let manager: IPerson;
|
||||
let assistant: IPerson;
|
||||
|
||||
if ('parent' in responseData) {
|
||||
manager = <IPerson>(responseData['parent']);
|
||||
}
|
||||
if ('assistant' in responseData) {
|
||||
assistant = <IPerson>(responseData['assistant']);
|
||||
}
|
||||
|
||||
const children = responseData['children'].map((child: any) => <IPerson>(child));
|
||||
const self = <IPerson>(responseData['self']);
|
||||
|
||||
return {
|
||||
manager: manager,
|
||||
children: children,
|
||||
self: self,
|
||||
assistant: assistant
|
||||
};
|
||||
},
|
||||
this.handleHttpError.bind(this));
|
||||
}
|
||||
|
||||
getPerson(id: string): IPromise<IPerson> {
|
||||
// Deferred object used for aborting requests. See promise.service.ts for more information
|
||||
let httpTimeout = this.$q.defer();
|
||||
|
||||
let request = this.$http
|
||||
.get(this.pwmService.getServerUrl('detail'), {
|
||||
cache: true,
|
||||
params: {userKey: id},
|
||||
timeout: httpTimeout.promise
|
||||
});
|
||||
|
||||
let promise = request.then(
|
||||
(response) => {
|
||||
if (response.data['error']) {
|
||||
return this.handlePwmError(response);
|
||||
}
|
||||
|
||||
let person: IPerson = <IPerson>(response.data['data']);
|
||||
return person;
|
||||
},
|
||||
this.handleHttpError.bind(this));
|
||||
|
||||
promise['_httpTimeout'] = httpTimeout;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
getTeamEmails(id: string, depth: number): IPromise<string[]> {
|
||||
const deferredValue: IDeferred<string[]> = this.$q.defer();
|
||||
|
||||
let request = this.$http
|
||||
.get(this.pwmService.getServerUrl('mailtoLinks'), {
|
||||
cache: true,
|
||||
params: {
|
||||
userKey: id,
|
||||
depth: depth
|
||||
}
|
||||
})
|
||||
.then((response) => {
|
||||
deferredValue.resolve(response.data['data']);
|
||||
});
|
||||
|
||||
return deferredValue.promise;
|
||||
}
|
||||
|
||||
search(query: string, params?: any): IPromise<SearchResult> {
|
||||
// Deferred object used for aborting requests. See promise.service.ts for more information
|
||||
let httpTimeout = this.$q.defer();
|
||||
|
||||
let formID: string = encodeURIComponent('&pwmFormID=' + this.PWM_GLOBAL['pwmFormID']);
|
||||
let url: string = this.pwmService.getServerUrl('search', params)
|
||||
+ '&pwmFormID=' + this.PWM_GLOBAL['pwmFormID'];
|
||||
let request = this.$http
|
||||
.post(url, {
|
||||
mode: 'simple',
|
||||
username: query,
|
||||
pwmFormID: formID,
|
||||
includeDisplayName: (params && params.includeDisplayName) ? params.includeDisplayName : false
|
||||
}, {
|
||||
cache: true,
|
||||
timeout: httpTimeout.promise,
|
||||
headers: {'Content-Type': 'multipart/form-data'},
|
||||
});
|
||||
|
||||
let promise = request.then(
|
||||
(response) => {
|
||||
if (response.data['error']) {
|
||||
return this.handlePwmError(response);
|
||||
}
|
||||
|
||||
let receivedData: any = response.data['data'];
|
||||
let searchResult: SearchResult = new SearchResult(receivedData);
|
||||
|
||||
return searchResult;
|
||||
},
|
||||
this.handleHttpError.bind(this));
|
||||
|
||||
promise['_httpTimeout'] = httpTimeout;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
private handleHttpError(error): void {
|
||||
this.$log.error(error);
|
||||
}
|
||||
|
||||
private handlePwmError(response): IPromise<any> {
|
||||
const errorMessage = `${response.data['errorCode']}: ${response.data['errorMessage']}`;
|
||||
this.$log.error(errorMessage);
|
||||
|
||||
return this.$q.reject(response.data['errorMessage']);
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { IPromise, IQService } from 'angular';
|
||||
import {ConfigBaseService} from './base-config.service.dev';
|
||||
import {IConfigService} from './base-config.service';
|
||||
import {AdvancedSearchConfig, IPeopleSearchConfigService} from './peoplesearch-config.service';
|
||||
|
||||
|
||||
export default class ConfigService
|
||||
extends ConfigBaseService
|
||||
implements IConfigService, IPeopleSearchConfigService {
|
||||
static $inject = [ '$q' ];
|
||||
constructor($q: IQService) {
|
||||
super($q);
|
||||
}
|
||||
|
||||
getColumnConfig(): IPromise<any> {
|
||||
return this.$q.resolve({
|
||||
givenName: 'First Name',
|
||||
sn: 'Last Name',
|
||||
title: 'Title',
|
||||
mail: 'Email',
|
||||
telephoneNumber: 'Telephone'
|
||||
});
|
||||
}
|
||||
|
||||
getOrgChartMaxParents(): IPromise<number> {
|
||||
return this.$q.resolve(50);
|
||||
}
|
||||
|
||||
orgChartEnabled(): IPromise<boolean> {
|
||||
return this.$q.resolve(true);
|
||||
}
|
||||
|
||||
orgChartShowChildCount(): IPromise<boolean> {
|
||||
return this.$q.resolve(true);
|
||||
}
|
||||
|
||||
advancedSearchConfig(): IPromise<AdvancedSearchConfig> {
|
||||
return this.$q.resolve({
|
||||
enabled: true,
|
||||
maxRows: 3,
|
||||
attributes: [
|
||||
{
|
||||
id: 'title',
|
||||
attribute: 'Title'
|
||||
},
|
||||
{
|
||||
id: 'givenName',
|
||||
attribute: 'Given Name'
|
||||
},
|
||||
{
|
||||
id: 'sn',
|
||||
attribute: 'First Name'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IDeferred, IHttpService, ILogService, IPromise, IQService} from 'angular';
|
||||
import IPwmService from './pwm.service';
|
||||
import PwmService from './pwm.service';
|
||||
import {
|
||||
ConfigBaseService,
|
||||
IConfigService,
|
||||
IAdvancedSearchConfig,
|
||||
ADVANCED_SEARCH_ENABLED,
|
||||
ADVANCED_SEARCH_MAX_ATTRIBUTES,
|
||||
ADVANCED_SEARCH_ATTRIBUTES, PHOTO_ENABLED
|
||||
} from './base-config.service';
|
||||
|
||||
const ORGCHART_ENABLED = 'orgChartEnabled';
|
||||
const ORGCHART_MAX_PARENTS = 'orgChartMaxParents';
|
||||
const ORGCHART_SHOW_CHILD_COUNT = 'orgChartShowChildCount';
|
||||
const EXPORT_ENABLED = 'enableExport';
|
||||
const EXPORT_MAX_DEPTH = 'exportMaxDepth';
|
||||
const MAILTO_ENABLED = 'enableMailtoLinks';
|
||||
const MAILTO_MAX_DEPTH = 'mailtoLinkMaxDepth';
|
||||
|
||||
export interface IPeopleSearchConfigService extends IConfigService {
|
||||
getOrgChartMaxParents(): IPromise<number>;
|
||||
orgChartEnabled(): IPromise<boolean>;
|
||||
orgChartShowChildCount(): IPromise<boolean>;
|
||||
advancedSearchConfig(): IPromise<IAdvancedSearchConfig>;
|
||||
personDetailsConfig(): IPromise<IPersonDetailsConfig>;
|
||||
}
|
||||
|
||||
export interface IPersonDetailsConfig {
|
||||
photosEnabled: boolean;
|
||||
orgChartEnabled: boolean;
|
||||
exportEnabled: boolean;
|
||||
emailTeamEnabled: boolean;
|
||||
maxExportDepth: number;
|
||||
maxEmailDepth: number;
|
||||
}
|
||||
|
||||
export default class PeopleSearchConfigService
|
||||
extends ConfigBaseService
|
||||
implements IConfigService, IPeopleSearchConfigService {
|
||||
|
||||
static $inject = ['$http', '$log', '$q', 'PwmService' ];
|
||||
constructor($http: IHttpService, $log: ILogService, $q: IQService, pwmService: IPwmService) {
|
||||
super($http, $log, $q, pwmService);
|
||||
}
|
||||
|
||||
getOrgChartMaxParents(): IPromise<number> {
|
||||
return this.getValue(ORGCHART_MAX_PARENTS);
|
||||
}
|
||||
|
||||
orgChartEnabled(): IPromise<boolean> {
|
||||
return this.getValue(ORGCHART_ENABLED)
|
||||
.then(null, () => { return true; }); // On error use default
|
||||
}
|
||||
|
||||
orgChartShowChildCount(): IPromise<boolean> {
|
||||
return this.getValue(ORGCHART_SHOW_CHILD_COUNT);
|
||||
}
|
||||
|
||||
personDetailsConfig(): IPromise<IPersonDetailsConfig> {
|
||||
return this.$q.all([
|
||||
this.getValue(PHOTO_ENABLED),
|
||||
this.getValue(ORGCHART_ENABLED),
|
||||
this.getValue(EXPORT_ENABLED),
|
||||
this.getValue(EXPORT_MAX_DEPTH),
|
||||
this.getValue(MAILTO_ENABLED),
|
||||
this.getValue(MAILTO_MAX_DEPTH),
|
||||
]).then((results: any[]) => {
|
||||
return {
|
||||
photosEnabled: results[0],
|
||||
orgChartEnabled: results[1],
|
||||
exportEnabled: results[2],
|
||||
maxExportDepth: results[3],
|
||||
emailTeamEnabled: results[4],
|
||||
maxEmailDepth: results[5]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
advancedSearchConfig(): IPromise<IAdvancedSearchConfig> {
|
||||
return this.$q.all([
|
||||
this.getValue(ADVANCED_SEARCH_ENABLED),
|
||||
this.getValue(ADVANCED_SEARCH_MAX_ATTRIBUTES),
|
||||
this.getValue(ADVANCED_SEARCH_ATTRIBUTES)
|
||||
]).then((result) => {
|
||||
return {
|
||||
enabled: result[0],
|
||||
maxRows: result[1],
|
||||
attributes: result[2]
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPromise, IQService } from 'angular';
|
||||
|
||||
// Pattern explained at https://www.bennadel.com/blog/2731-canceling-a-promise-in-angularjs.htm
|
||||
export default class PromiseService {
|
||||
static $inject = [ '$q' ];
|
||||
constructor(private $q: IQService) {}
|
||||
|
||||
abort(promise: IPromise<any>) {
|
||||
if (promise && promise['_httpTimeout'] && promise['_httpTimeout'].resolve) {
|
||||
promise['_httpTimeout'].resolve();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHttpRequestOptions, IPwmService} from './pwm.service';
|
||||
|
||||
export default class PwmService implements IPwmService {
|
||||
getServerUrl(processAction: string, additionalParameters?: any): string {
|
||||
return null;
|
||||
}
|
||||
|
||||
httpRequest<T>(url: string, options: IHttpRequestOptions): angular.IPromise<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
get ajaxTypingWait(): number {
|
||||
return 300;
|
||||
}
|
||||
|
||||
get localeStrings(): any {
|
||||
return {};
|
||||
}
|
||||
|
||||
get startupFunctions(): any[] {
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {IHttpService, ILogService, IPromise, IQService, IWindowService} from 'angular';
|
||||
|
||||
export interface IHttpRequestOptions {
|
||||
data?: any;
|
||||
preventCache?: boolean;
|
||||
}
|
||||
|
||||
export interface IPwmService {
|
||||
getServerUrl(processAction: string, additionalParameters?: any): string;
|
||||
httpRequest<T>(url: string, options: IHttpRequestOptions): IPromise<T>;
|
||||
ajaxTypingWait: number;
|
||||
localeStrings: any;
|
||||
startupFunctions: any[];
|
||||
}
|
||||
|
||||
const DEFAULT_AJAX_TYPING_WAIT = 700;
|
||||
|
||||
export default class PwmService implements IPwmService {
|
||||
PWM_GLOBAL: any;
|
||||
PWM_MAIN: any;
|
||||
|
||||
urlContext: string;
|
||||
|
||||
static $inject = [ '$http', '$log', '$q', '$window' ];
|
||||
constructor(private $http: IHttpService,
|
||||
private $log: ILogService,
|
||||
private $q: IQService,
|
||||
$window: IWindowService) {
|
||||
this.urlContext = '';
|
||||
|
||||
// Search window references to PWM_GLOBAL and PWM_MAIN add by legacy PWM code
|
||||
if ($window['PWM_GLOBAL']) {
|
||||
this.PWM_GLOBAL = $window['PWM_GLOBAL'];
|
||||
this.urlContext = this.PWM_GLOBAL['url-context'];
|
||||
}
|
||||
else {
|
||||
this.$log.warn('PWM_GLOBAL is not defined on window');
|
||||
}
|
||||
|
||||
if ($window['PWM_MAIN']) {
|
||||
this.PWM_MAIN = $window['PWM_MAIN'];
|
||||
}
|
||||
else {
|
||||
this.$log.warn('PWM_MAIN is not defined on window');
|
||||
}
|
||||
}
|
||||
|
||||
getServerUrl(processAction: string, additionalParameters?: any): string {
|
||||
let url: string = window.location.pathname + '?processAction=' + processAction;
|
||||
url = this.addParameters(url, additionalParameters);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
private handlePwmError(response): IPromise<any> {
|
||||
// TODO: show error dialog (like PWM_MAIN.ajaxRequest)
|
||||
const errorMessage = `${response.data['errorCode']}: ${response.data['errorMessage']}`;
|
||||
this.$log.error(errorMessage);
|
||||
|
||||
return this.$q.reject(response.data['errorMessage']);
|
||||
}
|
||||
|
||||
httpRequest<T>(url: string, options: IHttpRequestOptions): IPromise<T> {
|
||||
// TODO: implement alternate http method, no Content-Type if no options.data
|
||||
let formID: string = encodeURIComponent('&pwmFormID=' + this.PWM_GLOBAL['pwmFormID']);
|
||||
url += '&pwmFormID=' + this.PWM_GLOBAL['pwmFormID'];
|
||||
let promise = this.$http
|
||||
.post(url, options.data, {
|
||||
cache: !options.preventCache,
|
||||
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data['error']) {
|
||||
return this.handlePwmError(response);
|
||||
}
|
||||
|
||||
// Note: sometimes response.data looks like this:
|
||||
// {
|
||||
// "error": false,
|
||||
// "errorCode": 0,
|
||||
// "data": {
|
||||
// "foo": "1",
|
||||
// "bar": "2"
|
||||
// }
|
||||
// }
|
||||
|
||||
// Note: other times, response.data looks like this:
|
||||
// {
|
||||
// "error": false,
|
||||
// "errorCode": 0,
|
||||
// "successMessage": "The operation has been successfully completed."
|
||||
// }
|
||||
|
||||
// Since we can't make assumptions about the structure, we just need to return the whole response.data
|
||||
// payload:
|
||||
return <T>response.data;
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
get ajaxTypingWait(): number {
|
||||
if (this.PWM_GLOBAL) {
|
||||
return this.PWM_GLOBAL['client.ajaxTypingWait'] || DEFAULT_AJAX_TYPING_WAIT;
|
||||
}
|
||||
|
||||
return DEFAULT_AJAX_TYPING_WAIT;
|
||||
}
|
||||
|
||||
get localeStrings(): any {
|
||||
if (this.PWM_GLOBAL) {
|
||||
return this.PWM_GLOBAL['localeStrings'];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
get startupFunctions(): any[] {
|
||||
if (this.PWM_GLOBAL) {
|
||||
return this.PWM_GLOBAL['startupFunctions'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private addParameters(url: string, params: any): string {
|
||||
if (!this.PWM_MAIN) {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (params) {
|
||||
for (let name in params) {
|
||||
if (params.hasOwnProperty(name)) {
|
||||
url = this.PWM_MAIN.addParamToUrl(url, name, params[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import 'angular-translate';
|
||||
import { IQService } from 'angular';
|
||||
import IPwmService from './pwm.service';
|
||||
|
||||
export default [
|
||||
'$q',
|
||||
'PwmService',
|
||||
($q: IQService, pwmService: IPwmService) => {
|
||||
return function () {
|
||||
return $q.resolve(pwmService.localeStrings['Display']);
|
||||
};
|
||||
}];
|
|
@ -1,76 +0,0 @@
|
|||
/*!
|
||||
* Password Management Servlets (PWM)
|
||||
* http://www.pwm-project.org
|
||||
*
|
||||
* Copyright (c) 2006-2009 Novell, Inc.
|
||||
* Copyright (c) 2009-2021 The PWM Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Only apply the ux-ias styles when the elements are a child of .ias-styles-root. This ensures we are not mucking with the
|
||||
// styles for the header and footer (things outside of this angular app).
|
||||
.ias-styles-root {
|
||||
@import "~@microfocus/ux-ias/src/ux-ias";
|
||||
|
||||
.ias-dialog {
|
||||
// Need to fix this minor css problem in ux-ias until a version > 1.0.0-rc comes out.
|
||||
position: fixed;
|
||||
|
||||
> .ias-dialog-container {
|
||||
&.grow-with-content {
|
||||
@media (min-width: 768px) {
|
||||
max-width: 100%;
|
||||
min-width: 375px;
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
> .ias-dialog-label > .ias-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The ias-menu is added at the root of the document. This fixes when we have to add the ias-styles-root class
|
||||
// to the ias-menu itself to get the IAS styles applied.
|
||||
&.ias-menu {
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
|
||||
bottom: 0;
|
||||
display: none;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
|
||||
&.ias-open {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
// ux-ias normally applies the following to the body tag, but the way we are including ux-ias under .ias-styles-root
|
||||
// means we have to define it here.
|
||||
background-color: $body-bg-color;
|
||||
color: $text-color;
|
||||
font-family: $font-family;
|
||||
font-size: $font-size;
|
||||
font-weight: $font-weight;
|
||||
line-height: normal;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue