GUI -- Added applications view.
Fixed table.js column width computation.
Fixed app.xml files to leave out ONOS from description.
Change-Id: Icfe323e63c7965dd8c3a268421ea58065c5c8236
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
new file mode 100644
index 0000000..800b57d
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationViewMessageHandler.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.
+ */
+package org.onosproject.ui.impl;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.core.Application;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.onosproject.app.ApplicationState.ACTIVE;
+
+/**
+ * Message handler for application view related messages.
+ */
+public class ApplicationViewMessageHandler extends AbstractTabularViewMessageHandler {
+
+ /**
+ * Creates a new message handler for the application messages.
+ */
+ protected ApplicationViewMessageHandler() {
+ super(ImmutableSet.of("appDataRequest"));
+ }
+
+ @Override
+ public void process(ObjectNode message) {
+ ObjectNode payload = payload(message);
+ String sortCol = string(payload, "sortCol", "id");
+ String sortDir = string(payload, "sortDir", "asc");
+
+ ApplicationService service = get(ApplicationService.class);
+ TableRow[] rows = generateTableRows(service);
+ RowComparator rc =
+ new RowComparator(sortCol, RowComparator.direction(sortDir));
+ Arrays.sort(rows, rc);
+ ArrayNode applications = generateArrayNode(rows);
+ ObjectNode rootNode = mapper.createObjectNode();
+ rootNode.set("applications", applications);
+
+ connection().sendMessage("appDataResponse", 0, rootNode);
+ }
+
+ private TableRow[] generateTableRows(ApplicationService service) {
+ List<TableRow> list = service.getApplications().stream()
+ .map(application -> new ApplicationTableRow(service, application))
+ .collect(Collectors.toList());
+ return list.toArray(new TableRow[list.size()]);
+ }
+
+ /**
+ * TableRow implementation for {@link org.onosproject.core.Application applications}.
+ */
+ private static class ApplicationTableRow extends AbstractTableRow {
+
+ private static final String STATE = "state";
+ private static final String STATE_IID = "_iconid_state";
+ private static final String ID = "id";
+ private static final String VERSION = "version";
+ private static final String ORIGIN = "origin";
+ private static final String DESC = "desc";
+
+ private static final String[] COL_IDS = {
+ STATE, STATE_IID, ID, VERSION, ORIGIN, DESC
+ };
+
+ private static final String ICON_ID_ACTIVE = "appActive";
+ private static final String ICON_ID_INACTIVE = "appInactive";
+
+
+ public ApplicationTableRow(ApplicationService service, Application app) {
+ ApplicationState state = service.getState(app.id());
+ String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE;
+
+ add(STATE, state.toString());
+ add(STATE_IID, iconId);
+ add(ID, app.id().name());
+ add(VERSION, app.version().toString());
+ add(ORIGIN, app.origin());
+ add(DESC, app.description());
+ }
+
+ @Override
+ protected String[] columnIds() {
+ return COL_IDS;
+ }
+ }
+
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
index 73f7b8e..f0541f6 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -59,12 +59,14 @@
List<UiView> coreViews = of(new UiView("topo", "Topology View"),
new UiView("device", "Devices"),
new UiView("host", "Hosts"),
+ new UiView("app", "Applications"),
new UiView("sample", "Sample"));
UiMessageHandlerFactory messageHandlerFactory =
() -> ImmutableList.of(
new TopologyViewMessageHandler(),
new DeviceViewMessageHandler(),
- new HostViewMessageHandler()
+ new HostViewMessageHandler(),
+ new ApplicationViewMessageHandler()
);
return new UiExtension(coreViews, messageHandlerFactory, "core",
UiExtensionManager.class.getClassLoader());
diff --git a/web/gui/src/main/resources/core/css.html b/web/gui/src/main/resources/core/css.html
index ca58b8c..3ff410c 100644
--- a/web/gui/src/main/resources/core/css.html
+++ b/web/gui/src/main/resources/core/css.html
@@ -2,3 +2,4 @@
<link rel="stylesheet" href="app/view/topo/topo.css">
<link rel="stylesheet" href="app/view/device/device.css">
<link rel="stylesheet" href="app/view/host/host.css">
+<link rel="stylesheet" href="app/view/app/app.css">
diff --git a/web/gui/src/main/resources/core/js.html b/web/gui/src/main/resources/core/js.html
index 0c4cffc..c2ad984 100644
--- a/web/gui/src/main/resources/core/js.html
+++ b/web/gui/src/main/resources/core/js.html
@@ -13,4 +13,5 @@
<script src="app/view/topo/topoToolbar.js"></script>
<script src="app/view/device/device.js"></script>
<script src="app/view/host/host.js"></script>
+<script src="app/view/app/app.js"></script>
<script src="app/view/sample/sample.js"></script>
diff --git a/web/gui/src/main/webapp/app/common.css b/web/gui/src/main/webapp/app/common.css
index 43755d3..5c7d2a1 100644
--- a/web/gui/src/main/webapp/app/common.css
+++ b/web/gui/src/main/webapp/app/common.css
@@ -43,10 +43,21 @@
background-color: #444;
}
-table.summary-list th {
- padding: 10px;
- letter-spacing: 0.02em;
+.light table.summary-list tr.selected {
+ background-color: deepskyblue;
+}
+
+.dark table.summary-list tr.selected {
+ background-color: #304860;
+}
+
+table.summary-list td,th {
+ padding: 6px 6px 6px 6px;
text-align: left;
+}
+
+table.summary-list th {
+ letter-spacing: 0.02em;
cursor: pointer;
}
table.summary-list th:first-child {
@@ -55,6 +66,7 @@
table.summary-list th:last-child {
border-radius: 0 8px 0 0;
}
+
.light table.summary-list th {
background-color: #bbb;
}
@@ -63,11 +75,6 @@
color: #ccc;
}
-table.summary-list td {
- padding: 6px 10px 6px 10px;
- text-align: left;
-}
-
.dark table.summary-list td {
color: #ccc;
}
diff --git a/web/gui/src/main/webapp/app/fw/svg/icon.css b/web/gui/src/main/webapp/app/fw/svg/icon.css
index 7f3e8f4..6d83da2 100644
--- a/web/gui/src/main/webapp/app/fw/svg/icon.css
+++ b/web/gui/src/main/webapp/app/fw/svg/icon.css
@@ -28,6 +28,29 @@
fill-rule: evenodd;
}
+/*
+ FIXME: The following should be consolidated to result in much less CSS and
+ not to require entries for every icon.
+ */
+
+.light svg.embeddedIcon .icon.appActive,
+.light svg.embeddedIcon .icon.appInactive {
+ fill: none;
+}
+
+.dark svg.embeddedIcon .icon.appActive,
+.dark svg.embeddedIcon .icon.appInactive {
+ fill: none;
+}
+
+.light svg.embeddedIcon .icon.appActive .glyph {
+ fill: green;
+}
+.dark svg.embeddedIcon .icon.appActive .glyph {
+ fill: #266610;
+}
+
+
.light svg.embeddedIcon .icon.deviceOnline,
.light svg.embeddedIcon .icon.deviceOffline {
fill: none;
@@ -73,6 +96,10 @@
fill: #ccc;
}
+.light svg.embeddedIcon .icon.appActive rect,
+.light svg.embeddedIcon .icon.appInactive rect,
+.dark svg.embeddedIcon .icon.appActive rect,
+.dark svg.embeddedIcon .icon.appInactive rect,
.light svg.embeddedIcon .icon.deviceOnline rect,
.light svg.embeddedIcon .icon.deviceOffline rect,
.dark svg.embeddedIcon .icon.deviceOnline rect,
diff --git a/web/gui/src/main/webapp/app/fw/svg/icon.js b/web/gui/src/main/webapp/app/fw/svg/icon.js
index 400b7aa..8a8b35f 100644
--- a/web/gui/src/main/webapp/app/fw/svg/icon.js
+++ b/web/gui/src/main/webapp/app/fw/svg/icon.js
@@ -29,6 +29,9 @@
// Maps icon ID to the glyph ID it uses.
// NOTE: icon ID maps to a CSS class for styling that icon
var glyphMapping = {
+ appActive: 'checkMark',
+ appInactive: 'unknown',
+
deviceOnline: 'checkMark',
deviceOffline: 'xMark',
devIcon_SWITCH: 'switch',
diff --git a/web/gui/src/main/webapp/app/fw/widget/table.js b/web/gui/src/main/webapp/app/fw/widget/table.js
index 2fc3277..285d21d 100644
--- a/web/gui/src/main/webapp/app/fw/widget/table.js
+++ b/web/gui/src/main/webapp/app/fw/widget/table.js
@@ -29,19 +29,31 @@
// Functions for creating a fixed header on a table (Angular Directive)
function setTableWidth(t) {
- var tHeaders, tdElement, colWidth, numCols,
+ var tHeaders, tdElement, colWidth, numIcons, numNonIcons,
winWidth = fs.windowSize().width;
tHeaders = t.selectAll('th');
- numCols = tHeaders[0].length;
- colWidth = Math.floor(winWidth / numCols);
+ numIcons = 0;
+ numNonIcons = 0;
+
+ // FIXME: This should observe custom-set width from the HTML
tHeaders.each(function(thElement, index) {
thElement = d3.select(this);
+ if (thElement.classed('table-icon')) {
+ numIcons = numIcons + 1;
+ } else {
+ numNonIcons = numNonIcons + 1;
+ }
+ });
+ colWidth = Math.floor((winWidth - (numIcons * tableIconTdSize)) / numNonIcons);
+
+ tHeaders.each(function(thElement, index) {
+ thElement = d3.select(this);
tdElement = t.select('td:nth-of-type(' + (index + 1) + ')');
- if (tdElement.classed('table-icon')) {
+ if (thElement.classed('table-icon')) {
thElement.style('width', tableIconTdSize + 'px');
tdElement.style('width', tableIconTdSize + 'px');
} else {
diff --git a/web/gui/src/main/webapp/app/view/app/app.css b/web/gui/src/main/webapp/app/view/app/app.css
new file mode 100644
index 0000000..b30f488
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/app/app.css
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.
+ */
+
+/*
+ ONOS GUI -- Host View -- CSS file
+ */
+
+#ov-app td {
+}
diff --git a/web/gui/src/main/webapp/app/view/app/app.html b/web/gui/src/main/webapp/app/view/app/app.html
new file mode 100644
index 0000000..c219eb8
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/app/app.html
@@ -0,0 +1,34 @@
+<!-- app partial HTML -->
+<div id="ov-app">
+ <h2>Applications ({{ctrl.appData.length}} total)</h2>
+ <table class="summary-list"
+ onos-fixed-header
+ onos-sortable-header
+ sort-callback="sortCallback(requestParams)">
+ <thead>
+ <tr>
+ <th colId="state" class="table-icon" sortable></th>
+ <th colId="id" sortable>App ID </th>
+ <th colId="version" sortable>Version</th>
+ <th colId="origin" sortable>Origin </th>
+ <th colId="desc">Description </th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <tr ng-repeat="app in ctrl.appData"
+ ng-click="setSelected(app.id)"
+ ng-class="{selected: app.id === selectedAppId}"
+ ng-repeat-done>
+ <td class="table-icon">
+ <div icon icon-id="{{app._iconid_state}}"></div>
+ </td>
+ <td>{{app.id}}</td>
+ <td>{{app.version}}</td>
+ <td>{{app.origin}}</td>
+ <td>{{app.desc}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
diff --git a/web/gui/src/main/webapp/app/view/app/app.js b/web/gui/src/main/webapp/app/view/app/app.js
new file mode 100644
index 0000000..d2bab39
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/app/app.js
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed 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.
+ */
+
+/*
+ ONOS GUI -- App View Module
+ */
+
+(function () {
+ 'use strict';
+
+ angular.module('ovApp', [])
+ .controller('OvAppCtrl',
+ ['$log', '$scope', '$location', 'FnService', 'WebSocketService',
+
+ function ($log, $scope, $location, fs, wss) {
+ var self = this;
+ self.appData = [];
+
+ $scope.responseCallback = function(data) {
+ self.appData = data.applications;
+ $scope.$apply();
+ };
+
+ $scope.sortCallback = function (requestParams) {
+ wss.sendEvent('appDataRequest', requestParams);
+ };
+
+ $scope.selectedAppId = null;
+ $scope.setSelected = function (appId) {
+ $scope.selectedAppId = appId;
+ };
+
+ var handlers = {
+ appDataResponse: $scope.responseCallback
+ };
+ wss.bindHandlers(handlers);
+
+ // Cleanup on destroyed scope
+ $scope.$on('$destroy', function () {
+ wss.unbindHandlers(handlers);
+ });
+
+ $scope.sortCallback();
+
+ $log.log('OvAppCtrl has been created');
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/device/device.css b/web/gui/src/main/webapp/app/view/device/device.css
index bfc1ebc..53cdabe 100644
--- a/web/gui/src/main/webapp/app/view/device/device.css
+++ b/web/gui/src/main/webapp/app/view/device/device.css
@@ -18,6 +18,5 @@
ONOS GUI -- Device View -- CSS file
*/
-#ov-device {
- /* placeholder */
-}
\ No newline at end of file
+#ov-device td {
+}
diff --git a/web/gui/src/main/webapp/app/view/device/device.html b/web/gui/src/main/webapp/app/view/device/device.html
index c37f295..1db7900 100644
--- a/web/gui/src/main/webapp/app/view/device/device.html
+++ b/web/gui/src/main/webapp/app/view/device/device.html
@@ -7,8 +7,8 @@
sort-callback="sortCallback(requestParams)">
<thead>
<tr>
- <th colId="available"></th>
- <th colId="type"></th>
+ <th colId="available" class="table-icon" sortable></th>
+ <th colId="type" class="table-icon" sortable></th>
<th colId="id" sortable>Device ID </th>
<th colId="mfr" sortable>Vendor </th>
<th colId="hw" sortable>H/W Version </th>
diff --git a/web/gui/src/main/webapp/app/view/host/host.css b/web/gui/src/main/webapp/app/view/host/host.css
index 708dc23..94815f5 100644
--- a/web/gui/src/main/webapp/app/view/host/host.css
+++ b/web/gui/src/main/webapp/app/view/host/host.css
@@ -19,5 +19,5 @@
*/
#ov-host td {
- padding: 12px 10px;
+ height: 20px;
}
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 89c5aff..ef45356 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -101,6 +101,7 @@
<script src="app/view/topo/topoToolbar.js"></script>
<script src="app/view/device/device.js"></script>
<script src="app/view/host/host.js"></script>
+ <script src="app/view/app/app.js"></script>
<script src="app/view/sample/sample.js"></script>
<!-- {INJECTED-JAVASCRIPT-END} -->
@@ -110,6 +111,7 @@
<link rel="stylesheet" href="app/view/topo/topo.css">
<link rel="stylesheet" href="app/view/device/device.css">
<link rel="stylesheet" href="app/view/host/host.css">
+ <link rel="stylesheet" href="app/view/app/app.css">
<link rel="stylesheet" href="app/view/sample/sample.css">
<!-- {INJECTED-STYLESHEETS-END} -->
diff --git a/web/gui/src/main/webapp/onos.js b/web/gui/src/main/webapp/onos.js
index d63d6d9..2822cf6 100644
--- a/web/gui/src/main/webapp/onos.js
+++ b/web/gui/src/main/webapp/onos.js
@@ -39,6 +39,7 @@
'topo',
'device',
'host',
+ 'app',
'sample',
// {INJECTED-VIEW-IDS-END}