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; ...@@ -22,15 +22,18 @@ import org.onosproject.core.Application;
22 import org.onosproject.core.ApplicationId; 22 import org.onosproject.core.ApplicationId;
23 23
24 import javax.ws.rs.Consumes; 24 import javax.ws.rs.Consumes;
25 +import javax.ws.rs.DefaultValue;
25 import javax.ws.rs.GET; 26 import javax.ws.rs.GET;
26 import javax.ws.rs.POST; 27 import javax.ws.rs.POST;
27 import javax.ws.rs.Path; 28 import javax.ws.rs.Path;
28 import javax.ws.rs.PathParam; 29 import javax.ws.rs.PathParam;
29 import javax.ws.rs.Produces; 30 import javax.ws.rs.Produces;
31 +import javax.ws.rs.QueryParam;
30 import javax.ws.rs.core.MediaType; 32 import javax.ws.rs.core.MediaType;
31 import javax.ws.rs.core.Response; 33 import javax.ws.rs.core.Response;
32 import java.io.IOException; 34 import java.io.IOException;
33 import java.io.InputStream; 35 import java.io.InputStream;
36 +import java.util.Objects;
34 37
35 /** 38 /**
36 * Application upload resource. 39 * Application upload resource.
...@@ -38,11 +41,20 @@ import java.io.InputStream; ...@@ -38,11 +41,20 @@ import java.io.InputStream;
38 @Path("applications") 41 @Path("applications")
39 public class ApplicationResource extends BaseResource { 42 public class ApplicationResource extends BaseResource {
40 43
44 + static String lastInstalledAppName = null;
45 +
46 +
41 @Path("upload") 47 @Path("upload")
42 @POST 48 @POST
43 @Consumes(MediaType.MULTIPART_FORM_DATA) 49 @Consumes(MediaType.MULTIPART_FORM_DATA)
44 - public Response upload(@FormDataParam("file") InputStream stream) throws IOException { 50 + public Response upload(@QueryParam("activate") @DefaultValue("false") String activate,
45 - get(ApplicationAdminService.class).install(stream); 51 + @FormDataParam("file") InputStream stream) throws IOException {
52 + ApplicationAdminService service = get(ApplicationAdminService.class);
53 + Application app = service.install(stream);
54 + lastInstalledAppName = app.id().name();
55 + if (Objects.equals(activate, "true")) {
56 + service.activate(app.id());
57 + }
46 return Response.ok().build(); 58 return Response.ok().build();
47 } 59 }
48 60
......
...@@ -30,6 +30,7 @@ import org.onosproject.ui.table.TableRequestHandler; ...@@ -30,6 +30,7 @@ import org.onosproject.ui.table.TableRequestHandler;
30 30
31 import java.util.Collection; 31 import java.util.Collection;
32 32
33 +import static com.google.common.base.Strings.isNullOrEmpty;
33 import static org.onosproject.app.ApplicationState.ACTIVE; 34 import static org.onosproject.app.ApplicationState.ACTIVE;
34 35
35 /** 36 /**
...@@ -160,6 +161,13 @@ public class ApplicationViewMessageHandler extends UiMessageHandler { ...@@ -160,6 +161,13 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
160 public void process(long sid, ObjectNode payload) { 161 public void process(long sid, ObjectNode payload) {
161 String id = string(payload, ID); 162 String id = string(payload, ID);
162 ApplicationService as = get(ApplicationService.class); 163 ApplicationService as = get(ApplicationService.class);
164 +
165 + // If the ID was not specified in the payload, use the name of the
166 + // most recently uploaded app.
167 + if (isNullOrEmpty(id)) {
168 + id = ApplicationResource.lastInstalledAppName;
169 + }
170 +
163 ApplicationId appId = as.getId(id); 171 ApplicationId appId = as.getId(id);
164 ApplicationState state = as.getState(appId); 172 ApplicationState state = as.getState(appId);
165 Application app = as.getApplication(appId); 173 Application app = as.getApplication(appId);
...@@ -198,5 +206,6 @@ public class ApplicationViewMessageHandler extends UiMessageHandler { ...@@ -198,5 +206,6 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
198 rootNode.set(DETAILS, data); 206 rootNode.set(DETAILS, data);
199 sendMessage(APP_DETAILS_RESP, 0, rootNode); 207 sendMessage(APP_DETAILS_RESP, 0, rootNode);
200 } 208 }
209 +
201 } 210 }
202 } 211 }
......
1 <!-- app partial HTML --> 1 <!-- app partial HTML -->
2 -<div id="ov-app"> 2 +<div id="ov-app" filedrop on-file-drop="appDropped()">
3 <div class="tabular-header"> 3 <div class="tabular-header">
4 <h2>Applications ({{tableData.length}} total)</h2> 4 <h2>Applications ({{tableData.length}} total)</h2>
5 <div class="ctrl-btns"> 5 <div class="ctrl-btns">
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
14 type="file" size="50" accept=".oar" 14 type="file" size="50" accept=".oar"
15 file-model="appFile"> 15 file-model="appFile">
16 </form> 16 </form>
17 +
17 <div icon icon-size="36" icon-id="plus" 18 <div icon icon-size="36" icon-id="plus"
18 class="active" trigger-form 19 class="active" trigger-form
19 tooltip tt-msg="uploadTip"> 20 tooltip tt-msg="uploadTip">
......
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
31 top, 31 top,
32 middle, 32 middle,
33 bottom, 33 bottom,
34 - wSize = false; 34 + wSize = false,
35 + activateImmediately;
35 36
36 // constants 37 // constants
37 var INSTALLED = 'INSTALLED', 38 var INSTALLED = 'INSTALLED',
...@@ -43,6 +44,7 @@ ...@@ -43,6 +44,7 @@
43 detailsReq = 'appDetailsRequest', 44 detailsReq = 'appDetailsRequest',
44 detailsResp = 'appDetailsResponse', 45 detailsResp = 'appDetailsResponse',
45 fileUploadUrl = 'applications/upload', 46 fileUploadUrl = 'applications/upload',
47 + activateOption = '?activate=true',
46 iconUrlPrefix = 'rs/applications/', 48 iconUrlPrefix = 'rs/applications/',
47 iconUrlSuffix = '/icon', 49 iconUrlSuffix = '/icon',
48 dialogId = 'app-dialog', 50 dialogId = 'app-dialog',
...@@ -200,16 +202,18 @@ ...@@ -200,16 +202,18 @@
200 202
201 function respDetailsCb(data) { 203 function respDetailsCb(data) {
202 $scope.panelData = data.details; 204 $scope.panelData = data.details;
205 + $scope.selId = data.details.id;
206 + $scope.ctrlBtnState.selection = data.details.id;
203 $scope.$apply(); 207 $scope.$apply();
204 } 208 }
205 209
206 angular.module('ovApp', []) 210 angular.module('ovApp', [])
207 .controller('OvAppCtrl', 211 .controller('OvAppCtrl',
208 - ['$log', '$scope', '$http', 212 + ['$log', '$scope', '$http', '$timeout',
209 'WebSocketService', 'FnService', 'KeyService', 'PanelService', 213 'WebSocketService', 'FnService', 'KeyService', 'PanelService',
210 'IconService', 'UrlFnService', 'DialogService', 'TableBuilderService', 214 'IconService', 'UrlFnService', 'DialogService', 'TableBuilderService',
211 215
212 - function (_$log_, _$scope_, $http, _wss_, _fs_, _ks_, _ps_, _is_, ufs, ds, tbs) { 216 + function (_$log_, _$scope_, $http, $timeout, _wss_, _fs_, _ks_, _ps_, _is_, ufs, ds, tbs) {
213 $log = _$log_; 217 $log = _$log_;
214 $scope = _$scope_; 218 $scope = _$scope_;
215 wss = _wss_; 219 wss = _wss_;
...@@ -302,6 +306,11 @@ ...@@ -302,6 +306,11 @@
302 sortCol: spar.sortCol, 306 sortCol: spar.sortCol,
303 sortDir: spar.sortDir 307 sortDir: spar.sortDir
304 }); 308 });
309 + if (action == 'uninstall') {
310 + detailsPanel.hide();
311 + } else {
312 + wss.sendEvent(detailsReq, {id: itemId});
313 + }
305 } 314 }
306 315
307 function dCancel() { 316 function dCancel() {
...@@ -323,22 +332,32 @@ ...@@ -323,22 +332,32 @@
323 }; 332 };
324 333
325 $scope.$on('FileChanged', function () { 334 $scope.$on('FileChanged', function () {
326 - var formData = new FormData(); 335 + var formData = new FormData(),
336 + url;
327 if ($scope.appFile) { 337 if ($scope.appFile) {
328 formData.append('file', $scope.appFile); 338 formData.append('file', $scope.appFile);
329 - $http.post(ufs.rsUrl(fileUploadUrl), formData, { 339 + url = fileUploadUrl + (activateImmediately || '');
340 + $http.post(ufs.rsUrl(url), formData, {
330 transformRequest: angular.identity, 341 transformRequest: angular.identity,
331 headers: { 342 headers: {
332 'Content-Type': undefined 343 'Content-Type': undefined
333 } 344 }
334 }) 345 })
335 .finally(function () { 346 .finally(function () {
347 + activateImmediately = null;
336 $scope.sortCallback($scope.sortParams); 348 $scope.sortCallback($scope.sortParams);
337 document.getElementById('inputFileForm').reset(); 349 document.getElementById('inputFileForm').reset();
350 + $timeout(function () { wss.sendEvent(detailsReq); }, 250);
338 }); 351 });
339 } 352 }
340 }); 353 });
341 354
355 + $scope.appDropped = function() {
356 + activateImmediately = activateOption;
357 + $scope.$emit('FileChanged');
358 + $scope.appFile = null;
359 + };
360 +
342 $scope.$on('$destroy', function () { 361 $scope.$on('$destroy', function () {
343 ks.unbindKeys(); 362 ks.unbindKeys();
344 wss.unbindHandlers(handlers); 363 wss.unbindHandlers(handlers);
...@@ -380,6 +399,42 @@ ...@@ -380,6 +399,42 @@
380 }; 399 };
381 }]) 400 }])
382 401
402 + .directive("filedrop", function ($parse, $document) {
403 + return {
404 + restrict: "A",
405 + link: function (scope, element, attrs) {
406 + var onAppDrop = $parse(attrs.onFileDrop);
407 +
408 + // When an item is dragged over the document
409 + var onDragOver = function (e) {
410 + e.preventDefault();
411 + };
412 +
413 + // When the user leaves the window, cancels the drag or drops the item
414 + var onDragEnd = function (e) {
415 + e.preventDefault();
416 + };
417 +
418 + // When a file is dropped
419 + var loadFile = function (file) {
420 + scope.appFile = file;
421 + scope.$apply(onAppDrop(scope));
422 + };
423 +
424 + // Dragging begins on the document
425 + $document.bind("dragover", onDragOver);
426 +
427 + // Dragging ends on the overlay, which takes the whole window
428 + element.bind("dragleave", onDragEnd)
429 + .bind("drop", function (e) {
430 + $log.info('Drag leave', e);
431 + loadFile(e.dataTransfer.files[0]);
432 + onDragEnd(e);
433 + });
434 + }
435 + };
436 + })
437 +
383 .directive('applicationDetailsPanel', 438 .directive('applicationDetailsPanel',
384 ['$rootScope', '$window', '$timeout', 'KeyService', 439 ['$rootScope', '$window', '$timeout', 'KeyService',
385 function ($rootScope, $window, $timeout, ks) { 440 function ($rootScope, $window, $timeout, ks) {
......