588 lines
22 KiB
HTML
588 lines
22 KiB
HTML
<link rel="import" href="../polymer/polymer.html">
|
|
|
|
<link rel="import" href="../iron-ajax/iron-ajax.html">
|
|
<link rel="import" href="../iron-pages/iron-pages.html">
|
|
<link rel="import" href="../iron-icons/iron-icons.html">
|
|
|
|
<link rel="import" href="../paper-styles/color.html">
|
|
|
|
<link rel="import" href="../paper-material/paper-material.html">
|
|
<link rel="import" href="../paper-dialog/paper-dialog.html">
|
|
<link rel="import" href="../paper-input/paper-input.html">
|
|
<link rel="import" href="../paper-button/paper-button.html">
|
|
<link rel="import" href="../paper-icon-button/paper-icon-button.html">
|
|
|
|
<link rel="import" href="../paper-dropdown-menu/paper-dropdown-menu.html">
|
|
<link rel="import" href="../paper-item/paper-item.html">
|
|
<link rel="import" href="../paper-listbox/paper-listbox.html">
|
|
|
|
<link rel="import" href="../paper-tabs/paper-tabs.html">
|
|
|
|
<link rel="import" href="../strolch-wc-styles/strolch-wc-styles.html">
|
|
<link rel="import" href="../strolch-wc-debounced-input/strolch-wc-debounced-input.html">
|
|
|
|
<link rel="import" href="strolch-wc-inspector-behavior.html">
|
|
<link rel="import" href="strolch-wc-inspector-paging-behavior.html">
|
|
|
|
<dom-module id="strolch-wc-users">
|
|
|
|
<template>
|
|
<style is="custom-style" include="strolch-wc-styles">
|
|
:host {
|
|
display: block;
|
|
margin: 0;
|
|
padding: 0;
|
|
height: 100%;
|
|
}
|
|
|
|
paper-button {
|
|
background-color: white;
|
|
}
|
|
|
|
paper-material {
|
|
background: white;
|
|
margin-top: 0.3em;
|
|
margin-bottom: 0.3em;
|
|
padding: 0.3em;
|
|
}
|
|
|
|
strolch-wc-debounced-input {
|
|
max-width: 200px;
|
|
display: inline-flex;
|
|
|
|
--regular-color: #919191;
|
|
--focus-color: #000000;
|
|
}
|
|
|
|
.table {
|
|
min-width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.table-row {
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: row;
|
|
margin-top: -1px;
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
}
|
|
|
|
.table-heading {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.table-heading,
|
|
.table-cell {
|
|
display: flex;
|
|
padding: 0 12px;
|
|
overflow: hidden;
|
|
align-items: center;
|
|
flex-basis: 80px;
|
|
flex-grow: 1;
|
|
flex-shrink: 0;
|
|
justify-content: flex-start;
|
|
min-height: 48px;
|
|
}
|
|
|
|
.state,
|
|
.locale {
|
|
flex-grow: 0;
|
|
}
|
|
|
|
.username {
|
|
flex-grow: 0;
|
|
flex-basis: 250px;
|
|
}
|
|
|
|
.actions {
|
|
flex-grow: 0;
|
|
flex-basis: 120px;
|
|
}
|
|
|
|
.history {
|
|
flex-grow: 0;
|
|
flex-basis: 250px;
|
|
}
|
|
|
|
.data {
|
|
padding-top: 10px;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.data:nth-child(even) {
|
|
background: #f7f7f7;
|
|
}
|
|
|
|
</style>
|
|
|
|
<div class="g-row g-pt-4">
|
|
<div class="g-12">
|
|
<paper-icon-button class="g-pull-right"
|
|
icon="refresh"
|
|
title="Refresh"
|
|
on-tap="reload"></paper-icon-button>
|
|
<paper-button class="g-pull-right" on-tap="_addUser" raised>Add</paper-button>
|
|
<strolch-wc-debounced-input id="debouncedInput"
|
|
class="g-pull-right"
|
|
debounced-input="{{searchTerm}}"
|
|
disabled="[[arrayFilled(filters)]]"></strolch-wc-debounced-input>
|
|
</div>
|
|
</div>
|
|
|
|
<paper-material elevation="1" class="g-ml-3 g-mr-3">
|
|
|
|
<div class="table">
|
|
<div class="table-row">
|
|
<div class="table-heading username">Username / Name</div>
|
|
<div class="table-heading state">State</div>
|
|
<div class="table-heading locale">Locale</div>
|
|
<div class="table-heading roles">Roles</div>
|
|
<div class="table-heading history">History</div>
|
|
<div class="table-heading actions"></div>
|
|
</div>
|
|
|
|
<template is="dom-repeat" items="[[dataObj.data]]">
|
|
<div class="table-row data">
|
|
<div class="table-cell username">[[item.username]]<br /><br />[[_getName(item)]]</div>
|
|
<div class="table-cell state">[[item.userState]]</div>
|
|
<div class="table-cell locale">[[item.locale]]</div>
|
|
<div class="table-cell roles">[[arrayToString(item.roles)]]</div>
|
|
<div class="table-cell history">
|
|
FirstLogin: [[toLocalDateTime(item.history.firstLogin)]]<br /> LastLogin:
|
|
[[toLocalDateTime(item.history.lastLogin)]]<br /> PasswordChange:
|
|
[[toLocalDateTime(item.history.lastPasswordChange)]]
|
|
</div>
|
|
<div class="table-cell actions">
|
|
<paper-icon-button icon="create" title="Edit" on-tap="_editUser"></paper-icon-button>
|
|
<paper-icon-button icon="lock-outline"
|
|
title="Set password"
|
|
on-tap="_openPwDlg"></paper-icon-button>
|
|
<paper-icon-button icon="delete" title="Remove" on-tap="_removeUser"></paper-icon-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<div class="g-row">
|
|
<div class="g-3"></div>
|
|
<div class="g-6">
|
|
<div class="g-m-2 g-center">
|
|
<paper-icon-button icon="first-page" on-tap="_toFirstPage"></paper-icon-button>
|
|
<paper-icon-button icon="chevron-left" on-tap="_toPreviousPage"></paper-icon-button>
|
|
<span>[[dataObj.offset]] to [[_getEnd(dataObj)]] of [[dataObj.size]] ([[dataObj.dataSetSize]])</span>
|
|
<paper-icon-button icon="chevron-right" on-tap="_toNextPage"></paper-icon-button>
|
|
<paper-icon-button icon="last-page" on-tap="_toLastPage"></paper-icon-button>
|
|
</div>
|
|
</div>
|
|
<div class="g-3">
|
|
<paper-button on-tap="_setLimit">10</paper-button>
|
|
<paper-button on-tap="_setLimit">50</paper-button>
|
|
<paper-button on-tap="_setLimit">100</paper-button>
|
|
</div>
|
|
</div>
|
|
|
|
</paper-material>
|
|
|
|
<paper-dialog id="dlg" modal>
|
|
<h2>[[dlgTitle]]</h2>
|
|
<p>[[dlgText]]</p>
|
|
<div class="buttons">
|
|
<paper-button dialog-confirm autofocus>Close</paper-button>
|
|
</div>
|
|
</paper-dialog>
|
|
|
|
<paper-dialog id="pwDlg" modal>
|
|
<h2>Set Password for [[user.username]]</h2>
|
|
<paper-input label="Password" value="{{password0}}" type="password"></paper-input>
|
|
<paper-input label="Repeat" value="{{password1}}" type="password"></paper-input>
|
|
<div class="buttons">
|
|
<paper-button on-tap="_clearPassword">Clear</paper-button>
|
|
<paper-button dialog-confirm autofocus>Close</paper-button>
|
|
<paper-button on-tap="_setPassword" autofocus>Save</paper-button>
|
|
</div>
|
|
</paper-dialog>
|
|
|
|
<paper-dialog id="userDlg" modal>
|
|
<h2>
|
|
<template is="dom-if" if="[[isEmptyString(user.userId)]]">New User</template>
|
|
<template is="dom-if" if="[[isNotEmptyString(user.userId)]]">Edit User</template>
|
|
</h2>
|
|
|
|
<div class="g-row">
|
|
<div class="g-6 g-p-4">
|
|
|
|
<paper-input label="UserId" value="{{user.userId}}" readonly></paper-input>
|
|
<paper-input label="Username"
|
|
value="{{user.username}}"
|
|
readonly="[[isNotEmptyString(user.userId)]]"></paper-input>
|
|
|
|
<paper-input label="Firstname" value="{{user.firstname}}"></paper-input>
|
|
<paper-input label="Lastname" value="{{user.lastname}}"></paper-input>
|
|
|
|
<paper-dropdown-menu always-float-label label="Locale" class="custom">
|
|
<paper-listbox label="Locale"
|
|
attr-for-selected="value"
|
|
class="dropdown-content"
|
|
selected="{{user.locale}}">
|
|
<paper-item value="en">English</paper-item>
|
|
<paper-item value="de">German</paper-item>
|
|
<paper-item value="fr">French</paper-item>
|
|
</paper-listbox>
|
|
</paper-dropdown-menu>
|
|
|
|
<paper-dropdown-menu always-float-label label="State" class="custom">
|
|
<paper-listbox attr-for-selected="value" class="dropdown-content" selected="{{user.userState}}">
|
|
|
|
<paper-item value="NEW">NEW</paper-item>
|
|
<paper-item value="EXPIRED">EXPIRED</paper-item>
|
|
<paper-item value="ENABLED">ENABLED</paper-item>
|
|
<paper-item value="DISABLED">DISABLED</paper-item>
|
|
<paper-item value="SYSTEM">SYSTEM</paper-item>
|
|
|
|
</paper-listbox>
|
|
</paper-dropdown-menu>
|
|
|
|
</div>
|
|
|
|
<div class="g-6 g-p-4" style="max-height: 360px; overflow:auto;">
|
|
Roles:
|
|
<paper-listbox always-float-label
|
|
label="Roles"
|
|
class="dropdown-content"
|
|
multi
|
|
attr-for-selected="value"
|
|
selected-values="{{selectedRoles}}">
|
|
<template is="dom-repeat" items="[[roles]]">
|
|
<paper-item value="[[item.position]]">[[item.name]]</paper-item>
|
|
</template>
|
|
</paper-listbox>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="g-row g-m-0">
|
|
<div class="g-12 g-pr-4 g-pl-4" style="max-height: 150px; overflow:auto;">
|
|
Properties:
|
|
<template is="dom-repeat" items="[[user.properties]]" as="property">
|
|
<div class="g-row">
|
|
<div class="g-5">
|
|
<paper-input value="{{property.key}}"></paper-input>
|
|
</div>
|
|
<div class="g-5 g-pl-4">
|
|
<paper-input value="{{property.value}}"></paper-input>
|
|
</div>
|
|
<div class="g-2" style="line-height: 62px">
|
|
<paper-icon-button icon="clear"
|
|
title="Remove"
|
|
on-tap="_removeProperty"></paper-icon-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<paper-button class="g-pull-right" on-tap="_addEmptyProperty">Add</paper-button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="buttons">
|
|
<paper-button dialog-confirm autofocus>Close</paper-button>
|
|
<paper-button on-tap="_addOrUpdateUser" autofocus>Save</paper-button>
|
|
</div>
|
|
</paper-dialog>
|
|
|
|
<iron-ajax id="ajax"
|
|
content-type="application/json"
|
|
handle-as="json"
|
|
on-response="_handleAjaxResponse"
|
|
on-error="onAjaxError"></iron-ajax>
|
|
|
|
</template>
|
|
|
|
<script>
|
|
|
|
Polymer({
|
|
|
|
is: 'strolch-wc-users',
|
|
|
|
behaviors: [
|
|
StrolchInspectorBehavior,
|
|
StrolchInspectorPagingBehavior
|
|
],
|
|
|
|
properties: {
|
|
roles: {
|
|
type: Array
|
|
},
|
|
users: {
|
|
type: Array
|
|
},
|
|
user: {
|
|
type: Object
|
|
},
|
|
selectedRoles: {
|
|
type: Array,
|
|
value: []
|
|
},
|
|
searchTerm: {
|
|
type: String,
|
|
observer: 'searchTermChanged'
|
|
},
|
|
propagateShowDialog: {
|
|
type: Boolean,
|
|
value: function () {
|
|
return false
|
|
}
|
|
}
|
|
},
|
|
|
|
_getName: function (user) {
|
|
return user.firstname + ' ' + user.lastname;
|
|
},
|
|
|
|
listeners: {
|
|
"strolch-show-dialog": "onShowDialog"
|
|
},
|
|
onShowDialog: function (event) {
|
|
if (!this.propagateShowDialog) {
|
|
event.cancelBubble = true;
|
|
event.stopPropagation();
|
|
this.showError(event.detail.title, event.detail.text);
|
|
}
|
|
},
|
|
showError: function (title, text) {
|
|
this.dlgTitle = title;
|
|
this.dlgText = text;
|
|
console.log('ERROR: ' + this.dlgTitle + ': ' + this.dlgText);
|
|
this.$.dlg.open();
|
|
},
|
|
|
|
searchTermChanged: function (newValue, oldValue) {
|
|
if (newValue != null && oldValue != null) {
|
|
this.queryPage();
|
|
}
|
|
},
|
|
|
|
ready: function () {
|
|
//
|
|
},
|
|
|
|
reload: function () {
|
|
this.queryPage();
|
|
},
|
|
|
|
queryPage: function () {
|
|
|
|
this.dlgTitle = 'User query failed';
|
|
this._handleAjaxResponse = function (data) {
|
|
this.dataObj = data.detail.response;
|
|
|
|
this.dlgTitle = 'Role query failed';
|
|
this._handleAjaxResponse = function (data) {
|
|
var response = data.detail.response;
|
|
for (var i = 0; i < response.length; i++) {
|
|
response[i].position = i;
|
|
}
|
|
this.roles = response;
|
|
};
|
|
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/roles';
|
|
this.$.ajax.method = 'GET';
|
|
this.$.ajax.generateRequest();
|
|
};
|
|
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/users';
|
|
this.$.ajax.method = 'GET';
|
|
this.$.ajax.params = {
|
|
query: this.searchTerm,
|
|
offset: this.dataObj.offset,
|
|
limit: this.dataObj.limit,
|
|
orderBy: 'username'
|
|
};
|
|
this.$.ajax.generateRequest();
|
|
},
|
|
|
|
_setPassword: function (e) {
|
|
|
|
if (this.isEmptyString(this.password0)) {
|
|
this.showError('Password invalid', 'Password can not be empty!');
|
|
return;
|
|
}
|
|
if (this.password0 !== this.password1) {
|
|
this.showError('Password invalid', 'Password are not the same!');
|
|
return;
|
|
}
|
|
|
|
this.dlgTitle = 'Set password failed';
|
|
this._handleAjaxResponse = function (data) {
|
|
this.reload();
|
|
|
|
this.password0 = '';
|
|
this.password1 = '';
|
|
this.$.pwDlg.close();
|
|
};
|
|
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/users/' + this.user.username + '/password';
|
|
this.$.ajax.method = 'PUT';
|
|
this.$.ajax.body = {
|
|
password: btoa(unescape(encodeURIComponent(this.password0)))
|
|
};
|
|
this.$.ajax.generateRequest();
|
|
},
|
|
|
|
_clearPassword: function (e) {
|
|
this.dlgTitle = 'Clear password failed';
|
|
this._handleAjaxResponse = function (data) {
|
|
this.reload();
|
|
|
|
this.password0 = '';
|
|
this.password1 = '';
|
|
this.$.pwDlg.close();
|
|
};
|
|
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/users/' + this.user.username + '/password';
|
|
this.$.ajax.method = 'DELETE';
|
|
this.$.ajax.body = {};
|
|
this.$.ajax.generateRequest();
|
|
},
|
|
|
|
_removeUser: function (e) {
|
|
var user = e.model.item;
|
|
if (!confirm('Do you really want to delete user ' + user.username + ': ' + user.firstname + ' ' + user.lastname)) {
|
|
return;
|
|
}
|
|
|
|
this.dlgTitle = 'Remove user failed';
|
|
this._handleAjaxResponse = function (data) {
|
|
this.reload();
|
|
};
|
|
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/users/' + user.username;
|
|
this.$.ajax.method = 'DELETE';
|
|
this.$.ajax.body = {};
|
|
this.$.ajax.generateRequest();
|
|
},
|
|
|
|
_addOrUpdateUser: function () {
|
|
|
|
var msg = this._validateUser(this.user);
|
|
if (this.isNotEmptyString(msg)) {
|
|
this.showError('Empty field', msg);
|
|
return;
|
|
}
|
|
|
|
var roles = [];
|
|
for (var i = 0; i < this.selectedRoles.length; i++) {
|
|
var selectedRoleIndex = this.selectedRoles[i];
|
|
var selectedRole = this.roles[Number(selectedRoleIndex)];
|
|
var selectedRoleName = selectedRole.name;
|
|
roles.push(selectedRoleName);
|
|
}
|
|
this.user.roles = roles;
|
|
|
|
this.dlgTitle = 'Save user failed';
|
|
this._handleAjaxResponse = function (data) {
|
|
this.$.userDlg.close();
|
|
this.reload();
|
|
};
|
|
|
|
if (this.isEmptyString(this.user.userId)) {
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/users';
|
|
this.$.ajax.method = 'POST';
|
|
} else {
|
|
this.$.ajax.url = this.basePath + 'rest/strolch/privilege/users/' + this.user.username;
|
|
this.$.ajax.method = 'PUT';
|
|
}
|
|
this.$.ajax.body = this.user;
|
|
this.$.ajax.generateRequest();
|
|
},
|
|
|
|
_handleAjaxResponse: function (evt) {
|
|
console.log('NOT YET DEFINED!');
|
|
},
|
|
|
|
_validateUser: function (user) {
|
|
if (this.isEmptyString(user.username))
|
|
return 'Username is not set!';
|
|
if (this.isEmptyString(user.firstname))
|
|
return 'Firstname is not set!';
|
|
if (this.isEmptyString(user.lastname))
|
|
return 'Lastname is not set!';
|
|
if (this.isEmptyString(user.userState))
|
|
return 'User state is not set!';
|
|
if (this.isEmptyString(user.locale))
|
|
return 'Locale is not set!';
|
|
if (this.selectedRoles.length === 0)
|
|
return 'No role selected!';
|
|
|
|
return '';
|
|
},
|
|
|
|
_addEmptyProperty: function () {
|
|
this.push('user.properties', {
|
|
key: '',
|
|
value: ''
|
|
});
|
|
},
|
|
|
|
_removeProperty: function (e) {
|
|
var property = e.model.property;
|
|
|
|
var i = 0;
|
|
for (; i < this.user.properties.length; i++) {
|
|
if (this.user.properties[i].key === property.key) {
|
|
this.user.properties.splice(i, 1);
|
|
this.notifySplices('user.properties', [
|
|
{
|
|
index: i,
|
|
removed: [property],
|
|
addedCount: 0,
|
|
obect: this.user.properties,
|
|
type: 'splice'
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
},
|
|
|
|
_addUser: function () {
|
|
this.user = {
|
|
userId: '',
|
|
username: '',
|
|
firstname: '',
|
|
lastname: '',
|
|
userState: 'NEW',
|
|
locale: 'en',
|
|
roles: [],
|
|
properties: []
|
|
};
|
|
|
|
this.selectedRoles = [];
|
|
this.$.userDlg.open();
|
|
},
|
|
|
|
_openPwDlg: function (e) {
|
|
this.user = e.model.item;
|
|
this.$.pwDlg.open();
|
|
},
|
|
|
|
_editUser: function (e) {
|
|
this.user = e.model.item;
|
|
var selectedRoles = [];
|
|
for (var i = 0; i < this.roles.length; i++) {
|
|
var role = this.roles[i];
|
|
for (var j = 0; j < this.user.roles.length; j++) {
|
|
var r = this.user.roles[j];
|
|
if (role.name === r)
|
|
selectedRoles.push(i);
|
|
}
|
|
}
|
|
|
|
this.selectedRoles = selectedRoles;
|
|
this.$.userDlg.open();
|
|
}
|
|
});
|
|
|
|
</script>
|
|
|
|
</dom-module>
|