Added actions to the Apps view of web/gui2

Change-Id: I3d96a324590bee4de0875d4f533cc723c7f6ba52
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
index 79fa310..ffd7b37 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.ts
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015-present Open Networking Foundation
+ * Copyright 2018-present Open Networking Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the 'License');
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';
+
 import { DialogService } from '../../../fw/layer/dialog.service';
 import { FnService } from '../../../fw/util/fn.service';
 import { IconService } from '../../../fw/svg/icon.service';
@@ -28,14 +30,13 @@
 
 const INSTALLED = 'INSTALLED';
 const ACTIVE = 'ACTIVE';
-const appMgmtReq = 'appManagementRequest';
-const topPdg = 60;
-const panelWidth = 540;
-const pName = 'application-details-panel';
-const detailsReq = 'appDetailsRequest';
-const detailsResp = 'appDetailsResponse';
-const fileUploadUrl = 'applications/upload';
-const activateOption = '?activate=true';
+const APPMGMTREQ = 'appManagementRequest';
+const DETAILSREQ = 'appDetailsRequest';
+const FILEUPLOADURL = 'upload';
+const FILEDOWNLOADURL = 'download';
+const ACTIVATEOPTION = '?activate=true';
+const DRAGDROPMSG1 = 'Drag and drop one file at a time';
+const DRAGDROPMSGEXT = 'Only files ending in .oar can be dropped';
 
 /** Prefix to access the REST service for applications */
 export const APPURLPREFIX = '../../ui/rs/applications/'; // TODO: This is a hack to work off GUIv1 URL
@@ -81,6 +82,13 @@
     _iconid_state: string;
 }
 
+export enum AppAction {
+    NONE = 0,
+    ACTIVATE = 1,
+    DEACTIVATE = 2,
+    UNINSTALL = 3,
+}
+
 /**
  * Model of the Control Button
  */
