Repository: nifi
Updated Branches:
  refs/heads/NIFI-108 0676165e0 -> cd50dbc59


NIFI-108:
- Adding initial support for viewing flowfile details dialog.
- Adding initial support for click to content.


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/cd50dbc5
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/cd50dbc5
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/cd50dbc5

Branch: refs/heads/NIFI-108
Commit: cd50dbc59a9f22a4af63a7d509c36e92422d1811
Parents: 0676165
Author: Matt Gilman <[email protected]>
Authored: Mon Dec 21 11:32:09 2015 -0500
Committer: Matt Gilman <[email protected]>
Committed: Mon Dec 21 11:32:09 2015 -0500

----------------------------------------------------------------------
 .../apache/nifi/web/api/dto/FlowFileDTO.java    |  91 ++++++++++++
 .../nifi/web/StandardNiFiContentAccess.java     |  52 +++++--
 .../org/apache/nifi/web/api/dto/DtoFactory.java |  13 ++
 .../src/main/webapp/WEB-INF/pages/canvas.jsp    |   2 +
 .../partials/canvas/flowfile-details-dialog.jsp | 114 ++++++++++++++
 .../src/main/webapp/css/queue-listing.css       | 137 +++++++++++++++++
 .../src/main/webapp/js/nf/canvas/nf-canvas.js   |   5 +
 .../webapp/js/nf/canvas/nf-queue-listing.js     | 147 ++++++++++++++++---
 .../src/main/webapp/js/nf/nf-common.js          |  10 ++
 .../js/nf/provenance/nf-provenance-table.js     |  12 +-
 10 files changed, 539 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowFileDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowFileDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowFileDTO.java
