Thomas Vachuska
Committed by Gerrit Code Review

Adding ability to drop OAR files to install apps.

Change-Id: I989a92db4c94ef86d029d6b36f769f28e4fee52d
......@@ -22,15 +22,18 @@ import org.onosproject.core.Application;
import org.onosproject.core.ApplicationId;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
/**
* Application upload resource.
......@@ -38,11 +41,20 @@ import java.io.InputStream;
@Path("applications")
public class ApplicationResource extends BaseResource {
static String lastInstalledAppName = null;
@Path("upload")
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response upload(@FormDataParam("file") InputStream stream) throws IOException {
get(ApplicationAdminService.class).install(stream);
public Response upload(@QueryParam("activate") @DefaultValue("false") String activate,
@FormDataParam("file") InputStream stream) throws IOException {
ApplicationAdminService service = get(ApplicationAdminService.class);
Application app = service.install(stream);
lastInstalledAppName = app.id().name();
if (Objects.equals(activate, "true")) {
service.activate(app.id());
}
return Response.ok().build();
}
......
......@@ -30,6 +30,7 @@ import org.onosproject.ui.table.TableRequestHandler;
import java.util.Collection;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onosproject.app.ApplicationState.ACTIVE;
/**
......@@ -113,15 +114,15 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE;
row.cell(STATE, state)
.cell(STATE_IID, iconId)
.cell(ID, id.name())
.cell(ICON, id.name())
.cell(VERSION, app.version())
.cell(CATEGORY, app.category())
.cell(ORIGIN, app.origin())
.cell(TITLE, app.title())
.cell(DESC, app.description())
.cell(URL, app.url());
.cell(STATE_IID, iconId)
.cell(ID, id.name())
.cell(ICON, id.name())
.cell(VERSION, app.version())
.cell(CATEGORY, app.category())
.cell(ORIGIN, app.origin())
.cell(TITLE, app.title())
.cell(DESC, app.description())
.cell(URL, app.url());
}
}
......@@ -160,6 +161,13 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
public void process(long sid, ObjectNode payload) {
String id = string(payload, ID);
ApplicationService as = get(ApplicationService.class);
// If the ID was not specified in the payload, use the name of the
// most recently uploaded app.
if (isNullOrEmpty(id)) {
id = ApplicationResource.lastInstalledAppName;
}
ApplicationId appId = as.getId(id);
ApplicationState state = as.getState(appId);
Application app = as.getApplication(appId);
......@@ -198,5 +206,6 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
rootNode.set(DETAILS, data);
sendMessage(APP_DETAILS_RESP, 0, rootNode);
}
}
}
......
......@@ -172,4 +172,4 @@
}
.dark #application-details-panel .bottom tr:nth-child(even) {
background-color: #555;
}
\ No newline at end of file
}
......
<!-- app partial HTML -->
<div id="ov-app">
<div id="ov-app" filedrop on-file-drop="appDropped()">
<div class="tabular-header">
<h2>Applications ({{tableData.length}} total)</h2>
<div class="ctrl-btns">
......@@ -14,6 +14,7 @@
type="file" size="50" accept=".oar"
file-model="appFile">
</form>
<div icon icon-size="36" icon-id="plus"
class="active" trigger-form
tooltip tt-msg="uploadTip">
......
......@@ -25,13 +25,14 @@
var $log, $scope, wss, fs, ks, ps, is;
// internal state
var detailsPanel,
pStartY,
pHeight,
top,
middle,
bottom,
wSize = false;
var detailsPanel,
pStartY,
pHeight,
top,
middle,
bottom,
wSize = false,
activateImmediately;
// constants
var INSTALLED = 'INSTALLED',
......@@ -43,6 +44,7 @@
detailsReq = 'appDetailsRequest',
detailsResp = 'appDetailsResponse',
fileUploadUrl = 'applications/upload',
activateOption = '?activate=true',
iconUrlPrefix = 'rs/applications/',
iconUrlSuffix = '/icon',
dialogId = 'app-dialog',
......@@ -200,16 +202,18 @@
function respDetailsCb(data) {
$scope.panelData = data.details;
$scope.selId = data.details.id;
$scope.ctrlBtnState.selection = data.details.id;
$scope.$apply();
}
angular.module('ovApp', [])
.controller('OvAppCtrl',
['$log', '$scope', '$http',
['$log', '$scope', '$http', '$timeout',
'WebSocketService', 'FnService', 'KeyService', 'PanelService',
'IconService', 'UrlFnService', 'DialogService', 'TableBuilderService',
function (_$log_, _$scope_, $http, _wss_, _fs_, _ks_, _ps_, _is_, ufs, ds, tbs) {
function (_$log_, _$scope_, $http, $timeout, _wss_, _fs_, _ks_, _ps_, _is_, ufs, ds, tbs) {
$log = _$log_;
$scope = _$scope_;
wss = _wss_;
......@@ -302,6 +306,11 @@
sortCol: spar.sortCol,
sortDir: spar.sortDir
});
if (action == 'uninstall') {
detailsPanel.hide();
} else {
wss.sendEvent(detailsReq, {id: itemId});
}
}
function dCancel() {
......@@ -323,22 +332,32 @@
};
$scope.$on('FileChanged', function () {
var formData = new FormData();
var formData = new FormData(),
url;
if ($scope.appFile) {
formData.append('file', $scope.appFile);
$http.post(ufs.rsUrl(fileUploadUrl), formData, {
url = fileUploadUrl + (activateImmediately || '');
$http.post(ufs.rsUrl(url), formData, {
transformRequest: angular.identity,
headers: {
'Content-Type': undefined
}
})
.finally(function () {
activateImmediately = null;
$scope.sortCallback($scope.sortParams);
document.getElementById('inputFileForm').reset();
$timeout(function () { wss.sendEvent(detailsReq); }, 250);
});
}
});
$scope.appDropped = function() {
activateImmediately = activateOption;
$scope.$emit('FileChanged');
$scope.appFile = null;
};
$scope.$on('$destroy', function () {
ks.unbindKeys();
wss.unbindHandlers(handlers);
......@@ -380,6 +399,42 @@
};
}])
.directive("filedrop", function ($parse, $document) {
return {
restrict: "A",
link: function (scope, element, attrs) {
var onAppDrop = $parse(attrs.onFileDrop);
// When an item is dragged over the document
var onDragOver = function (e) {
e.preventDefault();
};
// When the user leaves the window, cancels the drag or drops the item
var onDragEnd = function (e) {
e.preventDefault();
};
// When a file is dropped
var loadFile = function (file) {
scope.appFile = file;
scope.$apply(onAppDrop(scope));
};
// Dragging begins on the document
$document.bind("dragover", onDragOver);
// Dragging ends on the overlay, which takes the whole window
element.bind("dragleave", onDragEnd)
.bind("drop", function (e) {
$log.info('Drag leave', e);
loadFile(e.dataTransfer.files[0]);
onDragEnd(e);
});
}
};
})
.directive('applicationDetailsPanel',
['$rootScope', '$window', '$timeout', 'KeyService',
function ($rootScope, $window, $timeout, ks) {
......