add groups feature

This commit is contained in:
Denis barbaron
2019-06-12 10:29:07 +02:00
parent 6fd750dad5
commit 7f5dc4c152
119 changed files with 12416 additions and 402 deletions

View File

@@ -0,0 +1,18 @@
/**
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
**/
require('./users.css')
module.exports = angular.module('stf.settings.users', [
require('stf/app-state').name,
require('stf/settings').name,
require('stf/util/common').name,
require('stf/users').name
])
.run(['$templateCache', function($templateCache) {
$templateCache.put(
'settings/users/users.pug', require('./users.pug')
)
}])
.controller('UsersCtrl', require('./users-controller'))

View File

@@ -0,0 +1,229 @@
/**
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
**/
const _ = require('lodash')
module.exports = function UsersCtrl(
$scope
, UsersService
, AppState
, SettingsService
, ItemsPerPageOptionsService
, GenericModalService
, CommonService
) {
const usersByEmail = {}
const userFields =
'email,' +
'name,' +
'privilege,' +
'groups.quotas'
function addUser(user, timeStamp) {
return CommonService.add(
$scope.users
, usersByEmail
, user
, 'email'
, timeStamp)
}
function updateUser(user, timeStamp) {
return CommonService.update(
$scope.users
, usersByEmail
, user
, 'email'
, timeStamp)
}
function deleteUser(email, timeStamp) {
return CommonService.delete(
$scope.users
, usersByEmail
, email
, timeStamp)
}
function initScope() {
UsersService.getOboeUsers(userFields, function(user) {
addUser(user, -1)
})
.done(function() {
$scope.$digest()
if (CommonService.isExisting(usersByEmail[AppState.user.email])) {
$scope.adminUser = $scope.users[usersByEmail[AppState.user.email].index]
}
})
}
SettingsService.bind($scope, {
target: 'removingFilters'
, source: 'UsersRemovingFilters'
, defaultValue: {
groupOwner: 'False'
}
})
$scope.users = []
$scope.confirmRemove = {value: true}
$scope.scopeUsersCtrl = $scope
$scope.itemsPerPageOptions = ItemsPerPageOptionsService
SettingsService.bind($scope, {
target: 'userItemsPerPage'
, source: 'userItemsPerPage'
, defaultValue: $scope.itemsPerPageOptions[2]
})
$scope.tmpEnv = {}
$scope.nameRegex = /^[0-9a-zA-Z-_. ]{1,50}$/
$scope.nameRegexStr = '/^[0-9a-zA-Z-_. ]{1,50}$/'
$scope.removingFilterOptions = ['True', 'False', 'Any']
$scope.mailTo = function(users) {
CommonService.copyToClipboard(users.map(function(user) {
return user.email
})
.join(SettingsService.get('emailSeparator')))
.url('mailto:?body=*** Paste the email addresses from the clipboard! ***')
}
$scope.removeUser = function(email, askConfirmation) {
if (askConfirmation) {
GenericModalService.open({
message: 'Really delete this user?'
, type: 'Warning'
, size: 'sm'
, cancel: true
})
.then(function() {
CommonService.errorWrapper(
UsersService.removeUser
, [email, $scope.removingFilters]
)
})
}
else {
CommonService.errorWrapper(
UsersService.removeUser
, [email, $scope.removingFilters]
)
}
}
$scope.removeUsers = function(search, filteredUsers, askConfirmation) {
function removeUsers() {
CommonService.errorWrapper(
UsersService.removeUsers
, search ?
[$scope.removingFilters, filteredUsers.map(function(user) { return user.email }).join()] :
[$scope.removingFilters]
)
}
if (askConfirmation) {
GenericModalService.open({
message: 'Really delete this selection of users?'
, type: 'Warning'
, size: 'sm'
, cancel: true
})
.then(function() {
removeUsers()
})
}
else {
removeUsers()
}
}
$scope.conditionForDefaultQuotasSaving = function(formInvalidStatus) {
if (formInvalidStatus) {
$scope.tmpEnv.defaultQuotasTooltip = 'Bad syntax'
return false
}
if ($scope.tmpEnv.defaultGroupsNumber
!== $scope.adminUser.groups.quotas.defaultGroupsNumber ||
$scope.tmpEnv.defaultGroupsDuration
!== $scope.adminUser.groups.quotas.defaultGroupsDuration ||
$scope.tmpEnv.defaultGroupsRepetitions
!== $scope.adminUser.groups.quotas.defaultGroupsRepetitions
) {
$scope.tmpEnv.defaultQuotasTooltip = ''
return true
}
$scope.tmpEnv.defaultQuotasTooltip = 'No change'
return false
}
$scope.initTemporaryDefaultQuotas = function() {
$scope.tmpEnv.defaultGroupsNumber = $scope.adminUser.groups.quotas.defaultGroupsNumber
$scope.tmpEnv.defaultGroupsDuration = $scope.adminUser.groups.quotas.defaultGroupsDuration
$scope.tmpEnv.defaultGroupsRepetitions = $scope.adminUser.groups.quotas.defaultGroupsRepetitions
$scope.tmpEnv.defaultQuotasTooltip = 'No change'
}
$scope.updateDefaultUserGroupsQuotas = function() {
CommonService.errorWrapper(UsersService.updateDefaultUserGroupsQuotas, [
$scope.tmpEnv.defaultGroupsNumber
, $scope.tmpEnv.defaultGroupsDuration
, $scope.tmpEnv.defaultGroupsRepetitions
])
}
$scope.updateUserGroupsQuotas = function(user) {
CommonService.errorWrapper(UsersService.updateUserGroupsQuotas, [
user.email
, user.groupsNumber
, user.groupsDuration
, user.groupsRepetitions
])
}
$scope.initTemporaryUser = function() {
$scope.tmpEnv.userName = $scope.tmpEnv.userEmail = ''
$scope.tmpEnv.userTooltip = 'Bad syntax'
}
$scope.conditionForQuotasSaving = function(user, formInvalidStatus) {
if (formInvalidStatus) {
user.quotasTooltip = 'Bad syntax'
return false
}
if (user.groupsNumber !== user.groups.quotas.allocated.number ||
user.groupsDuration !== user.groups.quotas.allocated.duration ||
user.groupsRepetitions !== user.groups.quotas.repetitions) {
user.quotasTooltip = ''
return true
}
user.quotasTooltip = 'No change'
return false
}
$scope.initTemporaryQuotas = function(user) {
user.groupsNumber = user.groups.quotas.allocated.number
user.groupsDuration = user.groups.quotas.allocated.duration
user.groupsRepetitions = user.groups.quotas.repetitions
user.quotasTooltip = 'No change'
}
$scope.createUser = function() {
CommonService.errorWrapper(
UsersService.createUser
, [$scope.tmpEnv.userName, $scope.tmpEnv.userEmail]
)
}
$scope.$on('user.settings.users.created', function(event, message) {
addUser(message.user, message.timeStamp)
})
$scope.$on('user.settings.users.deleted', function(event, message) {
deleteUser(message.user.email, message.timeStamp)
})
$scope.$on('user.settings.users.updated', function(event, message) {
updateUser(message.user, message.timeStamp)
})
initScope()
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
**/
describe('UsersCtrl', function() {
beforeEach(angular.mock.module(require('./index').name))
var scope, ctrl
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new()
ctrl = $controller('UsersCtrl', {$scope: scope})
}))
it('should ...', inject(function() {
expect(1).toEqual(1)
}))
})