@@ -91,7 +99,7 @@
 }
 
 /**
- * ONOS GUI -- Apps View Component
+ * ONOS GUI -- Apps View Component extends TableBaseImpl
  */
 @Component({
   selector: 'onos-apps',
@@ -108,15 +116,18 @@
     warnDeactivate: string;
     warnOwnRisk: string;
     ctrlBtnState: CtrlBtnState;
-    detailsPanel: any;
     appFile: any;
-    activateImmediately = '';
 
     uploadTip: string;
     activateTip: string;
     deactivateTip: string;
     uninstallTip: string;
     downloadTip: string;
+    alertMsg: string;
+    AppActionEnum: any = AppAction;
+    appAction: AppAction = AppAction.NONE;
+    confirmMsg: string = '';
+    strongWarning: string = '';
 
     constructor(
         protected fs: FnService,
@@ -129,9 +140,11 @@
         private ufs: UrlFnService,
         protected wss: WebSocketService,
         @Inject('Window') private window: Window,
+        private httpClient: HttpClient
     ) {
         super(fs, null, log, wss, 'app');
         this.responseCallback = this.appResponseCb;
+        this.parentSelCb =  this.rowSelection;
         // pre-populate sort so active apps are at the top of the list
         this.sortParams = {
             firstCol: 'state',
@@ -180,78 +193,100 @@
         this.log.debug('App response received for ', data.apps.length, 'apps');
     }
 
-    refreshCtrls() {
-        let row;
-        let rowIdx;
-        if (this.ctrlBtnState.selection) {
-            rowIdx = this.fs.find(this.selId, this.tableData);
-            row = rowIdx >= 0 ? this.tableData[rowIdx] : null;
+    /**
+     * called when a row is selected - sets the state of control icons
+     */
+    rowSelection(event: any, selRow: any) {
+        this.ctrlBtnState.installed = this.selId && selRow && selRow.state === INSTALLED;
+        this.ctrlBtnState.active = this.selId && selRow && selRow.state === ACTIVE;
+        this.ctrlBtnState.selection = this.selId;
+        this.log.debug('Row ', this.selId, 'selected', this.ctrlBtnState);
+    }
 
-            this.ctrlBtnState.installed = row && row.state === INSTALLED;
-            this.ctrlBtnState.active = row && row.state === ACTIVE;
-        } else {
-            this.ctrlBtnState.installed = false;
-            this.ctrlBtnState.active = false;
+
+    /**
+     * Perform one of the app actions - activate, deactivate or uninstall
+     * Raises a dialog which calls back the dOk() below
+     */
+    confirmAction(action: AppAction): void {
+        this.appAction = action;
+        const appActionLc = (<string>AppAction[this.appAction]).toLowerCase();
+
+        this.confirmMsg = this.lionFn(appActionLc) + ' ' + this.selId;
+        if (strongWarning[this.selId]) {
+            this.strongWarning = this.warnDeactivate + '\n' + this.warnOwnRisk;
         }
+
+        this.log.debug('Initiating', this.appAction, 'of', this.selId);
     }
 
-    createConfirmationText(action, itemId) {
-//        let content = this.ds.createDiv();
-//        content.append('p').text(this.lionFn(action) + ' ' + itemId);
-//        if (strongWarning[itemId]) {
-//            content.append('p').html(
-//                this.fs.sanitize(this.warnDeactivate) +
-//                '<br>' +
-//                this.fs.sanitize(this.warnOwnRisk)
-//            ).classed('strong', true);
-//        }
-//        return content;
-    }
+    /**
+     * Callback when the Confirm dialog is shown and a choice is made
+     */
+    dOk(choice: boolean) {
+        const appActionLc = (<string>AppAction[this.appAction]).toLowerCase();
+        if (choice) {
+            this.log.debug('Confirmed', appActionLc, 'on', this.selId);
 
-    confirmAction(action): void {
-        const itemId = this.selId;
-        const spar = this.sortParams;
-
-        function dOk() {
-            this.log.debug('Initiating', action, 'of', itemId);
-            this.wss.sendEvent(appMgmtReq, {
-                action: action,
-                name: itemId,
-                sortCol: spar.firstCol,
-                sortDir: spar.firstDir,
+            this.wss.sendEvent(APPMGMTREQ, {
+                action: appActionLc,
+                name: this.selId,
+                sortCol: this.sortParams.firstCol,
+                sortDir: SortDir[this.sortParams.firstDir],
             });
-            if (action === 'uninstall') {
-                this.detailsPanel.hide();
+            if (this.appAction === AppAction.UNINSTALL) {
+                this.selId = '';
             } else {
-                this.wss.sendEvent(detailsReq, { id: itemId });
+                this.wss.sendEvent(DETAILSREQ, { id: this.selId });
             }
-        }
 
-        function dCancel() {
-            this.log.debug('Canceling', action, 'of', itemId);
+        } else {
+            this.log.debug('Cancelled', appActionLc, 'on', this.selId);
         }
-
-//        this.ds.openDialog(dialogId, dialogOpts)
-//            .setTitle(this.lionFn('dlg_confirm_action'))
-//            .addContent(this.createConfirmationText(action, itemId))
-//            .addOk(dOk)
-//            .addCancel(dCancel)
-//            .bindKeys();
-    }
-
-    appAction(action) {
-        if (this.ctrlBtnState.selection) {
-            this.confirmAction(action);
-        }
+        this.confirmMsg = '';
+        this.strongWarning = '';
     }
 
     downloadApp() {
         if (this.ctrlBtnState.selection) {
-            (<any>this.window).location = APPURLPREFIX + this.selId + ICONURLSUFFIX;
+            (<any>this.window).location = APPURLPREFIX + this.selId + '/' + FILEDOWNLOADURL;
         }
     }
 
     /**
+     * When the file is selected this fires
+     * It passes the file on to the server through a POST request
+     * If there is an error its logged and raised to the user through Flash Component
+     */
+    fileEvent(event: any, activateImmediately?: boolean) {
+        this.log.debug('File event for', event.target.files[0]);
+        const formData = new FormData();
+        formData.append('file', event.target.files[0]);
+        let url = APPURLPREFIX + FILEUPLOADURL;
+        if (activateImmediately) {
+            url += ACTIVATEOPTION;
+        }
+        this.httpClient
+            .post<any>(APPURLPREFIX + FILEUPLOADURL, formData)
+            .subscribe(
+                data => this.log.debug(data),
+                err => {
+                    this.log.warn(err.message);
+                    this.alertMsg = err.message; // This will activate flash msg
+                }
+            );
+
+    }
+
+    /**
+     * When the upload button is clicked pass this on to the file input (hidden)
+     */
+    triggerForm() {
+        document.getElementById('uploadFile')
+                .dispatchEvent(new MouseEvent('click'));
+    }
+
+    /**
      * Read the LION bundle for App and set up the lionFn
      */
     doLion() {
@@ -267,9 +302,39 @@
         this.downloadTip = this.lionFn('tt_ctl_download');
     }
 
-    appDropped() {
-        this.activateImmediately = activateOption;
-//        $scope.$emit('FileChanged'); // TODO: Implement this
-        this.appFile = null;
+    onDrop(event: DragEvent) {
+        event.preventDefault();
+        event.stopPropagation();
+
+        const dt = event.dataTransfer;
+        const droppedFiles = dt.files;
+
+        this.log.debug(droppedFiles.length, 'File(s) dropped');
+        if (droppedFiles.length !== 1) {
+            this.log.error(DRAGDROPMSG1, droppedFiles.length, 'were dropped');
+            this.alertMsg = DRAGDROPMSG1;
+            return;
+        } else if (droppedFiles[0].name.slice(droppedFiles[0].name.length - 4) !== '.oar') {
+            this.log.error(DRAGDROPMSGEXT, droppedFiles[0].name, 'rejected');
+            this.alertMsg = DRAGDROPMSGEXT;
+            return;
+        }
+
+        const fileEvent = {
+            target: {
+                files: droppedFiles
+            }
+        };
+        this.fileEvent(fileEvent, true);
+    }
+
+    onDragOver(evt) {
+        evt.preventDefault();
+        evt.stopPropagation();
+    }
+
+    onDragLeave(evt) {
+        evt.preventDefault();
+        evt.stopPropagation();
     }
 }