You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by an...@apache.org on 2015/07/22 13:00:30 UTC

[02/10] incubator-ignite git commit: # ignite-843 moved

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/routes/metadata.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/routes/metadata.js b/modules/web-control-center/src/main/js/routes/metadata.js
new file mode 100644
index 0000000..64b8763
--- /dev/null
+++ b/modules/web-control-center/src/main/js/routes/metadata.js
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+var router = require('express').Router();
+var db = require('../db');
+
+/* GET metadata page. */
+router.get('/', function (req, res) {
+    res.render('configuration/metadata');
+});
+
+/**
+ * Get spaces and metadata accessed for user account.
+ *
+ * @param req Request.
+ * @param res Response.
+ */
+router.post('/list', function (req, res) {
+    var user_id = req.currentUserId();
+
+    // Get owned space and all accessed space.
+    db.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) {
+        if (err)
+            return res.status(500).send(err.message);
+
+        var space_ids = spaces.map(function (value) {
+            return value._id;
+        });
+
+        // Get all metadata for spaces.
+        db.CacheTypeMetadata.find({space: {$in: space_ids}}).sort('name').exec(function (err, metadatas) {
+            if (err)
+                return res.status(500).send(err.message);
+
+            res.json({spaces: spaces, metadatas: metadatas});
+        });
+    });
+});
+
+/**
+ * Save metadata.
+ */
+router.post('/save', function (req, res) {
+    if (req.body._id)
+        db.CacheTypeMetadata.update({_id: req.body._id}, req.body, {upsert: true}, function (err) {
+            if (err)
+                return res.status(500).send(err.message);
+
+            res.send(req.body._id);
+        });
+    else {
+        db.CacheTypeMetadata.findOne({name: req.body.name}, function (err, metadata) {
+            if (err)
+                return res.status(500).send(err.message);
+
+            if (metadata)
+                return res.status(500).send('Cache type metadata with name: "' + metadata.name + '" already exist.');
+
+            (new db.CacheTypeMetadata(req.body)).save(function (err, metadata) {
+                if (err)
+                    return res.status(500).send(err.message);
+
+                res.send(metadata._id);
+            });
+        });
+    }
+});
+
+/**
+ * Remove metadata by ._id.
+ */
+router.post('/remove', function (req, res) {
+    db.CacheTypeMetadata.remove(req.body, function (err) {
+        if (err)
+            return res.status(500).send(err.message);
+
+        res.sendStatus(200);
+    })
+});
+
+module.exports = router;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/routes/profile.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/routes/profile.js b/modules/web-control-center/src/main/js/routes/profile.js
new file mode 100644
index 0000000..0269e7d
--- /dev/null
+++ b/modules/web-control-center/src/main/js/routes/profile.js
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+var router = require('express').Router();
+var db = require('../db');
+
+router.all('/profile/*', function (req, res, next) {
+    var userId = req.body._id;
+
+    if (userId != req.currentUserId() && userId != req.user._id)
+        return res.sendStatus(403);
+    else
+        next();
+});
+
+/**
+ * Get user profile page.
+ */
+router.get('/', function (req, res) {
+    var user_id = req.currentUserId();
+
+    db.Account.findById(user_id, function (err) {
+        if (err)
+            return res.status(500).send(err.message);
+
+        res.render('settings/profile');
+    });
+});
+
+/**
+ * Save user profile.
+ */
+router.post('/saveUser', function (req, res) {
+    var params = req.body;
+
+    if (params.newPassword) {
+        var newPassword = params.newPassword;
+
+        if (!newPassword || newPassword.length == 0)
+            return res.status(500).send('Wrong value for new password');
+
+        db.Account.findById(params._id, function (err, user) {
+            if (err)
+                return res.status(500).send(err);
+
+            user.setPassword(newPassword, function (err, updatedUser) {
+                if (err)
+                    return res.status(500).send(err.message);
+
+                if (params.userName)
+                    updatedUser.username = params.userName;
+
+                if (params.email)
+                    updatedUser.email = params.email;
+
+                updatedUser.save(function (err) {
+                    if (err)
+                        return res.status(500).send(err.message);
+
+                    res.json(user);
+                });
+            });
+        });
+    }
+    else if (params.userName || params.email) {
+        var upd = {};
+
+        if (params.userName)
+            upd.username = params.userName;
+
+        if (params.email)
+            upd.email = params.email;
+
+        db.Account.findByIdAndUpdate(params._id, upd, {new: true}, function (err, val) {
+            if (err)
+                return res.status(500).send(err.message);
+
+            res.json(val);
+        })
+    }
+});
+
+module.exports = router;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/routes/public.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/routes/public.js b/modules/web-control-center/src/main/js/routes/public.js
new file mode 100644
index 0000000..290ba90
--- /dev/null
+++ b/modules/web-control-center/src/main/js/routes/public.js
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+var router = require('express').Router();
+var passport = require('passport');
+var db = require('../db');
+
+// GET dropdown-menu template.
+router.get('/select', function (req, res) {
+    res.render('templates/select', {});
+});
+
+// GET tabs template.
+router.get('/tab', function (req, res) {
+    res.render('templates/tab', {});
+});
+
+// GET confirmation dialog.
+router.get('/confirm', function (req, res) {
+    res.render('templates/confirm', {});
+});
+
+// GET save as dialog.
+router.get('/copy', function (req, res) {
+    res.render('templates/copy', {});
+});
+
+/* GET login page. */
+router.get('/login', function (req, res) {
+    res.render('login');
+});
+
+/**
+ * Register new account.
+ */
+router.post('/register', function (req, res) {
+    db.Account.count(function (err, cnt) {
+        if (err)
+            return res.status(401).send(err.message);
+
+        req.body.admin = cnt == 0;
+
+        db.Account.register(new db.Account(req.body), req.body.password, function (err, account) {
+            if (err)
+                return res.status(401).send(err.message);
+
+            if (!account)
+                return res.status(500).send('Failed to create account.');
+
+            new db.Space({name: 'Personal space', owner: account._id}).save();
+
+            req.logIn(account, {}, function (err) {
+                if (err)
+                    return res.status(401).send(err.message);
+
+                return res.redirect('/configuration/clusters');
+            });
+        });
+    });
+});
+
+/**
+ * Login in exist account.
+ */
+router.post('/login', function (req, res, next) {
+    passport.authenticate('local', function (err, user) {
+        if (err)
+            return res.status(401).send(err.message);
+
+        if (!user)
+            return res.status(401).send('Invalid email or password');
+
+        req.logIn(user, {}, function (err) {
+            if (err)
+                return res.status(401).send(err.message);
+
+            res.redirect('/configuration/clusters');
+        });
+    })(req, res, next);
+});
+
+/**
+ * Logout.
+ */
+router.get('/logout', function (req, res) {
+    req.logout();
+
+    res.redirect('/');
+});
+
+/* GET home page. */
+router.get('/', function (req, res) {
+    if (req.isAuthenticated())
+        res.redirect('/configuration/clusters');
+    else
+        res.render('index');
+});
+
+///* GET sql page. */
+//router.get('/sql', function(req, res) {
+//    res.render('sql', { user: req.user });
+//});
+//
+///* GET clients page. */
+//router.get('/clients', function(req, res) {
+//    res.render('clients', { user: req.user });
+//});
+
+module.exports = router;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/routes/sql.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/routes/sql.js b/modules/web-control-center/src/main/js/routes/sql.js
new file mode 100644
index 0000000..ce4565d
--- /dev/null
+++ b/modules/web-control-center/src/main/js/routes/sql.js
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+var router = require('express').Router();
+var db = require('../db');
+router.get('/', function(req, res) {
+    res.render('sql/sql');
+});
+
+module.exports = router;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/routes/summary.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/routes/summary.js b/modules/web-control-center/src/main/js/routes/summary.js
new file mode 100644
index 0000000..f766945
--- /dev/null
+++ b/modules/web-control-center/src/main/js/routes/summary.js
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+var db = require('../db');
+
+var router = require('express').Router();
+
+var generatorXml = require('./generator/xml');
+var generatorJava = require('./generator/java');
+var generatorDocker = require('./generator/docker');
+
+/* GET summary page. */
+router.get('/', function (req, res) {
+    res.render('configuration/summary');
+});
+
+router.post('/generator', function (req, res) {
+    // Get cluster.
+    db.Cluster.findById(req.body._id).populate('caches').exec(function (err, cluster) {
+        if (err)
+            return res.status(500).send(err.message);
+
+        if (!cluster)
+            return res.sendStatus(404);
+
+        var clientCache = req.body.clientNearConfiguration;
+
+        if (clientCache)
+            return res.send({
+                xmlClient: generatorXml.generateClusterConfiguration(cluster, clientCache),
+                javaClient: generatorJava.generateClusterConfiguration(cluster, req.body.javaClass, clientCache)
+            });
+
+        return res.send({
+            xmlServer: generatorXml.generateClusterConfiguration(cluster),
+            javaSnippetServer: generatorJava.generateClusterConfiguration(cluster, false),
+            javaClassServer: generatorJava.generateClusterConfiguration(cluster, true),
+            docker: generatorDocker.generateClusterConfiguration(cluster, '%OS%')
+        });
+    });
+});
+
+router.post('/download', function (req, res) {
+    // Get cluster.
+    db.Cluster.findById(req.body._id).populate('caches').exec(function (err, cluster) {
+        if (err)
+            return res.status(500).send(err.message);
+
+        if (!cluster)
+            return res.sendStatus(404);
+
+        var clientNearConfiguration = req.body.clientNearConfiguration;
+
+        var archiver = require('archiver');
+
+        // Creating archive.
+        var zip = archiver('zip');
+
+        zip.on('error', function (err) {
+            res.status(500).send({error: err.message});
+        });
+
+        // On stream closed we can end the request.
+        res.on('close', function () {
+            return res.status(200).send('OK').end();
+        });
+
+        // Set the archive name.
+        res.attachment(cluster.name + (clientNearConfiguration ? '-client' : '') + '-configuration.zip');
+
+        var generatorCommon = require('./generator/common');
+
+        // Send the file to the page output.
+        zip.pipe(res);
+
+        var javaClass = req.body.javaClass;
+
+        if (!clientNearConfiguration) {
+            zip.append(generatorDocker.generateClusterConfiguration(cluster, req.body.os), {name: "Dockerfile"});
+
+            var props = generatorCommon.generateProperties(cluster);
+
+            if (props)
+                zip.append(props, {name: "secret.properties"});
+        }
+
+        zip.append(generatorXml.generateClusterConfiguration(cluster, clientNearConfiguration), {name: cluster.name + ".xml"})
+            .append(generatorJava.generateClusterConfiguration(cluster, javaClass, clientNearConfiguration),
+                {name: javaClass ? 'ConfigurationFactory.java' : cluster.name + '.snipplet.java'})
+            .finalize();
+    });
+});
+
+module.exports = router;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/configuration/caches.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/configuration/caches.jade b/modules/web-control-center/src/main/js/views/configuration/caches.jade
new file mode 100644
index 0000000..3a011fc
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/configuration/caches.jade
@@ -0,0 +1,74 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends sidebar
+
+append scripts
+    script(src='/caches-controller.js')
+
+include ../includes/controls
+
+block content
+    .docs-header
+        h1 Create and Configure Ignite Caches
+        hr
+    .docs-body(ng-controller='cachesController')
+        +block-callout('{{screenTip.workflowTitle}}', 'joinTip(screenTip.workflowContent)', '{{screenTip.whatsNextTitle}}', 'joinTip(screenTip.whatsNextContent)')
+        .links(ng-hide='caches.length == 0')
+            .padding-dflt
+                lable.labelHeader Caches:
+                table(st-table='caches')
+                    tbody
+                        tr(ng-repeat='row in caches track by row._id')
+                            td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
+                                a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}, {{row.mode | displayValue:modes:'Cache mode not set'}}, {{row.atomicityMode | displayValue:atomicities:'Cache atomicity not set'}}
+        .padding-top-dflt
+            button.btn.btn-primary(ng-click='createItem()') Add cache
+        hr
+        form.form-horizontal(name='inputForm' ng-if='backupItem' novalidate)
+            div(bs-collapse data-start-collapsed='false')
+                .panel.panel-default
+                    .panel-heading
+                        h3
+                            a(bs-collapse-toggle) General
+                    .panel-collapse(bs-collapse-target)
+                        .panel-body
+                            .settings-row(ng-repeat='field in general')
+                                +form-row(['col-sm-3'], ['col-sm-3'])
+            .panel-group(bs-collapse data-allow-multiple="true")
+                div(bs-collapse data-start-collapsed='true')
+                    .panel-title(ng-show='expanded')
+                        h3
+                            a(bs-collapse-toggle='0' ng-click='expanded = !expanded;') {{expanded ? 'Hide advanced settings...' : 'Show advanced settings...'}}
+                    .panel-collapse(bs-collapse-target)
+                        .span(bs-collapse data-start-collapsed='true' data-allow-multiple='true')
+                            .panel.panel-default(ng-repeat='group in advanced')
+                                .panel-heading
+                                    h3
+                                        a(bs-collapse-toggle) {{group.label}}
+                                        i.tipLabel.fa.fa-question-circle(ng-if='group.tip' bs-tooltip='joinTip(group.tip)' type='button')
+                                        i.tipLabel.fa.fa-question-circle.blank(ng-if='!group.tip')
+                                .panel-collapse(bs-collapse-target)
+                                    .panel-body
+                                        .settings-row(ng-repeat='field in group.fields')
+                                            +form-row
+                    .panel-title
+                        h3
+                            a(bs-collapse-toggle='0' ng-click='expanded = !expanded;') {{expanded ? 'Hide advanced settings...' : 'Show advanced settings...'}}
+            div
+                button.btn.btn-primary(ng-disabled='inputForm.$invalid' ng-click='saveItem()') Save
+                button.btn.btn-primary(ng-show='backupItem._id' ng-disabled='inputForm.$invalid' ng-click='saveItemAs()') Copy
+                button.btn.btn-primary.btn-second(ng-show='backupItem._id' ng-click='removeItem()') Remove

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/configuration/clusters.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/configuration/clusters.jade b/modules/web-control-center/src/main/js/views/configuration/clusters.jade
new file mode 100644
index 0000000..81acfed
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/configuration/clusters.jade
@@ -0,0 +1,77 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends sidebar
+
+append scripts
+    script(src='/clusters-controller.js')
+
+include ../includes/controls
+
+block content
+    .docs-header
+        h1 Create and Configure Ignite Clusters
+        hr
+    .docs-body(ng-controller='clustersController')
+        +block-callout('{{screenTip.workflowTitle}}', 'joinTip(screenTip.workflowContent)', '{{screenTip.whatsNextTitle}}', 'joinTip(screenTip.whatsNextContent)')
+        .links(ng-hide='clusters.length == 0')
+            .padding-dflt
+                lable.labelHeader Clusters:
+                table(st-table='clusters')
+                    tbody
+                        tr(ng-repeat='row in clusters track by row._id')
+                            td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
+                                a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}, {{row.discovery.kind | displayValue:discoveries:'Discovery not set'}}
+        .padding-top-dflt
+            button.btn.btn-primary(ng-click='createItem()') &nbspAdd cluster
+            label(style='margin-left: 10px; margin-right: 10px') Use template:
+            button.btn.btn-default.base-control(ng-init='create.template = templates[0].value' ng-model='create.template' data-template='/select' data-placeholder='Choose cluster template' bs-options='item.value as item.label for item in templates' bs-select)
+            i.tiplabel.fa.fa-question-circle(bs-tooltip data-title='{{joinTip(templateTip)}}' type='button')
+        hr
+        form.form-horizontal(name='inputForm' ng-if='backupItem' novalidate)
+            div(bs-collapse data-start-collapsed='false')
+                .panel.panel-default
+                    .panel-heading
+                        h3
+                            a(bs-collapse-toggle) General
+                    .panel-collapse(bs-collapse-target)
+                        .panel-body
+                            .settings-row(ng-repeat='field in general')
+                                +form-row
+            .panel-group(bs-collapse data-allow-multiple="true")
+                div(bs-collapse data-start-collapsed='true')
+                    .panel-title(ng-show='expanded')
+                        h3
+                            a(bs-collapse-toggle='0' ng-click='expanded = !expanded;') {{expanded ? 'Hide advanced settings...' : 'Show advanced settings...'}}
+                    .panel-collapse(bs-collapse-target)
+                        .span(bs-collapse data-start-collapsed='true' data-allow-multiple='true')
+                            .panel.panel-default(ng-repeat='group in advanced')
+                                .panel-heading
+                                    h3
+                                        a(bs-collapse-toggle) {{group.label}}
+                                        i.tipLabel.fa.fa-question-circle(ng-if='group.tip' bs-tooltip='joinTip(group.tip)' type='button')
+                                        i.tipLabel.fa.fa-question-circle.blank(ng-if='!group.tip')
+                                .panel-collapse(bs-collapse-target)
+                                    .panel-body
+                                        .settings-row(ng-repeat='field in group.fields')
+                                            +form-row
+                    .panel-title
+                        h3
+                            a(bs-collapse-toggle='0' ng-click='expanded = !expanded;') {{expanded ? 'Hide advanced settings...' : 'Show advanced settings...'}}
+            div
+                button.btn.btn-primary(ng-disabled='inputForm.$invalid' ng-click='saveItem()') Save
+                button.btn.btn-primary(ng-show='backupItem._id' ng-disabled='inputForm.$invalid' ng-click='saveItemAs()') Copy
+                button.btn.btn-primary(ng-show='backupItem._id' ng-click='removeItem()') Remove
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/configuration/metadata.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/configuration/metadata.jade b/modules/web-control-center/src/main/js/views/configuration/metadata.jade
new file mode 100644
index 0000000..d5bac06
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/configuration/metadata.jade
@@ -0,0 +1,121 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends sidebar
+
+append scripts
+    script(src='/metadata-controller.js')
+
+include ../includes/controls
+
+block content
+    .docs-header
+        h1 Create and Configure Cache Type Metadata
+        hr
+    .docs-body(ng-controller='metadataController')
+        +block-callout('{{screenTip.workflowTitle}}', 'joinTip(screenTip.workflowContent)', '{{screenTip.whatsNextTitle}}', 'joinTip(screenTip.whatsNextContent)')
+        .links(ng-hide='metadatas.length == 0')
+            .padding-dflt
+                lable.labelHeader Types metadata:
+                table(st-table='metadatas')
+                    tbody
+                        tr(ng-repeat='row in metadatas track by row._id')
+                            td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
+                                a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}
+        .padding-top-dflt
+            button.btn.btn-primary(ng-click='panels.activePanel = [0]; createItem()') &nbspAdd metadata
+            label(style='margin-left: 6px; margin-right: 10px') For:
+            button.btn.btn-default(ng-model='template' data-template='/select' data-placeholder='Choose metadata type' bs-options='item.value as item.label for item in templates' bs-select)
+            i.tiplabel.fa.fa-question-circle(bs-tooltip data-title='{{joinTip(templateTip)}}' type='button')
+        hr
+        .panel-group(bs-collapse ng-model='panels.activePanel' data-allow-multiple="false")
+            .panel.panel-default
+                .panel-heading
+                    h3
+                        a(bs-collapse-toggle) Manual
+                .panel-collapse(role="tabpanel" bs-collapse-target)
+                    .panel-body
+                        form.form-horizontal(name='manualForm' ng-if='backupItem' novalidate)
+                            .settings-row(ng-repeat='field in metadataManual')
+                                +form-row
+                            button.btn.btn-primary(ng-disabled='manualForm.$invalid' ng-click='saveItem()') Save
+                            button.btn.btn-primary(ng-show='backupItem._id' ng-disabled='inputForm.$invalid' ng-click='saveItemAs()') Copy
+                            button.btn.btn-primary.btn-second(ng-show='backupItem._id' ng-click='removeItem()') Remove
+            .panel.panel-default
+                .panel-heading
+                    h3
+                        a(bs-collapse-toggle) Load from database
+                .panel-collapse(bs-collapse-target)
+                    .panel-body
+                        form.form-horizontal(name='dbForm' novalidate)
+                            .settings-row(ng-repeat='field in metadataDb')
+                                +form-row
+                        div(ng-hide='data.tables.length == 0')
+                            table.table-bordered.table-condensed.links-edit-small-padding.col-sm-12(st-table='data.tables')
+                                thead
+                                    tr
+                                        th.col-sm-3 Schema/Table
+                                        th Key class
+                                        th Value class
+                                tbody
+                                    tr(ng-repeat='row in data.tables')
+                                        td(colspan='{{row.tableName ? 1 : 3}}')
+                                            div.checkbox(ng-if='!row.tableName')
+                                                label(ng-click='selectSchema($index)')
+                                                    input(type='checkbox' ng-checked='row.use')
+                                                    | {{row.schemaName}}
+                                            div.checkbox(ng-if='row.tableName')
+                                                label(style='padding-left: 30px' ng-click='selectTable($index)')
+                                                    input(type='checkbox' ng-checked = 'row.use')
+                                                    | {{row.tableName}}
+                                        td(ng-if='row.tableName')
+                                            a(ng-show='data.curTableIdx != $index' ng-click='selectTable($index)') {{row.keyClass}}
+                                            input.form-control(type='text' ng-show='data.curTableIdx == $index' ng-model='data.curKeyClass' placeholder='Key class full name')
+                                        td(ng-if='row.tableName')
+                                            a(ng-show='data.curTableIdx != $index' ng-click='selectTable($index)') {{row.valueClass}}
+                                            input.form-control(type='text' ng-show='data.curTableIdx == $index' ng-model='data.curValueClass' placeholder='Value class full name')
+                        //div(ng-hide='data.curTableIdx < 0')
+                        //    table.table-bordered.table-condensed.links-edit-small-padding.col-sm-12(st-table='data.tables[data.curTableIdx].fields')
+                        //        thead
+                        //            tr
+                        //                th(style='width:45px') Use
+                        //                th(style='width:45px') Key
+                        //                th(style='width:45px') Ak
+                        //                th DB Name
+                        //                th DB Type
+                        //                th Java Name
+                        //                th Java Type
+                        //        tbody
+                        //            tr(ng-repeat='row in data.tables[data.curTableIdx].fields')
+                        //                td
+                        //                    +dbcheck('row.use')
+                        //                td
+                        //                    +dbcheck('row.key')
+                        //                td
+                        //                    +dbcheck('row.ak')
+                        //                td
+                        //                    label {{row.dbName}}
+                        //                td
+                        //                    label {{row.dbType}}
+                        //                td
+                        //                    a(ng-show='data.curFieldIdx != $index' ng-click='selectField($index)') {{row.javaName}}
+                        //                    input.form-control(type='text' ng-show='data.curFieldIdx == $index' ng-model='data.curJavaName' placeholder='Field Java name')
+                        //                td
+                        //                    a(ng-show='data.curFieldIdx != $index' ng-click='selectField($index)') {{row.javaType}}
+                        //                    input.form-control(type='text' ng-show='data.curFieldIdx == $index' ng-model='data.curJavaType' placeholder='Field Java type')
+                        button.btn.btn-primary(ng-disabled='dbForm.$invalid' ng-click='saveItem()') Save
+                        button.btn.btn-primary.btn-second(ng-show='backupItem._id' ng-click='removeItem()') Remove
+                        button.btn.btn-primary.btn-second(ng-click='reloadMetadata()') Reload
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/configuration/sidebar.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/configuration/sidebar.jade b/modules/web-control-center/src/main/js/views/configuration/sidebar.jade
new file mode 100644
index 0000000..7289f3e
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/configuration/sidebar.jade
@@ -0,0 +1,39 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends ../templates/layout
+
+mixin sidebar-item(ref, num, txt)
+    li
+        a(ng-class='{active: isActive("#{ref}")}' href='#{ref}')
+            span.fa-stack
+                i.fa.fa-circle-thin.fa-stack-2x
+                i.fa.fa-stack-1x #{num}
+            | #{txt}
+
+block container
+    .row
+        .col-sm-2.border-right.section-left.greedy
+            .sidebar-nav(bs-affix)
+                ul.menu(ng-controller='activeLink')
+                    +sidebar-item('/configuration/clusters', 1, 'Clusters')
+                    +sidebar-item('/configuration/metadata', 2, 'Metadata')
+                    +sidebar-item('/configuration/caches', 3, 'Caches')
+                    +sidebar-item('/configuration/summary', 4, 'Summary')
+
+        .col-sm-10.border-left.section-right
+            .docs-content
+                block content
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/configuration/summary.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/configuration/summary.jade b/modules/web-control-center/src/main/js/views/configuration/summary.jade
new file mode 100644
index 0000000..6f2f6d8
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/configuration/summary.jade
@@ -0,0 +1,117 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends sidebar
+
+append scripts
+    script(src='/summary-controller.js')
+
+    script(src="//cdn.jsdelivr.net/angularjs/1.3.15/angular-animate.min.js")
+    script(src="//cdn.jsdelivr.net/angularjs/1.3.15/angular-sanitize.min.js")
+
+    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/theme-chrome.js')
+    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-xml.js')
+    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-java.js')
+    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-dockerfile.js')
+
+append css
+
+include ../includes/controls
+
+mixin hard-link(ref, txt)
+    a(style='color:#ec1c24' href=ref target="_blank") #{txt}
+
+block content
+    .docs-header
+        h1 Configurations Summary
+        hr
+    .docs-body(ng-controller='summaryController')
+        +block-callout('{{screenTip.workflowTitle}}', 'joinTip(screenTip.workflowContent)', '{{screenTip.whatsNextTitle}}', 'joinTip(screenTip.whatsNextContent)')
+        .padding-dflt(ng-if='clusters.length == 0')
+            | No cluster configured. You can&nbsp;
+            a(href='clusters') configure
+            | &nbsp;it.
+        .padding-dflt(ng-if='clusters.length > 0')
+            lable.labelHeader Clusters:
+            .links
+                table(st-table='clusters')
+                    tbody
+                        tr(ng-repeat='row in clusters track by row._id')
+                            td.col-sm-6(ng-class='{active: row._id == selectedItem._id}')
+                                a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}}
+        div(ng-show='selectedItem' role="tab" method='post' action='summary/download')
+            .padding-dflt(bs-collapse data-start-collapsed='false')
+                .panel.panel-default
+                    form.panel-heading(role='tab' method='post' action='summary/download')
+                        input(type="hidden" name="_id" value="{{selectedItem._id}}")
+                        input(type="hidden" name="os" value="{{os}}")
+                        input(type="hidden" name="javaClass" value="{{javaClassServer}}")
+                        h3
+                            a(bs-collapse-toggle) Server
+                            button.btn.btn-primary.pull-right(type='submit' style='margin-top: -5px') Download
+                    .panel-collapse(role="tabpanel" bs-collapse-target)
+                        .panel-body
+                            .configBox(ng-show='selectedItem' bs-tabs)
+                                div(title='<img src="/images/xml.png" width="16px" height="16px"/> XML' bs-pane)
+                                    .configBox(ui-ace='{ onLoad: aceInit, mode: "xml" }' ng-model='xmlServer' style='margin: 0.65em 0;')
+                                div(title='<img src="/images/java.png" width="16px" height="16px"/> Java' bs-pane)
+                                    .settings-row
+                                        .col-sm-1
+                                            label Generate:
+                                        .col-sm-3
+                                            button.form-control(type='button' ng-model='configServer.javaClassServer'  bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in javaClassItems' data-sort='false')
+                                    .configBox(ui-ace='{ onLoad: aceInit, mode: "java" }' ng-model='javaServer')
+                                div(title='<img src="/images/docker.png" width="16px" height="16px"/> Dockerfile' bs-pane)
+                                    .settings-row
+                                        p
+                                            +hard-link('https://docs.docker.com/reference/builder', 'Docker')
+                                            | &nbsp;file is a text file with instructions to create Docker image.<br/>
+                                            | To build image you have to store following Docker file with your Ignite XML configuration to the same directory.<br>
+                                            | Also you could use predefined&nbsp;
+                                            +hard-link('https://ignite.incubator.apache.org/download.html#docker', 'Apache Ignite docker image')
+                                            | . For more information about using Ignite with Docker please read&nbsp;
+                                            +hard-link('http://apacheignite.readme.io/docs/docker-deployment', 'documentation')
+                                            |.
+                                        .col-sm-2
+                                            label(for='os') Operation System:
+                                        .col-sm-4
+                                            input#os.form-control(type='text', ng-model='configServer.os' placeholder='debian:8' data-min-length="0" data-html="1" data-auto-select="true" data-animation="am-flip-x" bs-typeahead bs-options='os for os in oss')
+                                    div(ui-ace='{ onLoad: aceInit, mode: "dockerfile" }' ng-model='dockerServer')
+            .padding-dflt(bs-collapse data-start-collapsed='false')
+                .panel.panel-default
+                    form.panel-heading(role='tab' method='post' action='summary/download')
+                        input(type="hidden" name="_id" value="{{selectedItem._id}}")
+                        input(type="hidden" name="javaClass" value="{{javaClassClient}}")
+                        input(type="hidden" name="clientNearConfiguration" value="{{backupItem}}")
+
+                        h3
+                            a(bs-collapse-toggle) Client
+                            button.btn.btn-primary.pull-right(type='submit' style='margin-top: -5px') Download
+                    .panel-collapse(role="tabpanel" bs-collapse-target)
+                        .panel-body
+                            div(ng-show='selectedItem')
+                                .settings-row(ng-repeat='field in clientFields')
+                                    +form-row-custom(['col-sm-3'], ['col-sm-3'])
+                                .configBox(bs-tabs)
+                                    div(title='<img src="/images/xml.png" width="16px" height="16px"/> XML' bs-pane)
+                                        .configBox(ui-ace='{ onLoad: aceInit, mode: "xml" }' ng-model='xmlClient' style='margin: 0.65em 0;')
+                                    div(title='<img src="/images/java.png" width="16px" height="16px"/> Java' bs-pane)
+                                        .settings-row
+                                            .col-sm-1
+                                                label Generate:
+                                            .col-sm-4
+                                                button.form-control(type='button' ng-model='backupItem.javaClassClient' bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in javaClassItems' data-sort='false')
+                                        div(ui-ace='{ onLoad: aceInit, mode: "java" }' ng-model='javaClient')

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/error.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/error.jade b/modules/web-control-center/src/main/js/views/error.jade
new file mode 100644
index 0000000..b458fb7
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/error.jade
@@ -0,0 +1,22 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends templates/layout
+
+block container
+  h1= message
+  h2= error.status
+  pre #{error.stack}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/includes/controls.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/includes/controls.jade b/modules/web-control-center/src/main/js/views/includes/controls.jade
new file mode 100644
index 0000000..4a618fa
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/includes/controls.jade
@@ -0,0 +1,322 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+mixin block-callout(titleWorkflow, contentWorkflow, whatsNextWorkflow, whatsNextContent)
+    .block-callout-parent.block-callout-border.margin-bottom-dflt
+        .block-callout
+            i.fa.fa-check-square
+            label #{titleWorkflow}
+            p(ng-bind-html=contentWorkflow)
+        .block-callout
+            i.fa.fa-check-square
+            label #{whatsNextWorkflow}
+            p(ng-bind-html=whatsNextContent)
+
+
+mixin tipField(lines)
+    i.tipField.fa.fa-question-circle(ng-if=lines bs-tooltip='joinTip(#{lines})' type='button')
+    i.tipField.fa.fa-question-circle.blank(ng-if='!#{lines}')
+
+mixin tipLabel(lines)
+    i.tipLabel.fa.fa-question-circle(ng-if=lines bs-tooltip='joinTip(#{lines})' type='button')
+    i.tipLabel.fa.fa-question-circle.blank(ng-if='!#{lines}')
+
+mixin ico-exclamation(mdl, err, msg)
+    i.fa.fa-exclamation-triangle.form-control-feedback(ng-show='inputForm["#{mdl}"].$error.#{err}' bs-tooltip data-title='#{msg}' type='button')
+
+mixin btn-save(show, click)
+    i.tipField.fa.fa-floppy-o(ng-show=show ng-click=click)
+
+mixin btn-add(click)
+    i.tipField.fa.fa-plus(ng-click=click)
+
+mixin btn-remove(click)
+    i.tipField.fa.fa-remove(ng-click=click)
+
+mixin btn-up(show, click)
+    i.tipField.fa.fa-arrow-up(ng-show=show ng-click=click)
+
+mixin btn-down(show, click)
+    i.tipField.fa.fa-arrow-down(ng-show=show ng-click=click)
+
+mixin table-pair-edit(keyModel, valModel, keyPlaceholder, valPlaceholder)
+    .col-sm-6(style='float: right')
+        input.form-control(type='text' ng-model=valModel placeholder=valPlaceholder)
+    label.fieldSep /
+    .input-tip
+        input.form-control(type='text' ng-model=keyModel placeholder=keyPlaceholder)
+
+mixin table-pair(header, tblMdl, keyFld, valFld, keyPlaceholder, valPlaceholder)
+    .col-sm-6
+        label.table-header #{header}:
+        +tipLabel('field.tip')
+        button.btn.btn-primary.fieldButton(ng-click='tableNewItem(field)') Add
+    table.links-edit.col-sm-12(st-table=tblMdl)
+        tbody
+            tr.col-sm-12(ng-repeat='item in #{tblMdl}')
+                td.col-sm-6
+                    div(ng-show='!tableEditing(field, $index)')
+                        a.labelFormField(ng-click='curPair = tableStartEdit(backupItem, field, $index); curKey = curPair.#{keyFld}; curValue = curPair.#{valFld}') {{$index + 1}}) {{item.#{keyFld}}} / {{item.#{valFld}}}
+                        +btn-remove('tableRemove(backupItem, field, $index)')
+                    div(ng-show='tableEditing(field, $index)')
+                        label.labelField {{$index + 1}})
+                        +btn-save('tablePairSaveVisible(curKey, curValue)', 'tablePairSave(tablePairValid, backupItem, field, curKey, curValue, $index)')
+                        +table-pair-edit('curKey', 'curValue', keyPlaceholder, valPlaceholder)
+            tr.col-sm-12(ng-show='tableNewItemActive(field)')
+                td.col-sm-6
+                    +btn-save('tablePairSaveVisible(newKey, newValue)', 'tablePairSave(tablePairValid, backupItem, field, newKey, newValue, -1)')
+                    +table-pair-edit('newKey', 'newValue', keyPlaceholder, valPlaceholder)
+
+mixin details-row
+    - var lblDetailClasses = ['col-sm-4', 'details-label']
+
+    - var detailMdl = 'getModel(backupItem, detail)[detail.model]';
+    - var detailCommon = {'ng-model': detailMdl, 'ng-required': 'detail.required'};
+
+    - var customValidators = {'ng-attr-ipaddress': '{{detail.ipaddress}}'}
+
+    div(ng-switch='detail.type')
+        div(ng-switch-when='label')
+            label {{detail.label}}
+        div.checkbox(ng-switch-when='check')
+            label
+                input(type='checkbox')&attributes(detailCommon)
+                |{{detail.label}}
+                +tipLabel('detail.tip')
+        div(ng-switch-when='text')
+            label(class=lblDetailClasses ng-class='{required: detail.required}') {{detail.label}}:
+            .col-sm-8
+                +tipField('detail.tip')
+                .input-tip
+                    input.form-control(type='text' placeholder='{{detail.placeholder}}')&attributes(detailCommon)
+        div(ng-switch-when='number' )
+            label(class=lblDetailClasses ng-class='{required: detail.required}') {{detail.label}}:
+            .col-sm-8
+                +tipField('detail.tip')
+                .input-tip
+                    input.form-control(name='{{detail.model}}' type='number' placeholder='{{detail.placeholder}}' min='{{detail.min ? detail.min : 0}}' max='{{detail.max ? detail.max : Number.MAX_VALUE}}')&attributes(detailCommon)
+                    +ico-exclamation('{{detail.model}}', 'min', 'Value is less than allowable minimum.')
+                    +ico-exclamation('{{detail.model}}', 'max', 'Value is more than allowable maximum.')
+                    +ico-exclamation('{{detail.model}}', 'number', 'Invalid value. Only numbers allowed.')
+        div(ng-switch-when='dropdown')
+            label(class=lblDetailClasses ng-class='{required: detail.required}') {{detail.label}}:
+            .col-sm-8
+                +tipField('detail.tip')
+                .input-tip
+                    button.form-control(bs-select data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in {{detail.items}}')&attributes(detailCommon)
+        div(ng-switch-when='dropdown-multiple')
+            label(class=lblDetailClasses ng-class='{required: detail.required}') {{detail.label}}:
+            .col-sm-8
+                button.form-control(bs-select data-multiple='1' data-placeholder='{{detail.placeholder}}' bs-options='item.value as item.label for item in {{detail.items}}')&attributes(detailCommon)
+            +tipField('detail.tip')
+        div(ng-switch-when='table-simple')&attributes(detailCommon)
+            div(ng-if='detail.label')
+                label.table-header {{detail.label}}:
+                +tipLabel('detail.tableTip')
+            table.col-sm-12.links-edit-details(st-table='#{detailMdl}')
+                tbody
+                    tr(ng-repeat='item in #{detailMdl} track by $index')
+                        td
+                            div(ng-show='!tableEditing(detail, $index)')
+                                a.labelFormField(ng-click='curValue = tableStartEdit(backupItem, detail, $index)') {{$index + 1}}) {{item}}
+                                +btn-remove('tableRemove(backupItem, detail, $index)')
+                                +btn-down('detail.reordering && tableSimpleDownVisible(backupItem, detail, $index)', 'tableSimpleDown(backupItem, detail, $index)')
+                                +btn-up('detail.reordering && $index > 0', 'tableSimpleUp(backupItem, detail, $index)')
+                            div(ng-show='tableEditing(detail, $index)')
+                                label.labelField {{$index + 1}})
+                                +btn-save('tableSimpleSaveVisible(curValue)', 'tableSimpleSave(tableSimpleValid, backupItem, detail, curValue, $index)')
+                                .input-tip.form-group.has-feedback
+                                    input.form-control(name='{{detail.model}}.edit' type='text' ng-model='curValue' placeholder='{{detail.placeholder}}')&attributes(customValidators)
+                                    +ico-exclamation('{{detail.model}}.edit', 'ipaddress', 'Invalid address, see help for format description.')
+            button.btn.btn-primary.fieldButton(ng-disabled='!newValue' ng-click='tableSimpleSave(tableSimpleValid, backupItem, detail, newValue, -1)') Add
+            +tipField('detail.tip')
+            .input-tip.form-group.has-feedback
+                input.form-control(name='{{detail.model}}' type='text' ng-model='newValue' ng-focus='tableNewItem(detail)' placeholder='{{detail.placeholder}}')&attributes(customValidators)
+                +ico-exclamation('{{detail.model}}', 'ipaddress', 'Invalid address, see help for format description.')
+
+mixin table-db-field-edit(dbName, dbType, javaName, javaType)
+    div(style='width: 22%; float: right')
+        button.form-control(ng-model=javaType bs-select data-placeholder='Java type' bs-options='item.value as item.label for item in {{javaTypes}}')
+    label.fieldSep /
+    div(style='width: 20%; float: right')
+        input.form-control(type='text' ng-model=javaName placeholder='Java name')
+    label.fieldSep /
+    div(style='width: 22%; float: right')
+        button.form-control(ng-model=dbType bs-select data-placeholder='JDBC type' bs-options='item.value as item.label for item in {{jdbcTypes}}')
+    label.fieldSep /
+    .input-tip
+        input.form-control(type='text' ng-model=dbName placeholder='DB name')
+
+mixin table-group-item-edit(fieldName, className, direction)
+    div(style='width: 15%; float: right')
+        button.form-control(ng-model=direction bs-select data-placeholder='Sort' bs-options='item.value as item.label for item in {{sortDirections}}')
+    label.fieldSep /
+    div(style='width: 38%; float: right')
+        input.form-control(type='text' ng-model=className placeholder='Class name')
+    label.fieldSep /
+    .input-tip
+        input.form-control(type='text' ng-model=fieldName placeholder='Field name')
+
+mixin form-row
+    +form-row-custom(['col-sm-2'], ['col-sm-4'])
+
+mixin form-row-custom(lblClasses, fieldClasses)
+    - var fieldMdl = 'getModel(backupItem, field)[field.model]';
+    - var fieldCommon = {'ng-model': fieldMdl, 'ng-required': 'field.required || required(field)'};
+    - var fieldRequiredClass = '{true: "required"}[field.required || required(field)]'
+    - var fieldHide = '{{field.hide}}'
+
+    div(ng-switch='field.type')
+        div.col-sm-6(ng-switch-when='label')
+            label {{field.label}}
+        div.checkbox.col-sm-6(ng-switch-when='check' ng-hide=fieldHide)
+            label
+                input(type='checkbox')&attributes(fieldCommon)
+                | {{field.label}}
+                +tipLabel('field.tip')
+        div(ng-switch-when='text' ng-hide=fieldHide)
+            label(class=lblClasses ng-class=fieldRequiredClass) {{field.label}}:
+            div(class=fieldClasses)
+                +tipField('field.tip')
+                .input-tip
+                    input.form-control(type='text' placeholder='{{field.placeholder}}')&attributes(fieldCommon)
+        div(ng-switch-when='password' ng-hide=fieldHide)
+            label(class=lblClasses ng-class=fieldRequiredClass) {{field.label}}:
+            div(class=fieldClasses)
+                +tipField('field.tip')
+                .input-tip
+                    input.form-control(type='password' placeholder='{{field.placeholder}}')&attributes(fieldCommon)
+        div(ng-switch-when='number' ng-hide=fieldHide)
+            label(class=lblClasses ng-class=fieldRequiredClass) {{field.label}}:
+            div(class=fieldClasses)
+                +tipField('field.tip')
+                .input-tip
+                    input.form-control(name='{{field.model}}' type='number' placeholder='{{field.placeholder}}' min='{{field.min ? field.min : 0}}' max='{{field.max ? field.max : Number.MAX_VALUE}}')&attributes(fieldCommon)
+                    +ico-exclamation('{{field.model}}', 'min', 'Value is less than allowable minimum.')
+                    +ico-exclamation('{{field.model}}', 'max', 'Value is more than allowable maximum.')
+                    +ico-exclamation('{{field.model}}', 'number', 'Invalid value. Only numbers allowed.')
+        div(ng-switch-when='dropdown' ng-hide=fieldHide)
+            label(class=lblClasses ng-class=fieldRequiredClass) {{field.label}}:
+            div(class=fieldClasses)
+                +tipField('field.tip')
+                .input-tip
+                    button.form-control(bs-select data-placeholder='{{field.placeholder}}' bs-options='item.value as item.label for item in {{field.items}}')&attributes(fieldCommon)
+        div(ng-switch-when='dropdown-multiple' ng-hide=fieldHide)
+            label(class=lblClasses ng-class=fieldRequiredClass) {{field.label}}:
+            div(class=fieldClasses)
+                +tipField('field.tip')
+                .input-tip
+                    button.form-control(bs-select ng-disabled='{{field.items}}.length == 0' data-multiple='1' data-placeholder='{{field.placeholder}}' bs-options='item.value as item.label for item in {{field.items}}')&attributes(fieldCommon)
+            a.customize(ng-show='field.addLink' ng-href='{{field.addLink.ref}}') {{field.addLink.label}}
+        div(ng-switch-when='dropdown-details' ng-hide=fieldHide)
+            - var expanded = 'field.details[' + fieldMdl + '].expanded'
+
+            label(class=lblClasses ng-class=fieldRequiredClass) {{field.label}}:
+            div(class=fieldClasses)
+                +tipField('field.tip')
+                .input-tip
+                    button.form-control(bs-select data-placeholder='{{field.placeholder}}' bs-options='item.value as item.label for item in {{field.items}}')&attributes(fieldCommon)
+            a.customize(ng-show='#{fieldMdl} && field.details[#{fieldMdl}].fields' ng-click='#{expanded} = !#{expanded}') {{#{expanded} ? "Hide settings" : "Show settings"}}
+            .col-sm-6.panel-details(ng-show='#{expanded} && #{fieldMdl}')
+                .details-row(ng-repeat='detail in field.details[#{fieldMdl}].fields')
+                    +details-row
+        div(ng-switch-when='table-simple' ng-hide=fieldHide)&attributes(fieldCommon)
+            .col-sm-6
+                label.table-header {{field.label}}:
+                +tipLabel('field.tableTip')
+                button.btn.btn-primary.fieldButton(ng-click='tableNewItem(field)') Add
+            table.links-edit.col-sm-12(st-table='#{fieldMdl}')
+                tbody
+                    tr.col-sm-12(ng-repeat='item in #{fieldMdl} track by $index')
+                        td.col-sm-6
+                            div(ng-show='!tableEditing(field, $index)')
+                                a.labelFormField(ng-click='curValue = tableStartEdit(backupItem, field, $index)') {{$index + 1}}) {{item | compact}}
+                                +btn-remove('tableRemove(backupItem, field, $index)')
+                                +btn-down('field.reordering && tableSimpleDownVisible(backupItem, field, $index)', 'tableSimpleDown(backupItem, field, $index)')
+                                +btn-up('field.reordering && $index > 0', 'tableSimpleUp(backupItem, field, $index)')
+                            div(ng-show='tableEditing(field, $index)')
+                                label.labelField {{$index + 1}})
+                                +btn-save('tableSimpleSaveVisible(curValue)', 'tableSimpleSave(tableSimpleValid, backupItem, field, curValue, $index)')
+                                .input-tip
+                                    input.form-control(type='text' ng-model='curValue' placeholder='{{field.placeholder}}')
+                    tr.col-sm-12(ng-show='tableNewItemActive(field)')
+                        td.col-sm-6
+                            +btn-save('tableSimpleSaveVisible(newValue)', 'tableSimpleSave(tableSimpleValid, backupItem, field, newValue, -1)')
+                            .input-tip
+                                input.form-control(type='text' ng-model='newValue' placeholder='{{field.placeholder}}')
+        div(ng-switch-when='indexedTypes')
+            +table-pair('Index key-value type pairs', fieldMdl, 'keyClass', 'valueClass', 'Key class full name', 'Value class full name')
+        div(ng-switch-when='queryFields' ng-hide=fieldHide)
+            +table-pair('{{field.label}}', fieldMdl, 'name', 'className', 'Field name', 'Field class full name')
+        div(ng-switch-when='dbFields' ng-hide=fieldHide)
+            .col-sm-6
+                label.table-header {{field.label}}:
+                +tipLabel('field.tip')
+                button.btn.btn-primary.fieldButton(ng-click='tableNewItem(field)') Add
+            table.links-edit.col-sm-12(st-table=fieldMdl)
+                tbody
+                    tr.col-sm-12(ng-repeat='item in #{fieldMdl}')
+                        td.col-sm-6
+                            div(ng-show='!tableEditing(field, $index)')
+                                a.labelFormField(ng-click='curField = tableStartEdit(backupItem, field, $index); curDbName = curField.dbName; curDbType = curField.dbType; curJavaName = curField.javaName; curJavaType = curField.javaType') {{$index + 1}}) {{item.dbName}} / {{item.dbType}} / {{item.javaName}} / {{item.javaType}}
+                                +btn-remove('tableRemove(backupItem, field, $index)')
+                            div(ng-if='tableEditing(field, $index)')
+                                label.labelField {{$index + 1}})
+                                +btn-save('tableDbFieldSaveVisible(curDbName, curDbType, curJavaName, curJavaType)', 'tableDbFieldSave(field, curDbName, curDbType, curJavaName, curJavaType, $index)')
+                                +table-db-field-edit('curDbName', 'curDbType', 'curJavaName', 'curJavaType')
+                    tr(ng-show='tableNewItemActive(field)')
+                        td.col-sm-6
+                            +btn-save('tableDbFieldSaveVisible(newDbName, newDbType, newJavaName, newJavaType)', 'tableDbFieldSave(field, newDbName, newDbType, newJavaName, newJavaType, -1)')
+                            +table-db-field-edit('newDbName', 'newDbType', 'newJavaName', 'newJavaType')
+        div(ng-switch-when='queryGroups' ng-hide=fieldHide)
+            .col-sm-6
+                label.table-header {{field.label}}:
+                +tipLabel('field.tip')
+                button.btn.btn-primary.fieldButton(ng-click='tableNewItem(field)') Add
+            table.links-edit.col-sm-12(st-table=fieldMdl)
+                tbody
+                    tr.col-sm-12(ng-repeat='group in #{fieldMdl}')
+                        td.col-sm-6
+                            div
+                                .col-sm-12(ng-show='!tableEditing(field, $index)')
+                                    a.labelFormField(ng-click='curGroup = tableStartEdit(backupItem, field, $index); curGroupName = curGroup.name; curFields = curGroup.fields') {{$index + 1}}) {{group.name}}
+                                    +btn-remove('tableRemove(backupItem, field, $index)')
+                                    +btn-add('tableGroupNewItem($index); newDirection = "ASC"')
+                                div(ng-show='tableEditing(field, $index)')
+                                    label.labelField {{$index + 1}})
+                                    +btn-save('tableGroupSaveVisible(curGroupName)', 'tableGroupSave(curGroupName, $index)')
+                                    .input-tip
+                                        input.form-control(type='text' ng-model='curGroupName' placeholder='Index name')
+                                div
+                                    table.links-edit.col-sm-12(st-table='group.fields' ng-init='groupIndex = $index')
+                                        tr(ng-repeat='groupItem in group.fields')
+                                            td
+                                                div(ng-show='!tableGroupItemEditing(groupIndex, $index)')
+                                                    a.labelFormField(ng-click='curGroupItem = tableGroupItemStartEdit(groupIndex, $index); curFieldName = curGroupItem.name; curClassName = curGroupItem.className; curDirection = curGroupItem.direction') {{$index + 1}}) {{groupItem.name}} / {{groupItem.className}} / {{groupItem.direction}}
+                                                    +btn-remove('tableRemoveGroupItem(group, $index)')
+                                                div(ng-show='tableGroupItemEditing(groupIndex, $index)')
+                                                    label.labelField {{$index + 1}})
+                                                    +btn-save('tableGroupItemSaveVisible(curFieldName, curClassName)', 'tableGroupItemSave(curFieldName, curClassName, curDirection, groupIndex, $index)')
+                                                    +table-group-item-edit('curFieldName', 'curClassName', 'curDirection')
+                                        tr.col-sm-12(style='padding-left: 18px' ng-show='tableGroupNewItemActive(groupIndex)')
+                                            td
+                                                +btn-save('tableGroupItemSaveVisible(newFieldName, newClassName)', 'tableGroupItemSave(newFieldName, newClassName, newDirection, groupIndex, -1)')
+                                                +table-group-item-edit('newFieldName', 'newClassName', 'newDirection')
+                    tr.col-sm-12(ng-show='tableNewItemActive(field)')
+                        td.col-sm-6
+                            +btn-save('tableGroupSaveVisible(newGroupName)', 'tableGroupSave(newGroupName, -1)')
+                            .input-tip
+                                input.form-control(type='text' ng-model='newGroupName' placeholder='Group name')
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/includes/footer.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/includes/footer.jade b/modules/web-control-center/src/main/js/views/includes/footer.jade
new file mode 100644
index 0000000..d8ff5d7
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/includes/footer.jade
@@ -0,0 +1,22 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+.container.container-footer
+    footer
+        center
+            p Apache Ignite Control Center, version 1.0.0
+            p © 2015 The Apache Software Foundation.
+            p Apache, Apache Ignite, the Apache feather and the Apache Ignite logo are trademarks of The Apache Software Foundation.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/includes/header.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/includes/header.jade b/modules/web-control-center/src/main/js/views/includes/header.jade
new file mode 100644
index 0000000..ab2d31e
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/includes/header.jade
@@ -0,0 +1,39 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+mixin header-item(active, ref, txt)
+    li
+        a(ng-class='{active: isActive("#{active}")}' href='#{ref}') #{txt}
+
+header.header(id='header')
+    .viewedUser(ng-show='becomeUsed') Currently assuming "
+        strong {{user.username}}
+        | ",&nbsp;&nbsp;
+        a(href='/admin/become') revert to your identity.
+    .container
+        h1.navbar-brand
+            a(href='/') Apache Ignite Web Configurator
+        .navbar-collapse.collapse(ng-controller='auth')
+            ul.nav.navbar-nav(ng-controller='activeLink' ng-show='user')
+                +header-item('/configuration', '/configuration/clusters', 'Configuration')
+                //+header-item('/monitoring', '/monitoring', 'Monitoring')
+                //+header-item('/sql', '/sql', 'SQL')
+                //+header-item('/deploy', '/deploy', 'Deploy')
+            ul.nav.navbar-nav.pull-right
+                li(ng-if='user')
+                    a.dropdown-toggle(data-toggle='dropdown' bs-dropdown='userDropdown' data-placement='bottom-right') {{user.username}}
+                        span.caret
+                li.nav-login(ng-if='!user')
+                    a(ng-click='login()' href='#') Log In
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/index.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/index.jade b/modules/web-control-center/src/main/js/views/index.jade
new file mode 100644
index 0000000..999c4f8
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/index.jade
@@ -0,0 +1,30 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends templates/layout
+
+block container
+    .row
+        .docs-content
+            div
+                p
+                    | Apache Ignite<sup>tm</sup> In-Memory Data Fabric is a high-performance,
+                    | integrated and distributed in-memory platform for computing and transacting on large-scale data
+                    | sets in real-time, orders of magnitude faster than possible with traditional disk-based or flash technologies.
+        .block-image.block-display-image
+                img(ng-src='https://www.filepicker.io/api/file/lydEeGB6Rs9hwbpcQxiw' alt='Apache Ignite stack')
+        .text-center(ng-controller='auth')
+            button.btn.btn-primary(ng-click='login()' href='#') Configure Now

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/login.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/login.jade b/modules/web-control-center/src/main/js/views/login.jade
new file mode 100644
index 0000000..5bb39dd
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/login.jade
@@ -0,0 +1,55 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+mixin lbl(txt)
+    label.col-sm-3.required #{txt}
+
+.modal.center(role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header.header
+                div(id='errors-container')
+                button.close(type='button', ng-click='$hide()', aria-hidden='true') &times;
+                h1.navbar-brand
+                    a(href='/') Apache Ignite Web Configurator
+                h4.modal-title(style='padding-right: 55px') Authentication
+                p(style='padding-right: 55px') Log in or register in order to collaborate
+            form.form-horizontal(name='loginForm')
+                .modal-body.row
+                    .col-sm-9.login.col-sm-offset-1
+                        .details-row(ng-show='action == "register"')
+                            +lbl('Full Name:')
+                            .col-sm-9
+                                input.form-control(type='text', ng-model='user_info.username', placeholder='John Smith', focus-me='action=="register"', ng-required='action=="register"')
+                        .details-row
+                            +lbl('Email:')
+                            .col-sm-9
+                                input.form-control(type='email', ng-model='user_info.email', placeholder='you@domain.com', focus-me='action=="login"', required)
+                        .details-row
+                            +lbl('Password:')
+                            .col-sm-9
+                                input.form-control(type='password', ng-model='user_info.password', placeholder='Password', required, ng-keyup='action == "login" && $event.keyCode == 13 ? auth(action, user_info) : null')
+                        .details-row(ng-show='action == "register"')
+                            +lbl('Confirm:')
+                            .col-sm-9.input-tip.has-feedback
+                                input.form-control(type='password', ng-model='user_info.confirm', match="user_info.password" placeholder='Confirm password', required, ng-keyup='$event.keyCode == 13 ? auth(action, user_info) : null')
+
+            .modal-footer
+                a.show-signup.ng-hide(ng-show='action != "login"', ng-click='action = "login";') log in
+                a.show-signup(ng-show="action != 'register'", ng-click='action = "register";') sign up
+                | &nbsp;or&nbsp;
+                button.btn.btn-primary(ng-show='action == "login"' ng-click='auth(action, user_info)') Log In
+                button.btn.btn-primary(ng-show='action == "register"' ng-disabled='loginForm.$invalid || user_info.password != user_info.confirm' ng-click='auth(action, user_info)') Sign Up

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/settings/admin.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/settings/admin.jade b/modules/web-control-center/src/main/js/views/settings/admin.jade
new file mode 100644
index 0000000..4d50631
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/settings/admin.jade
@@ -0,0 +1,58 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends ../templates/layout
+
+append scripts
+    script(src='/admin-controller.js')
+
+block container
+    .row(ng-controller='adminController')
+        .docs-content
+            .docs-header
+                h1 List of registered users
+                hr
+            .docs-body
+                table.table.table-striped.admin(st-table='displayedUsers' st-safe-src='users')
+                    thead
+                        tr
+                            th.header(colspan='5')
+                                .col-sm-2.pull-right
+                                    input.form-control(type='text' st-search='' placeholder='Filter users...')
+                        tr
+                            th(st-sort='username') User name
+                            th(st-sort='email') Email
+                            th.col-sm-2(st-sort='lastLogin') Last login
+                            th(width='1%'  st-sort='admin') Admin
+                            th(width='1%') Actions
+                    tbody
+                        tr(ng-repeat='row in displayedUsers')
+                            td {{row.username}}
+                            td
+                                a(ng-href='mailto:{{row.email}}') {{row.email}}
+                            td
+                                span {{row.lastLogin | date:'medium'}}
+                            td(style='text-align: center;')
+                                input(type='checkbox' ng-disabled='row.adminChanging || row._id == user._id'
+                                    ng-model='row.admin' ng-change='toggleAdmin(row)')
+                            td(style='text-align: center;')
+                                a(ng-click='removeUser(row)' ng-show='row._id != user._id' bs-tooltip data-title='Remove user')
+                                    i.fa.fa-remove
+                                a(style='margin-left: 5px' ng-href='admin/become?viewedUserId={{row._id}}' ng-show='row._id != user._id' bs-tooltip data-title='Become this user')
+                                    i.fa.fa-eye
+                    tfoot
+                        tr
+                            td(colspan='5' class="text-right")
+                                div(st-pagination st-items-by-page='15' st-displayed-pages='5')
+

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/settings/profile.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/settings/profile.jade b/modules/web-control-center/src/main/js/views/settings/profile.jade
new file mode 100644
index 0000000..dbc6dea
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/settings/profile.jade
@@ -0,0 +1,58 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+extends ../templates/layout
+
+mixin lbl(txt)
+    label.col-sm-2.required.labelFormField #{txt}
+
+append scripts
+    script(src='/profile-controller.js')
+
+block container
+    .row(ng-controller='profileController')
+        .docs-content
+            .docs-header
+                h1 User profile
+                hr
+            .docs-body
+                form.form-horizontal(name='profileForm' novalidate)
+                    .col-sm-10(style='padding: 0')
+                        .details-row
+                            +lbl('User name:')
+                            .col-sm-4
+                                input.form-control(type='text' ng-model='profileUser.username' placeholder='Input name' required)
+                        .details-row
+                            +lbl('Email:')
+                            .col-sm-4
+                                input.form-control(type='email' ng-model='profileUser.email' placeholder='you@domain.com' required)
+                        .details-row
+                            .checkbox
+                                label
+                                    input(type="checkbox" ng-model='profileUser.changePassword')
+                                    | Change password
+                        div(ng-show='profileUser.changePassword')
+                            .details-row
+                                +lbl('New password:')
+                                .col-sm-4
+                                    input.form-control(type='password', ng-model='profileUser.newPassword' placeholder='New password' ng-required='profileUser.changePassword')
+                            .details-row
+                                +lbl('Confirm:')
+                                .col-sm-4
+                                    input.form-control(type='password', ng-model='profileUser.confirmPassword' match='profileUser.newPassword' placeholder='Confirm new password' ng-required='profileUser.changePassword')
+                    .col-sm-12.details-row
+                        button.btn.btn-primary(ng-disabled='profileForm.$invalid' ng-click='saveUser()') Save
+

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8a335724/modules/web-control-center/src/main/js/views/templates/confirm.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/templates/confirm.jade b/modules/web-control-center/src/main/js/views/templates/confirm.jade
new file mode 100644
index 0000000..bdaf9bf
--- /dev/null
+++ b/modules/web-control-center/src/main/js/views/templates/confirm.jade
@@ -0,0 +1,27 @@
+//-
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You 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.
+
+.modal(tabindex='-1' role='dialog')
+    .modal-dialog
+        .modal-content
+            .modal-header
+                button.close(type="button" ng-click="$hide()") &times;
+                h4.modal-title Confirmation
+            .modal-body(ng-show='content')
+                p(ng-bind-html='content' style='text-align: center;')
+            .modal-footer
+                button.btn.btn-default(type="button" ng-click="$hide()") Cancel
+                button.btn.btn-primary(type="button" ng-click="ok()") Confirm
\ No newline at end of file