index 95e91ef..98f60e8 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowFileDTO.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/FlowFileDTO.java
@@ -26,6 +26,13 @@ public class FlowFileDTO extends FlowFileSummaryDTO {
 
     private Map<String, String> attributes;
 
+    private String contentClaimSection;
+    private String contentClaimContainer;
+    private String contentClaimIdentifier;
+    private Long contentClaimOffset;
+    private String contentClaimFileSize;
+    private Long contentClaimFileSizeBytes;
+
     /**
      * @return the FlowFile attributes
      */
@@ -39,4 +46,88 @@ public class FlowFileDTO extends FlowFileSummaryDTO {
     public void setAttributes(Map<String, String> attributes) {
         this.attributes = attributes;
     }
+
+    /**
+     * @return the Section in which the Content Claim lives, or 
<code>null</code> if no Content Claim exists
+     */
+    @ApiModelProperty(
+        value = "The section in which the content claim lives."
+    )
+    public String getContentClaimSection() {
+        return contentClaimSection;
+    }
+
+    public void setContentClaimSection(String contentClaimSection) {
+        this.contentClaimSection = contentClaimSection;
+    }
+
+    /**
+     * @return the Container in which the Content Claim lives, or 
<code>null</code> if no Content Claim exists
+     */
+    @ApiModelProperty(
+        value = "The container in which the content claim lives."
+    )
+    public String getContentClaimContainer() {
+        return contentClaimContainer;
+    }
+
+    public void setContentClaimContainer(String contentClaimContainer) {
+        this.contentClaimContainer = contentClaimContainer;
+    }
+
+    /**
+     * @return the Identifier of the Content Claim, or <code>null</code> if no 
Content Claim exists
+     */
+    @ApiModelProperty(
+        value = "The identifier of the content claim."
+    )
+    public String getContentClaimIdentifier() {
+        return contentClaimIdentifier;
+    }
+
+    public void setContentClaimIdentifier(String contentClaimIdentifier) {
+        this.contentClaimIdentifier = contentClaimIdentifier;
+    }
+
+    /**
+     * @return the offset into the the Content Claim where the FlowFile's 
content begins, or <code>null</code> if no Content Claim exists
+     */
+    @ApiModelProperty(
+        value = "The offset into the content claim where the flowfiles content 
begins."
+    )
+    public Long getContentClaimOffset() {
+        return contentClaimOffset;
+    }
+
+    public void setContentClaimOffset(Long contentClaimOffset) {
+        this.contentClaimOffset = contentClaimOffset;
+    }
+
+    /**
+     * @return the formatted file size of the content claim
+     */
+    @ApiModelProperty(
+        value = "The file size of the content claim formatted."
+    )
+    public String getContentClaimFileSize() {
+        return contentClaimFileSize;
+    }
+
+    public void setContentClaimFileSize(String contentClaimFileSize) {
+        this.contentClaimFileSize = contentClaimFileSize;
+    }
+
+    /**
+     * @return the number of bytes of the content claim
+     */
+    @ApiModelProperty(
+        value = "The file size of the content claim in bytes."
+    )
+    public Long getContentClaimFileSizeBytes() {
+        return contentClaimFileSizeBytes;
+    }
+
+    public void setContentClaimFileSizeBytes(Long contentClaimFileSizeBytes) {
+        this.contentClaimFileSizeBytes = contentClaimFileSizeBytes;
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
index 2f75004..f994c52 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiContentAccess.java
@@ -25,6 +25,8 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.core.MultivaluedMap;
 import org.apache.commons.lang3.StringUtils;
@@ -51,6 +53,12 @@ public class StandardNiFiContentAccess implements 
ContentAccess {
     private static final Logger logger = 
LoggerFactory.getLogger(StandardNiFiContentAccess.class);
     public static final String CLIENT_ID_PARAM = "clientId";
 
+    private static final Pattern FLOWFILE_CONTENT_URI_PATTERN = Pattern
+        
.compile("/controller/process-groups/((?:root)|(?:[a-f0-9\\-]{36}))/connections/([a-f0-9\\-]{36})/flowfiles/([a-f0-9\\-]{36})/content");
+
+    private static final Pattern PROVENANCE_CONTENT_URI_PATTERN = Pattern
+        
.compile("/controller/provenance/events/([0-9]+)/content/((?:input)|(?:output))");
+
     private NiFiProperties properties;
     private NiFiServiceFacade serviceFacade;
     private WebClusterManager clusterManager;
@@ -115,21 +123,41 @@ public class StandardNiFiContentAccess implements 
ContentAccess {
             // create the downloadable content
             return new DownloadableContent(filename, contentType, 
clientResponse.getEntityInputStream());
         } else {
-            // example URI: 
http://localhost:8080/nifi-api/controller/provenance/events/1/content/input
-            final String eventDetails = 
StringUtils.substringAfterLast(request.getDataUri(), "events/");
-            final String rawEventId = 
StringUtils.substringBefore(eventDetails, "/content/");
-            final String rawDirection = 
StringUtils.substringAfterLast(eventDetails, "/content/");
+            // example URIs:
+            // 
http://localhost:8080/nifi-api/controller/provenance/events/{id}/content/{input|output}
+            // 
http://localhost:8080/nifi-api/controller/process-groups/{root|uuid}/connections/{uuid}/flowfiles/{uuid}/content
 
-            // get the content type
-            final Long eventId;
-            final ContentDirection direction;
-            try {
-                eventId = Long.parseLong(rawEventId);
-                direction = 
ContentDirection.valueOf(rawDirection.toUpperCase());
-            } catch (final IllegalArgumentException iae) {
+            // get just the context path for comparison
+            final String dataUri = 
StringUtils.substringAfter(request.getDataUri(), "/nifi-api");
+            if (StringUtils.isBlank(dataUri)) {
                 throw new IllegalArgumentException("The specified data 
reference URI is not valid.");
             }
-            return serviceFacade.getContent(eventId, request.getDataUri(), 
direction);
+
+            // flowfile listing content
+            final Matcher flowFileMatcher = 
FLOWFILE_CONTENT_URI_PATTERN.matcher(dataUri);
+            if (flowFileMatcher.matches()) {
+                final String groupId = flowFileMatcher.group(1);
+                final String connectionId = flowFileMatcher.group(2);
+                final String flowfileId = flowFileMatcher.group(3);
+
+                return serviceFacade.getContent(groupId, connectionId, 
flowfileId, dataUri);
+            }
+
+            // provenance event content
+            final Matcher provenanceMatcher = 
PROVENANCE_CONTENT_URI_PATTERN.matcher(dataUri);
+            if (provenanceMatcher.matches()) {
+                try {
+                    final Long eventId = 
Long.parseLong(provenanceMatcher.group(1));
+                    final ContentDirection direction = 
ContentDirection.valueOf(provenanceMatcher.group(2).toUpperCase());
+
+                    return serviceFacade.getContent(eventId, dataUri, 
direction);
+                } catch (final IllegalArgumentException iae) {
+                    throw new IllegalArgumentException("The specified data 
reference URI is not valid.");
+                }
+            }
+
+            // invalid uri
+            throw new IllegalArgumentException("The specified data reference 
URI is not valid.");
         }
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
----------------------------------------------------------------------
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 22031bb..99bffc4 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
@@ -79,6 +79,8 @@ import org.apache.nifi.controller.queue.FlowFileSummary;
 import org.apache.nifi.controller.queue.ListFlowFileState;
 import org.apache.nifi.controller.queue.ListFlowFileStatus;
 import org.apache.nifi.controller.repository.FlowFileRecord;
+import org.apache.nifi.controller.repository.claim.ContentClaim;
+import org.apache.nifi.controller.repository.claim.ResourceClaim;
 import org.apache.nifi.controller.status.ConnectionStatus;
 import org.apache.nifi.controller.status.PortStatus;
 import org.apache.nifi.controller.status.ProcessGroupStatus;
@@ -443,6 +445,17 @@ public final class DtoFactory {
         final long age = now.getTime() - record.getLineageStartDate();
         dto.setLineageDuration(age);
 
+        final ContentClaim contentClaim = record.getContentClaim();
+        if (contentClaim != null) {
+            final ResourceClaim resourceClaim = 
contentClaim.getResourceClaim();
+            dto.setContentClaimSection(resourceClaim.getSection());
+            dto.setContentClaimContainer(resourceClaim.getContainer());
+            dto.setContentClaimIdentifier(resourceClaim.getId());
+            dto.setContentClaimOffset(contentClaim.getOffset());
+            dto.setContentClaimFileSizeBytes(contentClaim.getLength());
+            
dto.setContentClaimFileSize(FormatUtils.formatDataSize(contentClaim.getLength()));
+        }
+
         return dto;
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/pages/canvas.jsp
----------------------------------------------------------------------
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 337b2b0..5a9eab7 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,11 +119,13 @@
         <jsp:include page="/WEB-INF/partials/canvas/label-configuration.jsp"/>
         <jsp:include 
page="/WEB-INF/partials/canvas/connection-configuration.jsp"/>
         <jsp:include 
page="/WEB-INF/partials/canvas/drop-request-status-dialog.jsp"/>
+        <jsp:include 
page="/WEB-INF/partials/canvas/flowfile-details-dialog.jsp"/>
         <jsp:include 
page="/WEB-INF/partials/canvas/listing-request-status-dialog.jsp"/>
         <jsp:include page="/WEB-INF/partials/canvas/queue-listing.jsp"/>
         <jsp:include page="/WEB-INF/partials/connection-details.jsp"/>
         <div id="faded-background"></div>
         <div id="glass-pane"></div>
         <div id="context-menu" class="unselectable"></div>
+        <span id="nifi-content-viewer-url" class="hidden"></span>
     </body>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
new file mode 100644
index 0000000..cead866
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/flowfile-details-dialog.jsp
@@ -0,0 +1,114 @@
+<%--
+ 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" %>
+<div id="flowfile-details-dialog">
+    <div id="flowfile-details-dialog-content">
+        <div id="flowfile-details-tabs"></div>
+        <div id="flowfile-details-tabs-content">
+            <div id="flowfile-details-tab-content" class="details-tab">
+                <span id="flowfile-uri" class="hidden"></span>
+                <span id="flowfile-cluster-node-id" class="hidden"></span>
+                <div class="settings-left">
+                    <div id="flowfile-details">
+                        <div class="flowfile-header">FlowFile Details</div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">UUID</div>
+                            <div id="flowfile-uuid" class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">Filename</div>
+                            <div id="flowfile-filename" 
class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">File Size</div>
+                            <div id="flowfile-file-size" 
class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">Queue Position</div>
+                            <div id="flowfile-queue-position" 
class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">Queued Duration</div>
+                            <div id="flowfile-queued-duration" 
class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">Lineage Duration</div>
+                            <div id="flowfile-lineage-duration" 
class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="detail-name">Penalized</div>
+                            <div id="flowfile-penalized" 
class="detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div id="additional-flowfile-details"></div>
+                    </div>
+                </div>
+                <div class="spacer">&nbsp;</div>
+                <div class="settings-right">
+                    <div id="flowfile-content-details" class="content-details">
+                        <div class="flowfile-header">Content Claim</div>
+                        <div class="flowfile-detail">
+                            <div class="content-detail-name">Container</div>
+                            <div id="content-container" 
class="content-detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="content-detail-name">Section</div>
+                            <div id="content-section" 
class="content-detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="content-detail-name">Identifier</div>
+                            <div id="content-identifier" 
class="content-detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="content-detail-name">Offset</div>
+                            <div id="content-offset" 
class="content-detail-value"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div class="content-detail-name">Size</div>
+                            <div id="content-size" 
class="content-detail-value"></div>
+                            <div id="content-bytes" 
class="content-detail-value hidden"></div>
+                            <div class="clear"></div>
+                        </div>
+                        <div class="flowfile-detail">
+                            <div id="content-download" 
class="button">Download</div>
+                            <div id="content-view" class="button 
hidden">View</div>
+                            <div class="clear"></div>
+                        </div>
+                    </div>
+                </div>
+                <div class="clear"></div>
+            </div>
+            <div id="flowfile-attributes-tab-content" class="details-tab">
+                <div id="flowfile-attributes-details">
+                    <div id="flowfile-attributes-header" 
class="flowfile-header">Attribute Values</div>
+                    <div class="clear"></div>
+                    <div id="flowfile-attributes-container"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
index 2d7205e..689ceaf 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/queue-listing.css
@@ -76,3 +76,140 @@
     border: 1px solid #666;
     overflow: hidden;
 }
+
+/* flowfile details */
+
+#flowfile-details-dialog {
+    display: none;
+    width: 800px;
+    height: 450px;
+    z-index: 1301;
+}
+
+#flowfile-details-dialog-content {
+    margin-top: -10px;
+    padding: 5px 11px
+}
+
+#flowfile-details-tabs {
+    background-color: transparent;
+    border-bottom: 3px solid #666666;
+    height: 21px;
+    width: 778px;
+}
+
+#flowfile-details-dialog div.details-tab {
+    background: url("../images/bgTabContainer.png") repeat-x scroll 0 0 
#EEEEEE;
+    display: none;
+    height: 330px;
+    padding: 10px;
+}
+
+#flowfile-details-tab-content div.settings-left {
+    float: left;
+    width: 420px;
+}
+
+#flowfile-details-tab-content div.spacer {
+    float: left;
+    margin-right: 40px;
+}
+
+#flowfile-details-tab-content div.settings-right {
+    float: left;
+    width: 292px;
+}
+
+div.flowfile-header {
+    color: #264C58;
+    font-size: 11px;
+    font-weight: bold;
+    margin-bottom: 5px;
+}
+
+#flowfile-attributes-container {
+    height: 290px;
+    overflow: auto;
+    border: 1px solid #aaa;
+    padding: 5px;
+}
+
+div.detail-name {
+    float: left;
+    width: 145px;
+    color: #527991;
+    font-size: 10px;
+    font-weight: bold;
+    overflow: hidden;
+    white-space: nowrap;
+    line-height: normal;
+}
+
+div.detail-value {
+    float: left;
+    width: 270px;
+    overflow: hidden;
+    white-space: nowrap;
+    line-height: normal;
+}
+
+div.attribute-detail {
+    margin-bottom: 5px;
+}
+
+div.attribute-name {
+    float: left;
+    width: 225px;
+    color: #527991;
+    font-size: 10px;
+    font-weight: bold;
+    overflow: hidden;
+    white-space: nowrap;
+    line-height: normal;
+}
+
+div.attribute-value {
+    float: left;
+    width: 490px;
+    margin-left: 10px;
+    overflow: hidden;
+    white-space: nowrap;
+    line-height: normal;
+}
+
+#flowfile-attributes-header {
+    float: left;
+}
+
+div.flowfile-detail {
+    margin-bottom: 4px;
+}
+
+#content-download {
+    float: left;
+    margin: 0;
+}
+
+#content-view {
+    float: left;
+    margin-left: 5px;
+}
+
+div.content-detail-name {
+    color: #527991;
+    float: left;
+    font-size: 10px;
+    font-weight: bold;
+    line-height: normal;
+    overflow: hidden;
+    white-space: nowrap;
+    width: 90px;
+}
+
+div.content-detail-value {
+    width: 200px;
+    float: left;
+    line-height: normal;
+    overflow: hidden;
+    white-space: nowrap;
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-canvas.js
----------------------------------------------------------------------
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 25d7255..005f7e3 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
@@ -1122,6 +1122,11 @@ nf.Canvas = (function () {
                     var configDetails = configResponse.config;
                     var loginDetails = loginResponse.config;
 
+                    // store the content viewer url if available
+                    if (!nf.Common.isBlank(configDetails.contentViewerUrl)) {
+                        
$('#nifi-content-viewer-url').text(configDetails.contentViewerUrl);
+                    }
+
                     // when both request complete, load the application
                     isClusteredRequest.done(function () {
                         // get the auto refresh interval

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
index cba598a..b0bba1f 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-queue-listing.js
@@ -55,39 +55,30 @@ nf.QueueListing = (function () {
     };
 
     /**
-     * Downloads the content for the provenance event that is currently loaded 
in the specified direction.
-     *
-     * @param {string} direction
+     * Downloads the content for the flowfile currently being viewed.
      */
-    var downloadContent = function (direction) {
-        var connection = $('#queue-listing-table').data('connection');
-
-        // build the url to the data
-        var url = config.urls.provenance + '/events/' + 
encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
+    var downloadContent = function () {
+        var dataUri = $('#flowfile-uri').text() + '/content';
 
         // conditionally include the cluster node id
-        var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+        var clusterNodeId = $('#flowfile-cluster-node-id').text();
         if (!nf.Common.isBlank(clusterNodeId)) {
-            window.open(url + '?' + $.param({
+            window.open(dataUri + '?' + $.param({
                     'clusterNodeId': clusterNodeId
                 }));
         } else {
-            window.open(url);
+            window.open(dataUri);
         }
     };
 
     /**
-     * Views the content for the provenance event that is currently loaded in 
the specified direction.
-     *
-     * @param {string} direction
+     * Views the content for the flowfile currently being viewed.
      */
-    var viewContent = function (direction) {
-
-        // build the uri to the data
-        var dataUri = controllerUri + '/provenance/events/' + 
encodeURIComponent(eventId) + '/content/' + encodeURIComponent(direction);
+    var viewContent = function () {
+        var dataUri = $('#flowfile-uri').text() + '/content';
 
         // conditionally include the cluster node id
-        var clusterNodeId = $('#provenance-event-cluster-node-id').text();
+        var clusterNodeId = $('#flowfile-cluster-node-id').text();;
         if (!nf.Common.isBlank(clusterNodeId)) {
             var parameters = {
                 'clusterNodeId': clusterNodeId
@@ -118,7 +109,46 @@ nf.QueueListing = (function () {
      * Initializes the flowfile details dialog.
      */
     var initFlowFileDetailsDialog = function () {
+        $('#content-download').on('click', downloadContent);
+
+        // only show if content viewer is configured
+        if (nf.Common.isContentViewConfigured()) {
+            $('#content-view').show();
+            $('#content-view').on('click', viewContent);
+        }
+
+        $('#flowfile-details-tabs').tabbs({
+            tabStyle: 'tab',
+            selectedTabStyle: 'selected-tab',
+            tabs: [{
+                name: 'Details',
+                tabContentId: 'flowfile-details-tab-content'
+            }, {
+                name: 'Attributes',
+                tabContentId: 'flowfile-attributes-tab-content'
+            }]
+        });
 
+        $('#flowfile-details-dialog').modal({
+            headerText: 'FlowFile',
+            overlayBackground: false,
+            buttons: [{
+                buttonText: 'Ok',
+                handler: {
+                    click: function () {
+                        $('#flowfile-details-dialog').modal('hide');
+                    }
+                }
+            }],
+            handler: {
+                close: function () {
+                    // clear the details
+                    $('#flowfile-attributes-container').empty();
+                    $('#flowfile-cluster-node-id').text('');
+                    $('#additional-flowfile-details').empty();
+                }
+            }
+        });
     };
 
     /**
@@ -129,7 +159,6 @@ nf.QueueListing = (function () {
      * @param sortAsc if sort is asc
      */
     var performListing = function (connection, sortCol, sortAsc) {
-
         var MAX_DELAY = 4;
         var cancelled = false;
         var listingRequest = null;
@@ -270,15 +299,91 @@ nf.QueueListing = (function () {
      * @param flowFileSummary the flowfile summary
      */
     var showFlowFileDetails = function (flowFileSummary) {
+        // formats an flowfile detail
+        var formatFlowFileDetail = function (label, value) {
+            $('<div class="flowfile-detail"></div>').append(
+                $('<div class="detail-name"></div>').text(label)).append(
+                $('<div class="detail-value">' + nf.Common.formatValue(value) 
+ '</div>').ellipsis()).append(
+                $('<div 
class="clear"></div>')).appendTo('#additional-flowfile-details');
+        };
+
+        // formats the content value
+        var formatContentValue = function (element, value) {
+            if (nf.Common.isDefinedAndNotNull(value)) {
+                element.removeClass('unset').text(value);
+            } else {
+                element.addClass('unset').text('No value set');
+            }
+        };
+
         $.ajax({
             type: 'GET',
             url: flowFileSummary.uri,
             dataType: 'json'
         }).done(function(response) {
-            var flowFile = response.flowfile;
+            var flowFile = response.flowFile;
+
+            // show the URI to this flowfile
+            $('#flowfile-uri').text(flowFile.uri);
 
             // show the flowfile details dialog
+            $('#flowfile-uuid').html(nf.Common.formatValue(flowFile.uuid));
+            
$('#flowfile-filename').html(nf.Common.formatValue(flowFile.filename));
+            
$('#flowfile-queue-position').html(nf.Common.formatValue(flowFile.position));
+            
$('#flowfile-file-size').html(nf.Common.formatValue(flowFile.contentClaimFileSize));
+            
$('#flowfile-queued-duration').text(nf.Common.formatDuration(flowFile.queuedDuration));
+            
$('#flowfile-lineage-duration').text(nf.Common.formatDuration(flowFile.lineageDuration));
+            $('#flowfile-penalized').text(flowFile.penalized === true ? 'Yes' 
: 'No');
+
+            // conditionally show the cluster node identifier
+            if (nf.Common.isDefinedAndNotNull(flowFile.clusterNodeId)) {
+                // save the cluster node id
+                $('#flowfile-cluster-node-id').text(flowFile.clusterNodeId);
+
+                // render the cluster node address
+                formatFlowFileDetail('Node Address', 
flowFile.clusterNodeAddress);
+            }
+
+            if (nf.Common.isDefinedAndNotNull(flowFile.contentClaimContainer)) 
{
+                // content claim
+                formatContentValue($('#content-container'), 
flowFile.contentClaimContainer);
+                formatContentValue($('#content-section'), 
flowFile.contentClaimSection);
+                formatContentValue($('#content-identifier'), 
flowFile.contentClaimIdentifier);
+                formatContentValue($('#content-offset'), 
flowFile.contentClaimOffset);
+                formatContentValue($('#content-bytes'), 
flowFile.contentClaimFileSizeBytes);
+
+                // input content file size
+                var contentSize = $('#content-size');
+                formatContentValue(contentSize, flowFile.contentClaimFileSize);
+                if 
(nf.Common.isDefinedAndNotNull(flowFile.contentClaimFileSize)) {
+                    // over the default tooltip with the actual byte count
+                    contentSize.attr('title', 
nf.Common.formatInteger(flowFile.contentClaimFileSizeBytes) + ' bytes');
+                }
+
+                // show the content details
+                $('#flowfile-content-details').show();
+            } else {
+                $('#flowfile-content-details').hide();
+            }
+
+            // attributes
+            var attributesContainer = $('#flowfile-attributes-container');
+
+            // get any action details
+            $.each(flowFile.attributes, function (attributeName, 
attributeValue) {
+                // create the attribute record
+                var attributeRecord = $('<div class="attribute-detail"></div>')
+                    .append($('<div class="attribute-name">' + 
nf.Common.formatValue(attributeName) + '</div>').ellipsis())
+                    .appendTo(attributesContainer);
+
+                // add the current value
+                attributeRecord
+                    .append($('<div class="attribute-value">' + 
nf.Common.formatValue(attributeValue) + '</div>').ellipsis())
+                    .append('<div class="clear"></div>');
+            });
 
+            // show the dialog
+            $('#flowfile-details-dialog').modal('show');
         }).fail(nf.Common.handleAjaxError);
     };
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/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 cf83e16..6b4a5ea 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
@@ -463,6 +463,16 @@ nf.Common = (function () {
         },
 
         /**
+         * Returns whether a content viewer has been configured.
+         *
+         * @returns {boolean}
+         */
+        isContentViewConfigured: function () {
+            var contentViewerUrl = $('#nifi-content-viewer-url').text();
+            return !nf.Common.isBlank(contentViewerUrl);
+        },
+
+        /**
          * 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.'

http://git-wip-us.apache.org/repos/asf/nifi/blob/cd50dbc5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
index faa6adc..0ea44b2 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/provenance/nf-provenance-table.js
@@ -70,16 +70,6 @@ nf.ProvenanceTable = (function () {
     };
 
     /**
-     * Returns whether a content viewer has been configured.
-     * 
-     * @returns {boolean}
-     */
-    var isContentViewConfigured = function () {
-        var contentViewerUrl = $('#nifi-content-viewer-url').text();
-        return !nf.Common.isBlank(contentViewerUrl);
-    };
-
-    /**
      * Downloads the content for the provenance event that is currently loaded 
in the specified direction.
      * 
      * @param {string} direction
@@ -206,7 +196,7 @@ nf.ProvenanceTable = (function () {
         });
 
         // if a content viewer url is specified, use it
-        if (isContentViewConfigured()) {
+        if (nf.Common.isContentViewConfigured()) {
             // input view
             $('#input-content-view').on('click', function () {
                 viewContent('input');

Reply via email to