diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DropRequestDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DropRequestDTO.java index f0224693d6..93dc7dd7d4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DropRequestDTO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/DropRequestDTO.java @@ -38,11 +38,14 @@ public class DropRequestDTO { private Boolean finished; private Integer currentCount; - private String currentSize; + private Long currentSize; + private String current; private Integer originalCount; - private String originalSize; + private Long originalSize; + private String original; private Integer droppedCount; - private String droppedSize; + private Long droppedSize; + private String dropped; private String state; @@ -151,19 +154,33 @@ public class DropRequestDTO { } /** - * @return the siez of the flow files currently queued. + * @return the size of the flow files currently queued in bytes. */ @ApiModelProperty( - value = "The size of flow files currently queued." + value = "The size of flow files currently queued in bytes." ) - public String getCurrentSize() { + public Long getCurrentSize() { return currentSize; } - public void setCurrentSize(String currentSize) { + public void setCurrentSize(Long currentSize) { this.currentSize = currentSize; } + /** + * @return the count and size of the currently queued flow files. + */ + @ApiModelProperty( + value = "The count and size of flow files currently queued." + ) + public String getCurrent() { + return current; + } + + public void setCurrent(String current) { + this.current = current; + } + /** * @return the number of flow files to be dropped as a result of this request. */ @@ -179,19 +196,33 @@ public class DropRequestDTO { } /** - * @return the size of the flow files to be dropped as a result of this request. + * @return the size of the flow files to be dropped as a result of this request in bytes. */ @ApiModelProperty( - value = "The size of flow files to be dropped as a result of this request." + value = "The size of flow files to be dropped as a result of this request in bytes." ) - public String getOriginalSize() { + public Long getOriginalSize() { return originalSize; } - public void setOriginalSize(String originalSize) { + public void setOriginalSize(Long originalSize) { this.originalSize = originalSize; } + /** + * @return the count and size of flow files to be dropped as a result of this request. + */ + @ApiModelProperty( + value = "The count and size of flow files to be dropped as a result of this request." + ) + public String getOriginal() { + return original; + } + + public void setOriginal(String original) { + this.original = original; + } + /** * @return the number of flow files that have been dropped thus far. */ @@ -207,19 +238,33 @@ public class DropRequestDTO { } /** - * @return the size of the flow files that have been dropped thus far. + * @return the size of the flow files that have been dropped thus far in bytes. */ @ApiModelProperty( - value = "The size of flow files that have been dropped thus far." + value = "The size of flow files that have been dropped thus far in bytes." ) - public String getDroppedSize() { + public Long getDroppedSize() { return droppedSize; } - public void setDroppedSize(String droppedSize) { + public void setDroppedSize(Long droppedSize) { this.droppedSize = droppedSize; } + /** + * @return the count and size of the flow files that have been dropped thus far. + */ + @ApiModelProperty( + value = "The count and size of flow files that have been dropped thus far." + ) + public String getDropped() { + return dropped; + } + + public void setDropped(String dropped) { + this.dropped = dropped; + } + /** * @return the current state of the drop request. */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java index 0703a41486..2d3355ae43 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiServiceFacade.java @@ -552,8 +552,9 @@ public interface NiFiServiceFacade { * @param groupId group * @param connectionId The ID of the connection * @param dropRequestId The flow file drop request + * @return The DropRequest */ - void deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId); + DropRequestDTO deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId); // ---------------------------------------- // InputPort methods diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index b2e600003e..51f87517e7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -810,8 +810,8 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { } @Override - public void deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) { - connectionDAO.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId); + public DropRequestDTO deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) { + return dtoFactory.createDropRequestDTO(connectionDAO.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId)); } @Override diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java index 0d00468aff..b170d39145 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ConnectionResource.java @@ -1086,7 +1086,8 @@ public class ConnectionResource extends ApplicationResource { } // delete the drop request - serviceFacade.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId); + final DropRequestDTO dropRequest = serviceFacade.deleteFlowFileDropRequest(groupId, connectionId, dropRequestId); + dropRequest.setUri(generateResourceUri("controller", "process-groups", groupId, "connections", connectionId, "contents", "drop-requests", dropRequestId)); // create the revision final RevisionDTO revision = new RevisionDTO(); @@ -1095,6 +1096,7 @@ public class ConnectionResource extends ApplicationResource { // create the response entity final DropRequestEntity entity = new DropRequestEntity(); entity.setRevision(revision); + entity.setDropRequest(dropRequest); return generateOkResponse(entity).build(); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index d123704126..264268bf0a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -312,13 +312,16 @@ public final class DtoFactory { final QueueSize dropped = dropRequest.getDroppedSize(); dto.setDroppedCount(dropped.getObjectCount()); - dto.setDroppedSize(FormatUtils.formatDataSize(dropped.getByteCount())); + dto.setDroppedSize(dropped.getByteCount()); + dto.setDropped(FormatUtils.formatCount(dropped.getObjectCount()) + " / " + FormatUtils.formatDataSize(dropped.getByteCount())); if (dropRequest.getOriginalSize() != null) { final QueueSize original = dropRequest.getOriginalSize(); dto.setOriginalCount(original.getObjectCount()); - dto.setOriginalSize(FormatUtils.formatDataSize(original.getByteCount())); - dto.setPercentCompleted((dropped.getObjectCount() * 100 ) / original.getObjectCount()); + dto.setOriginalSize(original.getByteCount()); + dto.setOriginal(FormatUtils.formatCount(original.getObjectCount()) + " / " + FormatUtils.formatDataSize(original.getByteCount())); + + dto.setPercentCompleted((dropped.getObjectCount() * 100) / original.getObjectCount()); } else { dto.setPercentCompleted(0); } @@ -326,7 +329,8 @@ public final class DtoFactory { if (dropRequest.getCurrentSize() != null) { final QueueSize current = dropRequest.getCurrentSize(); dto.setCurrentCount(current.getObjectCount()); - dto.setCurrentSize(FormatUtils.formatDataSize(current.getByteCount())); + dto.setCurrentSize(current.getByteCount()); + dto.setCurrent(FormatUtils.formatCount(current.getObjectCount()) + " / " + FormatUtils.formatDataSize(current.getByteCount())); } return dto; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java index 3665a4e728..2be4403243 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ConnectionDAO.java @@ -134,6 +134,7 @@ public interface ConnectionDAO { * @param groupId group id * @param id The id of the connection * @param dropRequestId The drop request id + * @return The drop request */ - void deleteFlowFileDropRequest(String groupId, String id, String dropRequestId); + DropFlowFileStatus deleteFlowFileDropRequest(String groupId, String id, String dropRequestId); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java index 399ffa1a57..66730a385c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardConnectionDAO.java @@ -496,10 +496,10 @@ public class StandardConnectionDAO extends ComponentDAO implements ConnectionDAO } @Override - public void deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) { + public DropFlowFileStatus deleteFlowFileDropRequest(String groupId, String connectionId, String dropRequestId) { final Connection connection = locateConnection(groupId, connectionId); final FlowFileQueue queue = connection.getFlowFileQueue(); - queue.cancelDropFlowFileRequest(dropRequestId); + return queue.cancelDropFlowFileRequest(dropRequestId); } /* setters */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp index c81bb9d769..1bf63a6e42 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp @@ -119,6 +119,7 @@ +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/drop-request-status-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/drop-request-status-dialog.jsp new file mode 100644 index 0000000000..b001a1ad45 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/drop-request-status-dialog.jsp @@ -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. +--%> +<%@ page contentType="text/html" pageEncoding="UTF-8" session="false" %> +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css index 88555e76c0..6cb6dba96b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css @@ -269,6 +269,31 @@ div.go-to-link { height: 195px; } +#drop-request-status-dialog { + display: none; + width: 400px; + height: 135px; + z-index: 1301; +} + +#drop-request-percent-complete { + width: 378px; + border-radius: 0; +} + +#drop-request-percent-complete .ui-progressbar-value { + border-radius: 0; +} + +div.progress-label { + color: #000000; + display: block; + font-weight: bold; + text-align: center; + width: 378px; + margin-top: 4px; +} + #nf-ok-dialog { z-index: 1302; display: none; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEmptyQueue.png b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEmptyQueue.png new file mode 100644 index 0000000000..42e5a15fd0 Binary files /dev/null and b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/images/iconEmptyQueue.png differ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js index 2a94ec5268..ba144ef560 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-actions.js @@ -24,6 +24,31 @@ nf.Actions = (function () { controller: '../nifi-api/controller' } }; + + /** + * Initializes the drop request status dialog. + */ + var initializeDropRequestStatusDialog = function () { + // initialize the drop requst progress bar + var dropRequestProgressBar = $('#drop-request-percent-complete').progressbar(); + + // configure the drop request status dialog + $('#drop-request-status-dialog').modal({ + headerText: 'Emptying queue', + overlayBackground: false, + handler: { + close: function () { + // reset the progress bar + dropRequestProgressBar.find('div.progress-label').remove(); + + // update the progress bar + var label = $('
').text('0%'); + dropRequestProgressBar.progressbar('value', 0).append(label); + } + } + }); + }; + /** * Updates the resource with the specified data. @@ -78,6 +103,13 @@ nf.Actions = (function () { }; return { + /** + * Initializes the actions. + */ + init: function () { + initializeDropRequestStatusDialog(); + }, + /** * Enters the specified process group. * @@ -855,56 +887,144 @@ nf.Actions = (function () { return; } - var MAX_DELAY = 4; + // prompt the user before emptying the queue + nf.Dialog.showYesNoDialog({ + dialogContent: 'Are you sure you want to empty the queue? All data enqueued at the time of the request will be removed from the dataflow.', + overlayBackground: false, + yesHandler: function () { + // get the connection data + var connection = selection.datum(); + + var MAX_DELAY = 4; + var cancelled = false; + var dropRequest = null; + var dropRequestTimer = null; - // process the drop request - var processDropRequest = function (dropRequest, nextDelay) { - // see if the drop request has completed - if (dropRequest.finished === true) { - // request is finished so it can be removed - deleteDropRequest(dropRequest); - } else { - // update the UI with the current status of the drop request - schedule(dropRequest, nextDelay); - } - }; + // updates the progress bar + var updateProgress = function (percentComplete) { + // remove existing labels + var progressBar = $('#drop-request-percent-complete'); + progressBar.find('div.progress-label').remove(); - // schedule for the next poll iteration - var schedule = function (dropRequest, delay) { - setTimeout(function () { + // update the progress bar + var label = $('
').text(percentComplete + '%'); + if (percentComplete > 0) { + label.css('margin-top', '-19px'); + } + progressBar.progressbar('value', percentComplete).append(label); + }; + + // update the button model of the drop request status dialog + $('#drop-request-status-dialog').modal('setButtonModel', [{ + buttonText: 'Cancel', + handler: { + click: function () { + cancelled = true; + + // we are waiting for the next poll attempt + if (dropRequestTimer !== null) { + // cancel it + clearTimeout(dropRequestTimer); + + // cancel the provenance + completeDropRequest(); + } + } + } + }]); + + // completes the drop request by removing it and showing how many flowfiles were deleted + var completeDropRequest = function () { + if (nf.Common.isDefinedAndNotNull(dropRequest)) { + $.ajax({ + type: 'DELETE', + url: dropRequest.uri, + dataType: 'json' + }).done(function(response) { + // report the results of this drop request + dropRequest = response.dropRequest; + + // parse the dropped stats to render results + var tokens = dropRequest.dropped.split(/ \/ /); + var results = $('
').text('Successfully removed ' + tokens[0] + ' (' + tokens[1] + ') FlowFiles'); + nf.Dialog.showOkDialog({ + dialogContent: results, + overlayBackground: false + }); + }).always(function() { + $('#drop-request-status-dialog').modal('hide'); + }); + } else { + // nothing was removed + nf.Dialog.showYesNoDialog({ + dialogContent: 'No FlowFiles were removed.', + overlayBackground: false + }); + + // close the dialog + $('#drop-request-status-dialog').modal('hide'); + } + }; + + // process the drop request + var processDropRequest = function (delay) { + // update the percent complete + updateProgress(dropRequest.percentCompleted); + + // update the status of the drop request + $('#drop-request-status-message').text(dropRequest.state); + + // update the current number of enqueued flowfiles + if (nf.Common.isDefinedAndNotNull(dropRequest.currentCount)) { + connection.status.queued = dropRequest.current; + nf.Connection.refresh(connection.id); + } + + // close the dialog if the + if (dropRequest.finished === true || cancelled === true) { + completeDropRequest(); + } else { + // wait delay to poll again + dropRequestTimer = setTimeout(function () { + // clear the drop request timer + dropRequestTimer = null; + + // schedule to poll the status again in nextDelay + pollDropRequest(Math.min(MAX_DELAY, delay * 2)); + }, delay * 1000); + } + }; + + // schedule for the next poll iteration + var pollDropRequest = function (nextDelay) { + $.ajax({ + type: 'GET', + url: dropRequest.uri, + dataType: 'json' + }).done(function(response) { + dropRequest = response.dropRequest; + processDropRequest(nextDelay); + }).fail(completeDropRequest); + }; + + // issue the request to delete the flow files $.ajax({ - type: 'GET', - url: dropRequest.uri, + type: 'DELETE', + url: connection.component.uri + '/contents', dataType: 'json' }).done(function(response) { - var dropRequest = response.dropRequest; - processDropRequest(dropRequest, Math.min(MAX_DELAY, delay * 2)); - }).fail(nf.Common.handleAjaxError); - }, delay * 1000); - }; - - // delete the drop request - var deleteDropRequest = function (dropRequest) { - $.ajax({ - type: 'DELETE', - url: dropRequest.uri, - dataType: 'json' - }).done(function() { - // drop request has been deleted - }).fail(nf.Common.handleAjaxError); - }; - - // get the connection data - var connection = selection.datum(); - - // issue the request to delete the flow files - $.ajax({ - type: 'DELETE', - url: connection.component.uri + '/contents', - dataType: 'json' - }).done(function(response) { - processDropRequest(response.dropRequest, 1); - }).fail(nf.Common.handleAjaxError); + // initialize the progress bar value + updateProgress(0); + + // show the progress dialog + $('#drop-request-status-dialog').modal('show'); + + // process the drop request + dropRequest = response.dropRequest; + processDropRequest(1); + }).fail(completeDropRequest); + } + }); }, /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js index 33f116e0b6..7137fe4be9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js @@ -188,7 +188,7 @@ nf.Canvas = (function () { if (!refreshContainer.is(':visible')) { $('#stats-last-refreshed').addClass('alert'); var refreshMessage = "This flow has been modified by '" + revision.lastModifier + "'. Please refresh."; - + // update the tooltip var refreshRequiredIcon = $('#refresh-required-icon'); if (refreshRequiredIcon.data('qtip')) { @@ -198,10 +198,10 @@ nf.Canvas = (function () { content: refreshMessage }, nf.CanvasUtils.config.systemTooltipConfig)); } - + refreshContainer.show(); } - + // insert the refresh needed text in the settings - if necessary if (!settingsRefreshIcon.is(':visible')) { $('#settings-last-refreshed').addClass('alert'); @@ -333,7 +333,7 @@ nf.Canvas = (function () { 'offset': '100%', 'stop-color': '#ffffff' }); - + // define the gradient for the expiration icon var expirationBackground = defs.append('linearGradient') .attr({ @@ -343,7 +343,7 @@ nf.Canvas = (function () { 'x2': '0%', 'y2': '100%' }); - + expirationBackground.append('stop') .attr({ 'offset': '0%', @@ -397,106 +397,106 @@ nf.Canvas = (function () { // prevent further propagation (to parents and others handlers // on the same element to prevent zoom behavior) d3.event.stopImmediatePropagation(); - + // prevents the browser from changing to a text selection cursor d3.event.preventDefault(); } }) - .on('mousemove.selection', function () { - // update selection box if shift is held down - if (d3.event.shiftKey) { - // get the selection box - var selectionBox = d3.select('rect.selection'); - if (!selectionBox.empty()) { - // get the original position - var originalPosition = selectionBox.datum(); - var position = d3.mouse(canvas.node()); - - var d = {}; - if (originalPosition[0] < position[0]) { - d.x = originalPosition[0]; - d.width = position[0] - originalPosition[0]; - } else { - d.x = position[0]; - d.width = originalPosition[0] - position[0]; + .on('mousemove.selection', function () { + // update selection box if shift is held down + if (d3.event.shiftKey) { + // get the selection box + var selectionBox = d3.select('rect.selection'); + if (!selectionBox.empty()) { + // get the original position + var originalPosition = selectionBox.datum(); + var position = d3.mouse(canvas.node()); + + var d = {}; + if (originalPosition[0] < position[0]) { + d.x = originalPosition[0]; + d.width = position[0] - originalPosition[0]; + } else { + d.x = position[0]; + d.width = originalPosition[0] - position[0]; + } + + if (originalPosition[1] < position[1]) { + d.y = originalPosition[1]; + d.height = position[1] - originalPosition[1]; + } else { + d.y = position[1]; + d.height = originalPosition[1] - position[1]; + } + + // update the selection box + selectionBox.attr(d); + + // prevent further propagation (to parents) + d3.event.stopPropagation(); + } + } + }) + .on('mouseup.selection', function () { + // ensure this originated from clicking the canvas, not a component. + // when clicking on a component, the event propagation is stopped so + // it never reaches the canvas. we cannot do this however on up events + // since the drag events break down + if (canvasClicked === false) { + return; } - if (originalPosition[1] < position[1]) { - d.y = originalPosition[1]; - d.height = position[1] - originalPosition[1]; - } else { - d.y = position[1]; - d.height = originalPosition[1] - position[1]; + // reset the canvas click flag + canvasClicked = false; + + // get the selection box + var selectionBox = d3.select('rect.selection'); + if (!selectionBox.empty()) { + var selectionBoundingBox = { + x: parseInt(selectionBox.attr('x'), 10), + y: parseInt(selectionBox.attr('y'), 10), + width: parseInt(selectionBox.attr('width'), 10), + height: parseInt(selectionBox.attr('height'), 10) + }; + + // see if a component should be selected or not + d3.selectAll('g.component').classed('selected', function (d) { + // consider it selected if its already selected or enclosed in the bounding box + return d3.select(this).classed('selected') || + d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) && + d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height); + }); + + // see if a connection should be selected or not + d3.selectAll('g.connection').classed('selected', function (d) { + // consider all points + var points = [d.start].concat(d.bends, [d.end]); + + // determine the bounding box + var x = d3.extent(points, function (pt) { + return pt.x; + }); + var y = d3.extent(points, function (pt) { + return pt.y; + }); + + // consider it selected if its already selected or enclosed in the bounding box + return d3.select(this).classed('selected') || + x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) && + y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height); + }); + + // remove the selection box + selectionBox.remove(); + } else if (panning === false) { + // deselect as necessary if we are not panning + nf.CanvasUtils.getSelection().classed('selected', false); } - // update the selection box - selectionBox.attr(d); - - // prevent further propagation (to parents) - d3.event.stopPropagation(); - } - } - }) - .on('mouseup.selection', function () { - // ensure this originated from clicking the canvas, not a component. - // when clicking on a component, the event propagation is stopped so - // it never reaches the canvas. we cannot do this however on up events - // since the drag events break down - if (canvasClicked === false) { - return; - } - - // reset the canvas click flag - canvasClicked = false; - - // get the selection box - var selectionBox = d3.select('rect.selection'); - if (!selectionBox.empty()) { - var selectionBoundingBox = { - x: parseInt(selectionBox.attr('x'), 10), - y: parseInt(selectionBox.attr('y'), 10), - width: parseInt(selectionBox.attr('width'), 10), - height: parseInt(selectionBox.attr('height'), 10) - }; - - // see if a component should be selected or not - d3.selectAll('g.component').classed('selected', function (d) { - // consider it selected if its already selected or enclosed in the bounding box - return d3.select(this).classed('selected') || - d.component.position.x >= selectionBoundingBox.x && (d.component.position.x + d.dimensions.width) <= (selectionBoundingBox.x + selectionBoundingBox.width) && - d.component.position.y >= selectionBoundingBox.y && (d.component.position.y + d.dimensions.height) <= (selectionBoundingBox.y + selectionBoundingBox.height); + // update the toolbar + nf.CanvasToolbar.refresh(); }); - // see if a connection should be selected or not - d3.selectAll('g.connection').classed('selected', function (d) { - // consider all points - var points = [d.start].concat(d.bends, [d.end]); - - // determine the bounding box - var x = d3.extent(points, function (pt) { - return pt.x; - }); - var y = d3.extent(points, function (pt) { - return pt.y; - }); - - // consider it selected if its already selected or enclosed in the bounding box - return d3.select(this).classed('selected') || - x[0] >= selectionBoundingBox.x && x[1] <= (selectionBoundingBox.x + selectionBoundingBox.width) && - y[0] >= selectionBoundingBox.y && y[1] <= (selectionBoundingBox.y + selectionBoundingBox.height); - }); - - // remove the selection box - selectionBox.remove(); - } else if (panning === false) { - // deselect as necessary if we are not panning - nf.CanvasUtils.getSelection().classed('selected', false); - } - - // update the toolbar - nf.CanvasToolbar.refresh(); - }); - // define a function for update the graph dimensions var updateGraphSize = function () { // get the location of the bottom of the graph @@ -510,7 +510,7 @@ nf.Canvas = (function () { var top = parseInt(canvasContainer.css('top'), 10); var windowHeight = $(window).height(); var canvasHeight = (windowHeight - (bottom + top)); - + // canvas/svg canvasContainer.css({ 'height': canvasHeight + 'px', @@ -536,7 +536,7 @@ nf.Canvas = (function () { } }).on('keydown', function (evt) { var isCtrl = evt.ctrlKey || evt.metaKey; - + // consider escape, before checking dialogs if (!isCtrl && evt.keyCode === 27) { // esc @@ -552,7 +552,7 @@ nf.Canvas = (function () { // first consider read only property detail dialog if ($('div.property-detail').is(':visible')) { nf.Common.removeAllPropertyDetailDialogs(); - + // prevent further bubbling as we're already handled it evt.stopPropagation(); evt.preventDefault(); @@ -570,7 +570,7 @@ nf.Canvas = (function () { var dialogMax = null; // identify the top most cancellable - $.each(cancellables, function(_, cancellable) { + $.each(cancellables, function (_, cancellable) { var dialog = $(cancellable); var zIndex = dialog.css('zIndex'); @@ -615,10 +615,10 @@ nf.Canvas = (function () { } } } - + return; } - + // if a dialog is open, disable canvas shortcuts if ($('.dialog').is(':visible')) { return; @@ -833,7 +833,7 @@ nf.Canvas = (function () { bulletinIcon.show(); } } - + // update controller service and reporting task bulletins nf.Settings.setBulletins(controllerStatus.controllerServiceBulletins, controllerStatus.reportingTaskBulletins); @@ -934,21 +934,17 @@ nf.Canvas = (function () { }; return { - CANVAS_OFFSET: 0, - /** * Determines if the current broswer supports SVG. */ SUPPORTS_SVG: !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect, - /** * Hides the splash that is displayed while the application is loading. */ hideSplash: function () { $('#splash').fadeOut(); }, - /** * Stop polling for revision. */ @@ -956,7 +952,6 @@ nf.Canvas = (function () { // set polling flag revisionPolling = false; }, - /** * Remove the status poller. */ @@ -964,7 +959,6 @@ nf.Canvas = (function () { // set polling flag statusPolling = false; }, - /** * Reloads the flow from the server based on the currently specified group id. * To load another group, update nf.Canvas.setGroupId and call nf.Canvas.reload. @@ -973,7 +967,7 @@ nf.Canvas = (function () { return $.Deferred(function (deferred) { // hide the context menu nf.ContextMenu.hide(); - + // get the process group to refresh everything var processGroupXhr = reloadProcessGroup(nf.Canvas.getGroupId()); var statusXhr = reloadFlowStatus(); @@ -1007,7 +1001,6 @@ nf.Canvas = (function () { }); }).promise(); }, - /** * Reloads the status. */ @@ -1021,7 +1014,6 @@ nf.Canvas = (function () { }); }).promise(); }, - /** * Initialize NiFi. */ @@ -1099,6 +1091,7 @@ nf.Canvas = (function () { nf.GraphControl.init(); nf.Search.init(); nf.Settings.init(); + nf.Actions.init(); // initialize the component behaviors nf.Draggable.init(); @@ -1146,7 +1139,6 @@ nf.Canvas = (function () { }).fail(nf.Common.handleAjaxError); }).fail(nf.Common.handleAjaxError); }, - /** * Defines the gradient colors used to render processors. * @@ -1155,7 +1147,6 @@ nf.Canvas = (function () { defineProcessorColors: function (colors) { setColors(colors, 'processor'); }, - /** * Defines the gradient colors used to render label. * @@ -1164,7 +1155,6 @@ nf.Canvas = (function () { defineLabelColors: function (colors) { setColors(colors, 'label'); }, - /** * Return whether this instance of NiFi is clustered. * @@ -1173,14 +1163,12 @@ nf.Canvas = (function () { isClustered: function () { return clustered === true; }, - /** * Returns whether site to site communications is secure. */ isSecureSiteToSite: function () { return secureSiteToSite; }, - /** * Set the group id. * @@ -1189,14 +1177,12 @@ nf.Canvas = (function () { setGroupId: function (gi) { groupId = gi; }, - /** * Get the group id. */ getGroupId: function () { return groupId; }, - /** * Set the group name. * @@ -1205,14 +1191,12 @@ nf.Canvas = (function () { setGroupName: function (gn) { groupName = gn; }, - /** * Get the group name. */ getGroupName: function () { return groupName; }, - /** * Set the parent group id. * @@ -1221,16 +1205,14 @@ nf.Canvas = (function () { setParentGroupId: function (pgi) { parentGroupId = pgi; }, - /** * Get the parent group id. */ getParentGroupId: function () { return parentGroupId; }, - View: (function () { - + /** * Updates component visibility based on their proximity to the screen's viewport. */ @@ -1297,8 +1279,8 @@ nf.Canvas = (function () { .classed('entering', function () { return visible && !wasVisible; }).classed('leaving', function () { - return !visible && wasVisible; - }); + return !visible && wasVisible; + }); }; // get the all components @@ -1385,7 +1367,6 @@ nf.Canvas = (function () { // add the behavior to the canvas and disable dbl click zoom svg.call(behavior).on('dblclick.zoom', null); }, - /** * Whether or not a component should be rendered based solely on the current scale. * @@ -1394,7 +1375,6 @@ nf.Canvas = (function () { shouldRenderPerScale: function () { return nf.Canvas.View.scale() >= MIN_SCALE_TO_RENDER; }, - /** * Updates component visibility based on the current translation/scale. */ @@ -1402,7 +1382,6 @@ nf.Canvas = (function () { updateComponentVisibility(); nf.Graph.pan(); }, - /** * Sets/gets the current translation. * @@ -1415,7 +1394,6 @@ nf.Canvas = (function () { behavior.translate(translate); } }, - /** * Sets/gets the current scale. * @@ -1428,7 +1406,6 @@ nf.Canvas = (function () { behavior.scale(scale); } }, - /** * Zooms in a single zoom increment. */ @@ -1453,7 +1430,6 @@ nf.Canvas = (function () { height: 1 }); }, - /** * Zooms out a single zoom increment. */ @@ -1478,7 +1454,6 @@ nf.Canvas = (function () { height: 1 }); }, - /** * Zooms to fit the entire graph on the canvas. */ @@ -1525,7 +1500,6 @@ nf.Canvas = (function () { height: canvasHeight / newScale }); }, - /** * Zooms to the actual size (1 to 1). */ @@ -1574,7 +1548,6 @@ nf.Canvas = (function () { // center as appropriate nf.CanvasUtils.centerBoundingBox(box); }, - /** * Refreshes the view based on the configured translation and scale. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js index f2b358dcc4..2ce3f7d615 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-context-menu.js @@ -278,11 +278,11 @@ nf.ContextMenu = (function () { }; /** - * Only DFMs can delete flow files from a connection. + * Only DFMs can empty a queue. * * @param {selection} selection */ - var canDeleteFlowFiles = function (selection) { + var canEmptyQueue = function (selection) { return nf.Common.isDFM() && isConnection(selection); }; @@ -382,7 +382,7 @@ nf.ContextMenu = (function () { {condition: isCopyable, menuItem: {img: 'images/iconCopy.png', text: 'Copy', action: 'copy'}}, {condition: isPastable, menuItem: {img: 'images/iconPaste.png', text: 'Paste', action: 'paste'}}, {condition: canMoveToParent, menuItem: {img: 'images/iconMoveToParent.png', text: 'Move to parent group', action: 'moveIntoParent'}}, - {condition: canDeleteFlowFiles, menuItem: {img: 'images/iconDelete.png', text: 'Empty queue', action: 'emptyQueue'}}, + {condition: canEmptyQueue, menuItem: {img: 'images/iconEmptyQueue.png', text: 'Empty queue', action: 'emptyQueue'}}, {condition: isDeletable, menuItem: {img: 'images/iconDelete.png', text: 'Delete', action: 'delete'}} ];