You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2015/11/23 21:46:52 UTC
[41/50] [abbrv] nifi git commit: NIFI-655: - Refactoring web security
to use Spring Security Java Configuration. - Introducing security in Web UI
in order to get JWT.
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
new file mode 100644
index 0000000..6cefd4f
--- /dev/null
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/login/nf-login.js
@@ -0,0 +1,302 @@
+/*
+ * 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.
+ */
+
+/* global nf, top */
+
+$(document).ready(function () {
+ nf.Login.init();
+});
+
+nf.Login = (function () {
+
+ var supportsAnonymous = false;
+
+ var config = {
+ urls: {
+ identity: '../nifi-api/controller/identity',
+ users: '../nifi-api/controller/users',
+ token: '../nifi-api/access/token',
+ accessStatus: '../nifi-api/access',
+ accessConfig: '../nifi-api/access/config'
+ }
+ };
+
+ var initializeMessage = function () {
+ $('#login-message-container').show();
+ };
+
+ var showLogin = function () {
+ // reset the forms
+ $('#username').val('');
+ $('#password').val('');
+ $('#login-submission-button').text('Log in');
+
+ // update the form visibility
+ $('#login-container').show();
+ $('#nifi-registration-container').hide();
+
+ // set the focus
+ $('#username').focus();
+ };
+
+ var initializeNiFiRegistration = function () {
+ $('#nifi-registration-justification').count({
+ charCountField: '#remaining-characters'
+ });
+
+ // toggle between signup and login
+ $('#login-to-account-link').on('click', function () {
+ showLogin();
+ });
+ };
+
+ var showNiFiRegistration = function () {
+ // reset the forms
+ $('#login-submission-button').text('Submit');
+ $('#nifi-registration-justification').val('');
+
+ // update the form visibility
+ $('#login-container').hide();
+ $('#nifi-registration-container').show();
+ };
+
+ var initializeSubmission = function () {
+ $('#login-submission-button').on('click', function () {
+ if ($('#login-container').is(':visible')) {
+ login();
+ } else if ($('#nifi-registration-container').is(':visible')) {
+ submitJustification();
+ }
+ });
+
+ $('#login-submission-container').show();
+ };
+
+ var login = function () {
+ // show the logging message...
+ $('#login-progress-label').text('Logging in...');
+ $('#login-progress-container').show();
+ $('#login-submission-container').hide();
+
+ // login submit
+ $.ajax({
+ type: 'POST',
+ url: config.urls.token,
+ data: {
+ 'username': $('#username').val(),
+ 'password': $('#password').val()
+ }
+ }).done(function (jwt) {
+ // get the payload and store the token with the appropirate expiration
+ var token = nf.Common.getJwtPayload(jwt);
+ var expiration = parseInt(token['exp'], 10) * nf.Common.MILLIS_PER_SECOND;
+ nf.Storage.setItem('jwt', jwt, expiration);
+
+ // check to see if they actually have access now
+ $.ajax({
+ type: 'GET',
+ url: config.urls.identity,
+ dataType: 'json'
+ }).done(function (response) {
+ if (response.identity === 'anonymous') {
+ showLogoutLink();
+
+ // schedule automatic token refresh
+ nf.Common.scheduleTokenRefresh();
+
+ // show the user
+ $('#nifi-user-submit-justification').text(token['preferred_username']);
+
+ // show the registration form
+ initializeNiFiRegistration();
+ showNiFiRegistration();
+
+ // update the form visibility
+ $('#login-submission-container').show();
+ $('#login-progress-container').hide();
+ } else {
+ // reload as appropriate - no need to schedule token refresh as the page is reloading
+ if (top !== window) {
+ parent.window.location = '/nifi';
+ } else {
+ window.location = '/nifi';
+ }
+ }
+ }).fail(function (xhr, status, error) {
+ showLogoutLink();
+
+ // schedule automatic token refresh
+ nf.Common.scheduleTokenRefresh();
+
+ // show the user
+ $('#nifi-user-submit-justification').text(token['preferred_username']);
+
+ if (xhr.status === 401) {
+ initializeNiFiRegistration();
+ showNiFiRegistration();
+
+ // update the form visibility
+ $('#login-submission-container').show();
+ $('#login-progress-container').hide();
+ } else {
+ $('#login-message-title').text('Unable to log in');
+ $('#login-message').text(xhr.responseText);
+
+ // update visibility
+ $('#login-container').hide();
+ $('#login-submission-container').hide();
+ $('#login-progress-container').hide();
+ $('#login-message-container').show();
+ }
+ });
+ }).fail(function (xhr, status, error) {
+ nf.Dialog.showOkDialog({
+ dialogContent: nf.Common.escapeHtml(xhr.responseText),
+ overlayBackground: false
+ });
+
+ // update the form visibility
+ $('#login-submission-container').show();
+ $('#login-progress-container').hide();
+ });
+ };
+
+ var submitJustification = function () {
+ // show the logging message...
+ $('#login-progress-label').text('Submitting...');
+ $('#login-progress-container').show();
+ $('#login-submission-container').hide();
+
+ // attempt to create the nifi account registration
+ $.ajax({
+ type: 'POST',
+ url: config.urls.users,
+ data: {
+ 'justification': $('#nifi-registration-justification').val()
+ }
+ }).done(function (response) {
+ var markup = 'An administrator will process your request shortly.';
+ if (supportsAnonymous === true) {
+ markup += '<br/><br/>In the meantime you can continue accessing anonymously.';
+ }
+
+ $('#login-message-title').text('Thanks!');
+ $('#login-message').html(markup);
+ }).fail(function (xhr, status, error) {
+ $('#login-message-title').text('Unable to submit justification');
+ $('#login-message').text(xhr.responseText);
+ }).always(function () {
+ // update form visibility
+ $('#nifi-registration-container').hide();
+ $('#login-submission-container').hide();
+ $('#login-progress-container').hide();
+ $('#login-message-container').show();
+ });
+ };
+
+ var showLogoutLink = function () {
+ nf.Common.showLogoutLink();
+ };
+
+ return {
+ /**
+ * Initializes the login page.
+ */
+ init: function () {
+ nf.Storage.init();
+
+ if (nf.Storage.getItem('jwt') !== null) {
+ showLogoutLink();
+ }
+
+ // access status
+ var accessStatus = $.ajax({
+ type: 'GET',
+ url: config.urls.accessStatus,
+ dataType: 'json'
+ }).fail(function (xhr, status, error) {
+ $('#login-message-title').text('Unable to check Access Status');
+ $('#login-message').text(xhr.responseText);
+ initializeMessage();
+ });
+
+ // access config
+ var accessConfigXhr = $.ajax({
+ type: 'GET',
+ url: config.urls.accessConfig,
+ dataType: 'json'
+ });
+
+ $.when(accessStatus, accessConfigXhr).done(function (accessStatusResult, accessConfigResult) {
+ var accessStatusResponse = accessStatusResult[0];
+ var accessStatus = accessStatusResponse.accessStatus;
+
+ var accessConfigResponse = accessConfigResult[0];
+ var accessConfig = accessConfigResponse.config;
+
+ // record whether this NiFi supports anonymous access
+ supportsAnonymous = accessConfig.supportsAnonymous;
+
+ // possible login states
+ var needsLogin = false;
+ var needsNiFiRegistration = false;
+ var showMessage = false;
+
+ // handle the status appropriately
+ if (accessStatus.status === 'UNKNOWN') {
+ needsLogin = true;
+ } else if (accessStatus.status === 'UNREGISTERED') {
+ needsNiFiRegistration = true;
+
+ $('#nifi-user-submit-justification').text(accessStatus.username);
+ } else if (accessStatus.status === 'NOT_ACTIVE') {
+ showMessage = true;
+
+ $('#login-message-title').text('Access Denied');
+ $('#login-message').text(accessStatus.message);
+ } else if (accessStatus.status === 'ACTIVE') {
+ showMessage = true;
+
+ $('#login-message-title').text('Success');
+ $('#login-message').text('Your account is active and you are already logged in.');
+ }
+
+ // if login is required, verify its supported
+ if (accessConfig.supportsLogin === false && needsLogin === true) {
+ $('#login-message-title').text('Access Denied');
+ $('#login-message').text('This NiFi is not configured to support login.');
+ showMessage = true;
+ needsLogin = false;
+ }
+
+ // initialize the page as appropriate
+ if (showMessage === true) {
+ initializeMessage();
+ } else if (needsLogin === true) {
+ showLogin();
+ } else if (needsNiFiRegistration === true) {
+ initializeNiFiRegistration();
+ showNiFiRegistration();
+ }
+
+ if (needsLogin === true || needsNiFiRegistration === true) {
+ initializeSubmission();
+ }
+ });
+ }
+ };
+}());
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
index 06c34f9..321044f 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-common.js
@@ -50,948 +50,1110 @@ $(document).ready(function () {
// hide the loading indicator
$('div.loading-container').removeClass('ajax-loading');
});
-
- // initialize the tooltips
- $('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
-});
-
-// Define a common utility class used across the entire application.
-nf.Common = {
- config: {
- sensitiveText: 'Sensitive value set',
- tooltipConfig: {
- style: {
- classes: 'nifi-tooltip'
- },
- show: {
- solo: true,
- effect: false
- },
- hide: {
- effect: false
- },
- position: {
- at: 'top right',
- my: 'bottom left'
- }
- }
- },
-
- /**
- * Determines if the current broswer supports SVG.
- */
- SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
-
- /**
- * The authorities for the current user.
- */
- authorities: undefined,
- /**
- * Sets the authorities for the current user.
- *
- * @argument {array} roles The current users authorities
- */
- setAuthorities: function (roles) {
- nf.Common.authorities = roles;
- },
-
- /**
- * Loads a script at the specified URL. Supports caching the script on the browser.
- *
- * @param {string} url
- */
- cachedScript: function (url) {
- return $.ajax({
- dataType: 'script',
- cache: true,
- url: url
- });
- },
-
- /**
- * Determines whether the current user can access provenance.
- *
- * @returns {boolean}
- */
- canAccessProvenance: function () {
- var canAccessProvenance = false;
- if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
- $.each(nf.Common.authorities, function (i, authority) {
- if (authority === 'ROLE_PROVENANCE') {
- canAccessProvenance = true;
- return false;
- }
- });
- }
- return canAccessProvenance;
- },
-
- /**
- * Returns whether or not the current user is a DFM.
- */
- isDFM: function () {
- var dfm = false;
- if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
- $.each(nf.Common.authorities, function (i, authority) {
- if (authority === 'ROLE_DFM') {
- dfm = true;
+ // include jwt when possible
+ $.ajaxSetup({
+ 'beforeSend': function(xhr) {
+ var hadToken = nf.Storage.hasItem('jwt');
+
+ // get the token to include in all requests
+ var token = nf.Storage.getItem('jwt');
+ if (token !== null) {
+ xhr.setRequestHeader('Authorization', 'Bearer ' + token);
+ } else {
+ // if the current user was logged in with a token and the token just expired, reload
+ if (hadToken === true) {
return false;
}
- });
+ }
}
- return dfm;
- },
+ });
+
+ // initialize the tooltips
+ $('img.setting-icon').qtip(nf.Common.config.tooltipConfig);
- /**
- * Returns whether or not the current user is a DFM.
- */
- isAdmin: function () {
- var admin = false;
- if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
- $.each(nf.Common.authorities, function (i, authority) {
- if (authority === 'ROLE_ADMIN') {
- admin = true;
- return false;
- }
- });
- }
- return admin;
- },
+ // shows the logout link in the message-pane when appropriate and schedule token refresh
+ if (nf.Storage.getItem('jwt') !== null) {
+ $('#user-logout-container').show();
+ nf.Common.scheduleTokenRefresh();
+ }
- /**
- * Adds a mouse over effect for the specified selector using
- * the specified styles.
- *
- * @argument {string} selector The selector for the element to add a hover effect for
- * @argument {string} normalStyle The css style for the normal state
- * @argument {string} overStyle The css style for the over state
- */
- addHoverEffect: function (selector, normalStyle, overStyle) {
- $(document).on('mouseenter', selector, function () {
- $(this).removeClass(normalStyle).addClass(overStyle);
- }).on('mouseleave', selector, function () {
- $(this).removeClass(overStyle).addClass(normalStyle);
- });
- return $(selector).addClass(normalStyle);
- },
+ // handle logout
+ $('#user-logout').on('click', function () {
+ nf.Storage.removeItem('jwt');
+ window.location = '/nifi/login';
+ });
+});
+
+// Define a common utility class used across the entire application.
+nf.Common = (function () {
+ // interval for cancelling token refresh when necessary
+ var tokenRefreshInterval = null;
- /**
- * Method for handling ajax errors.
- *
- * @argument {object} xhr The XmlHttpRequest
- * @argument {string} status The status of the request
- * @argument {string} error The error
- */
- handleAjaxError: function (xhr, status, error) {
- // show the account registration page if necessary
- if (xhr.status === 401 && $('#registration-pane').length) {
- // show the registration pane
- $('#registration-pane').show();
-
- // close the canvas
- nf.Common.closeCanvas();
- return;
- }
+ return {
+ ANONYMOUS_USER_TEXT: 'Anonymous user',
- // if an error occurs while the splash screen is visible close the canvas show the error message
- if ($('#splash').is(':visible')) {
- $('#message-title').text('An unexpected error has occurred');
- if ($.trim(xhr.responseText) === '') {
- $('#message-content').text('Please check the logs.');
- } else {
- $('#message-content').text(xhr.responseText);
+ config: {
+ sensitiveText: 'Sensitive value set',
+ tooltipConfig: {
+ style: {
+ classes: 'nifi-tooltip'
+ },
+ show: {
+ solo: true,
+ effect: false
+ },
+ hide: {
+ effect: false
+ },
+ position: {
+ at: 'top right',
+ my: 'bottom left'
+ }
}
+ },
- // show the error pane
- $('#message-pane').show();
+ /**
+ * Determines if the current broswer supports SVG.
+ */
+ SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect,
- // close the canvas
- nf.Common.closeCanvas();
- return;
- }
-
- // status code 400, 404, and 409 are expected response codes for common errors.
- if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
- nf.Dialog.showOkDialog({
- dialogContent: nf.Common.escapeHtml(xhr.responseText),
- overlayBackground: false
+ /**
+ * The authorities for the current user.
+ */
+ authorities: undefined,
+
+ /**
+ * Sets the authorities for the current user.
+ *
+ * @argument {array} roles The current users authorities
+ */
+ setAuthorities: function (roles) {
+ nf.Common.authorities = roles;
+ },
+
+ /**
+ * Loads a script at the specified URL. Supports caching the script on the browser.
+ *
+ * @param {string} url
+ */
+ cachedScript: function (url) {
+ return $.ajax({
+ dataType: 'script',
+ cache: true,
+ url: url
});
- } else {
- if (xhr.status < 99 || xhr.status === 12007 || xhr.status === 12029) {
- var content = 'Please ensure the application is running and check the logs for any errors.';
- if (nf.Common.isDefinedAndNotNull(status)) {
- if (status === 'timeout') {
- content = 'Request has timed out. Please ensure the application is running and check the logs for any errors.';
- } else if (status === 'abort') {
- content = 'Request has been aborted.';
- } else if (status === 'No Transport') {
- content = 'Request transport mechanism failed. Please ensure the host where the application is running is accessible.';
+ },
+
+ /**
+ * Automatically refresh tokens by checking once an hour if its going to expire soon.
+ */
+ scheduleTokenRefresh: function () {
+ // if we are currently polling for token refresh, cancel it
+ if (tokenRefreshInterval !== null) {
+ clearInterval(tokenRefreshInterval);
+ }
+
+ // set the interval to one hour
+ var interval = nf.Common.MILLIS_PER_MINUTE;
+
+ var checkExpiration = function () {
+ var expiration = nf.Storage.getItemExpiration('jwt');
+
+ // ensure there is an expiration and token present
+ if (expiration !== null) {
+ var expirationDate = new Date(expiration);
+ var now = new Date();
+
+ // get the time remainging plus a little bonus time to reload the token
+ var timeRemaining = expirationDate.valueOf() - now.valueOf() - (30 * nf.Common.MILLIS_PER_SECOND);
+ if (timeRemaining < interval) {
+ if ($('#current-user').text() !== nf.Common.ANONYMOUS_USER_TEXT && !$('#anonymous-user-alert').is(':visible')) {
+ // if the token will expire before the next interval minus some bonus time, notify the user to re-login
+ $('#anonymous-user-alert').show().qtip($.extend({}, nf.Common.config.tooltipConfig, {
+ content: 'Your session will expire soon. Please log in again to avoid being automatically logged out.',
+ position: {
+ my: 'top right',
+ at: 'bottom left'
+ }
+ }));
+ }
}
}
- $('#message-title').text('Unable to communicate with NiFi');
- $('#message-content').text(content);
- } else if (xhr.status === 401) {
- $('#message-title').text('Unauthorized');
- if ($.trim(xhr.responseText) === '') {
- $('#message-content').text('Authorization is required to use this NiFi.');
+ };
+
+ // perform initial check
+ checkExpiration();
+
+ // schedule subsequent checks
+ tokenRefreshInterval = setInterval(checkExpiration, interval);
+ },
+
+ /**
+ * Sets the anonymous user label.
+ */
+ setAnonymousUserLabel: function () {
+ var anonymousUserAlert = $('#anonymous-user-alert');
+ if (anonymousUserAlert.data('qtip')) {
+ anonymousUserAlert.qtip('api').destroy(true);
+ }
+
+ // alert user's of anonymous access
+ anonymousUserAlert.show().qtip($.extend({}, nf.Common.config.tooltipConfig, {
+ content: 'You are accessing with limited authority. Log in or request an account to access with additional authority granted to you by an administrator.',
+ position: {
+ my: 'top right',
+ at: 'bottom left'
+ }
+ }));
+
+ // render the anonymous user text
+ $('#current-user').text(nf.Common.ANONYMOUS_USER_TEXT).show();
+ },
+
+ /**
+ * Extracts the subject from the specified jwt. If the jwt is not as expected
+ * an empty string is returned.
+ *
+ * @param {string} jwt
+ * @returns {string}
+ */
+ getJwtPayload: function (jwt) {
+ if (nf.Common.isDefinedAndNotNull(jwt)) {
+ var segments = jwt.split(/\./);
+ if (segments.length !== 3) {
+ return '';
+ }
+
+ var rawPayload = $.base64.atob(segments[1]);
+ var payload = JSON.parse(rawPayload);
+
+ if (nf.Common.isDefinedAndNotNull(payload)) {
+ return payload;
} else {
- $('#message-content').text(xhr.responseText);
+ return null;
}
- } else if (xhr.status === 403) {
- $('#message-title').text('Forbidden');
- if ($.trim(xhr.responseText) === '') {
- $('#message-content').text('Unable to authorize you to use this NiFi.');
+ }
+
+ return null;
+ },
+
+ /**
+ * Determines whether the current user can access provenance.
+ *
+ * @returns {boolean}
+ */
+ canAccessProvenance: function () {
+ var canAccessProvenance = false;
+ if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
+ $.each(nf.Common.authorities, function (i, authority) {
+ if (authority === 'ROLE_PROVENANCE') {
+ canAccessProvenance = true;
+ return false;
+ }
+ });
+ }
+ return canAccessProvenance;
+ },
+
+ /**
+ * Returns whether or not the current user is a DFM.
+ */
+ isDFM: function () {
+ var dfm = false;
+ if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
+ $.each(nf.Common.authorities, function (i, authority) {
+ if (authority === 'ROLE_DFM') {
+ dfm = true;
+ return false;
+ }
+ });
+ }
+ return dfm;
+ },
+
+ /**
+ * Returns whether or not the current user is a DFM.
+ */
+ isAdmin: function () {
+ var admin = false;
+ if (nf.Common.isDefinedAndNotNull(nf.Common.authorities)) {
+ $.each(nf.Common.authorities, function (i, authority) {
+ if (authority === 'ROLE_ADMIN') {
+ admin = true;
+ return false;
+ }
+ });
+ }
+ return admin;
+ },
+
+ /**
+ * Adds a mouse over effect for the specified selector using
+ * the specified styles.
+ *
+ * @argument {string} selector The selector for the element to add a hover effect for
+ * @argument {string} normalStyle The css style for the normal state
+ * @argument {string} overStyle The css style for the over state
+ */
+ addHoverEffect: function (selector, normalStyle, overStyle) {
+ $(document).on('mouseenter', selector, function () {
+ $(this).removeClass(normalStyle).addClass(overStyle);
+ }).on('mouseleave', selector, function () {
+ $(this).removeClass(overStyle).addClass(normalStyle);
+ });
+ return $(selector).addClass(normalStyle);
+ },
+
+ /**
+ * Method for handling ajax errors.
+ *
+ * @argument {object} xhr The XmlHttpRequest
+ * @argument {string} status The status of the request
+ * @argument {string} error The error
+ */
+ handleAjaxError: function (xhr, status, error) {
+ if (status === 'canceled') {
+ if ($('#splash').is(':visible')) {
+ $('#message-title').text('Session Expired');
+ $('#message-content').text('Your session has expired. Please reload to log in again.');
+
+ // show the error pane
+ $('#message-pane').show();
} else {
- $('#message-content').text(xhr.responseText);
+ nf.Dialog.showOkDialog({
+ dialogContent: 'Your session has expired. Please press Ok to log in again.',
+ overlayBackground: false,
+ okHandler: function () {
+ window.location = '/nifi';
+ }
+ });
}
- } else if (xhr.status === 500) {
- $('#message-title').text('An unexpected error has occurred');
- if ($.trim(xhr.responseText) === '') {
- $('#message-content').text('An error occurred communicating with the application core. Please check the logs and fix any configuration issues before restarting.');
+
+ // close the canvas
+ nf.Common.closeCanvas();
+ return;
+ }
+
+ // if an error occurs while the splash screen is visible close the canvas show the error message
+ if ($('#splash').is(':visible')) {
+ if (xhr.status === 401) {
+ $('#message-title').text('Unauthorized');
+ } else if (xhr.status === 403) {
+ $('#message-title').text('Access Denied');
} else {
- $('#message-content').text(xhr.responseText);
+ $('#message-title').text('An unexpected error has occurred');
}
- } else if (xhr.status === 200 || xhr.status === 201) {
- $('#message-title').text('Parse Error');
+
if ($.trim(xhr.responseText) === '') {
- $('#message-content').text('Unable to interpret response from NiFi.');
+ $('#message-content').text('Please check the logs.');
} else {
$('#message-content').text(xhr.responseText);
}
+
+ // show the error pane
+ $('#message-pane').show();
+
+ // close the canvas
+ nf.Common.closeCanvas();
+ return;
+ }
+
+ // status code 400, 404, and 409 are expected response codes for common errors.
+ if (xhr.status === 400 || xhr.status === 404 || xhr.status === 409) {
+ nf.Dialog.showOkDialog({
+ dialogContent: nf.Common.escapeHtml(xhr.responseText),
+ overlayBackground: false
+ });
} else {
- $('#message-title').text(xhr.status + ': Unexpected Response');
- $('#message-content').text('An unexpected error has occurred. Please check the logs.');
+ if (xhr.status < 99 || xhr.status === 12007 || xhr.status === 12029) {
+ var content = 'Please ensure the application is running and check the logs for any errors.';
+ if (nf.Common.isDefinedAndNotNull(status)) {
+ if (status === 'timeout') {
+ content = 'Request has timed out. Please ensure the application is running and check the logs for any errors.';
+ } else if (status === 'abort') {
+ content = 'Request has been aborted.';
+ } else if (status === 'No Transport') {
+ content = 'Request transport mechanism failed. Please ensure the host where the application is running is accessible.';
+ }
+ }
+ $('#message-title').text('Unable to communicate with NiFi');
+ $('#message-content').text(content);
+ } else if (xhr.status === 401) {
+ $('#message-title').text('Unauthorized');
+ if ($.trim(xhr.responseText) === '') {
+ $('#message-content').text('Authorization is required to use this NiFi.');
+ } else {
+ $('#message-content').text(xhr.responseText);
+ }
+ } else if (xhr.status === 403) {
+ $('#message-title').text('Access Denied');
+ if ($.trim(xhr.responseText) === '') {
+ $('#message-content').text('Unable to authorize you to use this NiFi.');
+ } else {
+ $('#message-content').text(xhr.responseText);
+ }
+ } else if (xhr.status === 500) {
+ $('#message-title').text('An unexpected error has occurred');
+ if ($.trim(xhr.responseText) === '') {
+ $('#message-content').text('An error occurred communicating with the application core. Please check the logs and fix any configuration issues before restarting.');
+ } else {
+ $('#message-content').text(xhr.responseText);
+ }
+ } else if (xhr.status === 200 || xhr.status === 201) {
+ $('#message-title').text('Parse Error');
+ if ($.trim(xhr.responseText) === '') {
+ $('#message-content').text('Unable to interpret response from NiFi.');
+ } else {
+ $('#message-content').text(xhr.responseText);
+ }
+ } else {
+ $('#message-title').text(xhr.status + ': Unexpected Response');
+ $('#message-content').text('An unexpected error has occurred. Please check the logs.');
+ }
+
+ // show the error pane
+ $('#message-pane').show();
+
+ // close the canvas
+ nf.Common.closeCanvas();
}
+ },
- // show the error pane
- $('#message-pane').show();
+ /**
+ * Closes the canvas by removing the splash screen and stats poller.
+ */
+ closeCanvas: function () {
+ nf.Common.showLogoutLink();
+
+ // ensure this javascript has been loaded in the nf canvas page
+ if (nf.Common.isDefinedAndNotNull(nf.Canvas)) {
+ // hide the splash screen if required
+ if ($('#splash').is(':visible')) {
+ nf.Canvas.hideSplash();
+ }
- // close the canvas
- nf.Common.closeCanvas();
- }
- },
-
- /**
- * Closes the canvas by removing the splash screen and stats poller.
- */
- closeCanvas: function () {
- // ensure this javascript has been loaded in the nf canvas page
- if (nf.Common.isDefinedAndNotNull(nf.Canvas)) {
- // hide the splash screen if required
- if ($('#splash').is(':visible')) {
- nf.Canvas.hideSplash();
+ // hide the context menu
+ nf.ContextMenu.hide();
+
+ // shut off the auto refresh
+ nf.Canvas.stopRevisionPolling();
+ nf.Canvas.stopStatusPolling();
}
+ },
- // hide the context menu
- nf.ContextMenu.hide();
+ /**
+ * Shows the logout link if appropriate.
+ */
+ showLogoutLink: function () {
+ if (nf.Storage.getItem('jwt') === null) {
+ $('#user-logout-container').hide();
+ } else {
+ $('#user-logout-container').show();
+ }
+ },
- // shut off the auto refresh
- nf.Canvas.stopRevisionPolling();
- nf.Canvas.stopStatusPolling();
- }
- },
-
- /**
- * Populates the specified field with the specified value. If the value is
- * undefined, the field will read 'No value set.' If the value is an empty
- * string, the field will read 'Empty string set.'
- *
- * @argument {string} target The dom Id of the target
- * @argument {string} value The value
- */
- populateField: function (target, value) {
- if (nf.Common.isUndefined(value) || nf.Common.isNull(value)) {
- return $('#' + target).addClass('unset').text('No value set');
- } else if (value === '') {
- return $('#' + target).addClass('blank').text('Empty string set');
- } else {
- return $('#' + target).text(value);
- }
- },
-
- /**
- * Clears the specified field. Removes any style that may have been applied
- * by a preceeding call to populateField.
- *
- * @argument {string} target The dom Id of the target
- */
- clearField: function (target) {
- return $('#' + target).removeClass('unset blank').text('');
- },
-
- /**
- * Cleans up any tooltips that have been created for the specified container.
- *
- * @param {jQuery} container
- * @param {string} tooltipTarget
- */
- cleanUpTooltips: function(container, tooltipTarget) {
- container.find(tooltipTarget).each(function () {
- var tip = $(this);
- if (tip.data('qtip')) {
- var api = tip.qtip('api');
- api.destroy(true);
- }
- });
- },
-
- /**
- * Removes all read only property detail dialogs.
- */
- removeAllPropertyDetailDialogs: function () {
- var propertyDetails = $('body').children('div.property-detail');
- propertyDetails.find('div.nfel-editor').nfeditor('destroy');
- propertyDetails.hide().remove();
- },
-
- /**
- * Formats the tooltip for the specified property.
- *
- * @param {object} propertyDescriptor The property descriptor
- * @param {object} propertyHistory The property history
- * @returns {string}
- */
- formatPropertyTooltip: function (propertyDescriptor, propertyHistory) {
- var tipContent = [];
-
- // show the property description if applicable
- if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
- if (!nf.Common.isBlank(propertyDescriptor.description)) {
- tipContent.push(nf.Common.escapeHtml(propertyDescriptor.description));
- }
- if (!nf.Common.isBlank(propertyDescriptor.defaultValue)) {
- tipContent.push('<b>Default value:</b> ' + nf.Common.escapeHtml(propertyDescriptor.defaultValue));
- }
- if (!nf.Common.isBlank(propertyDescriptor.supportsEl)) {
- tipContent.push('<b>Supports expression language:</b> ' + nf.Common.escapeHtml(propertyDescriptor.supportsEl));
+ /**
+ * Populates the specified field with the specified value. If the value is
+ * undefined, the field will read 'No value set.' If the value is an empty
+ * string, the field will read 'Empty string set.'
+ *
+ * @argument {string} target The dom Id of the target
+ * @argument {string} value The value
+ */
+ populateField: function (target, value) {
+ if (nf.Common.isUndefined(value) || nf.Common.isNull(value)) {
+ return $('#' + target).addClass('unset').text('No value set');
+ } else if (value === '') {
+ return $('#' + target).addClass('blank').text('Empty string set');
+ } else {
+ return $('#' + target).text(value);
}
- }
+ },
- if (nf.Common.isDefinedAndNotNull(propertyHistory)) {
- if (!nf.Common.isEmpty(propertyHistory.previousValues)) {
- var history = [];
- $.each(propertyHistory.previousValues, function (_, previousValue) {
- history.push('<li>' + nf.Common.escapeHtml(previousValue.previousValue) + ' - ' + nf.Common.escapeHtml(previousValue.timestamp) + ' (' + nf.Common.escapeHtml(previousValue.userName) + ')</li>');
- });
- tipContent.push('<b>History:</b><ul class="property-info">' + history.join('') + '</ul>');
+ /**
+ * Clears the specified field. Removes any style that may have been applied
+ * by a preceeding call to populateField.
+ *
+ * @argument {string} target The dom Id of the target
+ */
+ clearField: function (target) {
+ return $('#' + target).removeClass('unset blank').text('');
+ },
+
+ /**
+ * Cleans up any tooltips that have been created for the specified container.
+ *
+ * @param {jQuery} container
+ * @param {string} tooltipTarget
+ */
+ cleanUpTooltips: function(container, tooltipTarget) {
+ container.find(tooltipTarget).each(function () {
+ var tip = $(this);
+ if (tip.data('qtip')) {
+ var api = tip.qtip('api');
+ api.destroy(true);
+ }
+ });
+ },
+
+ /**
+ * Removes all read only property detail dialogs.
+ */
+ removeAllPropertyDetailDialogs: function () {
+ var propertyDetails = $('body').children('div.property-detail');
+ propertyDetails.find('div.nfel-editor').nfeditor('destroy');
+ propertyDetails.hide().remove();
+ },
+
+ /**
+ * Formats the tooltip for the specified property.
+ *
+ * @param {object} propertyDescriptor The property descriptor
+ * @param {object} propertyHistory The property history
+ * @returns {string}
+ */
+ formatPropertyTooltip: function (propertyDescriptor, propertyHistory) {
+ var tipContent = [];
+
+ // show the property description if applicable
+ if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+ if (!nf.Common.isBlank(propertyDescriptor.description)) {
+ tipContent.push(nf.Common.escapeHtml(propertyDescriptor.description));
+ }
+ if (!nf.Common.isBlank(propertyDescriptor.defaultValue)) {
+ tipContent.push('<b>Default value:</b> ' + nf.Common.escapeHtml(propertyDescriptor.defaultValue));
+ }
+ if (!nf.Common.isBlank(propertyDescriptor.supportsEl)) {
+ tipContent.push('<b>Supports expression language:</b> ' + nf.Common.escapeHtml(propertyDescriptor.supportsEl));
+ }
}
- }
- if (tipContent.length > 0) {
- return tipContent.join('<br/><br/>');
- } else {
- return null;
- }
- },
-
- /**
- * Formats the specified property (name and value) accordingly.
- *
- * @argument {string} name The name of the property
- * @argument {string} value The value of the property
- */
- formatProperty: function (name, value) {
- return '<div><span class="label">' + nf.Common.formatValue(name) + ': </span>' + nf.Common.formatValue(value) + '</div>';
- },
-
- /**
- * Formats the specified value accordingly.
- *
- * @argument {string} value The value of the property
- */
- formatValue: function (value) {
- if (nf.Common.isDefinedAndNotNull(value)) {
- if (value === '') {
- return '<span class="blank">Empty string set</span>';
+ if (nf.Common.isDefinedAndNotNull(propertyHistory)) {
+ if (!nf.Common.isEmpty(propertyHistory.previousValues)) {
+ var history = [];
+ $.each(propertyHistory.previousValues, function (_, previousValue) {
+ history.push('<li>' + nf.Common.escapeHtml(previousValue.previousValue) + ' - ' + nf.Common.escapeHtml(previousValue.timestamp) + ' (' + nf.Common.escapeHtml(previousValue.userName) + ')</li>');
+ });
+ tipContent.push('<b>History:</b><ul class="property-info">' + history.join('') + '</ul>');
+ }
+ }
+
+ if (tipContent.length > 0) {
+ return tipContent.join('<br/><br/>');
} else {
- return nf.Common.escapeHtml(value);
+ return null;
}
- } else {
- return '<span class="unset">No value set</span>';
- }
- },
-
- /**
- * HTML escapes the specified string. If the string is null
- * or undefined, an empty string is returned.
- *
- * @returns {string}
- */
- escapeHtml: (function () {
- var entityMap = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
- '/': '/'
- };
-
- return function (string) {
- if (nf.Common.isDefinedAndNotNull(string)) {
- return String(string).replace(/[&<>"'\/]/g, function (s) {
- return entityMap[s];
- });
+ },
+
+ /**
+ * Formats the specified property (name and value) accordingly.
+ *
+ * @argument {string} name The name of the property
+ * @argument {string} value The value of the property
+ */
+ formatProperty: function (name, value) {
+ return '<div><span class="label">' + nf.Common.formatValue(name) + ': </span>' + nf.Common.formatValue(value) + '</div>';
+ },
+
+ /**
+ * Formats the specified value accordingly.
+ *
+ * @argument {string} value The value of the property
+ */
+ formatValue: function (value) {
+ if (nf.Common.isDefinedAndNotNull(value)) {
+ if (value === '') {
+ return '<span class="blank">Empty string set</span>';
+ } else {
+ return nf.Common.escapeHtml(value);
+ }
} else {
- return '';
+ return '<span class="unset">No value set</span>';
}
- };
- }()),
-
- /**
- * Determines if the specified property is sensitive.
- *
- * @argument {object} propertyDescriptor The property descriptor
- */
- isSensitiveProperty: function (propertyDescriptor) {
- if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
- return propertyDescriptor.sensitive === true;
- } else {
- return false;
- }
- },
-
- /**
- * Determines if the specified property is required.
- *
- * @param {object} propertyDescriptor The property descriptor
- */
- isRequiredProperty: function (propertyDescriptor) {
- if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
- return propertyDescriptor.required === true;
- } else {
- return false;
- }
- },
-
- /**
- * Determines if the specified property is required.
- *
- * @param {object} propertyDescriptor The property descriptor
- */
- isDynamicProperty: function (propertyDescriptor) {
- if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
- return propertyDescriptor.dynamic === true;
- } else {
- return false;
- }
- },
-
- /**
- * Gets the allowable values for the specified property.
- *
- * @argument {object} propertyDescriptor The property descriptor
- */
- getAllowableValues: function (propertyDescriptor) {
- if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
- return propertyDescriptor.allowableValues;
- } else {
- return null;
- }
- },
-
- /**
- * Returns whether the specified property supports EL.
- *
- * @param {object} propertyDescriptor The property descriptor
- */
- supportsEl: function (propertyDescriptor) {
- if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
- return propertyDescriptor.supportsEl === true;
- } else {
- return false;
- }
- },
-
- /**
- * Creates a form inline in order to submit the specified params to the specified URL
- * using the specified method.
- *
- * @param {string} url The URL
- * @param {object} params An object with the params to include in the submission
- */
- post: function (url, params) {
- // temporarily override beforeunload
- var previousBeforeUnload = window.onbeforeunload;
- window.onbeforeunload = null;
-
- // create a form for submission
- var form = $('<form></form>').attr({
- 'method': 'POST',
- 'action': url,
- 'style': 'display: none;'
- });
-
- // add each parameter when specified
- if (nf.Common.isDefinedAndNotNull(params)) {
- $.each(params, function (name, value) {
- $('<textarea></textarea>').attr('name', name).val(value).appendTo(form);
- });
- }
+ },
- // submit the form and clean up
- form.appendTo('body').submit().remove();
+ /**
+ * HTML escapes the specified string. If the string is null
+ * or undefined, an empty string is returned.
+ *
+ * @returns {string}
+ */
+ escapeHtml: (function () {
+ var entityMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '/': '/'
+ };
- // restore previous beforeunload if necessary
- if (previousBeforeUnload !== null) {
- window.onbeforeunload = previousBeforeUnload;
- }
- },
-
- /**
- * Formats the specified array as an unordered list. If the array is not an
- * array, null is returned.
- *
- * @argument {array} array The array to convert into an unordered list
- */
- formatUnorderedList: function (array) {
- if ($.isArray(array)) {
- var ul = $('<ul class="result"></ul>');
- $.each(array, function (_, item) {
- var li = $('<li></li>').appendTo(ul);
- if (item instanceof jQuery) {
- li.append(item);
+ return function (string) {
+ if (nf.Common.isDefinedAndNotNull(string)) {
+ return String(string).replace(/[&<>"'\/]/g, function (s) {
+ return entityMap[s];
+ });
} else {
- li.text(item);
+ return '';
}
+ };
+ }()),
+
+ /**
+ * Determines if the specified property is sensitive.
+ *
+ * @argument {object} propertyDescriptor The property descriptor
+ */
+ isSensitiveProperty: function (propertyDescriptor) {
+ if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+ return propertyDescriptor.sensitive === true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Determines if the specified property is required.
+ *
+ * @param {object} propertyDescriptor The property descriptor
+ */
+ isRequiredProperty: function (propertyDescriptor) {
+ if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+ return propertyDescriptor.required === true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Determines if the specified property is required.
+ *
+ * @param {object} propertyDescriptor The property descriptor
+ */
+ isDynamicProperty: function (propertyDescriptor) {
+ if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+ return propertyDescriptor.dynamic === true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Gets the allowable values for the specified property.
+ *
+ * @argument {object} propertyDescriptor The property descriptor
+ */
+ getAllowableValues: function (propertyDescriptor) {
+ if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+ return propertyDescriptor.allowableValues;
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * Returns whether the specified property supports EL.
+ *
+ * @param {object} propertyDescriptor The property descriptor
+ */
+ supportsEl: function (propertyDescriptor) {
+ if (nf.Common.isDefinedAndNotNull(propertyDescriptor)) {
+ return propertyDescriptor.supportsEl === true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Creates a form inline in order to submit the specified params to the specified URL
+ * using the specified method.
+ *
+ * @param {string} url The URL
+ * @param {object} params An object with the params to include in the submission
+ */
+ post: function (url, params) {
+ // temporarily override beforeunload
+ var previousBeforeUnload = window.onbeforeunload;
+ window.onbeforeunload = null;
+
+ // create a form for submission
+ var form = $('<form></form>').attr({
+ 'method': 'POST',
+ 'action': url,
+ 'style': 'display: none;'
});
- return ul;
- } else {
- return null;
- }
- },
-
- /**
- * Extracts the contents of the specified str after the strToFind. If the
- * strToFind is not found or the last part of the str, an empty string is
- * returned.
- *
- * @argument {string} str The full string
- * @argument {string} strToFind The substring to find
- */
- substringAfterLast: function (str, strToFind) {
- var result = '';
- var indexOfStrToFind = str.lastIndexOf(strToFind);
- if (indexOfStrToFind >= 0) {
- var indexAfterStrToFind = indexOfStrToFind + strToFind.length;
- if (indexAfterStrToFind < str.length) {
- result = str.substr(indexAfterStrToFind);
+
+ // add each parameter when specified
+ if (nf.Common.isDefinedAndNotNull(params)) {
+ $.each(params, function (name, value) {
+ $('<textarea></textarea>').attr('name', name).val(value).appendTo(form);
+ });
}
- }
- return result;
- },
-
- /**
- * Updates the mouse pointer.
- *
- * @argument {string} domId The id of the element for the new cursor style
- * @argument {boolean} isMouseOver Whether or not the mouse is over the element
- */
- setCursor: function (domId, isMouseOver) {
- if (isMouseOver) {
- $('#' + domId).addClass('pointer');
- } else {
- $('#' + domId).removeClass('pointer');
- }
- },
-
- /**
- * Constants for time duration formatting.
- */
- MILLIS_PER_DAY: 86400000,
- MILLIS_PER_HOUR: 3600000,
- MILLIS_PER_MINUTE: 60000,
- MILLIS_PER_SECOND: 1000,
-
- /**
- * Formats the specified duration.
- *
- * @param {integer} duration in millis
- */
- formatDuration: function (duration) {
- // don't support sub millisecond resolution
- duration = duration < 1 ? 0 : duration;
-
- // determine the number of days in the specified duration
- var days = duration / nf.Common.MILLIS_PER_DAY;
- days = days >= 1 ? parseInt(days, 10) : 0;
- duration %= nf.Common.MILLIS_PER_DAY;
-
- // remaining duration should be less than 1 day, get number of hours
- var hours = duration / nf.Common.MILLIS_PER_HOUR;
- hours = hours >= 1 ? parseInt(hours, 10) : 0;
- duration %= nf.Common.MILLIS_PER_HOUR;
-
- // remaining duration should be less than 1 hour, get number of minutes
- var minutes = duration / nf.Common.MILLIS_PER_MINUTE;
- minutes = minutes >= 1 ? parseInt(minutes, 10) : 0;
- duration %= nf.Common.MILLIS_PER_MINUTE;
-
- // remaining duration should be less than 1 minute, get number of seconds
- var seconds = duration / nf.Common.MILLIS_PER_SECOND;
- seconds = seconds >= 1 ? parseInt(seconds, 10) : 0;
-
- // remaining duration is the number millis (don't support sub millisecond resolution)
- duration = Math.floor(duration % nf.Common.MILLIS_PER_SECOND);
-
- // format the time
- var time = nf.Common.pad(hours, 2, '0') +
- ':' +
- nf.Common.pad(minutes, 2, '0') +
- ':' +
- nf.Common.pad(seconds, 2, '0') +
- '.' +
- nf.Common.pad(duration, 3, '0');
-
- // only include days if appropriate
- if (days > 0) {
- return days + ' days and ' + time;
- } else {
- return time;
- }
- },
-
- /**
- * Constants for formatting data size.
- */
- BYTES_IN_KILOBYTE: 1024,
- BYTES_IN_MEGABYTE: 1048576,
- BYTES_IN_GIGABYTE: 1073741824,
- BYTES_IN_TERABYTE: 1099511627776,
-
- /**
- * Formats the specified number of bytes into a human readable string.
- *
- * @param {integer} dataSize
- * @returns {string}
- */
- formatDataSize: function (dataSize) {
- // check terabytes
- var dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_TERABYTE);
- if (dataSizeToFormat > 1) {
- return dataSizeToFormat.toFixed(2) + " TB";
- }
- // check gigabytes
- dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_GIGABYTE);
- if (dataSizeToFormat > 1) {
- return dataSizeToFormat.toFixed(2) + " GB";
- }
+ // submit the form and clean up
+ form.appendTo('body').submit().remove();
- // check megabytes
- dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_MEGABYTE);
- if (dataSizeToFormat > 1) {
- return dataSizeToFormat.toFixed(2) + " MB";
- }
+ // restore previous beforeunload if necessary
+ if (previousBeforeUnload !== null) {
+ window.onbeforeunload = previousBeforeUnload;
+ }
+ },
- // check kilobytes
- dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_KILOBYTE);
- if (dataSizeToFormat > 1) {
- return dataSizeToFormat.toFixed(2) + " KB";
- }
+ /**
+ * Formats the specified array as an unordered list. If the array is not an
+ * array, null is returned.
+ *
+ * @argument {array} array The array to convert into an unordered list
+ */
+ formatUnorderedList: function (array) {
+ if ($.isArray(array)) {
+ var ul = $('<ul class="result"></ul>');
+ $.each(array, function (_, item) {
+ var li = $('<li></li>').appendTo(ul);
+ if (item instanceof jQuery) {
+ li.append(item);
+ } else {
+ li.text(item);
+ }
+ });
+ return ul;
+ } else {
+ return null;
+ }
+ },
- // default to bytes
- return parseFloat(dataSize).toFixed(2) + " bytes";
- },
-
- /**
- * Formats the specified integer as a string (adding commas). At this
- * point this does not take into account any locales.
- *
- * @param {integer} integer
- */
- formatInteger: function (integer) {
- var string = integer + '';
- var regex = /(\d+)(\d{3})/;
- while (regex.test(string)) {
- string = string.replace(regex, '$1' + ',' + '$2');
- }
- return string;
- },
-
- /**
- * Formats the specified float using two demical places.
- *
- * @param {float} f
- */
- formatFloat: function (f) {
- if (nf.Common.isUndefinedOrNull(f)) {
- return 0.00 + '';
- }
- return f.toFixed(2) + '';
- },
-
- /**
- * Pads the specified value to the specified width with the specified character.
- * If the specified value is already wider than the specified width, the original
- * value is returned.
- *
- * @param {integer} value
- * @param {integer} width
- * @param {string} character
- * @returns {string}
- */
- pad: function (value, width, character) {
- var s = value + '';
-
- // pad until wide enough
- while (s.length < width) {
- s = character + s;
- }
+ /**
+ * Extracts the contents of the specified str after the strToFind. If the
+ * strToFind is not found or the last part of the str, an empty string is
+ * returned.
+ *
+ * @argument {string} str The full string
+ * @argument {string} strToFind The substring to find
+ */
+ substringAfterLast: function (str, strToFind) {
+ var result = '';
+ var indexOfStrToFind = str.lastIndexOf(strToFind);
+ if (indexOfStrToFind >= 0) {
+ var indexAfterStrToFind = indexOfStrToFind + strToFind.length;
+ if (indexAfterStrToFind < str.length) {
+ result = str.substr(indexAfterStrToFind);
+ }
+ }
+ return result;
+ },
- return s;
- },
-
- /**
- * Formats the specified DateTime.
- *
- * @param {Date} date
- * @returns {String}
- */
- formatDateTime: function (date) {
- return nf.Common.pad(date.getMonth() + 1, 2, '0') +
- '/' +
- nf.Common.pad(date.getDate(), 2, '0') +
- '/' +
- nf.Common.pad(date.getFullYear(), 2, '0') +
- ' ' +
- nf.Common.pad(date.getHours(), 2, '0') +
- ':' +
- nf.Common.pad(date.getMinutes(), 2, '0') +
- ':' +
- nf.Common.pad(date.getSeconds(), 2, '0') +
- '.' +
- nf.Common.pad(date.getMilliseconds(), 3, '0');
- },
-
- /**
- * Parses the specified date time into a Date object. The resulting
- * object does not account for timezone and should only be used for
- * performing relative comparisons.
- *
- * @param {string} rawDateTime
- * @returns {Date}
- */
- parseDateTime: function (rawDateTime) {
- // handle non date values
- if (!nf.Common.isDefinedAndNotNull(rawDateTime)) {
- return new Date();
- }
- if (rawDateTime === 'No value set') {
- return new Date();
- }
- if (rawDateTime === 'Empty string set') {
- return new Date();
- }
+ /**
+ * Updates the mouse pointer.
+ *
+ * @argument {string} domId The id of the element for the new cursor style
+ * @argument {boolean} isMouseOver Whether or not the mouse is over the element
+ */
+ setCursor: function (domId, isMouseOver) {
+ if (isMouseOver) {
+ $('#' + domId).addClass('pointer');
+ } else {
+ $('#' + domId).removeClass('pointer');
+ }
+ },
- // parse the date time
- var dateTime = rawDateTime.split(/ /);
+ /**
+ * Constants for time duration formatting.
+ */
+ MILLIS_PER_DAY: 86400000,
+ MILLIS_PER_HOUR: 3600000,
+ MILLIS_PER_MINUTE: 60000,
+ MILLIS_PER_SECOND: 1000,
- // ensure the correct number of tokens
- if (dateTime.length !== 3) {
- return new Date();
- }
+ /**
+ * Formats the specified duration.
+ *
+ * @param {integer} duration in millis
+ */
+ formatDuration: function (duration) {
+ // don't support sub millisecond resolution
+ duration = duration < 1 ? 0 : duration;
- // get the date and time
- var date = dateTime[0].split(/\//);
- var time = dateTime[1].split(/:/);
+ // determine the number of days in the specified duration
+ var days = duration / nf.Common.MILLIS_PER_DAY;
+ days = days >= 1 ? parseInt(days, 10) : 0;
+ duration %= nf.Common.MILLIS_PER_DAY;
- // ensure the correct number of tokens
- if (date.length !== 3 || time.length !== 3) {
- return new Date();
- }
+ // remaining duration should be less than 1 day, get number of hours
+ var hours = duration / nf.Common.MILLIS_PER_HOUR;
+ hours = hours >= 1 ? parseInt(hours, 10) : 0;
+ duration %= nf.Common.MILLIS_PER_HOUR;
- // detect if there is millis
- var seconds = time[2].split(/\./);
- if (seconds.length === 2) {
- return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10));
- } else {
- return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(time[2], 10), 0);
- }
- },
-
- /**
- * Parses the specified duration and returns the total number of millis.
- *
- * @param {string} rawDuration
- * @returns {number} The number of millis
- */
- parseDuration: function (rawDuration) {
- var duration = rawDuration.split(/:/);
-
- // ensure the appropriate number of tokens
- if (duration.length !== 3) {
- return 0;
- }
+ // remaining duration should be less than 1 hour, get number of minutes
+ var minutes = duration / nf.Common.MILLIS_PER_MINUTE;
+ minutes = minutes >= 1 ? parseInt(minutes, 10) : 0;
+ duration %= nf.Common.MILLIS_PER_MINUTE;
- // detect if there is millis
- var seconds = duration[2].split(/\./);
- if (seconds.length === 2) {
- return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10)).getTime();
- } else {
- return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(duration[2], 10), 0).getTime();
- }
- },
-
- /**
- * Parses the specified size.
- *
- * @param {string} rawSize
- * @returns {int}
- */
- parseSize: function (rawSize) {
- var tokens = rawSize.split(/ /);
- var size = parseFloat(tokens[0].replace(/,/g, ''));
- var units = tokens[1];
-
- if (units === 'KB') {
- return size * 1024;
- } else if (units === 'MB') {
- return size * 1024 * 1024;
- } else if (units === 'GB') {
- return size * 1024 * 1024 * 1024;
- } else if (units === 'TB') {
- return size * 1024 * 1024 * 1024 * 1024;
- } else {
- return size;
- }
- },
-
- /**
- * Parses the specified count.
- *
- * @param {string} rawCount
- * @returns {int}
- */
- parseCount: function (rawCount) {
- // extract the count
- var count = rawCount.split(/ /, 1);
-
- // ensure the string was split successfully
- if (count.length !== 1) {
- return 0;
- }
+ // remaining duration should be less than 1 minute, get number of seconds
+ var seconds = duration / nf.Common.MILLIS_PER_SECOND;
+ seconds = seconds >= 1 ? parseInt(seconds, 10) : 0;
- // convert the count to an integer
- var intCount = parseInt(count[0].replace(/,/g, ''), 10);
+ // remaining duration is the number millis (don't support sub millisecond resolution)
+ duration = Math.floor(duration % nf.Common.MILLIS_PER_SECOND);
- // ensure it was parsable as an integer
- if (isNaN(intCount)) {
- return 0;
- }
- return intCount;
- },
-
- /**
- * Determines if the specified object is defined and not null.
- *
- * @argument {object} obj The object to test
- */
- isDefinedAndNotNull: function (obj) {
- return !nf.Common.isUndefined(obj) && !nf.Common.isNull(obj);
- },
-
- /**
- * Determines if the specified object is undefined or null.
- *
- * @param {object} obj The object to test
- */
- isUndefinedOrNull: function (obj) {
- return nf.Common.isUndefined(obj) || nf.Common.isNull(obj);
- },
-
- /**
- * Determines if the specified object is undefined.
- *
- * @argument {object} obj The object to test
- */
- isUndefined: function (obj) {
- return typeof obj === 'undefined';
- },
-
- /**
- * Determines whether the specified string is blank (or null or undefined).
- *
- * @argument {string} str The string to test
- */
- isBlank: function (str) {
- return nf.Common.isUndefined(str) || nf.Common.isNull(str) || $.trim(str) === '';
- },
-
- /**
- * Determines if the specified object is null.
- *
- * @argument {object} obj The object to test
- */
- isNull: function (obj) {
- return obj === null;
- },
-
- /**
- * Determines if the specified array is empty. If the specified arg is not an
- * array, then true is returned.
- *
- * @argument {array} arr The array to test
- */
- isEmpty: function (arr) {
- return $.isArray(arr) ? arr.length === 0 : true;
- },
-
- /**
- * Determines if these are the same bulletins. If both arguments are not
- * arrays, false is returned.
- *
- * @param {array} bulletins
- * @param {array} otherBulletins
- * @returns {boolean}
- */
- doBulletinsDiffer: function (bulletins, otherBulletins) {
- if ($.isArray(bulletins) && $.isArray(otherBulletins)) {
- if (bulletins.length === otherBulletins.length) {
- for (var i = 0; i < bulletins.length; i++) {
- if (bulletins[i].id !== otherBulletins[i].id) {
- return true;
+ // format the time
+ var time = nf.Common.pad(hours, 2, '0') +
+ ':' +
+ nf.Common.pad(minutes, 2, '0') +
+ ':' +
+ nf.Common.pad(seconds, 2, '0') +
+ '.' +
+ nf.Common.pad(duration, 3, '0');
+
+ // only include days if appropriate
+ if (days > 0) {
+ return days + ' days and ' + time;
+ } else {
+ return time;
+ }
+ },
+
+ /**
+ * Constants for formatting data size.
+ */
+ BYTES_IN_KILOBYTE: 1024,
+ BYTES_IN_MEGABYTE: 1048576,
+ BYTES_IN_GIGABYTE: 1073741824,
+ BYTES_IN_TERABYTE: 1099511627776,
+
+ /**
+ * Formats the specified number of bytes into a human readable string.
+ *
+ * @param {integer} dataSize
+ * @returns {string}
+ */
+ formatDataSize: function (dataSize) {
+ // check terabytes
+ var dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_TERABYTE);
+ if (dataSizeToFormat > 1) {
+ return dataSizeToFormat.toFixed(2) + " TB";
+ }
+
+ // check gigabytes
+ dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_GIGABYTE);
+ if (dataSizeToFormat > 1) {
+ return dataSizeToFormat.toFixed(2) + " GB";
+ }
+
+ // check megabytes
+ dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_MEGABYTE);
+ if (dataSizeToFormat > 1) {
+ return dataSizeToFormat.toFixed(2) + " MB";
+ }
+
+ // check kilobytes
+ dataSizeToFormat = parseFloat(dataSize / nf.Common.BYTES_IN_KILOBYTE);
+ if (dataSizeToFormat > 1) {
+ return dataSizeToFormat.toFixed(2) + " KB";
+ }
+
+ // default to bytes
+ return parseFloat(dataSize).toFixed(2) + " bytes";
+ },
+
+ /**
+ * Formats the specified integer as a string (adding commas). At this
+ * point this does not take into account any locales.
+ *
+ * @param {integer} integer
+ */
+ formatInteger: function (integer) {
+ var string = integer + '';
+ var regex = /(\d+)(\d{3})/;
+ while (regex.test(string)) {
+ string = string.replace(regex, '$1' + ',' + '$2');
+ }
+ return string;
+ },
+
+ /**
+ * Formats the specified float using two demical places.
+ *
+ * @param {float} f
+ */
+ formatFloat: function (f) {
+ if (nf.Common.isUndefinedOrNull(f)) {
+ return 0.00 + '';
+ }
+ return f.toFixed(2) + '';
+ },
+
+ /**
+ * Pads the specified value to the specified width with the specified character.
+ * If the specified value is already wider than the specified width, the original
+ * value is returned.
+ *
+ * @param {integer} value
+ * @param {integer} width
+ * @param {string} character
+ * @returns {string}
+ */
+ pad: function (value, width, character) {
+ var s = value + '';
+
+ // pad until wide enough
+ while (s.length < width) {
+ s = character + s;
+ }
+
+ return s;
+ },
+
+ /**
+ * Formats the specified DateTime.
+ *
+ * @param {Date} date
+ * @returns {String}
+ */
+ formatDateTime: function (date) {
+ return nf.Common.pad(date.getMonth() + 1, 2, '0') +
+ '/' +
+ nf.Common.pad(date.getDate(), 2, '0') +
+ '/' +
+ nf.Common.pad(date.getFullYear(), 2, '0') +
+ ' ' +
+ nf.Common.pad(date.getHours(), 2, '0') +
+ ':' +
+ nf.Common.pad(date.getMinutes(), 2, '0') +
+ ':' +
+ nf.Common.pad(date.getSeconds(), 2, '0') +
+ '.' +
+ nf.Common.pad(date.getMilliseconds(), 3, '0');
+ },
+
+ /**
+ * Parses the specified date time into a Date object. The resulting
+ * object does not account for timezone and should only be used for
+ * performing relative comparisons.
+ *
+ * @param {string} rawDateTime
+ * @returns {Date}
+ */
+ parseDateTime: function (rawDateTime) {
+ // handle non date values
+ if (!nf.Common.isDefinedAndNotNull(rawDateTime)) {
+ return new Date();
+ }
+ if (rawDateTime === 'No value set') {
+ return new Date();
+ }
+ if (rawDateTime === 'Empty string set') {
+ return new Date();
+ }
+
+ // parse the date time
+ var dateTime = rawDateTime.split(/ /);
+
+ // ensure the correct number of tokens
+ if (dateTime.length !== 3) {
+ return new Date();
+ }
+
+ // get the date and time
+ var date = dateTime[0].split(/\//);
+ var time = dateTime[1].split(/:/);
+
+ // ensure the correct number of tokens
+ if (date.length !== 3 || time.length !== 3) {
+ return new Date();
+ }
+
+ // detect if there is millis
+ var seconds = time[2].split(/\./);
+ if (seconds.length === 2) {
+ return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10));
+ } else {
+ return new Date(parseInt(date[2], 10), parseInt(date[0], 10), parseInt(date[1], 10), parseInt(time[0], 10), parseInt(time[1], 10), parseInt(time[2], 10), 0);
+ }
+ },
+
+ /**
+ * Parses the specified duration and returns the total number of millis.
+ *
+ * @param {string} rawDuration
+ * @returns {number} The number of millis
+ */
+ parseDuration: function (rawDuration) {
+ var duration = rawDuration.split(/:/);
+
+ // ensure the appropriate number of tokens
+ if (duration.length !== 3) {
+ return 0;
+ }
+
+ // detect if there is millis
+ var seconds = duration[2].split(/\./);
+ if (seconds.length === 2) {
+ return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(seconds[0], 10), parseInt(seconds[1], 10)).getTime();
+ } else {
+ return new Date(1970, 0, 1, parseInt(duration[0], 10), parseInt(duration[1], 10), parseInt(duration[2], 10), 0).getTime();
+ }
+ },
+
+ /**
+ * Parses the specified size.
+ *
+ * @param {string} rawSize
+ * @returns {int}
+ */
+ parseSize: function (rawSize) {
+ var tokens = rawSize.split(/ /);
+ var size = parseFloat(tokens[0].replace(/,/g, ''));
+ var units = tokens[1];
+
+ if (units === 'KB') {
+ return size * 1024;
+ } else if (units === 'MB') {
+ return size * 1024 * 1024;
+ } else if (units === 'GB') {
+ return size * 1024 * 1024 * 1024;
+ } else if (units === 'TB') {
+ return size * 1024 * 1024 * 1024 * 1024;
+ } else {
+ return size;
+ }
+ },
+
+ /**
+ * Parses the specified count.
+ *
+ * @param {string} rawCount
+ * @returns {int}
+ */
+ parseCount: function (rawCount) {
+ // extract the count
+ var count = rawCount.split(/ /, 1);
+
+ // ensure the string was split successfully
+ if (count.length !== 1) {
+ return 0;
+ }
+
+ // convert the count to an integer
+ var intCount = parseInt(count[0].replace(/,/g, ''), 10);
+
+ // ensure it was parsable as an integer
+ if (isNaN(intCount)) {
+ return 0;
+ }
+ return intCount;
+ },
+
+ /**
+ * Determines if the specified object is defined and not null.
+ *
+ * @argument {object} obj The object to test
+ */
+ isDefinedAndNotNull: function (obj) {
+ return !nf.Common.isUndefined(obj) && !nf.Common.isNull(obj);
+ },
+
+ /**
+ * Determines if the specified object is undefined or null.
+ *
+ * @param {object} obj The object to test
+ */
+ isUndefinedOrNull: function (obj) {
+ return nf.Common.isUndefined(obj) || nf.Common.isNull(obj);
+ },
+
+ /**
+ * Determines if the specified object is undefined.
+ *
+ * @argument {object} obj The object to test
+ */
+ isUndefined: function (obj) {
+ return typeof obj === 'undefined';
+ },
+
+ /**
+ * Determines whether the specified string is blank (or null or undefined).
+ *
+ * @argument {string} str The string to test
+ */
+ isBlank: function (str) {
+ return nf.Common.isUndefined(str) || nf.Common.isNull(str) || $.trim(str) === '';
+ },
+
+ /**
+ * Determines if the specified object is null.
+ *
+ * @argument {object} obj The object to test
+ */
+ isNull: function (obj) {
+ return obj === null;
+ },
+
+ /**
+ * Determines if the specified array is empty. If the specified arg is not an
+ * array, then true is returned.
+ *
+ * @argument {array} arr The array to test
+ */
+ isEmpty: function (arr) {
+ return $.isArray(arr) ? arr.length === 0 : true;
+ },
+
+ /**
+ * Determines if these are the same bulletins. If both arguments are not
+ * arrays, false is returned.
+ *
+ * @param {array} bulletins
+ * @param {array} otherBulletins
+ * @returns {boolean}
+ */
+ doBulletinsDiffer: function (bulletins, otherBulletins) {
+ if ($.isArray(bulletins) && $.isArray(otherBulletins)) {
+ if (bulletins.length === otherBulletins.length) {
+ for (var i = 0; i < bulletins.length; i++) {
+ if (bulletins[i].id !== otherBulletins[i].id) {
+ return true;
+ }
}
+ } else {
+ return true;
}
- } else {
+ } else if ($.isArray(bulletins) || $.isArray(otherBulletins)) {
return true;
}
- } else if ($.isArray(bulletins) || $.isArray(otherBulletins)) {
- return true;
+ return false;
+ },
+
+ /**
+ * Formats the specified bulletin list.
+ *
+ * @argument {array} bulletins The bulletins
+ * @return {array} The jQuery objects
+ */
+ getFormattedBulletins: function (bulletins) {
+ var formattedBulletins = [];
+ $.each(bulletins, function (j, bulletin) {
+ // format the node address
+ var nodeAddress = '';
+ if (nf.Common.isDefinedAndNotNull(bulletin.nodeAddress)) {
+ nodeAddress = '- ' + nf.Common.escapeHtml(bulletin.nodeAddress) + ' - ';
+ }
+
+ // set the bulletin message (treat as text)
+ var bulletinMessage = $('<pre></pre>').css({
+ 'white-space': 'pre-wrap'
+ }).text(bulletin.message);
+
+ // create the bulletin message
+ var formattedBulletin = $('<div>' +
+ nf.Common.escapeHtml(bulletin.timestamp) + ' ' +
+ nodeAddress + ' ' +
+ '<b>' + nf.Common.escapeHtml(bulletin.level) + '</b> ' +
+ '</div>').append(bulletinMessage);
+
+ formattedBulletins.push(formattedBulletin);
+ });
+ return formattedBulletins;
}
- return false;
- },
-
- /**
- * Formats the specified bulletin list.
- *
- * @argument {array} bulletins The bulletins
- * @return {array} The jQuery objects
- */
- getFormattedBulletins: function (bulletins) {
- var formattedBulletins = [];
- $.each(bulletins, function (j, bulletin) {
- // format the node address
- var nodeAddress = '';
- if (nf.Common.isDefinedAndNotNull(bulletin.nodeAddress)) {
- nodeAddress = '- ' + nf.Common.escapeHtml(bulletin.nodeAddress) + ' - ';
- }
-
- // set the bulletin message (treat as text)
- var bulletinMessage = $('<pre></pre>').css({
- 'white-space': 'pre-wrap'
- }).text(bulletin.message);
-
- // create the bulletin message
- var formattedBulletin = $('<div>' +
- nf.Common.escapeHtml(bulletin.timestamp) + ' ' +
- nodeAddress + ' ' +
- '<b>' + nf.Common.escapeHtml(bulletin.level) + '</b> ' +
- '</div>').append(bulletinMessage);
-
- formattedBulletins.push(formattedBulletin);
- });
- return formattedBulletins;
- }
-};
+ };
+}());
http://git-wip-us.apache.org/repos/asf/nifi/blob/aaf14c45/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
index 13f5cf3..9a926ef 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/nf-dialog.js
@@ -23,15 +23,6 @@ $(document).ready(function () {
// configure the ok dialog
$('#nf-ok-dialog').modal({
- buttons: [{
- buttonText: 'Ok',
- handler: {
- click: function () {
- // close the dialog
- $('#nf-ok-dialog').modal('hide');
- }
- }
- }],
handler: {
close: function () {
// clear the content
@@ -78,6 +69,20 @@ nf.Dialog = (function () {
var content = $('<p></p>').append(options.dialogContent);
$('#nf-ok-dialog-content').append(content);
+ // update the button model
+ $('#nf-ok-dialog').modal('setButtonModel', [{
+ buttonText: 'Ok',
+ handler: {
+ click: function () {
+ // close the dialog
+ $('#nf-ok-dialog').modal('hide');
+ if (typeof options.okHandler === 'function') {
+ options.okHandler.call(this);
+ }
+ }
+ }
+ }]);
+
// show the dialog
$('#nf-ok-dialog').modal('setHeaderText', options.headerText).modal('setOverlayBackground', options.overlayBackground).modal('show');
},