View File

@@ -0,0 +1,87 @@
/**
* Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
**/
.stf-users .selectable {
user-select: text;
}
.stf-pager-users-total-items {
margin-top: 5px;
}
.stf-users .user-creation, .user-default-quotas-item, .user-filters-item, .form-group.user-quotas-item {
margin: 0px 10px 15px 15px;
}
.stf-users .user-save, .user-default-quotas-save, .form-group.user-quotas-save {
margin: 5px 10px 15px 15px;
}
.stf-users .user-header {
margin-left: 10px;
}
.stf-users .user-filters-items {
margin-top: 5px;
margin-bottom: 15px;
}
.stf-users .user-default-quotas-items, .user-quotas-items {
margin: 0px 0px 15px 0px;
}
.stf-users .user-list-icon {
margin-right: 10px;
}
.stf-users .user-list-label {
font-weight: bold;
margin-right: 10px;
}
.stf-users input.ng-invalid {
border-color: red;
}
.stf-users .user-list .user-list-items {
margin: 10px 0px 0px 0px;
}
.stf-users .user-list .user-line {
padding: 10px;
border-bottom: 1px solid #dddddd;
}
.stf-users .user-list .user-line.user-actions {
padding-bottom: 23px;
}
.stf-users .user-list .heading.user-action-body {
margin-top: 22px;
}
.stf-users .user-list-details.selectable a {
padding: 0px;
border-bottom: none;
color: #167FFC;
}
.stf-users .user-list-details {
display: inline-block;
}
.stf-users .user-list-name {
color: #007aff;
font-size: 14px;
font-weight: 300;
margin: 2px 0 6px;
}
.stf-users .user-list-id {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
font-size: 10px;
margin-bottom: 2px;
color: #999999;
font-weight: 300;
}

