Committed by
Gerrit Code Review
Adding ability to drop OAR files to install apps.
Change-Id: I989a92db4c94ef86d029d6b36f769f28e4fee52d
Showing
5 changed files
with
101 additions
and
24 deletions
| ... | @@ -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 | /** |
| ... | @@ -113,15 +114,15 @@ public class ApplicationViewMessageHandler extends UiMessageHandler { | ... | @@ -113,15 +114,15 @@ public class ApplicationViewMessageHandler extends UiMessageHandler { |
| 113 | String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE; | 114 | String iconId = state == ACTIVE ? ICON_ID_ACTIVE : ICON_ID_INACTIVE; |
| 114 | 115 | ||
| 115 | row.cell(STATE, state) | 116 | row.cell(STATE, state) |
| 116 | - .cell(STATE_IID, iconId) | 117 | + .cell(STATE_IID, iconId) |
| 117 | - .cell(ID, id.name()) | 118 | + .cell(ID, id.name()) |
| 118 | - .cell(ICON, id.name()) | 119 | + .cell(ICON, id.name()) |
| 119 | - .cell(VERSION, app.version()) | 120 | + .cell(VERSION, app.version()) |
| 120 | - .cell(CATEGORY, app.category()) | 121 | + .cell(CATEGORY, app.category()) |
| 121 | - .cell(ORIGIN, app.origin()) | 122 | + .cell(ORIGIN, app.origin()) |
| 122 | - .cell(TITLE, app.title()) | 123 | + .cell(TITLE, app.title()) |
| 123 | - .cell(DESC, app.description()) | 124 | + .cell(DESC, app.description()) |
| 124 | - .cell(URL, app.url()); | 125 | + .cell(URL, app.url()); |
| 125 | } | 126 | } |
| 126 | } | 127 | } |
| 127 | 128 | ||
| ... | @@ -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 | } | ... | ... |
| ... | @@ -172,4 +172,4 @@ | ... | @@ -172,4 +172,4 @@ |
| 172 | } | 172 | } |
| 173 | .dark #application-details-panel .bottom tr:nth-child(even) { | 173 | .dark #application-details-panel .bottom tr:nth-child(even) { |
| 174 | background-color: #555; | 174 | background-color: #555; |
| 175 | -} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 175 | +} | ... | ... |
| 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"> | ... | ... |
| ... | @@ -25,13 +25,14 @@ | ... | @@ -25,13 +25,14 @@ |
| 25 | var $log, $scope, wss, fs, ks, ps, is; | 25 | var $log, $scope, wss, fs, ks, ps, is; |
| 26 | 26 | ||
| 27 | // internal state | 27 | // internal state |
| 28 | - var detailsPanel, | 28 | + var detailsPanel, |
| 29 | - pStartY, | 29 | + pStartY, |
| 30 | - pHeight, | 30 | + pHeight, |
| 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) { | ... | ... |
-
Please register or login to post a comment