blob: 79fa3108146b5745e202037bc705cb09d9f12b9d [file] [log] [blame]
Sean Condon83fc39f2018-04-19 18:56:13 +01001/*
2 * Copyright 2015-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the 'License');
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an 'AS IS' BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Sean Condona00bf382018-06-23 07:54:01 +010016import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
Sean Condon28ecc5f2018-06-25 12:50:16 +010017import { DialogService } from '../../../fw/layer/dialog.service';
18import { FnService } from '../../../fw/util/fn.service';
19import { IconService } from '../../../fw/svg/icon.service';
20import { KeyService } from '../../../fw/util/key.service';
21import { LionService } from '../../../fw/util/lion.service';
22import { LoadingService } from '../../../fw/layer/loading.service';
23import { LogService } from '../../../log.service';
24import { TableBaseImpl, TableResponse, TableFilter, SortParams, SortDir } from '../../../fw/widget/table.base';
25import { UrlFnService } from '../../../fw/remote/urlfn.service';
26import { WebSocketService } from '../../../fw/remote/websocket.service';
27import { TableFilterPipe } from '../../../fw/widget/tablefilter.pipe';
Sean Condon83fc39f2018-04-19 18:56:13 +010028
Sean Condon2bd11b72018-06-15 08:00:48 +010029const INSTALLED = 'INSTALLED';
30const ACTIVE = 'ACTIVE';
31const appMgmtReq = 'appManagementRequest';
32const topPdg = 60;
33const panelWidth = 540;
34const pName = 'application-details-panel';
35const detailsReq = 'appDetailsRequest';
36const detailsResp = 'appDetailsResponse';
37const fileUploadUrl = 'applications/upload';
38const activateOption = '?activate=true';
Sean Condon28ecc5f2018-06-25 12:50:16 +010039
40/** Prefix to access the REST service for applications */
41export const APPURLPREFIX = '../../ui/rs/applications/'; // TODO: This is a hack to work off GUIv1 URL
42/** Suffix to access the icon of the application - gives back an image */
43export const ICONURLSUFFIX = '/icon';
44
Sean Condon2bd11b72018-06-15 08:00:48 +010045const downloadSuffix = '/download';
46const dialogId = 'app-dialog';
47const dialogOpts = {
48 edge: 'right',
49 width: 400,
50};
51const strongWarning = {
52 'org.onosproject.drivers': true,
53};
54const propOrder = ['id', 'state', 'category', 'version', 'origin', 'role'];
55
Sean Condon28ecc5f2018-06-25 12:50:16 +010056/**
57 * Model of the data returned through the Web Socket about apps.
58 */
Sean Condon2bd11b72018-06-15 08:00:48 +010059interface AppTableResponse extends TableResponse {
Sean Condon28ecc5f2018-06-25 12:50:16 +010060 apps: App[];
Sean Condon2bd11b72018-06-15 08:00:48 +010061}
62
Sean Condon28ecc5f2018-06-25 12:50:16 +010063/**
64 * Model of the data returned through Web Socket for a single App
65 */
66export interface App {
Sean Condon2bd11b72018-06-15 08:00:48 +010067 category: string;
68 desc: string;
Sean Condon28ecc5f2018-06-25 12:50:16 +010069 features: string[];
Sean Condon2bd11b72018-06-15 08:00:48 +010070 icon: string;
71 id: string;
72 origin: string;
Sean Condon28ecc5f2018-06-25 12:50:16 +010073 permissions: string[];
Sean Condon2bd11b72018-06-15 08:00:48 +010074 readme: string;
Sean Condon28ecc5f2018-06-25 12:50:16 +010075 required_apps: string[];
Sean Condon2bd11b72018-06-15 08:00:48 +010076 role: string;
77 state: string;
78 title: string;
79 url: string;
80 version: string;
81 _iconid_state: string;
82}
83
Sean Condon28ecc5f2018-06-25 12:50:16 +010084/**
85 * Model of the Control Button
86 */
Sean Condon2bd11b72018-06-15 08:00:48 +010087interface CtrlBtnState {
88 installed: boolean;
89 selection: string;
90 active: boolean;
91}
92
Sean Condon83fc39f2018-04-19 18:56:13 +010093/**
94 * ONOS GUI -- Apps View Component
95 */
96@Component({
97 selector: 'onos-apps',
98 templateUrl: './apps.component.html',
Sean Condon2bd11b72018-06-15 08:00:48 +010099 styleUrls: [
100 './apps.component.css', './apps.theme.css',
Sean Condon28ecc5f2018-06-25 12:50:16 +0100101 '../../../fw/widget/table.css', '../../../fw/widget/table.theme.css'
Sean Condon2bd11b72018-06-15 08:00:48 +0100102 ]
Sean Condon83fc39f2018-04-19 18:56:13 +0100103})
Sean Condon2bd11b72018-06-15 08:00:48 +0100104export class AppsComponent extends TableBaseImpl implements OnInit, OnDestroy {
105
106 // deferred localization strings
107 lionFn; // Function
108 warnDeactivate: string;
109 warnOwnRisk: string;
Sean Condon2bd11b72018-06-15 08:00:48 +0100110 ctrlBtnState: CtrlBtnState;
111 detailsPanel: any;
Sean Condona00bf382018-06-23 07:54:01 +0100112 appFile: any;
113 activateImmediately = '';
114
115 uploadTip: string;
116 activateTip: string;
117 deactivateTip: string;
118 uninstallTip: string;
119 downloadTip: string;
Sean Condon83fc39f2018-04-19 18:56:13 +0100120
121 constructor(
Sean Condon2bd11b72018-06-15 08:00:48 +0100122 protected fs: FnService,
Sean Condon83fc39f2018-04-19 18:56:13 +0100123 private ds: DialogService,
124 private is: IconService,
125 private ks: KeyService,
Sean Condon2bd11b72018-06-15 08:00:48 +0100126 private lion: LionService,
127 protected ls: LoadingService,
128 protected log: LogService,
Sean Condon83fc39f2018-04-19 18:56:13 +0100129 private ufs: UrlFnService,
Sean Condon2bd11b72018-06-15 08:00:48 +0100130 protected wss: WebSocketService,
Sean Condona00bf382018-06-23 07:54:01 +0100131 @Inject('Window') private window: Window,
Sean Condon83fc39f2018-04-19 18:56:13 +0100132 ) {
Sean Condon2bd11b72018-06-15 08:00:48 +0100133 super(fs, null, log, wss, 'app');
134 this.responseCallback = this.appResponseCb;
Sean Condon28ecc5f2018-06-25 12:50:16 +0100135 // pre-populate sort so active apps are at the top of the list
Sean Condon2bd11b72018-06-15 08:00:48 +0100136 this.sortParams = {
137 firstCol: 'state',
Sean Condon28ecc5f2018-06-25 12:50:16 +0100138 firstDir: SortDir.desc,
Sean Condon2bd11b72018-06-15 08:00:48 +0100139 secondCol: 'title',
Sean Condon28ecc5f2018-06-25 12:50:16 +0100140 secondDir: SortDir.asc,
Sean Condon2bd11b72018-06-15 08:00:48 +0100141 };
Sean Condon28ecc5f2018-06-25 12:50:16 +0100142 // We want doLion() to be called only after the Lion
143 // service is populated (from the WebSocket)
144 // If lion is not ready we make do with a dummy function
145 // As soon a lion gets loaded this function will be replaced with
146 // the real thing
147 if (this.lion.ubercache.length === 0) {
148 this.lionFn = this.dummyLion;
149 this.lion.loadCbs.set('apps', () => this.doLion());
150 } else {
151 this.doLion();
152 }
153
Sean Condon2bd11b72018-06-15 08:00:48 +0100154 this.ctrlBtnState = <CtrlBtnState>{
155 installed: false,
156 active: false
157 };
Sean Condon83fc39f2018-04-19 18:56:13 +0100158 }
159
Sean Condon28ecc5f2018-06-25 12:50:16 +0100160 /**
161 * Initialize querying the WebSocket for App table details
162 */
Sean Condon83fc39f2018-04-19 18:56:13 +0100163 ngOnInit() {
Sean Condon2bd11b72018-06-15 08:00:48 +0100164 this.init();
Sean Condon83fc39f2018-04-19 18:56:13 +0100165 }
166
Sean Condon28ecc5f2018-06-25 12:50:16 +0100167 /**
168 * Stop sending queries to WebSocket
169 */
Sean Condon2bd11b72018-06-15 08:00:48 +0100170 ngOnDestroy() {
Sean Condon28ecc5f2018-06-25 12:50:16 +0100171 this.lion.loadCbs.delete('apps');
Sean Condon2bd11b72018-06-15 08:00:48 +0100172 this.destroy();
173 this.log.debug('AppComponent destroyed');
174 }
175
176 /**
177 * The callback called when App data returns from WSS
178 */
179 appResponseCb(data: AppTableResponse) {
180 this.log.debug('App response received for ', data.apps.length, 'apps');
181 }
182
183 refreshCtrls() {
184 let row;
185 let rowIdx;
186 if (this.ctrlBtnState.selection) {
187 rowIdx = this.fs.find(this.selId, this.tableData);
188 row = rowIdx >= 0 ? this.tableData[rowIdx] : null;
189
190 this.ctrlBtnState.installed = row && row.state === INSTALLED;
191 this.ctrlBtnState.active = row && row.state === ACTIVE;
192 } else {
193 this.ctrlBtnState.installed = false;
194 this.ctrlBtnState.active = false;
195 }
196 }
197
198 createConfirmationText(action, itemId) {
199// let content = this.ds.createDiv();
200// content.append('p').text(this.lionFn(action) + ' ' + itemId);
201// if (strongWarning[itemId]) {
202// content.append('p').html(
203// this.fs.sanitize(this.warnDeactivate) +
204// '<br>' +
205// this.fs.sanitize(this.warnOwnRisk)
206// ).classed('strong', true);
207// }
208// return content;
209 }
210
211 confirmAction(action): void {
212 const itemId = this.selId;
213 const spar = this.sortParams;
214
215 function dOk() {
216 this.log.debug('Initiating', action, 'of', itemId);
217 this.wss.sendEvent(appMgmtReq, {
218 action: action,
219 name: itemId,
Sean Condon28ecc5f2018-06-25 12:50:16 +0100220 sortCol: spar.firstCol,
221 sortDir: spar.firstDir,
Sean Condon2bd11b72018-06-15 08:00:48 +0100222 });
223 if (action === 'uninstall') {
224 this.detailsPanel.hide();
225 } else {
226 this.wss.sendEvent(detailsReq, { id: itemId });
227 }
228 }
229
230 function dCancel() {
231 this.log.debug('Canceling', action, 'of', itemId);
232 }
233
234// this.ds.openDialog(dialogId, dialogOpts)
235// .setTitle(this.lionFn('dlg_confirm_action'))
236// .addContent(this.createConfirmationText(action, itemId))
237// .addOk(dOk)
238// .addCancel(dCancel)
239// .bindKeys();
240 }
241
242 appAction(action) {
243 if (this.ctrlBtnState.selection) {
244 this.confirmAction(action);
245 }
246 }
247
248 downloadApp() {
249 if (this.ctrlBtnState.selection) {
Sean Condon28ecc5f2018-06-25 12:50:16 +0100250 (<any>this.window).location = APPURLPREFIX + this.selId + ICONURLSUFFIX;
Sean Condon2bd11b72018-06-15 08:00:48 +0100251 }
252 }
253
254 /**
Sean Condon28ecc5f2018-06-25 12:50:16 +0100255 * Read the LION bundle for App and set up the lionFn
Sean Condon2bd11b72018-06-15 08:00:48 +0100256 */
257 doLion() {
258 this.lionFn = this.lion.bundle('core.view.App');
259
260 this.warnDeactivate = this.lionFn('dlg_warn_deactivate');
261 this.warnOwnRisk = this.lionFn('dlg_warn_own_risk');
262
Sean Condon28ecc5f2018-06-25 12:50:16 +0100263 this.uploadTip = this.lionFn('tt_ctl_upload');
264 this.activateTip = this.lionFn('tt_ctl_activate');
265 this.deactivateTip = this.lionFn('tt_ctl_deactivate');
266 this.uninstallTip = this.lionFn('tt_ctl_uninstall');
267 this.downloadTip = this.lionFn('tt_ctl_download');
Sean Condon2bd11b72018-06-15 08:00:48 +0100268 }
Sean Condona00bf382018-06-23 07:54:01 +0100269
270 appDropped() {
271 this.activateImmediately = activateOption;
272// $scope.$emit('FileChanged'); // TODO: Implement this
273 this.appFile = null;
274 }
Sean Condon83fc39f2018-04-19 18:56:13 +0100275}