View File

@@ -0,0 +1,216 @@
//
Copyright © 2019 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
//
.widget-container.fluid-height.stf-users(ng-controller='UsersCtrl')
.heading
i.fa.fa-user
span(translate) User list
button.btn.btn-primary-outline.pull-right.btn-sm(
ng-click='showCreateUser = !showCreateUser; initTemporaryUser()'
ng-class='{ "btn-primary-outline": !showCreateUser, "btn-primary": showCreateUser }')
i.fa.fa-plus.fa-fw
a.pull-right.btn.btn-sm(ng-href='')
i.fa.fa-question-circle.fa-fw(uib-tooltip='{{"More about Users" | translate}}' tooltip-placement='left')
.widget-content.padded
nothing-to-show(icon='fa-user' message='{{"No Users" | translate}}' ng-if='!users.length')
div(ng-if='users.length')
ul.list-group.user-list
li.list-group-item(ng-if='showCreateUser')
.user-line
.heading
i.fa.fa-user
span(translate) Create new user
form.form-inline(name='userForm')
.form-group.user-creation
label.user-list-label(translate) Name
input.form-control.input-sm(
name='nameForm'
uib-tooltip="{{'Regex syntax' | translate}}: {{::nameRegexStr}}"
tooltip-placement='top'
tooltip-popup-delay='500'
tooltip-enable='userForm.nameForm.$invalid'
type='text' ng-model='tmpEnv.userName' ng-pattern="nameRegex" required)
.form-group.user-creation
label.user-list-label(translate) Email
input.form-control.input-sm(size='35' type='email' ng-model='tmpEnv.userEmail' required)
.form-group.user-save
button.btn.btn-sm.btn-primary(
type='button'
ng-click='createUser()'
ng-disabled='userForm.$invalid')
span(translate) Save
li.list-group-item
.user-line.user-actions
form.form-inline.user-header
.form-group
stf-pager(
tooltip-label="{{'User selection' | translate}}"
total-items='filteredUsers.length'
total-items-style='stf-pager-users-total-items'
items-per-page='scopeUsersCtrl.userItemsPerPage'
items-per-page-options='itemsPerPageOptions'
current-page='scopeUsersCtrl.userCurrentPage'
items-search='search')
button.btn.btn-xs.btn-danger.pull-right(
type='button'
uib-tooltip="{{'Remove the user selection' | translate}}"
tooltip-placement='bottom'
tooltip-popup-delay='500'
ng-disabled="!filteredUsers.length || filteredUsers.length === 1 && filteredUsers[0].privilege === 'admin'"
ng-click='removeUsers(search, filteredUsers, confirmRemove.value)')
i.fa.fa-trash-o
span(translate) Remove
button.btn.btn-xs.btn-success.pull-right(
type='button'
uib-tooltip="{{'Enable/Disable confirmation for user removing' | translate}}"
tooltip-placement='top'
tooltip-popup-delay='500'
ng-click='confirmRemove.value = !confirmRemove.value'
ng-class='{"btn-warning-outline": !confirmRemove.value, "btn-success": confirmRemove.value}')
i.fa.fa-lock(ng-if='confirmRemove.value')
i.fa.fa-unlock(ng-if='!confirmRemove.value')
span(translate) Confirm Remove
button.btn.btn-xs.btn-danger-outline.pull-right(
type='button'
uib-tooltip="{{'Set filters for user removing' | translate}}"
tooltip-placement='top'
tooltip-popup-delay='500'
ng-click='showFilters = !showFilters'
ng-class='{"btn-danger-outline": !showFilters, "btn-danger": showFilters}')
i.fa.fa-trash-o
span(translate) Filters
button.btn.btn-xs.btn-primary-outline.pull-right(
type='button'
uib-tooltip="{{'Set groups quotas for new users' | translate}}"
tooltip-placement='top'
tooltip-popup-delay='500'
ng-click='showDefaultGroupsQuotas = !showDefaultGroupsQuotas; initTemporaryDefaultQuotas()'
ng-class='{"btn-primary-outline": !showDefaultGroupsQuotas, "btn-primary": showDefaultGroupsQuotas}')
i.fa.fa-object-group
span(translate) Default Groups Quotas
button.btn.btn-xs.btn-primary-outline.pull-right(
type='button'
uib-tooltip="{{'Write an email to the user selection' | translate}}"
ng-disabled='!filteredUsers.length'
ng-click='mailTo(filteredUsers)'
tooltip-placement='top'
tooltip-popup-delay='500')
i.fa.fa-envelope-o
span(translate) Contact Users
li.list-group-item(ng-if='showFilters')
.user-line
.heading
i.fa.fa-trash-o
span(translate) Removing filters
form.form-inline.user-filters-items
.form-group.user-filters-item
label.user-list-label(
translate
uib-tooltip="{{'Filter on user group ownership' | translate}}"
tooltip-placement='top'
tooltip-popup-delay='500') Group Owner
select(ng-model='removingFilters.groupOwner' ng-options='option for option in removingFilterOptions')
li.list-group-item(ng-if='showDefaultGroupsQuotas')
.user-line
.heading
i.fa.fa-object-group
span(translate) Default groups quotas
form.form-inline.user-default-quotas-items(name='dafaultQuotasForm')
.form-group.user-default-quotas-item
label.user-list-label(translate) Number of groups
input.form-control.input-sm(type='number' min='0' ng-model='tmpEnv.defaultGroupsNumber' required)
.form-group.user-default-quotas-item
label.user-list-label Total duration of groups (ms)
input.form-control.input-sm(type='number' min='0' ng-model='tmpEnv.defaultGroupsDuration' required)
.form-group.user-default-quotas-item
label.user-list-label(translate) Number of repetitions per group
input.form-control.input-sm(type='number' min='0' ng-model='tmpEnv.defaultGroupsRepetitions' required)
.form-group.user-default-quotas-save
button.btn.btn-sm.btn-primary(
uib-tooltip='{{tmpEnv.defaultQuotasTooltip | translate}}'
tooltip-enable='tmpEnv.defaultQuotasTooltip'
tooltip-placement='top'
tooltip-popup-delay='500'
ng-click='updateDefaultUserGroupsQuotas()'
ng-disabled='!conditionForDefaultQuotasSaving(defaultQuotasForm.$invalid)')
span(translate) Save
li.list-group-item(ng-repeat="user in users \
| filter:search \
| orderBy: 'name' \
| pagedObjectsFilter:scopeUsersCtrl:'userCurrentPage':'userItemsPerPage':'filteredUsers' \
track by user.email")
.user-line.user-actions
i.fa.fa-user.fa-2x.fa-fw.user-list-icon
.user-list-details.selectable
a.user-list-name(ng-href="{{::'mailto:' + user.email}}") {{::user.name}}
.user-list-id
span(translate) Email
span(ng-bind-template="{{::': ' + user.email + ' - '}}")
span(translate) Privilege
span(ng-bind-template="{{::': ' + user.privilege}}")
button.btn.btn-xs.btn-danger-outline.pull-right(
type='button'
ng-click='removeUser(user.email, confirmRemove.value)'
ng-disabled='user.privilege === "admin"')
i.fa.fa-trash-o
span(translate) Remove
button.btn.btn-xs.btn-primary-outline.pull-right(
type='button'
ng-click='showGroupsQuotas = !showGroupsQuotas; initTemporaryQuotas(user)'
ng-class='{"btn-primary-outline": !showGroupsQuotas, "btn-primary": showGroupsQuotas}')
i.fa.fa-object-group
span(translate) Groups Quotas
ul.list-group.user-list.user-list-items(ng-if='showGroupsQuotas')
li.list-group-item
.heading.user-action-body
i.fa.fa-object-group
span(translate) Groups Quotas
form.form-inline(name='quotasForm')
.form-group.user-quotas-item
label.user-list-label(translate) Number of groups
input.form-control.input-sm(type='number' min='0' ng-max-length='5' ng-model='user.groupsNumber' required)
.form-group.user-quotas-item
label.user-list-label(translate) Total duration of groups (ms)
input.form-control.input-sm(type='number' min='0' ng-model='user.groupsDuration' required)
.form-group.user-quotas-item
label.user-list-label(translate) Number of repetitions per group
input.form-control.input-sm(type='number' min='0' ng-model='user.groupsRepetitions' required)
.form-group.user-quotas-save
button.btn.btn-sm.btn-primary(
uib-tooltip='{{user.quotasTooltip | translate}}'
tooltip-enable='user.quotasTooltip'
tooltip-placement='top'
tooltip-popup-delay='500'
ng-click='updateUserGroupsQuotas(user)'
ng-disabled='!conditionForQuotasSaving(user, quotasForm.$invalid)')
span(translate) Save