Added actions to the Apps view of web/gui2
Change-Id: I3d96a324590bee4de0875d4f533cc723c7f6ba52
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java
index d8f5a44..0826c52 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/ApplicationResource.java
@@ -51,14 +51,22 @@
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);
- synchronized (LAST_INSTALLED_APP_NAME_LOCK) {
- lastInstalledAppName = app.id().name();
+ try {
+ Application app = service.install(stream);
+ synchronized (LAST_INSTALLED_APP_NAME_LOCK) {
+ lastInstalledAppName = app.id().name();
+ }
+ if (Objects.equals(activate, "true")) {
+ service.activate(app.id());
+ }
+ return Response.ok().build();
+ } catch (IllegalStateException e) {
+ return Response.status(
+ Response.Status.PRECONDITION_FAILED.getStatusCode(),
+ e.getMessage()).build();
+ } catch (Exception e) {
+ throw e;
}
- if (Objects.equals(activate, "true")) {
- service.activate(app.id());
- }
- return Response.ok().build();
}
/**
diff --git a/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.css b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.css
new file mode 100644
index 0000000..80243b0
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.css
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Confirm Component (layout) -- CSS file
+ */
+#app-dialog {
+ top: 140px;
+ padding: 12px;
+}
+
+#app-dialog p {
+ font-size: 12pt;
+}
+
+#app-dialog p.strong {
+ font-weight: bold;
+ padding: 8px;
+ text-align: center;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.html b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.html
new file mode 100644
index 0000000..a3b76f4
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.html
@@ -0,0 +1,21 @@
+<!--
+~ 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.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<div id="app-dialog" class="floatpanel dialog" [@confirmDlgState]="message!==''">
+ <p>{{ message }}</p>
+ <p *ngIf="warning" class="warning strong">{{ warning }}</p>
+ <div tabindex="10" class="dialog-button" (click)="choice(true)">OK</div>
+ <div tabindex="11" class="dialog-button" (click)="choice(false)">Cancel</div>
+</div>
diff --git a/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.spec.ts b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.spec.ts
new file mode 100644
index 0000000..9a88dc9
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.spec.ts
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+
+import { ConsoleLoggerService } from '../../../consolelogger.service';
+import { LogService } from '../../../log.service';
+import { ConfirmComponent } from './confirm.component';
+
+/**
+ * ONOS GUI -- Layer -- Confirm Component - Unit Tests
+ */
+describe('ConfirmComponent', () => {
+ let log: LogService;
+ let component: ConfirmComponent;
+ let fixture: ComponentFixture<ConfirmComponent>;
+
+ beforeEach(async(() => {
+ log = new ConsoleLoggerService();
+ TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule ],
+ declarations: [ ConfirmComponent ],
+ providers: [
+ { provide: LogService, useValue: log },
+ ]
+ });
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ConfirmComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should have a div.dialog-button inside a div#app-dialog', () => {
+ const appDe: DebugElement = fixture.debugElement;
+ const divDe = appDe.query(By.css('div#app-dialog div.dialog-button'));
+ const div: HTMLElement = divDe.nativeElement;
+ // It selects the first one
+ expect(div.textContent).toEqual('OK');
+ });
+});
diff --git a/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.ts b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.ts
new file mode 100644
index 0000000..cd6aa7a
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.component.ts
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { Component, Input, Output, EventEmitter } from '@angular/core';
+import { trigger, state, style, animate, transition } from '@angular/animations';
+import { LogService } from '../../../log.service';
+
+/**
+ * ONOS GUI -- Layer -- Confirm Component
+ *
+ * Replaces Flash Service in old GUI.
+ * Provides a mechanism to present a confirm dialog to the screen
+ *
+ * To use add an element to the template like
+ * <onos-confirm message="Performing something dangerous. Would you like to proceed"></onos-flash>
+ *
+ * An event is raised with either OK or Cancel
+ */
+@Component({
+ selector: 'onos-confirm',
+ templateUrl: './confirm.component.html',
+ styleUrls: [
+ './confirm.component.css',
+ './confirm.theme.css',
+ '../dialog.css',
+ '../dialog.theme.css',
+ '../../widget/panel.css',
+ '../../widget/panel-theme.css'
+ ],
+ animations: [
+ trigger('confirmDlgState', [
+ state('true', style({
+ transform: 'translateX(-100%)',
+ opacity: '100'
+ })),
+ state('false', style({
+ transform: 'translateX(0%)',
+ opacity: '0'
+ })),
+ transition('0 => 1', animate('100ms ease-in')),
+ transition('1 => 0', animate('100ms ease-out'))
+ ])
+ ]
+})
+export class ConfirmComponent {
+ @Input() message: string;
+ @Input() warning: string;
+ @Output() chosen: EventEmitter<boolean> = new EventEmitter();
+
+ constructor(
+ private log: LogService,
+ ) {
+ this.log.debug('ConfirmComponent constructed');
+ }
+
+ /**
+ * When OK or Cancel is pressed, send an event to parent with choice
+ */
+ choice(chosen: boolean): void {
+ this.chosen.emit(chosen);
+ }
+}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.theme.css b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.theme.css
new file mode 100644
index 0000000..db97648
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/confirm/confirm.theme.css
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Confirm Component (theme) -- CSS file
+ */
+/* temporarily removed .light */
+#app-dialog p.strong {
+ color: white;
+ background-color: #ce5b58;
+}
+
+#app-dialog.floatpanel.dialog {
+ background-color: #ffffff;
+}
+
+#app-dialog p.strong {
+ color: white;
+ background-color: #ce5b58;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/dialog.css b/web/gui2/src/main/webapp/app/fw/layer/dialog.css
new file mode 100644
index 0000000..fdb33d1
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/dialog.css
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Dialog Service (layout) -- CSS file
+ */
+
+.dialog h2 {
+ margin: 0;
+ word-wrap: break-word;
+ display: inline-block;
+ width: 210px;
+ vertical-align: middle;
+}
+
+.dialog .dialog-button {
+ display: inline-block;
+ cursor: pointer;
+ height: 20px;
+ padding: 6px 8px 2px 8px;
+ margin: 4px;
+ float: right;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/dialog.theme.css b/web/gui2/src/main/webapp/app/fw/layer/dialog.theme.css
new file mode 100644
index 0000000..dfcc3cc
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/dialog.theme.css
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016-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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Dialog Service (theme) -- CSS file
+ */
+
+/* temporarily removed .light */
+.dialog .dialog-button {
+ background-color: #518ecc;
+ color: white;
+}
+
+
+/* ========== DARK Theme ========== */
+
+.dark .dialog .dialog-button {
+ background-color: #345e85;
+ color: #cccccd;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.css b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.css
index 829d5e2..66b8f3b 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.css
+++ b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.css
@@ -15,33 +15,54 @@
*/
/*
- ONOS GUI -- Flash Service (layout) -- CSS file
+ ONOS GUI -- Flash Component (layout) -- CSS file
*/
#flash {
- z-index: 1400;
-}
-
-#flash svg {
- /*position: absolute;*/
- bottom: 0;
- opacity: 0.8;
-}
-
-#flash svg g.flashItem text {
- stroke: none;
- text-anchor: middle;
- alignment-baseline: middle;
- font-size: 16pt;
-}
-
-/* Used for temp div */
-.centered {
position: fixed;
top: 50%;
left: 50%;
- -webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
- margin: 0;
+ z-index: 1400;
+}
+
+#flash.warning div#flashBox {
+ border: 2px solid #222222;
+ border-radius: 10px;
+ background: #FFFFFF;
padding: 10px;
}
+
+#flash div#flashBox {
+ background: #CCCCCC;
+ border-radius: 10px;
+ padding: 1px;
+}
+
+#flash div#flashBox div.dialog-button {
+ transform :translateY(-32px);
+}
+
+#flash.warning p#flashText {
+ stroke: #FF0000;
+ color: #FF0000;
+ text-anchor: middle;
+ alignment-baseline: middle;
+ text-align: center;
+ font-size: 16pt;
+ border-radius: 10px;
+ background: #FFFFFF;
+ padding: 10px;
+}
+
+#flash p#flashText {
+ stroke: none;
+ color: #222222;
+ text-anchor: middle;
+ alignment-baseline: middle;
+ text-align: center;
+ font-size: 16pt;
+ border-radius: 10px;
+ background: #CCCCCC;
+ padding: 5px;
+}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.html b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.html
index 5566013..46b6de9 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.html
+++ b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.html
@@ -13,15 +13,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<div id="flash">
- <svg *ngIf="enabled" [attr.width]="width" [attr.height]="height" [attr.viewBox]="vbox" >
- <g class="flashItem" [@flashState]="enabled?'active':'inactive'">
- <text #flashtext>{{ message }}</text>
- <rect [attr.opacity]="0.5" [attr.rx]="rx"
- [attr.x]="-flashtext.getBBox().width/2-xpad"
- [attr.y]="flashtext.getBBox().y-ypad"
- [attr.height]="flashtext.getBBox().height+ypad*2"
- [attr.width]="flashtext.getBBox().width+xpad*2"></rect>
- </g>
- </svg>
+<div id="flash" class="dialog" [ngClass]="warning?'warning':''" [@flashState]="visible">
+ <div id="flashBox" *ngIf="visible">
+ <p id="flashText">{{ message }}</p>
+ <div class="dialog-button" *ngIf="dwell>1200" (click)="closeNow()">Dismiss</div>
+ </div>
</div>
\ No newline at end of file
diff --git a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.spec.ts b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.spec.ts
new file mode 100644
index 0000000..14efa19
--- /dev/null
+++ b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.spec.ts
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+
+import { ConsoleLoggerService } from '../../../consolelogger.service';
+import { LogService } from '../../../log.service';
+import { FlashComponent } from './flash.component';
+
+/**
+ * ONOS GUI -- Layer -- Flash Component - Unit Tests
+ */
+describe('FlashComponent', () => {
+ let log: LogService;
+ let component: FlashComponent;
+ let fixture: ComponentFixture<FlashComponent>;
+
+ beforeEach(async(() => {
+ log = new ConsoleLoggerService();
+ TestBed.configureTestingModule({
+ imports: [ BrowserAnimationsModule ],
+ declarations: [ FlashComponent ],
+ providers: [
+ { provide: LogService, useValue: log },
+ ]
+ });
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FlashComponent);
+ component = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+// it('should have a div#flash', () => {
+// component.enabled = true;
+// const appDe: DebugElement = fixture.debugElement;
+// const divDe = appDe.query(By.css('div#flash'));
+// expect(divDe).toBeTruthy();
+// });
+});
diff --git a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.ts b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.ts
index 3a70c35..df08a96 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.ts
+++ b/web/gui2/src/main/webapp/app/fw/layer/flash/flash.component.ts
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component } from '@angular/core';
+import { Component, Input, Output, OnChanges, SimpleChange, EventEmitter } from '@angular/core';
import { LogService } from '../../../log.service';
import { trigger, state, style, animate, transition } from '@angular/animations';
@@ -24,55 +24,68 @@
* Provides a mechanism to flash short informational messages to the screen
* to alert the user of something, e.g. "Hosts visible" or "Hosts hidden".
*
+ * It can be used in a warning mode, where text will appear in red
+ * The dwell time (milliseconds) can be controlled or the default is 1200ms
+ *
* To use add an element to the template like
- * <onos-flash #flashComponent (click)="flashComponent.flash('Hosts visible')"></onos-flash>
- * 1) The (click) can be removed and the call to flash() can be called from anywhere else in the template.
- * 2) This whole element can be disabled until needed with an ngIf
+ * <onos-flash message="Hosts visible" dwell="2000" warning="true"></onos-flash>
+ * This whole element can be disabled until needed with an ngIf, but if this is done
+ * the animated fade-in and fade-out will not happen
+ * There is also a (closed) event that tells you when the message is closed, or
+ * fades-out
*/
@Component({
selector: 'onos-flash',
templateUrl: './flash.component.html',
- styleUrls: ['./flash.component.css'],
+ styleUrls: [
+ './flash.component.css',
+ '../dialog.css',
+ '../dialog.theme.css',
+ ],
animations: [
trigger('flashState', [
- state('inactive', style({
+ state('false', style({
+// transform: 'translateY(-400%)',
opacity: '0.0',
})),
- state('active', style({
+ state('true', style({
+// transform: 'translateY(0%)',
opacity: '1.0',
})),
- transition('inactive => active', animate('200ms ease-in')),
- transition('active => inactive', animate('200ms ease-out'))
+ transition('0 => 1', animate('200ms ease-in')),
+ transition('1 => 0', animate('200ms ease-out'))
])
]
})
-export class FlashComponent {
- public message: string;
+export class FlashComponent implements OnChanges {
+ @Input() message: string;
+ @Input() dwell: number = 1200; // milliseconds
+ @Input() warning: boolean = false;
+ @Output() closed: EventEmitter<boolean> = new EventEmitter();
- public width: string = '100%';
- public height: number = 200;
- public rx: number = 10;
- public vbox: string = '-200 -' + (this.height / 2) + ' 400 ' + this.height;
- public xpad: number = 20;
- public ypad: number = 10;
- public enabled: boolean = false;
-
- constructor(
- private log: LogService,
- ) {
- this.log.debug('FlashComponent constructed');
- }
+ public visible: boolean = false;
/**
* Flash a message up for 1200ms then disappear again.
* See animation parameter for the ease in and ease out params
*/
- flash(message: string): void {
- this.message = message;
- this.enabled = true;
+ ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
+ if (changes['message'] && this.message && this.message !== '') {
+ this.visible = true;
- setTimeout(() => {
- this.enabled = false;
- }, 1200);
+ setTimeout(() => {
+ this.visible = false;
+ this.closed.emit(false);
+ }, this.dwell);
+ }
+ }
+
+ /**
+ * The message will flash up for 'dwell' milliseconds
+ * If dwell is > 2000ms, then there will be a button that allows it to be dismissed now
+ */
+ closeNow() {
+ this.visible = false;
+ this.closed.emit(false);
}
}
diff --git a/web/gui2/src/main/webapp/app/fw/layer/layer.module.ts b/web/gui2/src/main/webapp/app/fw/layer/layer.module.ts
index 4e8abe7..21806c5 100644
--- a/web/gui2/src/main/webapp/app/fw/layer/layer.module.ts
+++ b/web/gui2/src/main/webapp/app/fw/layer/layer.module.ts
@@ -16,6 +16,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
+import { ConfirmComponent } from './confirm/confirm.component';
import { FlashComponent } from './flash/flash.component';
import { DialogService } from './dialog.service';
import { EditableTextService } from './editabletext.service';
@@ -28,6 +29,7 @@
*/
@NgModule({
exports: [
+ ConfirmComponent,
FlashComponent,
VeilComponent
],
@@ -35,6 +37,7 @@
CommonModule
],
declarations: [
+ ConfirmComponent,
FlashComponent,
VeilComponent
],
diff --git a/web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts b/web/gui2/src/main/webapp/app/fw/util/fn.service.spec.ts
similarity index 98%
rename from web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts
rename to web/gui2/src/main/webapp/app/fw/util/fn.service.spec.ts
index e3d3c87..43e4f85 100644
--- a/web/gui2/src/main/webapp/tests/app/fw/util/fn.service.spec.ts
+++ b/web/gui2/src/main/webapp/app/fw/util/fn.service.spec.ts
@@ -15,9 +15,9 @@
*/
import { TestBed, inject } from '@angular/core/testing';
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { FnService } from '../../../../app/fw/util/fn.service';
+import { LogService } from '../../log.service';
+import { ConsoleLoggerService } from '../../consolelogger.service';
+import { FnService } from '../../fw/util/fn.service';
import { ActivatedRoute, Params } from '@angular/router';
import { of } from 'rxjs';
import * as d3 from 'd3';
diff --git a/web/gui2/src/main/webapp/app/fw/widget/table.css b/web/gui2/src/main/webapp/app/fw/widget/table.css
index 3b761f6..9df99ef 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/table.css
+++ b/web/gui2/src/main/webapp/app/fw/widget/table.css
@@ -31,6 +31,7 @@
div.summary-list div.table-body {
overflow-y: scroll;
+ max-height:70vh;
}
div.summary-list div.table-body::-webkit-scrollbar {
diff --git a/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts b/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts
index bd25919..fef5123 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/tableresize.directive.ts
@@ -13,25 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Directive } from '@angular/core';
+import { Directive, ElementRef } from '@angular/core';
import { FnService } from '../util/fn.service';
import { LogService } from '../../log.service';
-import { MastService } from '../mast/mast.service';
/**
* ONOS GUI -- Widget -- Table Resize Directive
*/
@Directive({
- selector: '[onosTableResize]'
+ selector: '[onosTableResize]',
})
export class TableResizeDirective {
constructor(
private fs: FnService,
- private log: LogService,
- private ms: MastService
+ public log: LogService,
+ private el: ElementRef,
) {
+
+ this.windowSize();
this.log.debug('TableResizeDirective constructed');
}
+ windowSize() {
+ const wsz = this.fs.windowSize(0, 30);
+ this.el.nativeElement.style.width = wsz.width + 'px';
+ }
}
diff --git a/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts b/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
index 4a09032..b05b76b 100644
--- a/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
+++ b/web/gui2/src/main/webapp/app/fw/widget/widget.module.ts
@@ -43,7 +43,8 @@
TableFilterPipe
],
exports: [
- TableFilterPipe
+ TableFilterPipe,
+ TableResizeDirective
],
providers: [
]
diff --git a/web/gui2/src/main/webapp/app/onos.module.ts b/web/gui2/src/main/webapp/app/onos.module.ts
index 78cf112..b8c609d 100644
--- a/web/gui2/src/main/webapp/app/onos.module.ts
+++ b/web/gui2/src/main/webapp/app/onos.module.ts
@@ -15,11 +15,9 @@
*/
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
-import { AppsModule } from './view/apps/apps.module';
-import { DeviceModule } from './view/device/device.module';
-
import { LayerModule } from './fw/layer/layer.module';
import { MastModule } from './fw/mast/mast.module';
import { NavModule } from './fw/nav/nav.module';
@@ -45,6 +43,7 @@
imports: [
BrowserModule,
BrowserAnimationsModule,
+ HttpClientModule,
LayerModule,
MastModule,
NavModule,
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps.module.ts b/web/gui2/src/main/webapp/app/view/apps/apps.module.ts
index 0f38dbc..738ee18 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps.module.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps.module.ts
@@ -19,7 +19,7 @@
import { AppsRoutingModule } from './apps-routing.module';
import { AppsComponent } from './apps/apps.component';
import { AppsDetailsComponent } from './appsdetails/appsdetails.component';
-import { TriggerFormDirective } from './triggerform.directive';
+import { LayerModule } from '../../fw/layer/layer.module';
import { SvgModule } from '../../fw/svg/svg.module';
import { WidgetModule } from '../../fw/widget/widget.module';
@@ -36,12 +36,12 @@
AppsRoutingModule,
SvgModule,
WidgetModule,
- FormsModule
+ FormsModule,
+ LayerModule
],
declarations: [
AppsComponent,
- AppsDetailsComponent,
- TriggerFormDirective
+ AppsDetailsComponent
]
})
export class AppsModule { }
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css
index 5813f30..3096bae 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.css
@@ -17,13 +17,15 @@
/*
ONOS GUI -- Applications View (layout) -- CSS file
*/
+#ov-app .tabular-header {
+ text-align: left;
+}
#ov-app h2 {
display: inline-block;
}
#ov-app div.ctrl-btns {
- width: 290px;
}
/* -- Drag-n-Drop oar file upload -- */
@@ -35,19 +37,3 @@
.dropping {
}
-
-/* -- Confirmation Dialog -- */
-#app-dialog {
- top: 140px;
- padding: 12px;
-}
-
-#app-dialog p {
- font-size: 12pt;
-}
-
-#app-dialog p.strong {
- font-weight: bold;
- padding: 8px;
- text-align: center;
-}
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html
index 1c4a22f..943e26b 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.html
@@ -1,5 +1,5 @@
<!--
-~ Copyright 2014-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.
@@ -13,41 +13,42 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<div id="ov-app" filedrop on-file-drop="appDropped()">
+<div id="ov-app" (dragover)="onDragOver($event)" (dragleave)="onDragLeave($event)" (drop)="onDrop($event)">
<div class="tabular-header">
+ <onos-flash id="appMsgFlash" message="{{ alertMsg }}" dwell="5000" warning="true" (closed)="alertMsg = ''"></onos-flash>
+ <onos-confirm message="{{ confirmMsg }}" warning="{{ strongWarning }}" (chosen)="dOk($event)"></onos-confirm>
<h2>
{{lionFn('title_apps')}}
({{ tableData.length }}
{{ lionFn('total') }})
</h2>
<div class="ctrl-btns">
+ <form #inputFileForm="ngForm">
+ <input id="uploadFile" hidden
+ type="file" size="50" accept=".oar,.jar"
+ name="appFile" (change)="fileEvent($event)">
+ </form>
+
<div class="refresh" (click)="toggleRefresh()">
<onos-icon classes="{{ autoRefresh?'active refresh':'refresh' }}"
iconId="refresh" iconSize="42" toolTip="{{ autoRefreshTip }}"></onos-icon>
</div>
<div class="separator"></div>
- <!--<form id="inputFileForm">-->
- <!--<input id="uploadFile"-->
- <!--type="file" size="50" accept=".oar,.jar"-->
- <!--file-model="appFile">-->
- <!--</form>-->
-
- <div class="active" trigger-form>
+ <div class="active" (click)="triggerForm()">
<onos-icon classes="{{ 'active upload' }}"
- iconId="upload" iconSize="42" toolTip="{{ uploadTip }}"></onos-icon>
+ iconId="upload" iconSize="42" toolTip="{{ uploadTip }}"></onos-icon>
</div>
- <div (click)="appAction('activate')">
+
+ <div (click)="confirmAction(AppActionEnum.ACTIVATE)">
<onos-icon classes="{{ ctrlBtnState.installed?'active play':'play' }}"
iconId="play" iconSize="42" toolTip="{{ activateTip }}"></onos-icon>
</div>
- <div (click)="appAction('deactivate')">
+ <div (click)="confirmAction(AppActionEnum.DEACTIVATE)">
<onos-icon classes="{{ ctrlBtnState.active?'active stop':'stop' }}"
iconId="stop" iconSize="42" toolTip="{{ deactivateTip }}"></onos-icon>
</div>
- <div (click)="appAction('uninstall')">
- <!--[ngClass]="{active: ctrlBtnState.selection}">-->
- <!--tooltip tt-msg="uninstallTip"-->
+ <div (click)="confirmAction(AppActionEnum.UNINSTALL)">
<onos-icon classes="{{ ctrlBtnState.selection?'active garbage':'garbage' }}"
iconId="garbage" iconSize="42" toolTip="{{ uninstallTip }}"></onos-icon>
</div>
@@ -73,57 +74,61 @@
</div>
- <div class="summary-list" onos-table-resize>
- <table onos-flash-changes id-prop="id" width="100%">
- <tr class="table-header">
- <th colId="state" [ngStyle]="{width: '32px'}" class="table-icon" (click)="onSort('state')">
- <onos-icon classes="active" [iconId]="sortIcon('state')"></onos-icon>
- </th>
- <th colId="icon" [ngStyle]="{width: '32px'}" class="table-icon"></th>
- <th colId="title" (click)="onSort('title')">{{lionFn('title')}}
- <onos-icon classes="active" [iconId]="sortIcon('title')"></onos-icon>
- </th>
- <th colId="id" (click)="onSort('id')">{{lionFn('app_id')}}
- <onos-icon classes="active" [iconId]="sortIcon('id')"></onos-icon>
- </th>
- <th colId="version" (click)="onSort('version')"> {{lionFn('version')}}
- <onos-icon classes="active" [iconId]="sortIcon('version')"></onos-icon>
- </th>
- <th colId="category" (click)="onSort('category')"> {{lionFn('category')}}
- <onos-icon classes="active" [iconId]="sortIcon('category')"></onos-icon>
- </th>
- <th colId="origin" (click)="onSort('origin')"> {{lionFn('origin')}}
- <onos-icon classes="active" [iconId]="sortIcon('origin')"></onos-icon>
- </th>
- </tr>
-
- <tr *ngIf="tableData.length === 0" class="no-data">
- <td colspan="5">
- {{annots.no_rows_msg}}
- </td>
- </tr>
- <!-- See https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
- Angular has dropped the filter and order by pipe that were present in
- AngularJS - filter and sort the data at source instead -->
- <tr class="table-body" *ngFor="let app of tableData | filter: tableDataFilter"
- (click)="selectCallback($event, app)"
- [ngClass]="{selected: app.id === selId, 'data-change': isChanged(app.id)}">
- <td class="table-icon">
- <onos-icon iconId="{{app._iconid_state}}"></onos-icon>
- </td>
- <td class="table-icon">
- <!-- The path below gets the app icon from the old GUI path -->
- <img src="../../ui/rs/applications/{{app.icon}}/icon"
- height="24px" width="24px" />
- </td>
- <td>{{ app.title }}</td>
- <td>{{ app.id }}</td>
- <td>{{ app.version }}</td>
- <td>{{ app.category }}</td>
- <td>{{ app.origin }}</td>
- </tr>
- </table>
-
+ <div id="summary-list" class="summary-list">
+ <div class="table-header">
+ <table onosTableResize>
+ <tr>
+ <th colId="state" [ngStyle]="{width: '32px'}" class="table-icon" (click)="onSort('state')">
+ <onos-icon classes="active" [iconId]="sortIcon('state')"></onos-icon>
+ </th>
+ <th colId="icon" [ngStyle]="{width: '32px'}" class="table-icon"></th>
+ <th colId="title" (click)="onSort('title')">{{lionFn('title')}}
+ <onos-icon classes="active" [iconId]="sortIcon('title')"></onos-icon>
+ </th>
+ <th colId="id" (click)="onSort('id')">{{lionFn('app_id')}}
+ <onos-icon classes="active" [iconId]="sortIcon('id')"></onos-icon>
+ </th>
+ <th colId="version" (click)="onSort('version')"> {{lionFn('version')}}
+ <onos-icon classes="active" [iconId]="sortIcon('version')"></onos-icon>
+ </th>
+ <th colId="category" (click)="onSort('category')"> {{lionFn('category')}}
+ <onos-icon classes="active" [iconId]="sortIcon('category')"></onos-icon>
+ </th>
+ <th colId="origin" (click)="onSort('origin')"> {{lionFn('origin')}}
+ <onos-icon classes="active" [iconId]="sortIcon('origin')"></onos-icon>
+ </th>
+ </tr>
+ </table>
+ </div>
+ <div class="table-body">
+ <table onosTableResize>
+ <tr *ngIf="tableData.length === 0" class="no-data">
+ <td colspan="5">
+ {{annots.no_rows_msg}}
+ </td>
+ </tr>
+ <!-- See https://angular.io/guide/pipes#appendix-no-filterpipe-or-orderbypipe
+ Angular has dropped the filter and order by pipe that were present in
+ AngularJS - filter and sort the data at source instead -->
+ <tr *ngFor="let app of tableData | filter: tableDataFilter"
+ (click)="selectCallback($event, app)"
+ [ngClass]="{selected: app.id === selId, 'data-change': isChanged(app.id)}">
+ <td class="table-icon">
+ <onos-icon iconId="{{app._iconid_state}}"></onos-icon>
+ </td>
+ <td class="table-icon">
+ <!-- The path below gets the app icon from the old GUI path -->
+ <img src="../../ui/rs/applications/{{app.icon}}/icon"
+ height="24px" width="24px" />
+ </td>
+ <td>{{ app.title }}</td>
+ <td>{{ app.id }}</td>
+ <td>{{ app.version }}</td>
+ <td>{{ app.category }}</td>
+ <td>{{ app.origin }}</td>
+ </tr>
+ </table>
+ </div>
</div>
<!-- There are 2 ways this component can be included
1) Insert it in to the ngFor above and have it created as the row is rendered
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts
index 1889a44..cddf9ae 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.component.spec.ts
@@ -19,11 +19,14 @@
import { FormsModule } from '@angular/forms';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
+import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { LogService } from '../../../log.service';
import { AppsComponent } from './apps.component';
import { AppsDetailsComponent } from '../appsdetails/appsdetails.component';
+import { ConfirmComponent } from '../../../fw/layer/confirm/confirm.component';
import { DialogService } from '../../../fw/layer/dialog.service';
+import { FlashComponent } from '../../../fw/layer/flash/flash.component';
import { FnService } from '../../../fw/util/fn.service';
import { IconComponent } from '../../../fw/svg/icon/icon.component';
import { IconService } from '../../../fw/svg/icon.service';
@@ -47,6 +50,8 @@
class MockFnService {}
+class MockHttpClient {}
+
class MockIconService {
loadIconDef() {}
}
@@ -108,10 +113,18 @@
TestBed.configureTestingModule({
imports: [ BrowserAnimationsModule, FormsModule ],
- declarations: [ AppsComponent, IconComponent, AppsDetailsComponent, TableFilterPipe ],
+ declarations: [
+ AppsComponent,
+ ConfirmComponent,
+ IconComponent,
+ AppsDetailsComponent,
+ TableFilterPipe,
+ FlashComponent
+ ],
providers: [
{ provide: DialogService, useClass: MockDialogService },
{ provide: FnService, useValue: fs },
+ { provide: HttpClient, useClass: MockHttpClient },
{ provide: IconService, useClass: MockIconService },
{ provide: KeyService, useClass: MockKeyService },
{ provide: LionService, useFactory: (() => {
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();
}
}
diff --git a/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css b/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css
index 3de7ad8..13f1847 100644
--- a/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css
+++ b/web/gui2/src/main/webapp/app/view/apps/apps/apps.theme.css
@@ -23,16 +23,38 @@
border: solid 3px #0095d6;
}
-
-/* -- confirmation dialog -- */
-.light #app-dialog p.strong {
- color: white;
- background-color: #ce5b58;
+#ov-app .tabular-header {
+ text-align: left;
+}
+#ov-app div.summary-list .table-header td {
+ font-weight: bold;
+ font-variant: small-caps;
+ text-transform: uppercase;
+ font-size: 10pt;
+ padding-top: 8px;
+ padding-bottom: 8px;
+ letter-spacing: 0.02em;
+ cursor: pointer;
+ background-color: #e5e5e6;
+ color: #3c3a3a;
}
-.light #app-dialog.floatpanel.dialog {
- background-color: #ffffff;
+#ov-app div.summary-list .table-body {
+ overflow:scroll;
+ max-height:70vh;
}
+#ov-app h2 {
+ display: inline-block;
+}
+
+#ov-app, div.ctrl-btns {
+}
+
+#ov-app th, td {
+ text-align: left;
+ padding: 8px;
+}
+
/* ========== DARK Theme ========== */
@@ -40,13 +62,3 @@
.dark .app-title {
color: #dddddd;
}
-
-/* -- confirmation dialog -- */
-.dark #app-dialog p.strong {
- color: red;
- background-color: #ecd98e;
-}
-.dark #app-dialog.floatpanel.dialog {
- background-color: #282528;
- color:#ddddee;
-}
diff --git a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css
index bfa8d08..ce9af3a 100644
--- a/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css
+++ b/web/gui2/src/main/webapp/app/view/apps/appsdetails/appsdetails.component.css
@@ -103,6 +103,8 @@
#application-details-panel .bottom {
padding: 0px;
+ overflow: auto;
+ height: 200px;
}
#application-details-panel .bottom table {
diff --git a/web/gui2/src/main/webapp/app/view/apps/triggerform.directive.ts b/web/gui2/src/main/webapp/app/view/apps/triggerform.directive.ts
deleted file mode 100644
index 7134c3a..0000000
--- a/web/gui2/src/main/webapp/app/view/apps/triggerform.directive.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2015-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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an 'AS IS' BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { Directive } from '@angular/core';
-import { LogService } from '../../log.service';
-
-/**
- * ONOS GUI -- Apps -- Trigger Form Directive
- */
-@Directive({
- selector: '[onosTriggerForm]'
-})
-export class TriggerFormDirective {
-
- constructor(
- private log: LogService,
- ) {
- this.log.debug('TriggerFormDirective constructed');
- }
-
-}
diff --git a/web/gui2/src/main/webapp/tests/app/fw/layer/dialog.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/layer/dialog.service.spec.ts
deleted file mode 100644
index f51fb0e..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/layer/dialog.service.spec.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2017-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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { DialogService } from '../../../../app/fw/layer/dialog.service';
-import { FnService } from '../../../../app/fw/util/fn.service';
-import { KeyService } from '../../../../app/fw/util/key.service';
-
-class MockFnService {}
-
-class MockKeyService {}
-
-/**
- * ONOS GUI -- Layer -- Dialog Service - Unit Tests
- */
-describe('DialogService', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [DialogService,
- { provide: LogService, useValue: log },
- { provide: FnService, useClass: MockFnService },
- { provide: KeyService, useClass: MockKeyService },
- ]
- });
- });
-
- it('should be created', inject([DialogService], (service: DialogService) => {
- expect(service).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/layer/flash/flash.component.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/layer/flash/flash.component.spec.ts
deleted file mode 100644
index 53a7ecc..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/layer/flash/flash.component.spec.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { ConsoleLoggerService } from '../../../../../app/consolelogger.service';
-import { LogService } from '../../../../../app/log.service';
-import { FlashComponent } from '../../../../../app/fw/layer/flash/flash.component';
-
-/**
- * ONOS GUI -- Layer -- Flash Component - Unit Tests
- */
-describe('FlashComponent', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
- TestBed.configureTestingModule({
- declarations: [ FlashComponent ],
- providers: [
- { provide: LogService, useValue: log },
- ]
- });
- });
-
-
- it('should create', () => {
- const fixture = TestBed.createComponent(FlashComponent);
- const component = fixture.componentInstance;
- expect(component).toBeTruthy();
- });
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/widget/flashchanges.directive.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/widget/flashchanges.directive.spec.ts
deleted file mode 100644
index fd87910..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/widget/flashchanges.directive.spec.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2015-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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { FlashChangesDirective } from '../../../../app/fw/widget/flashchanges.directive';
-import { FnService } from '../../../../app/fw/util/fn.service';
-
-class MockFnService {}
-
-/**
- * ONOS GUI -- Widget -- Table Flash Changes Directive - Unit Tests
- */
-describe('FlashChangesDirective', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [ FlashChangesDirective,
- { provide: FnService, useClass: MockFnService },
- { provide: LogService, useValue: log },
- ]
- });
- });
-
- afterEach(() => {
- log = null;
- });
-
- it('should create an instance', inject([FlashChangesDirective], (directive: FlashChangesDirective) => {
- expect(directive).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/fw/widget/toolbar.service.spec.ts b/web/gui2/src/main/webapp/tests/app/fw/widget/toolbar.service.spec.ts
deleted file mode 100644
index cfc49e7..0000000
--- a/web/gui2/src/main/webapp/tests/app/fw/widget/toolbar.service.spec.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2015-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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { ToolbarService } from '../../../../app/fw/widget/toolbar.service';
-import { ButtonService } from '../../../../app/fw/widget/button.service';
-import { FnService } from '../../../../app/fw/util/fn.service';
-import { IconService } from '../../../../app/fw/svg/icon.service';
-
-class MockButtonService {}
-
-class MockIconService {}
-
-class MockFnService {}
-
-/**
- * ONOS GUI -- Widget -- Toolbar Service - Unit Tests
- */
-describe('ToolbarService', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [ToolbarService,
- { provide: LogService, useValue: log },
- { provide: ButtonService, useClass: MockButtonService },
- { provide: IconService, useClass: MockIconService },
- { provide: FnService, useClass: MockFnService },
- ]
- });
- });
-
- it('should be created', inject([ToolbarService], (service: ToolbarService) => {
- expect(service).toBeTruthy();
- }));
-});
diff --git a/web/gui2/src/main/webapp/tests/app/view/apps/triggerform.directive.spec.ts b/web/gui2/src/main/webapp/tests/app/view/apps/triggerform.directive.spec.ts
deleted file mode 100644
index be1be65..0000000
--- a/web/gui2/src/main/webapp/tests/app/view/apps/triggerform.directive.spec.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2015-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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an 'AS IS' BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { TestBed, inject } from '@angular/core/testing';
-
-import { LogService } from '../../../../app/log.service';
-import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
-import { TriggerFormDirective } from '../../../../app/view/apps/triggerform.directive';
-
-describe('TriggerFormDirective', () => {
- let log: LogService;
-
- beforeEach(() => {
- log = new ConsoleLoggerService();
-
- TestBed.configureTestingModule({
- providers: [ TriggerFormDirective,
- { provide: LogService, useValue: log },
- ]
- });
- });
-
- afterEach(() => {
- log = null;
- });
-
- it('should create an instance', inject([TriggerFormDirective], (directive: TriggerFormDirective) => {
- expect(directive).toBeTruthy();
- }));
-});