ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akuznet...@apache.org
Subject [02/21] ignite git commit: IGNITE-843 Implemented Web Console.
Date Tue, 17 May 2016 16:22:44 GMT
http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/serve/routes/public.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/public.js b/modules/web-console/src/main/js/serve/routes/public.js
new file mode 100644
index 0000000..0009e6a
--- /dev/null
+++ b/modules/web-console/src/main/js/serve/routes/public.js
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+'use strict';
+
+// Fire me up!
+
+module.exports = {
+    implements: 'public-routes',
+    inject: ['require(express)', 'require(passport)', 'require(nodemailer)', 'settings', 'mail', 'mongo']
+};
+
+module.exports.factory = function(express, passport, nodemailer, settings, mail, mongo) {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        const _randomString = () => {
+            const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+            const possibleLen = possible.length;
+
+            let res = '';
+
+            for (let i = 0; i < settings.tokenLength; i++)
+                res += possible.charAt(Math.floor(Math.random() * possibleLen));
+
+            return res;
+        };
+
+        // GET user.
+        router.post('/user', (req, res) => {
+            const becomeUsed = req.session.viewedUser && req.user.admin;
+
+            let user = req.user;
+
+            if (becomeUsed) {
+                user = req.session.viewedUser;
+
+                user.becomeUsed = true;
+            }
+            else if (user)
+                user = user.toJSON();
+            else
+                return res.json(user);
+
+            mongo.Space.findOne({owner: user._id, demo: true}).exec()
+                .then((demoSpace) => {
+                    if (user && demoSpace)
+                        user.demoCreated = true;
+
+                    res.json(user);
+                })
+                .catch((err) => {
+                    res.status(401).send(err.message);
+                });
+        });
+
+        /**
+         * Register new account.
+         */
+        router.post('/signup', (req, res) => {
+            mongo.Account.count().exec()
+                .then((cnt) => {
+                    req.body.admin = cnt === 0;
+
+                    req.body.token = _randomString();
+
+                    return new mongo.Account(req.body);
+                })
+                .then((account) => {
+                    return new Promise((resolve, reject) => {
+                        mongo.Account.register(account, req.body.password, (err, _account) => {
+                            if (err)
+                                reject(err);
+
+                            if (!_account)
+                                reject(new Error('Failed to create account.'));
+
+                            resolve(_account);
+                        });
+                    });
+                })
+                .then((account) => new mongo.Space({name: 'Personal space', owner: account._id}).save()
+                    .then(() => account)
+                )
+                .then((account) => {
+                    return new Promise((resolve, reject) => {
+                        req.logIn(account, {}, (err) => {
+                            if (err)
+                                reject(err);
+
+                            resolve(account);
+                        });
+                    });
+                })
+                .then((account) => {
+                    res.sendStatus(200);
+
+                    account.resetPasswordToken = _randomString();
+
+                    return account.save()
+                        .then(() => mail.send(account, `Thanks for signing up for ${settings.smtp.username}.`,
+                            `Hello ${account.firstName} ${account.lastName}!<br><br>` +
+                            `You are receiving this email because you have signed up to use <a href="http://${req.headers.host}">${settings.smtp.username}</a>.<br><br>` +
+                            'If you have not done the sign up and do not know what this email is about, please ignore it.<br>' +
+                            'You may reset the password by clicking on the following link, or paste this into your browser:<br><br>' +
+                            `http://${req.headers.host}/password/reset?token=${account.resetPasswordToken}`));
+                })
+                .catch((err) => {
+                    res.status(401).send(err.message);
+                });
+        });
+
+        /**
+         * Sign in into exist account.
+         */
+        router.post('/signin', (req, res, next) => {
+            passport.authenticate('local', (errAuth, user) => {
+                if (errAuth)
+                    return res.status(401).send(errAuth.message);
+
+                if (!user)
+                    return res.status(401).send('Invalid email or password');
+
+                req.logIn(user, {}, (errLogIn) => {
+                    if (errLogIn)
+                        return res.status(401).send(errLogIn.message);
+
+                    return res.sendStatus(200);
+                });
+            })(req, res, next);
+        });
+
+        /**
+         * Logout.
+         */
+        router.post('/logout', (req, res) => {
+            req.logout();
+
+            res.sendStatus(200);
+        });
+
+        /**
+         * Send e-mail to user with reset token.
+         */
+        router.post('/password/forgot', (req, res) => {
+            mongo.Account.findOne({email: req.body.email}).exec()
+                .then((user) => {
+                    if (!user)
+                        throw new Error('Account with that email address does not exists!');
+
+                    user.resetPasswordToken = _randomString();
+
+                    return user.save();
+                })
+                .then((user) => mail.send(user, 'Password Reset',
+                    `Hello ${user.firstName} ${user.lastName}!<br><br>` +
+                    'You are receiving this because you (or someone else) have requested the reset of the password for your account.<br><br>' +
+                    'Please click on the following link, or paste this into your browser to complete the process:<br><br>' +
+                    'http://' + req.headers.host + '/password/reset?token=' + user.resetPasswordToken + '<br><br>' +
+                    'If you did not request this, please ignore this email and your password will remain unchanged.',
+                    'Failed to send email with reset link!')
+                )
+                .then(() => res.status(200).send('An email has been sent with further instructions.'))
+                .catch((err) => {
+                    // TODO IGNITE-843 Send email to admin
+                    return res.status(401).send(err.message);
+                });
+        });
+
+        /**
+         * Change password with given token.
+         */
+        router.post('/password/reset', (req, res) => {
+            mongo.Account.findOne({resetPasswordToken: req.body.token}).exec()
+                .then((user) => {
+                    if (!user)
+                        throw new Error('Failed to find account with this token! Please check link from email.');
+
+                    return new Promise((resolve, reject) => {
+                        user.setPassword(req.body.password, (err, _user) => {
+                            if (err)
+                                return reject(new Error('Failed to reset password: ' + err.message));
+
+                            _user.resetPasswordToken = undefined; // eslint-disable-line no-undefined
+
+                            resolve(_user.save());
+                        });
+                    });
+                })
+                .then((user) => mail.send(user, 'Your password has been changed',
+                    `Hello ${user.firstName} ${user.lastName}!<br><br>` +
+                    `This is a confirmation that the password for your account on <a href="http://${req.headers.host}">${settings.smtp.username}</a> has just been changed.<br><br>`,
+                    'Password was changed, but failed to send confirmation email!'))
+                .then((user) => res.status(200).send(user.email))
+                .catch((err) => res.status(401).send(err.message));
+        });
+
+        /* GET reset password page. */
+        router.post('/password/validate/token', (req, res) => {
+            const token = req.body.token;
+
+            mongo.Account.findOne({resetPasswordToken: token}).exec()
+                .then((user) => {
+                    if (!user)
+                        throw new Error('Invalid token for password reset!');
+
+                    return res.json({token, email: user.email});
+                })
+                .catch((err) => res.status(401).send(err.message));
+        });
+
+        factoryResolve(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/serve/routes/routes.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/routes/routes.js b/modules/web-console/src/main/js/serve/routes/routes.js
new file mode 100644
index 0000000..cbee8bb
--- /dev/null
+++ b/modules/web-console/src/main/js/serve/routes/routes.js
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+'use strict';
+
+// Fire me up!
+
+module.exports = {
+    implements: 'routes',
+    inject: [
+        'public-routes',
+        'admin-routes',
+        'profile-routes',
+        'demo-routes',
+        'clusters-routes',
+        'domains-routes',
+        'caches-routes',
+        'igfs-routes',
+        'notebooks-routes',
+        'agent-routes',
+        'ignite_modules/routes:*' // Loads all routes modules of all plugins
+    ]
+};
+
+module.exports.factory = function(publicRoutes, adminRoutes, profileRoutes, demoRoutes,
+    clusterRoutes, domainRoutes, cacheRoutes, igfsRoutes, notebookRoutes, agentRoutes, pluginRoutes) {
+    return {
+        register: (app) => {
+            app.all('*', (req, res, next) => {
+                req.currentUserId = () => {
+                    if (!req.user)
+                        return null;
+
+                    if (req.session.viewedUser && req.user.admin)
+                        return req.session.viewedUser._id;
+
+                    return req.user._id;
+                };
+
+                next();
+            });
+
+            const _mustAuthenticated = (req, res, next) => req.isAuthenticated() ? next() : res.redirect('/');
+
+            const _adminOnly = (req, res, next) => req.isAuthenticated() && req.user.admin ? next() : res.sendStatus(403);
+
+            // Registering the standard routes
+            app.use('/', publicRoutes);
+            app.use('/admin', _mustAuthenticated, _adminOnly, adminRoutes);
+            app.use('/profile', _mustAuthenticated, profileRoutes);
+            app.use('/demo', _mustAuthenticated, demoRoutes);
+
+            app.all('/configuration/*', _mustAuthenticated);
+
+            app.use('/configuration/clusters', clusterRoutes);
+            app.use('/configuration/domains', domainRoutes);
+            app.use('/configuration/caches', cacheRoutes);
+            app.use('/configuration/igfs', igfsRoutes);
+
+            app.use('/notebooks', _mustAuthenticated, notebookRoutes);
+            app.use('/agent', _mustAuthenticated, agentRoutes);
+
+            // Registering the routes of all plugin modules
+            for (const name in pluginRoutes) {
+                if (pluginRoutes.hasOwnProperty(name))
+                    pluginRoutes[name].register(app, _mustAuthenticated, _adminOnly);
+            }
+
+            // Catch 404 and forward to error handler.
+            app.use((req, res, next) => {
+                const err = new Error('Not Found: ' + req.originalUrl);
+
+                err.status = 404;
+
+                next(err);
+            });
+
+            // Production error handler: no stacktraces leaked to user.
+            app.use((err, req, res) => {
+                res.status(err.status || 500);
+
+                res.render('error', {
+                    message: err.message,
+                    error: {}
+                });
+            });
+        }
+    };
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/serve/settings.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/serve/settings.js b/modules/web-console/src/main/js/serve/settings.js
new file mode 100644
index 0000000..5b14bcc
--- /dev/null
+++ b/modules/web-console/src/main/js/serve/settings.js
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+'use strict';
+
+// Fire me up!
+
+/**
+ * Module with server-side configuration.
+ */
+module.exports = {
+    implements: 'settings',
+    inject: ['require(nconf)', 'require(fs)']
+};
+
+module.exports.factory = function(nconf, fs) {
+    nconf.file({file: './serve/config/settings.json'});
+
+    /**
+     * Normalize a port into a number, string, or false.
+     */
+    const _normalizePort = function(val) {
+        const port = parseInt(val, 10);
+
+        // named pipe
+        if (isNaN(port))
+            return val;
+
+        // port number
+        if (port >= 0)
+            return port;
+
+        return false;
+    };
+
+    return {
+        agent: {
+            dists: 'serve/agent_dists',
+            port: _normalizePort(nconf.get('agent-server:port') || 3002),
+            legacyPort: _normalizePort(nconf.get('agent-server:legacyPort')),
+            SSLOptions: nconf.get('agent-server:ssl') && {
+                key: fs.readFileSync(nconf.get('agent-server:key')),
+                cert: fs.readFileSync(nconf.get('agent-server:cert')),
+                passphrase: nconf.get('agent-server:keyPassphrase')
+            }
+        },
+        server: {
+            port: _normalizePort(nconf.get('server:port') || 3000),
+            SSLOptions: nconf.get('server:ssl') && {
+                enable301Redirects: true,
+                trustXFPHeader: true,
+                key: fs.readFileSync(nconf.get('server:key')),
+                cert: fs.readFileSync(nconf.get('server:cert')),
+                passphrase: nconf.get('server:keyPassphrase')
+            }
+        },
+        smtp: {
+            service: nconf.get('smtp:service'),
+            username: nconf.get('smtp:username'),
+            sign: nconf.get('smtp:sign'),
+            email: nconf.get('smtp:email'),
+            password: nconf.get('smtp:password'),
+            address: (username, email) => username ? '"' + username + '" <' + email + '>' : email
+        },
+        mongoUrl: nconf.get('mongoDB:url') || 'mongodb://localhost/console',
+        cookieTTL: 3600000 * 24 * 30,
+        sessionSecret: 'keyboard cat',
+        tokenLength: 20
+    };
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/base.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/base.jade b/modules/web-console/src/main/js/views/base.jade
new file mode 100644
index 0000000..a910d1b
--- /dev/null
+++ b/modules/web-console/src/main/js/views/base.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.
+
+include includes/header
+
+.container.body-container
+    .main-content(ui-view='')
+
+include includes/footer

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/caches.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/caches.jade b/modules/web-console/src/main/js/views/configuration/caches.jade
new file mode 100644
index 0000000..a5b331f
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/caches.jade
@@ -0,0 +1,52 @@
+//-
+    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.
+
+include ../../app/helpers/jade/mixins.jade
+
+.docs-header
+    h1 Configure Ignite Caches
+.docs-body(ng-controller='cachesController')
+    ignite-information
+        ul
+            li Configure #[a(href='https://apacheignite.readme.io/docs/data-grid' target='_blank') memory] settings
+            li Configure persistence
+    div(ignite-loading='loadingCachesScreen' ignite-loading-text='Loading caches...' ignite-loading-position='top')
+        div(ng-show='ui.ready')
+            hr
+            +main-table('caches', 'caches', 'cacheName', 'selectItem(row)', '{{$index + 1}}) {{row.label}}', 'label')
+            .padding-top-dflt(bs-affix)
+                .panel-tip-container(data-placement='bottom' bs-tooltip='' data-title='Create new cache')
+                    button.btn.btn-primary(id='new-item' ng-click='createItem()') Add cache
+                +save-remove-clone-undo-buttons('cache')
+                hr
+            .bs-affix-fix
+            div(bs-collapse='' data-allow-multiple='true' ng-model='ui.activePanels')
+                form.form-horizontal(name='ui.inputForm' ng-show='contentVisible()' novalidate)
+                    .panel-group
+                        ignite-configuration-caches-general
+                        ignite-configuration-caches-memory
+                        ignite-configuration-caches-query
+                        ignite-configuration-caches-store
+
+                        +advanced-options-toggle-default
+
+                        div(ng-show='ui.expanded')
+                            ignite-configuration-caches-concurrency
+                            ignite-configuration-caches-rebalance
+                            ignite-configuration-caches-server-near-cache
+                            ignite-configuration-caches-statistics
+
+                            +advanced-options-toggle-default

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/clusters.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/clusters.jade b/modules/web-console/src/main/js/views/configuration/clusters.jade
new file mode 100644
index 0000000..6e29d86
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/clusters.jade
@@ -0,0 +1,60 @@
+//-
+    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.
+
+include ../../app/helpers/jade/mixins.jade
+
+.docs-header
+    h1 Configure Ignite Clusters
+.docs-body(ng-controller='clustersController')
+    ignite-information
+        ul
+            li Configure #[a(href='https://apacheignite.readme.io/docs/clustering' target='_blank') clusters] properties
+            li Associate clusters with caches and in-memory file systems
+    div(ignite-loading='loadingClustersScreen' ignite-loading-text='Loading clusters...' ignite-loading-position='top')
+        div(ng-show='ui.ready')
+            hr
+            +main-table('clusters', 'clusters', 'clusterName', 'selectItem(row)', '{{$index + 1}}) {{row.label}}', 'label')
+            .padding-top-dflt(bs-affix)
+                .panel-tip-container(data-placement='bottom' bs-tooltip='' data-title='Create new cluster')
+                    button.btn.btn-primary(id='new-item' ng-click='createItem()') Add cluster
+                +save-remove-clone-undo-buttons('cluster')
+                hr
+            .bs-affix-fix
+            div(bs-collapse='' data-allow-multiple='true' ng-model='ui.activePanels')
+                form.form-horizontal(name='ui.inputForm' ng-show='contentVisible()' novalidate)
+                    .panel-group
+                        ignite-configuration-clusters-general
+
+                        +advanced-options-toggle-default
+
+                        div(ng-show='ui.expanded')
+                            ignite-configuration-clusters-atomic
+                            ignite-configuration-clusters-binary
+                            ignite-configuration-clusters-communication
+                            ignite-configuration-clusters-connector
+                            ignite-configuration-clusters-deployment
+                            ignite-configuration-clusters-discovery
+                            ignite-configuration-clusters-events
+                            ignite-configuration-clusters-igfs
+                            ignite-configuration-clusters-marshaller
+                            ignite-configuration-clusters-metrics
+                            ignite-configuration-clusters-ssl
+                            ignite-configuration-clusters-swap
+                            ignite-configuration-clusters-thread
+                            ignite-configuration-clusters-time
+                            ignite-configuration-clusters-transactions
+
+                            +advanced-options-toggle-default

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/domains-import.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/domains-import.jade b/modules/web-console/src/main/js/views/configuration/domains-import.jade
new file mode 100644
index 0000000..46385f9
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/domains-import.jade
@@ -0,0 +1,196 @@
+//-
+    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 chk(mdl, change, tip)
+    input(type='checkbox' ng-model=mdl ng-change=change bs-tooltip='' data-title=tip data-trigger='hover' data-placement='top')
+
+mixin td-ellipses-lbl(w, lbl)
+    td.td-ellipsis(width='#{w}' style='min-width: #{w}; max-width: #{w}')
+        label #{lbl}
+
+.modal.modal-domain-import.center(role='dialog')
+    .modal-dialog
+        .modal-content(ignite-loading='importDomainFromDb' ignite-loading-text='{{importDomain.loadingOptions.text}}')
+            #errors-container.modal-header.header
+                button.close(ng-click='$hide()' aria-hidden='true') &times;
+                h4.modal-title(ng-if='!importDomain.demo') Import domain models from database
+                h4.modal-title(ng-if='importDomain.demo') Import domain models from demo database
+            .import-domain-model-wizard-page(ng-if='importDomain.action == "drivers" && !importDomain.jdbcDriversNotFound' style='margin-bottom: 321px')
+            .import-domain-model-wizard-page(ng-if='importDomain.action == "drivers" && importDomain.jdbcDriversNotFound' style='margin-bottom: 220px')
+                | Domain model could not be imported
+                ul
+                    li Agent failed to find JDBC drivers
+                    li Copy required JDBC drivers into agent '\jdbc-drivers' folder and try again
+                    li Refer to agent README.txt for more information
+            .import-domain-model-wizard-page(ng-show='importDomain.action == "connect" && importDomain.demo' style='margin-bottom: 211px')
+                div(ng-if='demoConnection.db == "H2"')
+                    label Demo description:
+                    ul
+                        li In-memory H2 database server will be started inside agent
+                        li Database will be populated with sample tables
+                        li You could test domain model generation with this demo database
+                        li Click "Next" to continue
+                div(ng-if='demoConnection.db != "H2"')
+                    label Demo could not be started
+                        ul
+                            li Agent failed to resolve H2 database jar
+                            li Copy h2-x.x.x.jar into agent '\jdbc-drivers' folder and try again
+                            li Refer to agent README.txt for more information
+            .import-domain-model-wizard-page(ng-show='importDomain.action == "connect" && !importDomain.demo' style='margin-bottom: 90px')
+                form.form-horizontal(name='connectForm' novalidate)
+                    .settings-row
+                        label.col-xs-4.col-sm-2.col-md-2 Driver JAR:
+                        .col-xs-8.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Select appropriate JAR with JDBC driver<br> To add another driver you need to place it into "/jdbc-drivers" folder of Ignite Web Agent<br> Refer to Ignite Web Agent README.txt for for more information')
+                            .input-tip
+                                button.select-toggle.form-control(id='jdbcDriverJar' bs-select data-container='.modal-domain-import' ng-model='ui.selectedJdbcDriverJar' ng-class='{placeholder: !(jdbcDriverJars && jdbcDriverJars.length > 0)}' placeholder='Choose JDBC driver' bs-options='item.value as item.label for item in jdbcDriverJars')
+                    .settings-row
+                        label.col-xs-4.col-sm-2.col-md-2 JDBC Driver:
+                        .col-xs-8.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Fully qualified class name of JDBC driver that will be used to connect to database')
+                            .input-tip
+                                input.form-control(id='jdbcDriverClass' type='text' ng-model='selectedPreset.jdbcDriverClass' placeholder='JDBC driver fully qualified class name' required=true)
+                    .settings-row
+                        label.col-xs-4.col-sm-2.col-md-2 JDBC URL:
+                        .col-xs-8.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='JDBC URL for connecting to database<br>Refer to your database documentation for details')
+                            .input-tip
+                                input.form-control(id='jdbcUrl' type='text' ng-model='selectedPreset.jdbcUrl' placeholder='JDBC URL' required=true)
+                    .settings-row
+                        label.col-xs-4.col-sm-2.col-md-2 User:
+                        .col-xs-8.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='User name for connecting to database')
+                            .input-tip
+                                input.form-control(id='user' type='text' ng-model='selectedPreset.user')
+                    .settings-row
+                        label.col-xs-4.col-sm-2.col-md-2 Password:
+                        .col-xs-8.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Password for connecting to database<br>Note, password would not be saved in preferences for security reasons')
+                            .input-tip
+                                input.form-control(id='password' type='password' ng-model='selectedPreset.password' on-enter='importDomainNext()')
+                    .settings-row
+                        .checkbox
+                            label
+                                input(id='tablesOnly' type='checkbox' ng-model='selectedPreset.tablesOnly')
+                                | Tables only
+                            i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='If selected, then only tables metadata will be parsed<br>Otherwise table and view metadata will be parsed')
+            .import-domain-model-wizard-page(ng-show='importDomain.action == "schemas"')
+                table.table.metadata(st-table='importDomain.displayedSchemas' st-safe-src='importDomain.schemas')
+                    thead
+                        tr
+                            th.header(colspan='2')
+                                .col-sm-4.pull-right(style='margin-bottom: 5px')
+                                    input.form-control(type='text' st-search='name' placeholder='Filter schemas...' ng-model='importDomain.displayedSchemasFilter' ng-change='selectSchema()')
+                        tr
+                            th(width='30px')
+                                +chk('importDomain.allSchemasSelected',  'selectAllSchemas()', 'Select all schemas')
+                            th
+                                label Schema
+                        tbody
+                            tr
+                                td(colspan='2')
+                                    .scrollable-y(style='height: 213px')
+                                        table.table-modal-striped(id='importSchemasData')
+                                            tbody
+                                                tr(ng-repeat='schema in importDomain.displayedSchemas')
+                                                    td(width='30px')
+                                                        input(type='checkbox' ng-model='schema.use' ng-change='selectSchema()')
+                                                    td
+                                                        label {{schema.name}}
+            .import-domain-model-wizard-page(ng-show='importDomain.action == "tables"')
+                table.table.metadata(st-table='importDomain.displayedTables' st-safe-src='importDomain.tables')
+                    thead
+                        tr
+                            th.header(colspan='6')
+                                .col-sm-4.pull-right(style='margin-bottom: 8px')
+                                    input.form-control(type='text' st-search='label' placeholder='Filter tables...' ng-model='importDomain.displayedTablesFilter' ng-change='selectTable()')
+                        tr
+                            th(width='30px')
+                                +chk('importDomain.allTablesSelected',  'selectAllTables()', 'Select all tables')
+                            th(width='130px')
+                                label Schema
+                            th(width='160px')
+                                label Table name
+                            th(colspan=2 width='288px')
+                                label Cache
+                            th
+                    tbody
+                        tr
+                            td(colspan='6')
+                                .scrollable-y(style='height: 143px')
+                                    table.table-modal-striped(id='importTableData')
+                                        tbody
+                                            tr(ng-repeat='table in importDomain.displayedTables track by $index')
+                                                td(width='30px' style='min-width: 30px; max-width: 30px')
+                                                    input(type='checkbox' ng-model='table.use' ng-change='selectTable()')
+                                                +td-ellipses-lbl('130px', '{{table.schema}}')
+                                                +td-ellipses-lbl('160px', '{{table.tbl}}')
+                                                td(colspan='2' width='288px' style='min-width: 160px; max-width: 160px')
+                                                    div.td-ellipsis
+                                                        a(ng-if='!table.edit' ng-click='startEditDbTableCache(table)') {{tableActionView(table)}}
+                                                        div(style='display: flex' ng-if='table.edit')
+                                                            button.select-toggle.form-control(style='width: 35%; margin-right: 5px' bs-select ng-model='table.action' data-container='.modal-domain-import' bs-options='item.value as item.shortLabel for item in importActions')
+                                                            button.select-toggle.form-control(style='width: 65%; margin-right: 0' bs-select ng-model='table.cacheOrTemplate' data-container='.modal-domain-import' bs-options='item.value as item.label for item in table.cachesOrTemplates')
+                                                td
+                .settings-row
+                    label Defaults to be applied for filtered tables
+                    i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Select and apply options for caches generation')
+                .settings-row
+                    .col-sm-11
+                        .col-sm-6(style='padding-right: 5px')
+                            button.select-toggle.form-control(bs-select ng-model='importCommon.action' data-container='.modal-domain-import' bs-options='item.value as item.label for item in importActions')
+                        .col-sm-6(style='padding-left: 5px; padding-right: 5px')
+                            button.select-toggle.form-control(bs-select ng-model='importCommon.cacheOrTemplate' data-container='.modal-domain-import' bs-options='item.value as item.label for item in importCommon.cachesOrTemplates')
+                    .col-sm-1(style='padding-left: 5px')
+                        button.btn.btn-primary(ng-click='applyDefaults()') Apply
+            .import-domain-model-wizard-page(ng-show='importDomain.action == "options"' style='margin-bottom: 176px')
+                form.form-horizontal(name='optionsForm' novalidate)
+                    .settings-row
+                        .col-xs-3.col-sm-2.col-md-2.required
+                            label.required Package:
+                        .col-xs-9.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Package that will be used for POJOs generation')
+                            .input-tip
+                                input.form-control(id='domainPackageName' type='text' ng-model='ui.packageName' placeholder='Package for POJOs generation')
+                    .settings-row
+                        .checkbox
+                            label
+                                input(id='domainBuiltinKeys' type='checkbox' ng-model='ui.builtinKeys')
+                                | Use Java built-in types for keys
+                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Use Java built-in types like "Integer", "Long", "String" instead of POJO generation in case when table primary key contains only one field')
+                    .settings-row
+                        .checkbox
+                            label
+                                input(id='domainUsePrimitives' type='checkbox' ng-model='ui.usePrimitives')
+                                | Use primitive types for NOT NULL table columns
+                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Use primitive types like "int", "long", "double" for POJOs fields generation in case of NOT NULL columns')
+                    .settings-row
+                        .checkbox
+                            label
+                                input(id='domainGenerateAliases' type='checkbox' ng-model='ui.generateAliases')
+                                | Generate aliases for query fields
+                                i.tipLabel.fa.fa-question-circle(bs-tooltip='' data-title='Generate aliases for query fields with names from database fields')
+                    .settings-row
+                        .col-xs-3.col-sm-2.col-md-2.required
+                            label Clusters:
+                        .col-xs-9.col-sm-10.col-md-10
+                            i.tipField.fa.fa-question-circle(bs-tooltip='' data-title='Choose clusters that will be associated with generated caches')
+                            .input-tip
+                                button.select-toggle.form-control(id='generatedCachesClusters' bs-select ng-model='ui.generatedCachesClusters' ng-class='{placeholder: !(ui.generatedCachesClusters && ui.generatedCachesClusters.length > 0)}' data-container='.modal-domain-import' data-multiple='1' placeholder='Choose clusters for generated caches' bs-options='item.value as item.label for item in clusters')
+            .modal-footer
+                label(ng-hide='importDomain.action == "drivers" || (importDomain.action == "connect" && importDomain.demo)').labelField {{importDomain.info}}
+                a.btn.btn-primary(ng-hide='importDomain.action == "drivers" || importDomain.action == "connect"' ng-click='importDomainPrev()' bs-tooltip='' data-title='{{prevTooltipText()}}' data-placement='bottom') Prev
+                a.btn.btn-primary(ng-click='importDomainNext()' ng-disabled='!importDomainNextAvailable()' bs-tooltip='' data-title='{{nextTooltipText()}}' data-placement='bottom') {{importDomain.button}}

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/domains.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/domains.jade b/modules/web-console/src/main/js/views/configuration/domains.jade
new file mode 100644
index 0000000..9975df9
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/domains.jade
@@ -0,0 +1,66 @@
+//-
+    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.
+
+include ../../app/helpers/jade/mixins.jade
+
+.docs-header
+    h1 Configure Domain Model And SQL Queries
+.docs-body(ng-controller='domainsController')
+    ignite-information
+        ul: li Import database schemas
+            li Configure indexed types
+    div(ignite-loading='loadingDomainModelsScreen' ignite-loading-text='Loading domain models...' ignite-loading-position='top')
+        div(ng-show='ui.ready')
+            hr
+            .padding-bottom-dflt(ng-show='domains && domains.length > 0')
+                table.links(st-table='displayedRows' st-safe-src='domains')
+                    thead
+                        tr
+                            th
+                                .col-sm-8
+                                    .col-sm-5
+                                        lable.labelHeader.labelFormField {{domainModelTitle()}}
+                                    .col-sm-7
+                                        .pull-right.labelLogin.additional-filter(ng-if='(domains | domainsValidation:false:true).length > 0')
+                                            a.labelFormField(ng-if='ui.showValid' ng-click='toggleValid()' bs-tooltip='' data-title='{{::ui.invalidKeyFieldsTooltip}}') Key fields should be configured: {{(displayedRows | domainsValidation:false:true).length}}&nbsp
+                                            a.labelFormField(ng-if='!ui.showValid' ng-click='toggleValid()') Show all domain models: {{displayedRows.length}}&nbsp
+                                .col-sm-4
+                                    input.form-control.pull-right(type='text' st-search='valueType' placeholder='Filter domain models...')
+                        tbody
+                            tr
+                                td
+                                    .scrollable-y(ng-show='(displayedRows | domainsValidation:ui.showValid:true).length > 0' style='max-height: 200px')
+                                        table
+                                            tbody
+                                                tr(ng-repeat='row in (displayedRows | domainsValidation:ui.showValid:true) track by row._id' ignite-bs-affix-update)
+                                                    td
+                                                        a(ng-class='{active: row._id == selectedItem._id}' ng-click='selectItem(row)') {{$index + 1}}) {{row.valueType}}
+                                    label.placeholder(ng-show='(displayedRows | domainsValidation:ui.showValid:true).length == 0') No domain models found
+            .padding-top-dflt(bs-affix)
+                .panel-tip-container(data-placement='bottom' bs-tooltip='' data-title='Create new domain model')
+                    button.btn.btn-primary(id='new-item' ng-click='createItem()') Add domain model
+                .panel-tip-container(bs-tooltip='' data-title='Import domain models from database' data-placement='bottom')
+                    button.btn.btn-primary(ng-click='showImportDomainModal()') Import from database
+                +save-remove-clone-undo-buttons('domain model')
+                .btn-group.panel-tip-container.pull-right(bs-tooltip='' data-title='Import domain models from demo database' data-placement='bottom')
+                hr
+            .bs-affix-fix
+            div(bs-collapse='' data-allow-multiple='true' ng-model='ui.activePanels')
+                form.form-horizontal(name='ui.inputForm' ng-show='contentVisible()' novalidate)
+                    .panel-group
+                        ignite-configuration-domains-general
+                        ignite-configuration-domains-query
+                        ignite-configuration-domains-store

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/igfs.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/igfs.jade b/modules/web-console/src/main/js/views/configuration/igfs.jade
new file mode 100644
index 0000000..b889a97
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/igfs.jade
@@ -0,0 +1,51 @@
+//-
+    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.
+
+include ../../app/helpers/jade/mixins.jade
+
+.docs-header
+    h1 Configure Ignite In-memory File Systems
+.docs-body(ng-controller='igfsController')
+    ignite-information(data-title='Configure IGFS only if you are going to use In-memory File System')
+        ul
+            li Ignite File System (#[a(href='https://apacheignite-fs.readme.io/docs/in-memory-file-system' target='_blank') IGFS]) is an in-memory file system allowing work with files and directories over existing cache infrastructure
+            li IGFS can either work as purely in-memory file system, or delegate to another file system (e.g. various Hadoop file system implementations) acting as a caching layer (see #[a(href='https://apacheignite-fs.readme.io/docs/secondary-file-system' target='_blank') secondary file system]  for more detail)
+            li In addition IGFS provides API to execute map-reduce tasks over file system data
+    div(ignite-loading='loadingIgfsScreen' ignite-loading-text='Loading IGFS screen...' ignite-loading-position='top')
+        div(ng-show='ui.ready')
+            hr
+            +main-table('IGFS', 'igfss', 'igfsName', 'selectItem(row)', '{{$index + 1}}) {{row.name}}', 'name')
+            .padding-top-dflt(bs-affix)
+                .panel-tip-container(data-placement='bottom' bs-tooltip='' data-title='Create new IGFS')
+                    button.btn.btn-primary(id='new-item' ng-click='createItem()') Add IGFS
+                +save-remove-clone-undo-buttons('IGFS')
+                hr
+            .bs-affix-fix
+            div(bs-collapse='' data-allow-multiple='true' ng-model='ui.activePanels')
+                form.form-horizontal(name='ui.inputForm' ng-show='contentVisible()' novalidate)
+                    .panel-group
+                        ignite-configuration-igfs-general
+
+                        +advanced-options-toggle-default
+
+                        div(ng-show='ui.expanded')
+                            ignite-configuration-igfs-secondary
+                            ignite-configuration-igfs-ipc
+                            ignite-configuration-igfs-fragmentizer
+                            ignite-configuration-igfs-dual
+                            ignite-configuration-igfs-misc
+
+                            +advanced-options-toggle-default

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/sidebar.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/sidebar.jade b/modules/web-console/src/main/js/views/configuration/sidebar.jade
new file mode 100644
index 0000000..bba6b25
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/sidebar.jade
@@ -0,0 +1,29 @@
+//-
+    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.
+
+.row
+    .col-xs-3.col-sm-3.col-md-2.border-right.section-left.greedy
+        .sidebar-nav(bs-affix)
+            ul.menu(ignite-sidebar)
+                li(ng-repeat='item in sidebar.items')
+                    a(ui-sref-active='active' ui-sref='{{::item.sref}}')
+                        span.fa-stack
+                            i.fa.fa-circle-thin.fa-stack-2x
+                            i.fa.fa-stack-1x {{::$index + 1}}
+                        | {{::item.text}}
+
+    .col-xs-9.col-sm-9.col-md-10.border-left.section-right
+        .docs-content(ui-view='')

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/summary-project-structure.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/summary-project-structure.jade b/modules/web-console/src/main/js/views/configuration/summary-project-structure.jade
new file mode 100644
index 0000000..aa09437
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/summary-project-structure.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.
+.popover.summary-project-structure
+    h3.popover-title
+        label.labelField Project structure
+        button.close(id='summary-project-structure-close' ng-click='$hide()') &times;
+    .popover-content
+        treecontrol.tree-classic(tree-model='projectStructure' options='projectStructureOptions' expanded-nodes='projectStructureExpanded')
+            span(ng-switch='' on='node.type')
+                span(ng-switch-when='folder')
+                    label {{node.name}}
+                span(ng-switch-when='file')
+                    i.fa.fa-file-text-o
+                    label {{node.name}}

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/summary-tabs.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/summary-tabs.jade b/modules/web-console/src/main/js/views/configuration/summary-tabs.jade
new file mode 100644
index 0000000..847b42f
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/summary-tabs.jade
@@ -0,0 +1,25 @@
+//-
+    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.
+
+ul.nav(ng-class='$navClass', role='tablist')
+    li(role='presentation' ng-repeat='$pane in $panes track by $index' ng-class='[ $isActive($pane, $index) ? $activeClass : "", $pane.disabled ? "disabled" : "" ]')
+        a.summary-tab(ng-show='$pane.title != "POJO" || (cluster | hasPojo)' ng-switch='$pane.title' role='tab' data-toggle='tab' ng-click='!$pane.disabled && $setActive($pane.name || $index)' data-index='{{ $index }}' aria-controls='$pane.title') {{$pane.title}}
+            img(ng-switch-when='XML' src='/images/xml.png')
+            img(ng-switch-when='Java' src='/images/java.png')
+            img(ng-switch-when='POM' src='/images/xml.png')
+            img(ng-switch-when='POJO' src='/images/java.png')
+            img(ng-switch-when='Dockerfile' src='/images/docker.png')
+.tab-content(ng-transclude style='fontSize: 12px; min-height: 25em')

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/configuration/summary.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/configuration/summary.jade b/modules/web-console/src/main/js/views/configuration/summary.jade
new file mode 100644
index 0000000..830adc1
--- /dev/null
+++ b/modules/web-console/src/main/js/views/configuration/summary.jade
@@ -0,0 +1,152 @@
+//-
+    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.
+
+include ../../app/helpers/jade/mixins.jade
+
+mixin hard-link(ref, txt)
+    a(style='color:#ec1c24' href=ref target='_blank') #{txt}
+
+.docs-header
+    h1 Configurations Summary
+.docs-body
+    ignite-information
+        ul
+            li Preview XML configurations for #[a(href='https://apacheignite.readme.io/docs/clients-vs-servers' target='_blank') server and client] nodes
+            li Preview code configuration
+            li Preview #[a(href='https://apacheignite.readme.io/docs/docker-deployment' target='_blank') Docker file]
+            li Preview POM dependencies
+            li Download ready-to-use Maven project
+
+    hr
+    .padding-dflt(ng-if='ui.ready && (!clusters || clusters.length == 0)')
+        | You have no clusters configured. Please configure them #[a(ui-sref='base.configuration.clusters') here].
+
+    div(ng-show='clusters && clusters.length > 0' ignite-loading='summaryPage' ignite-loading-text='Loading summary screen...' ignite-loading-position='top')
+        +main-table('clusters', 'clustersView', 'clusterName', 'selectItem(row)', '{{$index + 1}}) {{row.name}}', 'name')
+        div(ng-show='selectedItem && contentVisible(displayedRows, selectedItem)')
+            .padding-top-dflt(bs-affix)
+                button.btn.btn-primary(id='download' ng-click='downloadConfiguration()' bs-tooltip='' data-title='Download project' data-placement='bottom') Download project
+                .btn.btn-primary(bs-tooltip='' data-title='Preview generated project structure' data-placement='bottom')
+                    div(bs-popover data-template-url='/configuration/summary-project-structure.html', data-placement='bottom', data-trigger='click' data-auto-close='true')
+                        i.fa.fa-sitemap
+                        label.tipLabel Project structure
+                button.btn.btn-primary(id='proprietary-jdbc-drivers' ng-if='downloadJdbcDriversVisible()' ng-click='downloadJdbcDrivers()' bs-tooltip='' data-title='Open proprietary JDBC drivers download pages' data-placement='bottom') Download JDBC drivers
+                hr
+            .bs-affix-fix
+            .panel-group(bs-collapse ng-init='ui.activePanels=[0,1]' ng-model='ui.activePanels' data-allow-multiple='true')
+                .panel.panel-default
+                    .panel-heading(role='tab' bs-collapse-toggle)
+                        ignite-form-panel-chevron
+                        label Server
+
+                    .panel-collapse(id='server' role='tabpanel' bs-collapse-target)
+                        ignite-ui-ace-tabs.summary-tabs
+                            div(bs-tabs data-bs-active-pane="tabsServer.activeTab" template='configuration/summary-tabs.html')
+                                div(bs-pane title='XML')
+                                    ignite-ui-ace-xml(ng-if='tabsServer.activeTab == 0 || tabsServer.init[0]' ng-init='tabsServer.init[0] = true' data-master='cluster' data-no-deep-watch)
+                                div(bs-pane title='Java')
+                                    ignite-ui-ace-java(ng-if='tabsServer.activeTab == 1 || tabsServer.init[1]' ng-init='tabsServer.init[1] = true' data-master='cluster' data-no-deep-watch)
+                                div(bs-pane title='POM')
+                                    ignite-ui-ace-pom(ng-if='tabsServer.activeTab == 2 || tabsServer.init[2]' ng-init='tabsServer.init[2] = true' data-cluster='cluster' data-no-deep-watch)
+                                div(bs-pane title='Dockerfile')
+                                    ignite-ui-ace-docker(ng-if='tabsServer.activeTab == 3 || tabsServer.init[3]' ng-init='tabsServer.init[3] = true' data-cluster='cluster' data-no-deep-watch ng-model='ctrl.data.docker')
+
+                .panel.panel-default
+                    .panel-heading(role='tab' bs-collapse-toggle)
+                        ignite-form-panel-chevron
+                        label Client
+
+                    .panel-collapse(id='client' role='tabpanel' bs-collapse-target)
+                        form(name='clientForm' novalidate)
+                            -var nearCfg = 'ctrl.cluster.clientNearCfg'
+                            -var nearCfgEvictionPolicy = nearCfg + '.nearEvictionPolicy[' + nearCfg + '.nearEvictionPolicy.kind]'
+
+                            .group-content
+                                .settings-row
+                                    ignite-form-field.col-xs-8.col-sm-8.col-md-7(data-label='Near cache start size')
+                                        ignite-form-field-tooltip
+                                            | Initial cache size for near cache which will be used to pre-create internal hash table after start
+                                        ignite-form-field-input-number(
+                                            data-name='nearStartSize'
+                                            data-ng-model='#{nearCfg}.nearStartSize'
+                                            data-placeholder='375000'
+                                        )
+
+                                .settings-row
+                                    ignite-form-field.col-xs-8.col-sm-8.col-md-7(data-label='Near cache eviction policy')
+                                        ignite-form-field-tooltip
+                                            | Cache expiration policy
+                                        ignite-form-field-dropdown(
+                                            data-id='evictionPolicies'
+                                            data-name='evictionPolicies'
+                                            data-placeholder='Not set'
+                                            data-options='[\
+                                                {value: "LRU", label: "LRU"},\
+                                                {value: "FIFO", label: "FIFO"},\
+                                                {value: "SORTED", label: "Sorted"},\
+                                                {value: undefined, label: "Not set"}\
+                                            ]'
+                                            data-ng-model='#{nearCfg}.nearEvictionPolicy.kind'
+                                        )
+                                    span(ng-if='#{nearCfg}.nearEvictionPolicy.kind')
+                                        a.customize(
+                                            ng-show='ctrl.__form.expanded'
+                                            ng-click='ctrl.__form.expanded = false'
+                                        ) Hide settings
+                                        a.customize(
+                                            ng-hide='ctrl.__form.expanded'
+                                            ng-click='ctrl.__form.expanded = true'
+                                        ) Show settings
+
+                                .settings-row
+                                    .panel-details.col-xs-12.col-sm-12.col-md-7(ng-if='ctrl.__form.expanded && #{nearCfg}.nearEvictionPolicy.kind')
+                                        .details-row
+                                            ignite-form-field(data-label='Batch size')
+                                                ignite-form-field-tooltip
+                                                    | Number of entries to remove on shrink
+                                                ignite-form-field-input-number(
+                                                    data-name='batchSize'
+                                                    data-ng-model='#{nearCfgEvictionPolicy}.batchSize'
+                                                    data-placeholder='1'
+                                                )
+                                        .details-row
+                                            ignite-form-field(data-label='Max memory size')
+                                                ignite-form-field-tooltip
+                                                    | Maximum allowed cache size in bytes
+                                                ignite-form-field-input-number(
+                                                    data-name='maxMemorySize'
+                                                    data-ng-model='#{nearCfgEvictionPolicy}.maxMemorySize'
+                                                    data-placeholder='0'
+                                                )
+                                        .details-row
+                                            ignite-form-field(data-label='Max size')
+                                                ignite-form-field-tooltip
+                                                    | Maximum allowed size of cache before entry will start getting evicted
+                                                ignite-form-field-input-number(
+                                                    data-name='maxSize'
+                                                    data-ng-model='#{nearCfgEvictionPolicy}.maxSize'
+                                                    data-placeholder='100000'
+                                                )
+                        .summary-tabs(ignite-ui-ace-tabs)
+                            div(bs-tabs data-bs-active-pane="tabsClient.activeTab" template='configuration/summary-tabs.html')
+                                div(bs-pane title='XML')
+                                    ignite-ui-ace-xml(ng-if='tabsClient.activeTab == 0 || tabsClient.init[0]' ng-init='tabsClient.init[0] = true' data-master='cluster' data-no-deep-watch data-cluster-cfg='#{nearCfg}')
+                                div(bs-pane title='Java')
+                                    ignite-ui-ace-java(ng-if='tabsClient.activeTab == 1 || tabsClient.init[1]' ng-init='tabsClient.init[1] = true' data-master='cluster' data-no-deep-watch data-cluster-cfg='#{nearCfg}')
+                                div(bs-pane title='POM')
+                                    ignite-ui-ace-pom(ng-if='tabsClient.activeTab == 2 || tabsClient.init[2]' ng-init='tabsClient.init[2] = true' data-cluster='cluster' data-no-deep-watch)
+                                div(bs-pane title='POJO' ng-if='cluster | hasPojo')
+                                    ignite-ui-ace-pojos(ng-if='tabsClient.activeTab == 3 || tabsClient.init[3]' ng-init='tabsClient.init[3] = true' data-cluster='cluster' data-no-deep-watch ng-model='ctrl.data.pojos')

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/includes/footer.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/includes/footer.jade b/modules/web-console/src/main/js/views/includes/footer.jade
new file mode 100644
index 0000000..4ef3bf6
--- /dev/null
+++ b/modules/web-console/src/main/js/views/includes/footer.jade
@@ -0,0 +1,23 @@
+//-
+    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
+        .col-md-offset-1.col-md-10
+            ignite-footer
+        .col-md-1
+            .pull-right
+                ignite-powered-by-apache

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/includes/header.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/includes/header.jade b/modules/web-console/src/main/js/views/includes/header.jade
new file mode 100644
index 0000000..2b66d82
--- /dev/null
+++ b/modules/web-console/src/main/js/views/includes/header.jade
@@ -0,0 +1,48 @@
+//-
+    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.
+
+header#header.header
+    .viewedUser(ng-show='$root.user.becomeUsed')
+        | Currently assuming #[strong {{$root.user.firstName}} {{$root.user.lastName}}], #[a(ng-click='$root.revertIdentity()') revert to your identity].
+    table.container
+        tr
+            td.col-xs-3.col-sm-3.col-md-2
+                ignite-header-logo
+            td(ng-if='$root.user' style='padding-top: 20px')
+                ul.nav.navbar-nav(ignite-sidebar ignite-navbar)
+                    li(ng-class='{active: $state.includes("base.configuration")}')
+                        a.dropdown-toggle(data-toggle='dropdown' bs-dropdown='sidebar.items' data-placement='bottom-right') Configuration
+                            span.caret
+
+                    li.sql-notebooks(ng-class='{active: $state.includes("base.sql")}' ng-controller='notebooks')
+                        a(ng-if='IgniteDemoMode' ui-sref='base.sql.demo') SQL
+
+                        a(ng-if='!IgniteDemoMode && !notebooks.length' ng-click='inputNotebookName()') SQL
+
+                        a.dropdown-toggle(ng-if='!IgniteDemoMode && notebooks.length' data-toggle='dropdown' bs-dropdown='notebookDropdown' data-placement='bottom-left') SQL
+                            span.caret
+
+                    li(ui-sref-active='active'  ng-repeat='item in navbar.items')
+                        a(ui-sref='{{::item.sref}}') {{::item.text}}
+
+                a(ng-controller='demoController')
+                    button.btn.btn-info(ng-if='IgniteDemoMode' ng-click='closeDemo()') Close demo
+                    button.btn.btn-info(ng-if='!IgniteDemoMode' ng-click='startDemo()') Start demo
+
+                ul.nav.navbar-nav.pull-right(ignite-userbar)
+                    li(ng-class='{active: $state.includes("settings")}')
+                        a.dropdown-toggle(data-toggle='dropdown' bs-dropdown='userbar.items' data-placement='bottom-right') {{user.firstName}} {{user.lastName}}
+                            span.caret

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/index.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/index.jade b/modules/web-console/src/main/js/views/index.jade
new file mode 100644
index 0000000..8514533
--- /dev/null
+++ b/modules/web-console/src/main/js/views/index.jade
@@ -0,0 +1,66 @@
+//-
+    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.
+
+doctype html
+html(ng-app='ignite-console' id='app' ng-strict-di)
+    head
+        base(href='/')
+        link(rel='shortcut icon' href='favicon.ico')
+
+        meta(http-equiv='content-type' content='text/html; charset=UTF8')
+        meta(http-equiv='content-language' content='en')
+
+        title(ng-bind='$meta.title')
+
+        meta(name='fragment' content='!')
+        meta(name='description' content='{{$meta.description}}')
+        meta(name='keywords' content='{{$meta.keywords}}')
+        meta(ng-repeat='(key, value) in $meta.properties' name='{{::key}}' content='{{::value}}')
+
+        // build:css
+        link(rel='stylesheet', href='/vendors.css')
+        link(rel='stylesheet', href='/app.css')
+        // endbuild
+
+    body.theme-line.body-overlap.greedy
+
+        .splash.splash-max-foreground(hide-on-state-change)
+            .splash-wrapper
+                .spinner
+                    .bounce1
+                    .bounce2
+                    .bounce3
+
+                .splash-wellcome Loading...
+
+
+        .ribbon-wrapper.right(ng-cloak)
+            .ribbon(ng-style='IgniteDemoMode && {"background": "#1b6d88"}')
+                label {{IgniteDemoMode ? "Demo" : "Beta" }}
+
+        .wrapper(ui-view='')
+
+
+        // build:js
+        script(src='jspm_packages/system.js')
+        script(src='system.config.js')
+        script(src='vendors.js')
+        script(src='app.js')
+        script System.import('app/index');
+        // endbuild
+
+        // ignite:plugins
+        // endignite
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/reset.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/reset.jade b/modules/web-console/src/main/js/views/reset.jade
new file mode 100644
index 0000000..e5b8f12
--- /dev/null
+++ b/modules/web-console/src/main/js/views/reset.jade
@@ -0,0 +1,48 @@
+//-
+    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.
+
+header#header.header
+    table.container
+        tr
+            td.col-xs-3.col-sm-3.col-md-2
+                ignite-header-logo
+            td
+                ignite-header-title
+
+.container.body-container
+    .main-content(ng-controller='resetPassword')
+        .row
+            .text-center(ng-if='!token')
+                p Further instructions for password reset have been sent to your e-mail address.
+            .text-center(ng-if='error')
+                p {{::error}}
+            div(ng-if='token && !error')
+                form.form-horizontal(name='resetForm' ng-init='reset_info.token = token')
+                    .settings-row
+                        label.col-sm-1 E-mail:
+                        label {{::email}}
+                    .settings-row
+                        label.col-sm-1.required Password:
+                        .col-sm-3
+                            input#user_password.form-control(enter-focus-next='user_confirm' type='password' ng-model='reset_info.password' placeholder='New password' required)
+                    .settings-row
+                        label.col-sm-1.required Confirm:
+                        .col-sm-3
+                            input#user_confirm.form-control(type='password' ng-model='reset_info.confirm' match='reset_info.password' placeholder='Confirm new password' required on-enter='resetForm.$valid && resetPassword(user_info)')
+                    .settings-row
+                        button.btn.btn-primary(ng-disabled='resetForm.$invalid' ng-click='resetPassword(reset_info)') Reset Password
+
+include includes/footer

http://git-wip-us.apache.org/repos/asf/ignite/blob/eb5ac0ae/modules/web-console/src/main/js/views/settings/admin.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/settings/admin.jade b/modules/web-console/src/main/js/views/settings/admin.jade
new file mode 100644
index 0000000..862d959
--- /dev/null
+++ b/modules/web-console/src/main/js/views/settings/admin.jade
@@ -0,0 +1,76 @@
+//-
+    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.
+
+.row(ng-controller='adminController')
+    .docs-content.greedy
+        .docs-header
+            h1 List of registered users
+            hr
+        .docs-body
+            .col-xs-12
+                table.table.table-striped.table-vertical-middle.admin(st-table='displayedUsers' st-safe-src='users')
+                    thead
+                        tr
+                            th.header(colspan='10')
+                                .col-xs-3
+                                    input.form-control(type='text' st-search='label' placeholder='Filter users...')
+                                .col-xs-9.admin-summary.text-right(colspan='10')
+                                    strong Total users: {{ users.length }}
+                                .col-xs-offset-6.col-xs-6.text-right
+                                    div(st-pagination st-items-by-page='15' st-displayed-pages='5' st-template='../templates/pagination.html')
+                        tr
+                            th(st-sort='userName') User
+                            th(st-sort='email') Email
+                            th(st-sort='company') Company
+                            th(st-sort='country') Country
+                            th.col-xs-2(st-sort='lastLogin' st-sort-default='reverse') Last login
+                            th.text-nowrap(st-sort='counters.clusters' st-descending-first bs-tooltip='"Clusters count"' data-placement='top')
+                                i.fa.fa-sitemap()
+                            th.text-nowrap(st-sort='counters.models' st-descending-first bs-tooltip='"Models count"' data-placement='top')
+                                i.fa.fa-object-group()
+                            th.text-nowrap(st-sort='counters.caches' st-descending-first bs-tooltip='"Caches count"' data-placement='top')
+                                i.fa.fa-database()
+                            th.text-nowrap(st-sort='counters.igfs' st-descending-first bs-tooltip='"IGFS count"' data-placement='top')
+                                i.fa.fa-folder-o()
+                            th(width='1%') Actions
+                    tbody
+                        tr(ng-repeat='row in displayedUsers track by row._id')
+                            td {{::row.userName}}
+                            td
+                                a(ng-href='mailto:{{::row.email}}') {{::row.email}}
+                            td {{::row.company}}
+                            td {{::row.countryCode}}
+                            td {{::row.lastLogin | date:'medium'}}
+                            td {{::row.counters.clusters}}
+                            td {{::row.counters.models}}
+                            td {{::row.counters.caches}}
+                            td {{::row.counters.igfs}}
+                            td.text-center
+                                a.btn.btn-default.dropdown-toggle(bs-dropdown='' ng-show='row._id != user._id' data-placement='bottom-right')
+                                    i.fa.fa-gear &nbsp;
+                                    span.caret
+                                ul.dropdown-menu(role='menu')
+                                    li
+                                        a(ng-click='becomeUser(row)') Become this user
+                                    li
+                                        a(ng-click='toggleAdmin(row)' ng-if='row.admin && row._id !== user._id') Revoke admin
+                                        a(ng-click='toggleAdmin(row)' ng-if='!row.admin && row._id !== user._id')  Grant admin
+                                    li
+                                        a(ng-click='removeUser(row)') Remove user
+                    tfoot
+                        tr
+                            td.text-right(colspan='10')
+                                div(st-pagination st-items-by-page='15' st-displayed-pages='5' st-template='../templates/pagination.html')


Mime
View raw message