refactoring of Settings view. WIP

This commit is contained in:
Olivier Lambert
2015-04-14 17:27:28 +02:00
parent d86c2b64f2
commit 52d71e5c4a
14 changed files with 433 additions and 296 deletions

View File

@@ -150,8 +150,8 @@ export default angular.module('xoWebApp', [
}
// The user must have the `admin` permission to access the
// settings and admin pages.
if (/^admin\..*|settings|tree$/.test(state.name)) {
// settings pages.
if (/^settings\..*|tree$/.test(state.name)) {
event.preventDefault();
notify.error({
title: 'Restricted area',

View File

@@ -105,7 +105,7 @@ nav.navbar.navbar-inverse.navbar-fixed-top(role = 'navigation')
ui-sref-active = 'active'
ng-class = '{ disabled: navbar.user.permission !== "admin" }'
)
a(ui-sref="settings")
a(ui-sref="settings.index")
i.fa.fa-cog
| Settings
li.divider

View File

@@ -0,0 +1,80 @@
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import uiSelect from 'angular-ui-select';
import filter from 'lodash.filter';
import xoApi from 'xo-api';
import xoServices from 'xo-services';
import view from './view';
export default angular.module('settings.acls', [
uiRouter,
uiSelect,
xoApi,
xoServices,
])
.config(function ($stateProvider) {
$stateProvider.state('settings.acls', {
controller: 'SettingsAcls as ctrl',
url: '/acls',
resolve: {
acls(xo) {
return xo.acl.get();
},
users(xo) {
return xo.user.getAll();
},
},
template: view,
});
})
.controller('SettingsAcls', function ($scope, acls, users, xoApi, xo) {
this.acls = acls;
this.users = users;
{
let usersById = this.usersById = Object.create(null);
for (let user of users) {
usersById[user.id] = user;
}
}
this.objects = xoApi.all;
let refreshAcls = () => {
xo.acl.get().then(acls => {
this.acls = acls;
});
};
this.getUser = (id) => {
for (let user of this.users) {
if (user.id === id) {
return user;
}
}
};
this.addAcl = () => {
xo.acl.add(this.subject.id, this.object.id).then(refreshAcls);
};
this.removeAcl = (subject, object) => {
xo.acl.remove(subject, object).then(refreshAcls);
};
})
.filter('selectHighLevel', () => {
const HIGH_LEVEL_OBJECTS = {
pool: true,
host: true,
VM: true,
SR: true,
};
let isHighLevel = (object) => HIGH_LEVEL_OBJECTS[object.type];
return (objects) => filter(objects, isHighLevel);
})
.name
;

View File

@@ -0,0 +1,14 @@
//- .container-fluid: .row
//- //- Side menu
//- .col-md-2.acl-menu: .panel.panel-default: .panel-body: .side-menu
//- ul.nav
//- li
//- a(ui-sref = '.acls', ui-sref-active = 'active')
//- i.fa.fa-fw.fa-users
//- | ACLs
//- //- Content
//- .col-md-10: div(ui-view = '')
div(ui-view = '')

View File

@@ -1,179 +0,0 @@
angular = require 'angular'
#=====================================================================
# FIXME: Mutualize the code between users and servers.
# FIXME: should be merged in admin module.
module.exports = angular.module 'xoWebApp.settings', [
require 'angular-ui-router'
]
.config ($stateProvider) ->
$stateProvider.state 'settings',
url: '/settings'
controller: 'SettingsCtrl'
template: require './view'
.controller 'SettingsCtrl', ($scope, xo) ->
$scope.permissions = [
{
label: 'User'
value: 'none'
}
{
label: 'Admin'
value: 'admin'
}
]
# Users
do ->
# Fetches them.
$scope.users = []
xo.user.getAll().then (users) ->
$scope.users = users
# Which ones are selected?
selected = $scope.selectedUsers = {}
# New users to create.
$scope.newUsers = []
# Add a new user to be created.
$scope.addUser = ->
$scope.newUsers.push {
# Fake (unique) identifier needed by Angular.JS
id: Math.random()
# Default permission.
permission: 'none'
}
$scope.addUser()
# Saves any modifications.
$scope.saveUsers = ->
{users, newUsers} = $scope
# This will be the new list of users with those marked to
# delete removed.
updateUsers = []
for user in users
{id} = user
if selected[id]
delete selected[id]
# FIXME: this cast should not be necessary.
xo.user.delete "#{id}"
else
# Only sets the password if not empty.
delete user.password unless user.password
# TODO: only update users which have been modified.
xo.user.set user
# Remove the password from the interface.
delete user.password
updateUsers.push user
for user in newUsers
{email, permission, password} = user
# Required field.
continue unless email
# Sends the order to XO-Server.
xo.user.create {email, permission, password}
.then (id) ->
# Update user identifier.
user.id = id
return
# The password should not be displayed.
delete user.password
# Adds the user to out local list.
updateUsers.push user
$scope.users = updateUsers
$scope.newUsers = []
$scope.addUser()
# TODO: Retrieves an up to date users list from the server.
# Servers
do ->
# Fetches them.
$scope.servers = []
refreshServers = ->
xo.server.getAll().then (servers) ->
$scope.servers = servers
refreshServers()
# Which ones are selected?
selected = $scope.selectedServers = {}
# New servers to create.
$scope.newServers = []
# Add a new server to be created.
$scope.addServer = ->
$scope.newServers.push {
# Fake (unique) identifier needed by Angular.JS
id: Math.random()
}
$scope.addServer()
# Saves any modifications.
$scope.saveServers = ->
{servers, newServers} = $scope
# This will be the new list of servers with those marked to
# delete removed.
updateServers = []
for server in servers
{id} = server
if selected[id]
delete selected[id]
xo.server.remove id
else
# Only sets the password if not empty.
delete server.password unless server.password
# TODO: only update servers which have been modified.
xo.server.set server
# Remove the password from the interface.
delete server.password
updateServers.push server
for server in newServers
{host, username, password} = server
# Required field.
continue unless host
# Sends the order to XO-Server.
xo.server.add {host, username, password}
.then (id) ->
server.id = id
return
# The password should not be displayed.
delete server.password
# Adds the server to out local list.
updateServers.push server
$scope.servers = updateServers
$scope.newServers = []
$scope.addServer()
# TODO: Retrieves an up to date servers list from the server.
# A module exports its name.
.name

View File

@@ -0,0 +1,33 @@
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import acls from './acls';
import servers from './servers';
import users from './users';
import view from './view';
export default angular.module('settings', [
uiRouter,
acls,
servers,
users,
])
.config(function ($stateProvider) {
$stateProvider.state('settings', {
abstract: true,
template: view,
url: '/settings',
});
// Redirect to default sub-state.
$stateProvider.state('settings.index', {
url: '',
controller: function ($state) {
$state.go('settings.servers');
}
});
})
.name
;

View File

@@ -0,0 +1,106 @@
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import uiSelect from 'angular-ui-select';
import filter from 'lodash.filter';
import xoApi from 'xo-api';
import xoServices from 'xo-services';
import view from './view';
export default angular.module('settings.servers', [
uiRouter,
uiSelect,
xoApi,
xoServices,
])
.config(function ($stateProvider) {
$stateProvider.state('settings.servers', {
controller: 'SettingsServers as ctrl',
url: '/servers',
resolve: {
servers(xo) {
return xo.server.getAll();
},
},
template: view,
});
})
.controller('SettingsServers', function ($scope, $interval, servers, xoApi, xo, notify) {
this.servers = servers;
const selected = this.selectedServers = {};
const newServers = this.newServers = [];
const refreshServers = () => {
xo.server.getAll().then(servers => {
this.servers = servers;
});
};
const interval = $interval(refreshServers, 5e3)
$scope.$on('$destroy', () => {
$interval.cancel(interval)
})
this.addServer = () => {
newServers.push({
// Fake (unique) id needed by Angular.JS
id: Math.random(),
status: 'connecting'
});
};
this.addServer();
this.saveServers = () => {
const newServers = this.newServers;
const servers = this.servers;
const updateServers = [];
for (let i = 0, len = servers.length; i < len; i++) {
const server = servers[i];
const {id} = server;
if (selected[id]) {
delete selected[id];
xo.server.remove(id);
}
else {
if (!server.password) {
delete server.password;
}
xo.server.set(server);
delete server.password;
updateServers.push(server);
}
}
for (let i = 0, len = newServers.length; i < len; i++) {
const server = newServers[i];
const {host, username, password} = server;
if (!host) {
continue;
}
xo.server.add({
host,
username,
password,
autoConnect: false,
}).then(function(id) {
server.id = id;
xo.server.connect(id).catch(error => {
notify.error({
title: 'Server connection error',
message: error.message
});
});
});
delete server.password;
updateServers.push(server);
}
this.servers = updateServers;
this.newServers.length = 0;
this.addServer();
};
})
.name
;

View File

@@ -0,0 +1,58 @@
.grid
.panel.panel-default
.panel-heading.panel-title
i.fa.fa-cloud(style="color: #e25440;")
| Servers
form(ng-submit="ctrl.saveServers()", autocomplete="off").panel-body
table.table.table-hover
tr
th.col-md-5 Host
th.col-md-3 User
th.col-md-3 Password
th.col-md-1.text-center
i.fa.fa-trash-o.fa-lg(tooltip="Forget server")
tr(ng-repeat="server in ctrl.servers | orderBy:natural('host') track by server.id")
td
.input-group
span.input-group-addon(ng-if="server.status === 'connected'")
i.fa.fa-check-circle.fa-lg.text-success(tooltip="Connected")
span.input-group-addon(ng-if="server.status === 'disconnected'")
i.fa.fa-times-circle.fa-lg.text-danger(tooltip="Disconnected")
span.input-group-addon(ng-if="server.status === 'connecting'")
i.fa.fa-cog.fa-lg.fa-spin(tooltip="Connecting...")
input.form-control(type="text", ng-model="server.host")
td
input.form-control(type="text", ng-model="server.username")
td
input.form-control(type="password", ng-model="server.password", placeholder="Fill to change the password")
td.text-center
input(type="checkbox", ng-model="ctrl.selectedServers[server.id]")
tr(ng-repeat="server in ctrl.newServers")
td
input.form-control(
type = "text"
ng-model = "server.host"
placeholder = "address[:port]"
)
td
input.form-control(
type = "text"
ng-model = "server.username"
ng-required = "server.host"
placeholder = "user"
)
td
input.form-control(
type="password"
ng-model="server.password"
ng-required = "server.host"
placeholder="password"
)
td &#160;
p.text-center
button.btn.btn-primary(type="submit")
i.fa.fa-save
| Save
| &nbsp;
button.btn.btn-success(type="button", ng-click="ctrl.addServer()")
i.fa.fa-plus

View File

@@ -0,0 +1,35 @@
import angular from 'angular';
import uiRouter from 'angular-ui-router';
import uiSelect from 'angular-ui-select';
import filter from 'lodash.filter';
import xoApi from 'xo-api';
import xoServices from 'xo-services';
import view from './view';
export default angular.module('settings.users', [
uiRouter,
uiSelect,
xoApi,
xoServices,
])
.config(function ($stateProvider) {
$stateProvider.state('settings.users', {
controller: 'SettingsUsers as ctrl',
url: '/users',
resolve: {
users(xo) {
return xo.acl.get();
},
},
template: view,
});
})
.controller('SettingsUsers', function ($scope, users, xoApi, xo) {
this.users = users;
})
.name
;

View File

@@ -0,0 +1,50 @@
.grid
.panel.panel-default
.panel-heading.panel-title
i.fa.fa-users(style="color: #e25440;")
| Users
form(ng-submit="saveUsers()", autocomplete="off").panel-body
table.table.table-hover
tr
th.col-md-4 Email
th.col-md-4 Permissions
th.col-md-3 Password
th.col-md-1.text-center
i.fa.fa-trash-o.fa-lg(tooltip="Remove user")
tr(ng-repeat="user in users | orderBy:natural('email') track by user.id")
td
input.form-control(type="text", ng-model="user.email")
td
select.form-control(ng-options="p.value as p.label for p in permissions", ng-model="user.permission")
td
input.form-control(type="password", ng-model="user.password", placeholder="Fill to change the password")
td.text-center
input(type="checkbox", ng-model="selectedUsers[user.id]")
tr(ng-repeat="user in newUsers")
td
input.form-control(
type = "text"
ng-model = "user.email"
placeholder = "email"
)
td
select.form-control(
ng-options = "p.value as p.label for p in permissions"
ng-model = "user.permission"
ng-required = "user.email"
)
td
input.form-control(
type = "password"
ng-model = "user.password"
ng-required = "user.email"
placeholder = "password"
)
td &#160;
p.text-center
button.btn.btn-primary(type="submit")
i.fa.fa-save
| Save
| &nbsp;
button.btn.btn-success(type="button", ng-click="addUser()")
i.fa.fa-plus

View File

@@ -1,114 +1,17 @@
//- TODO: lots of stuff.
.grid
.panel.panel-default
p.page-title
i.fa.fa-cog
| XO Settings
//- Add server panel
.grid
.panel.panel-default
.panel-heading.panel-title
i.fa.fa-cloud(style="color: #e25440;")
| Servers
form(ng-submit="saveServers()", autocomplete="off").panel-body
table.table.table-hover
tr
th.col-md-5 Host
th.col-md-3 User
th.col-md-3 Password
th.col-md-1.text-center
i.fa.fa-trash-o.fa-lg(tooltip="Forget server")
tr(ng-repeat="server in servers | orderBy:natural('host') track by server.id")
td
.input-group
span.input-group-addon(ng-if="server.status === 'connected'")
i.fa.fa-check-circle.fa-lg.text-success(tooltip="Connected")
span.input-group-addon(ng-if="server.status === 'disconnected'")
i.fa.fa-times-circle.fa-lg.text-danger(tooltip="Disconnected")
span.input-group-addon(ng-if="server.status === 'connecting'")
i.fa.fa-circleo-notch.fa-lg.fa-spin(tooltip="Connecting...")
input.form-control(type="text", ng-model="server.host")
td
input.form-control(type="text", ng-model="server.username")
td
input.form-control(type="password", ng-model="server.password", placeholder="Fill to change the password")
td.text-center
input(type="checkbox", ng-model="selectedServers[server.id]")
tr(ng-repeat="server in newServers")
td
input.form-control(
type = "text"
ng-model = "server.host"
placeholder = "address[:port]"
)
td
input.form-control(
type = "text"
ng-model = "server.username"
ng-required = "server.host"
placeholder = "user"
)
td
input.form-control(
type="password"
ng-model="server.password"
ng-required = "server.host"
placeholder="password"
)
td &#160;
p.text-center
button.btn.btn-primary(type="submit")
i.fa.fa-save
| Save
| &nbsp;
button.btn.btn-success(type="button", ng-click="addServer()")
i.fa.fa-plus
.panel.panel-default
.panel-heading.panel-title
i.fa.fa-users(style="color: #e25440;")
| Users
form(ng-submit="saveUsers()", autocomplete="off").panel-body
table.table.table-hover
tr
th.col-md-4 Email
th.col-md-4 Permissions
th.col-md-3 Password
th.col-md-1.text-center
i.fa.fa-trash-o.fa-lg(tooltip="Remove user")
tr(ng-repeat="user in users | orderBy:natural('email') track by user.id")
td
input.form-control(type="text", ng-model="user.email")
td
select.form-control(ng-options="p.value as p.label for p in permissions", ng-model="user.permission")
td
input.form-control(type="password", ng-model="user.password", placeholder="Fill to change the password")
td.text-center
input(type="checkbox", ng-model="selectedUsers[user.id]")
tr(ng-repeat="user in newUsers")
td
input.form-control(
type = "text"
ng-model = "user.email"
placeholder = "email"
)
td
select.form-control(
ng-options = "p.value as p.label for p in permissions"
ng-model = "user.permission"
ng-required = "user.email"
)
td
input.form-control(
type = "password"
ng-model = "user.password"
ng-required = "user.email"
placeholder = "password"
)
td &#160;
p.text-center
button.btn.btn-primary(type="submit")
i.fa.fa-save
| Save
| &nbsp;
button.btn.btn-success(type="button", ng-click="addUser()")
i.fa.fa-plus
//- Side menu
.settings-menu
ul.nav
li
a(ui-sref = '.servers', ui-sref-active = 'active')
i.fa.fa-fw.fa-cloud &nbsp;
| Servers
a(ui-sref = '.users')
i.fa.fa-fw.fa-users &nbsp;
| Users
a(ui-sref = '.acls')
i.fa.fa-fw.fa-key &nbsp;
| ACLs
//- Content
div.settings-content(ui-view = '')

View File

@@ -118,7 +118,7 @@ div(ng-if="!pools.length")
h1 Welcome on Xen Orchestra!
h3 It seems you aren't connected to any Xen server:
br
a.btn.btn-success.big(ui-sref="settings")
a.btn.btn-success.big(ui-sref="settings.index")
i.fa.fa-plus-circle
| Add server
br

View File

@@ -88,6 +88,7 @@ module.exports = angular.module 'xoWebApp.services', [
argsMapper: (subject, object) => {subject, object},
})
pool:
disconnect: action 'Disconnect pool'
new_sr: action 'New SR' #temp fix before creating SR
@@ -124,6 +125,8 @@ module.exports = angular.module 'xoWebApp.services', [
set: action 'Save server', 'server.set', {
argsMapper: (params) -> angular.copy(params)
}
connect: action 'Connect to a server', 'server.connect', notification: false, argsMapper: (id) -> {id}
disconnect: action 'Disconnect from a server', 'server.disconnect', argsMapper: (id) -> {id}
task:
cancel: action 'Cancel task', 'task.cancel', argsMapper: (id) -> {id}

View File

@@ -605,3 +605,37 @@ img.navbar-logo {
font-variant: small-caps;
padding-top: 0.8em;
}
//////////////////////////////////////////////////////////////////////
// Settings
//////////////////////////////////////////////////////////////////////
.settings-menu {
position: fixed;
padding-top: 1em;
top: 51px;
bottom: 0;
left: 0;
z-index: 1000;
display: block;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
background-color: #242628;
border-right: 1px solid #eee;
width: 212px;
}
.settings-menu li a {
font-size: 1em;
padding-: 1em;
padding-left: 2em;
display: block;
color: #f8f8f8;
&:hover, &:focus {
background-color: #2e3133;
}
}
.settings-content {
margin-left: 212px;
}