From 01efaa5e768dbc08376761696b77cd9671023e06 Mon Sep 17 00:00:00 2001
From: Chris Hines <chris.hines@monash.edu>
Date: Thu, 10 Jan 2019 14:07:12 +1100
Subject: [PATCH] all sorts of changes

---
 e2e/app/authorisation.service.spec.ts         |  15 +
 e2e/app/authorisation.service.ts              |   9 +
 package-lock.json                             | 370 +++++++++++++-----
 package.json                                  |  31 +-
 src/app/app-routing.module.ts                 |   8 +-
 src/app/app.component.css                     |  11 +-
 src/app/app.component.html                    |   7 +-
 src/app/app.component.ts                      |  29 +-
 src/app/app.module.ts                         |  24 +-
 src/app/authorisation.service.ts              | 154 +++++++-
 src/app/batchinterface.ts                     |   1 -
 src/app/computesite.ts                        |   4 +
 src/app/computesites.service.ts               |  96 ++++-
 .../connecting.component.css}                 |   0
 src/app/connecting/connecting.component.html  |  11 +
 .../connecting/connecting.component.spec.ts   |  25 ++
 src/app/connecting/connecting.component.ts    |  73 ++++
 src/app/file-explorer/CREDIT                  |   1 +
 .../file-explorer/file-explorer.component.css |  39 ++
 .../file-explorer.component.html              | 139 +++++++
 .../file-explorer/file-explorer.component.ts  | 109 ++++++
 src/app/file-explorer/file-explorer.module.ts |  48 +++
 .../newFolderDialog.component.css             |   0
 .../newFolderDialog.component.html            |  12 +
 .../newFolderDialog.component.spec.ts         |  28 ++
 .../newFolderDialog.component.ts              |  15 +
 .../renameDialog/renameDialog.component.css   |   0
 .../renameDialog/renameDialog.component.html  |  12 +
 .../renameDialog/renameDialog.component.ts    |  15 +
 src/app/file-explorer/model/element.ts        |   6 +
 src/app/identity.ts                           |   4 +
 src/app/job.ts                                |   5 +-
 src/app/job/job.component.html                |  15 +
 src/app/job/job.component.ts                  |   8 +-
 src/app/joblist/joblist.component.html        |   6 +-
 src/app/joblist/joblist.component.ts          |  43 +-
 src/app/keygen/keygen.component.css           |   0
 src/app/keygen/keygen.component.html          |  11 +
 .../keygen.component.spec.ts}                 |  12 +-
 src/app/keygen/keygen.component.ts            |  46 +++
 src/app/launcher/launcher.component.css       |  11 +-
 src/app/launcher/launcher.component.html      |  38 +-
 src/app/launcher/launcher.component.ts        |  53 ++-
 .../logoutdialog/logoutdialog.component.ts    |   6 +-
 src/app/modaldialog/modaldialog.component.css |   0
 .../modaldialog/modaldialog.component.html    |  10 +
 .../modaldialog/modaldialog.component.spec.ts |  25 ++
 src/app/modaldialog/modaldialog.component.ts  |  20 +
 .../shareconnect/shareconnect.component.css   |   3 +
 .../shareconnect/shareconnect.component.html  |  38 ++
 .../shareconnect.component.spec.ts            |  25 ++
 .../shareconnect/shareconnect.component.ts    |  71 ++++
 src/app/strudelapp.ts                         |   5 +
 .../strudelapplist.component.css              |   0
 .../strudelapplist.component.html             |  16 +
 .../strudelapplist.component.spec.ts          |  25 ++
 .../strudelapplist.component.ts               |  38 ++
 src/app/submit-app.service.spec.ts            |  15 +
 src/app/submit-app.service.ts                 | 158 ++++++++
 .../sv2-side-nav/sv2-side-nav.component.css   |   0
 .../sv2-side-nav/sv2-side-nav.component.html  |  16 +
 .../sv2-side-nav.component.spec.ts            |  25 ++
 .../sv2-side-nav/sv2-side-nav.component.ts    |  31 ++
 src/app/tes.service.ts                        | 336 +++++++++-------
 src/app/teserrors/teserrors.component.html    |   3 -
 src/app/teserrors/teserrors.component.ts      |  31 --
 src/app/transfer/transfer.component.css       |   3 +
 src/app/transfer/transfer.component.html      |  54 +++
 src/app/transfer/transfer.component.spec.ts   |  25 ++
 src/app/transfer/transfer.component.ts        | 175 +++++++++
 src/assets/config/authservers.json            |  34 ++
 src/assets/config/computesites.json           |  40 ++
 src/assets/config/computesites.json~          |  26 ++
 src/assets/config/cvluwaapps.json             |  62 +++
 src/assets/config/m3apps.json                 |  62 +++
 src/assets/config/m3staticapps.json           |  10 +
 src/environments/environment.prod.ts          |   4 +-
 src/environments/environment.ts               |   3 +-
 src/index.html                                |   4 +-
 79 files changed, 2521 insertions(+), 422 deletions(-)
 create mode 100644 e2e/app/authorisation.service.spec.ts
 create mode 100644 e2e/app/authorisation.service.ts
 rename src/app/{teserrors/teserrors.component.css => connecting/connecting.component.css} (100%)
 create mode 100644 src/app/connecting/connecting.component.html
 create mode 100644 src/app/connecting/connecting.component.spec.ts
 create mode 100644 src/app/connecting/connecting.component.ts
 create mode 100644 src/app/file-explorer/CREDIT
 create mode 100644 src/app/file-explorer/file-explorer.component.css
 create mode 100644 src/app/file-explorer/file-explorer.component.html
 create mode 100644 src/app/file-explorer/file-explorer.component.ts
 create mode 100644 src/app/file-explorer/file-explorer.module.ts
 create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.css
 create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html
 create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts
 create mode 100644 src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts
 create mode 100644 src/app/file-explorer/modals/renameDialog/renameDialog.component.css
 create mode 100644 src/app/file-explorer/modals/renameDialog/renameDialog.component.html
 create mode 100644 src/app/file-explorer/modals/renameDialog/renameDialog.component.ts
 create mode 100644 src/app/file-explorer/model/element.ts
 create mode 100644 src/app/keygen/keygen.component.css
 create mode 100644 src/app/keygen/keygen.component.html
 rename src/app/{teserrors/teserrors.component.spec.ts => keygen/keygen.component.spec.ts} (55%)
 create mode 100644 src/app/keygen/keygen.component.ts
 create mode 100644 src/app/modaldialog/modaldialog.component.css
 create mode 100644 src/app/modaldialog/modaldialog.component.html
 create mode 100644 src/app/modaldialog/modaldialog.component.spec.ts
 create mode 100644 src/app/modaldialog/modaldialog.component.ts
 create mode 100644 src/app/shareconnect/shareconnect.component.css
 create mode 100644 src/app/shareconnect/shareconnect.component.html
 create mode 100644 src/app/shareconnect/shareconnect.component.spec.ts
 create mode 100644 src/app/shareconnect/shareconnect.component.ts
 create mode 100644 src/app/strudelapplist/strudelapplist.component.css
 create mode 100644 src/app/strudelapplist/strudelapplist.component.html
 create mode 100644 src/app/strudelapplist/strudelapplist.component.spec.ts
 create mode 100644 src/app/strudelapplist/strudelapplist.component.ts
 create mode 100644 src/app/submit-app.service.spec.ts
 create mode 100644 src/app/submit-app.service.ts
 create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.css
 create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.html
 create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.spec.ts
 create mode 100644 src/app/sv2-side-nav/sv2-side-nav.component.ts
 delete mode 100644 src/app/teserrors/teserrors.component.html
 delete mode 100644 src/app/teserrors/teserrors.component.ts
 create mode 100644 src/app/transfer/transfer.component.css
 create mode 100644 src/app/transfer/transfer.component.html
 create mode 100644 src/app/transfer/transfer.component.spec.ts
 create mode 100644 src/app/transfer/transfer.component.ts
 create mode 100644 src/assets/config/authservers.json
 create mode 100644 src/assets/config/computesites.json
 create mode 100644 src/assets/config/computesites.json~
 create mode 100644 src/assets/config/cvluwaapps.json
 create mode 100644 src/assets/config/m3apps.json
 create mode 100644 src/assets/config/m3staticapps.json

diff --git a/e2e/app/authorisation.service.spec.ts b/e2e/app/authorisation.service.spec.ts
new file mode 100644
index 0000000..3286e3a
--- /dev/null
+++ b/e2e/app/authorisation.service.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, inject } from '@angular/core/testing';
+
+import { AuthorisationService } from './authorisation.service';
+
+describe('AuthorisationService', () => {
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [AuthorisationService]
+    });
+  });
+
+  it('should be created', inject([AuthorisationService], (service: AuthorisationService) => {
+    expect(service).toBeTruthy();
+  }));
+});
diff --git a/e2e/app/authorisation.service.ts b/e2e/app/authorisation.service.ts
new file mode 100644
index 0000000..8326754
--- /dev/null
+++ b/e2e/app/authorisation.service.ts
@@ -0,0 +1,9 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AuthorisationService {
+
+  constructor() { }
+}
diff --git a/package-lock.json b/package-lock.json
index 19ec01c..46d1f11 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
       "dev": true,
       "requires": {
         "@angular-devkit/core": "0.6.8",
-        "rxjs": "6.2.0"
+        "rxjs": "6.3.3"
       }
     },
     "@angular-devkit/build-angular": {
@@ -54,7 +54,7 @@
         "postcss-url": "7.3.2",
         "raw-loader": "0.5.1",
         "resolve": "1.7.1",
-        "rxjs": "6.2.0",
+        "rxjs": "6.3.3",
         "sass-loader": "7.0.3",
         "silent-error": "1.1.0",
         "source-map-support": "0.5.6",
@@ -119,7 +119,7 @@
       "requires": {
         "ajv": "6.4.0",
         "chokidar": "2.0.3",
-        "rxjs": "6.2.0",
+        "rxjs": "6.3.3",
         "source-map": "0.5.7"
       },
       "dependencies": {
@@ -472,23 +472,32 @@
       "dev": true,
       "requires": {
         "@angular-devkit/core": "0.6.8",
-        "rxjs": "6.2.0"
+        "rxjs": "6.3.3"
       }
     },
     "@angular/animations": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.0.4.tgz",
-      "integrity": "sha512-Ro1XEwShk8XRAogfu73fKLTBLnND1s+MGhN+ymwr7ib9hqxVr7jMNE+MXPqG2/1BX9c+7NHb30B8G4woQn62vw==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.1.1.tgz",
+      "integrity": "sha512-iTNxhPPraCZsE4rgM23lguT1kDV4mfYAr+Bsi5J0+v9ZJA+VaKvi6eRW8ZGrx4/rDz6hzTnBn1jgPppHFbsOcw==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/cdk": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.0.0.tgz",
-      "integrity": "sha512-GVWUwmKWJPkK4gJTi0tgaLDs5QlRvkozIs6KnrsozkPUNDIsZyQCyEUB+llHiUB9AeDGcCDbpQyGIDLdya5khQ==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.1.0.tgz",
+      "integrity": "sha512-dY740pKcIRtKr6n6NomrgqfdEj988urTZ9I/bfJjxF5fdhnSjyhEvDlB55EHsrF+bTTZbZXRmv7AwOQ9GJnD9w==",
       "requires": {
+        "parse5": "5.1.0",
         "tslib": "1.9.0"
+      },
+      "dependencies": {
+        "parse5": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+          "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+          "optional": true
+        }
       }
     },
     "@angular/cli": {
@@ -504,7 +513,7 @@
         "@schematics/update": "0.6.8",
         "opn": "5.3.0",
         "resolve": "1.7.1",
-        "rxjs": "6.2.0",
+        "rxjs": "6.3.3",
         "semver": "5.5.0",
         "silent-error": "1.1.0",
         "symbol-observable": "1.2.0",
@@ -512,99 +521,258 @@
       }
     },
     "@angular/common": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.0.4.tgz",
-      "integrity": "sha512-z2UE6a43u49omsqjzwdHhwD+wpCPauuD13FMkVQMLwzugKhNri0LjMk5I0I9xLz9OyXICm2B+wPAQN4d07savg==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.1.1.tgz",
+      "integrity": "sha512-SngekFx9v39sjgi9pON0Wehxpu+NdUk7OEebw4Fa8dKqTgydTkuhmnNH+9WQe264asoeCt51oufPRjIqMLNohA==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/compiler": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.0.4.tgz",
-      "integrity": "sha512-qwjuPacuRKUroD+xev653mljV9Jwhi2tvwGo/cVadq0isJ2rVH1hO6sa1FWFnNf1KPnyolMobZlMgg8DFW/yRw==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.1.1.tgz",
+      "integrity": "sha512-oJvBe8XZ+DXF/W/DxWBTbBcixJTuPeZWdkcZIGWhJoQP7K5GnGnj8ffP9Lp6Dh4TKv85awtC6OfIKhbHxa650Q==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/compiler-cli": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-6.0.4.tgz",
-      "integrity": "sha512-bkN21JJrsbIdXNfOc9I9iB7AETUe4/QvApC25R3/pERSyhkK7UNNHTqg4FY5xMdSxlZd1ccWj8rvSbS+hFbTBw==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.1.1.tgz",
+      "integrity": "sha512-4NXlkDhOEQgaP3Agigqw93CvXJvsfnXa0xiglq9e/wjL+6XbtM9WcDb5lfRQz41N9RSkO3pEHGvKMweKZGgogA==",
       "dev": true,
       "requires": {
+        "canonical-path": "1.0.0",
         "chokidar": "1.7.0",
+        "convert-source-map": "1.5.1",
+        "dependency-graph": "0.7.2",
+        "magic-string": "0.25.1",
         "minimist": "1.2.0",
         "reflect-metadata": "0.1.12",
-        "tsickle": "0.29.0"
+        "shelljs": "0.8.3",
+        "source-map": "0.6.1",
+        "tslib": "1.9.0",
+        "yargs": "9.0.1"
       },
       "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+          "dev": true
+        },
+        "load-json-file": {
+          "version": "2.0.0",
+          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+          "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "4.1.11",
+            "parse-json": "2.2.0",
+            "pify": "2.3.0",
+            "strip-bom": "3.0.0"
+          }
+        },
         "minimist": {
           "version": "1.2.0",
           "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
+        },
+        "os-locale": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+          "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+          "dev": true,
+          "requires": {
+            "execa": "0.7.0",
+            "lcid": "1.0.0",
+            "mem": "1.1.0"
+          }
+        },
+        "path-type": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+          "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+          "dev": true,
+          "requires": {
+            "pify": "2.3.0"
+          }
+        },
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+          "dev": true
+        },
+        "read-pkg": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+          "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+          "dev": true,
+          "requires": {
+            "load-json-file": "2.0.0",
+            "normalize-package-data": "2.4.0",
+            "path-type": "2.0.0"
+          }
+        },
+        "read-pkg-up": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+          "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+          "dev": true,
+          "requires": {
+            "find-up": "2.1.0",
+            "read-pkg": "2.0.0"
+          }
+        },
+        "source-map": {
+          "version": "0.6.1",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+          "dev": true,
+          "requires": {
+            "is-fullwidth-code-point": "2.0.0",
+            "strip-ansi": "4.0.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "3.0.0"
+          }
+        },
+        "strip-bom": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+          "dev": true
+        },
+        "which-module": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+          "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+          "dev": true
+        },
+        "y18n": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+          "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+          "dev": true
+        },
+        "yargs": {
+          "version": "9.0.1",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz",
+          "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=",
+          "dev": true,
+          "requires": {
+            "camelcase": "4.1.0",
+            "cliui": "3.2.0",
+            "decamelize": "1.2.0",
+            "get-caller-file": "1.0.2",
+            "os-locale": "2.1.0",
+            "read-pkg-up": "2.0.0",
+            "require-directory": "2.1.1",
+            "require-main-filename": "1.0.1",
+            "set-blocking": "2.0.0",
+            "string-width": "2.1.1",
+            "which-module": "2.0.0",
+            "y18n": "3.2.1",
+            "yargs-parser": "7.0.0"
+          }
+        },
+        "yargs-parser": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+          "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
+          "dev": true,
+          "requires": {
+            "camelcase": "4.1.0"
+          }
         }
       }
     },
     "@angular/core": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.0.4.tgz",
-      "integrity": "sha512-WCW8wyRoIKkjGjOjVLFpUyhQw/K0//s3W0qo1/vH6m9njC63jP/VP+2Xnt8bdtgeET2NYUeyl+w/0vUXqYlnEA==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.1.1.tgz",
+      "integrity": "sha512-Osig5SRgDRQ+Hec/liN7nq/BCJieB+4/pqRh9rFbOXezb2ptgRZqdXOXN8P17i4AwPVf308Mh55V0niJ5Eu3Rw==",
+      "requires": {
+        "tslib": "1.9.0"
+      }
+    },
+    "@angular/flex-layout": {
+      "version": "7.0.0-beta.19",
+      "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.19.tgz",
+      "integrity": "sha512-MXq+zZ6/s5/+GsL9fZ42mKL0LjZ/+L0sVU5FaQuSAJ57soLl5QAGWvdxVmROtqcHd3Htp35R49nKSZBJ0nfAjg==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/forms": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.0.4.tgz",
-      "integrity": "sha512-JcKyCCkAwF3NlWuwPxtpRhFX/8QbFUkA4W0N62L+Odm8G0/P4QhMw/99o5kRs5xrsQRVyFlGvdYOrkgDyyZkGg==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.1.1.tgz",
+      "integrity": "sha512-yCWuPjpu23Wc3XUw7v/ACNn/e249oT0bYlM8aaMQ1F5OwrmmC4NJC12Rpl9Ihza61RIHIKzNcHVEgzc7WhcSag==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/http": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.0.4.tgz",
-      "integrity": "sha512-bq7cS0/5hM3Af3FolEe7HpKP19/7MJWIPGi5k9rYCGM4dXKFiKa+LA/SbXE0DMiKxhAQbZDwQ9HN04r70DRyBQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.1.1.tgz",
+      "integrity": "sha512-pRk+c/kz9aJ8te5xzCxlPLpFnwB0d/E9YkOo3/ydaXF9vZw13RTzk00YyzJ41PDzJf8oPDdXtueTQ+vtJ7Srtw==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/language-service": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-6.0.4.tgz",
-      "integrity": "sha512-LyFh/sFtnyjJJYO5UKM31nGSSkmGYFMwqzwbG+l0QNQXNoa9jj+OApvu0oOY5F0WP9DBrOAzj74j2M5olprtMQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.1.1.tgz",
+      "integrity": "sha512-X+5g20PMtNRGZIa3svMv4PLJdJehn4wqrS8nwOtzH5XkSn5vA3IxjsJVdSzAy2AN0/sKKJK5jmQorPtKO4saJg==",
       "dev": true
     },
     "@angular/material": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.0.0.tgz",
-      "integrity": "sha512-g6P6yg2Gmn+LJKO+KxRnH5FL1D/dApwb/vloA9UTEzddhHoUB5JdRhlE78yQTwmtrP/cCJJih6ssZkWKRAqEbg==",
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.1.0.tgz",
+      "integrity": "sha512-bgotNpSfGLjNZ1AcTyhs6XS7trF4I7UHwQmfa0l8y3Gf9plwErPDfQe2XqnayRyG9nTwHj9f1lQ45X5mr3/0/A==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/platform-browser": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.0.4.tgz",
-      "integrity": "sha512-4y6rSqOLuBs+RL0CIM+RZ5T8a5Tm1NisZcTUGvM8lVYPNkqTzNfHQXhBq8mTgRw9JopEwOj+UEqBbgrNX715yQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.1.1.tgz",
+      "integrity": "sha512-I6OPjecynGJSbPtzu0gvEgSmIR6X6/xEAhg4L9PycW1ryjzptTC9klWRTWIqsIBqMxhVnY44uKLeRNrDwMOwyA==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/platform-browser-dynamic": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.0.4.tgz",
-      "integrity": "sha512-uh7MoQi9kyKPFHrD7HPOU5gWyhF4pPJuGltyMTOtM/f5TNL9QQI2EugijKnnSFLQ8+7UQjmFAVeRqH9jylhIyQ==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.1.1.tgz",
+      "integrity": "sha512-ZIu48Vn4S6gjD7CMbGlKGaPQ8v9rYkWzlNYi4vTYzgiqKKNC3hqLsVESU3mSvr5oeQBxSIBidTdHSyafHFrA2w==",
       "requires": {
         "tslib": "1.9.0"
       }
     },
     "@angular/router": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.0.4.tgz",
-      "integrity": "sha512-IKUtbbRtHN8I+Hp8/pLqBPyXJmQHrPFGzKU3H43TovD++mmT5AaYVCeJgkGUB//wjtLN3y0X/yrmyWFWcN35Gw==",
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.1.1.tgz",
+      "integrity": "sha512-jbnqEq/1iDBkeH8Vn13hauGPTzhwllWM+MLfmdNGTiMzGRx4pmkWa57seDOeBF/GNYBL9JjkWTCrkKFAc2FJKw==",
       "requires": {
         "tslib": "1.9.0"
       }
@@ -629,6 +797,14 @@
         "@angular-devkit/core": "0.6.8",
         "@angular-devkit/schematics": "0.6.8",
         "typescript": "2.7.2"
+      },
+      "dependencies": {
+        "typescript": {
+          "version": "2.7.2",
+          "resolved": "http://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz",
+          "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==",
+          "dev": true
+        }
       }
     },
     "@schematics/update": {
@@ -640,7 +816,7 @@
         "@angular-devkit/core": "0.6.8",
         "@angular-devkit/schematics": "0.6.8",
         "npm-registry-client": "8.5.1",
-        "rxjs": "6.2.0",
+        "rxjs": "6.3.3",
         "semver": "5.5.0",
         "semver-intersect": "1.3.1"
       }
@@ -2259,6 +2435,12 @@
       "integrity": "sha512-Y1ecA1cL9wg0vni8t33nBw/poX8ypm+2c3fbwAESj8cm4ufK9CBFQ1+nUK8Dp5dtFo5Fc3JzkI5DKmQbuIo6hQ==",
       "dev": true
     },
+    "canonical-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz",
+      "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
+      "dev": true
+    },
     "caseless": {
       "version": "0.12.0",
       "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -2398,7 +2580,6 @@
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
       "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
       "dev": true,
-      "optional": true,
       "requires": {
         "string-width": "1.0.2",
         "strip-ansi": "3.0.1",
@@ -3185,6 +3366,12 @@
       "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
       "dev": true
     },
+    "dependency-graph": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz",
+      "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==",
+      "dev": true
+    },
     "deps-sort": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
@@ -6270,6 +6457,12 @@
         "meow": "3.7.0"
       }
     },
+    "interpret": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",
+      "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=",
+      "dev": true
+    },
     "invariant": {
       "version": "2.2.4",
       "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -7716,6 +7909,15 @@
         "yallist": "2.1.2"
       }
     },
+    "magic-string": {
+      "version": "0.25.1",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.1.tgz",
+      "integrity": "sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg==",
+      "dev": true,
+      "requires": {
+        "sourcemap-codec": "1.4.4"
+      }
+    },
     "mailcomposer": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz",
@@ -9818,6 +10020,15 @@
         "set-immediate-shim": "1.0.1"
       }
     },
+    "rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+      "dev": true,
+      "requires": {
+        "resolve": "1.7.1"
+      }
+    },
     "redent": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
@@ -10147,9 +10358,9 @@
       }
     },
     "rxjs": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.0.tgz",
-      "integrity": "sha512-qBzf5uu6eOKiCZuAE0SgZ0/Qp+l54oeVxFfC2t+mJ2SFI6IB8gmMdJHs5DUMu5kqifqcCtsKS2XHjhZu6RKvAw==",
+      "version": "6.3.3",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+      "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
       "requires": {
         "tslib": "1.9.0"
       }
@@ -10496,6 +10707,17 @@
         "jsonify": "0.0.0"
       }
     },
+    "shelljs": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
+      "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
+      "dev": true,
+      "requires": {
+        "glob": "7.1.2",
+        "interpret": "1.1.0",
+        "rechoir": "0.6.2"
+      }
+    },
     "signal-exit": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@@ -10831,6 +11053,12 @@
       "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
       "dev": true
     },
+    "sourcemap-codec": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz",
+      "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==",
+      "dev": true
+    },
     "spdx-correct": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
@@ -11520,42 +11748,6 @@
         }
       }
     },
-    "tsickle": {
-      "version": "0.29.0",
-      "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.29.0.tgz",
-      "integrity": "sha512-JpID0Lv8/irRtPmqJJxb5fCwfZhjZeKmav9Zna7UjqVuJoSbI49Wue/c2PPybX1SbRrjl7bbI/JsCl0dSUJygA==",
-      "dev": true,
-      "requires": {
-        "minimist": "1.2.0",
-        "mkdirp": "0.5.1",
-        "source-map": "0.6.1",
-        "source-map-support": "0.5.6"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "source-map-support": {
-          "version": "0.5.6",
-          "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
-          "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
-          "dev": true,
-          "requires": {
-            "buffer-from": "1.0.0",
-            "source-map": "0.6.1"
-          }
-        }
-      }
-    },
     "tslib": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz",
@@ -11673,9 +11865,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "2.7.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.2.tgz",
-      "integrity": "sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw==",
+      "version": "3.1.6",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz",
+      "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==",
       "dev": true
     },
     "uglify-js": {
diff --git a/package.json b/package.json
index 3fd2d28..1c6ee98 100644
--- a/package.json
+++ b/package.json
@@ -12,29 +12,30 @@
   },
   "private": true,
   "dependencies": {
-    "@angular/animations": "6.0.4",
-    "@angular/cdk": "^6.0.0",
-    "@angular/common": "6.0.4",
-    "@angular/compiler": "6.0.4",
-    "@angular/core": "6.0.4",
-    "@angular/forms": "6.0.4",
-    "@angular/http": "6.0.4",
-    "@angular/material": "^6.0.0",
-    "@angular/platform-browser": "6.0.4",
-    "@angular/platform-browser-dynamic": "6.0.4",
-    "@angular/router": "6.0.4",
+    "@angular/animations": "7.1.1",
+    "@angular/cdk": "^7.1.0",
+    "@angular/common": "7.1.1",
+    "@angular/compiler": "7.1.1",
+    "@angular/core": "7.1.1",
+    "@angular/flex-layout": "^7.0.0-beta.19",
+    "@angular/forms": "7.1.1",
+    "@angular/http": "7.1.1",
+    "@angular/material": "^7.1.0",
+    "@angular/platform-browser": "7.1.1",
+    "@angular/platform-browser-dynamic": "7.1.1",
+    "@angular/router": "7.1.1",
     "core-js": "^2.4.1",
     "hammerjs": "^2.0.8",
     "keypair": "^1.0.1",
     "node-forge": "^0.7.5",
-    "rxjs": "^6.2.0",
+    "rxjs": "^6.3.3",
     "rxjs-compat": "^6.2.0",
     "zone.js": "^0.8.26"
   },
   "devDependencies": {
     "@angular/cli": "6.0.8",
-    "@angular/compiler-cli": "6.0.4",
-    "@angular/language-service": "6.0.4",
+    "@angular/compiler-cli": "7.1.1",
+    "@angular/language-service": "7.1.1",
     "@types/jasmine": "~2.8.3",
     "@types/jasminewd2": "~2.0.2",
     "@types/node": "~6.0.60",
@@ -49,7 +50,7 @@
     "protractor": "~5.1.2",
     "ts-node": "~4.1.0",
     "tslint": "~5.9.1",
-    "typescript": "2.7.2",
+    "typescript": "3.1.6",
     "@angular-devkit/build-angular": "~0.6.8"
   }
 }
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index dcd7bea..bb6fdc4 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -3,6 +3,9 @@ import { RouterModule, Routes } from '@angular/router';
 import { LauncherComponent } from './launcher/launcher.component';
 import { KeygenComponent } from './keygen/keygen.component';
 import { ConnectingComponent } from './connecting/connecting.component';
+import { TransferComponent } from './transfer/transfer.component';
+import { ShareconnectComponent } from './shareconnect/shareconnect.component';
+
 
 // import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
 
@@ -13,7 +16,10 @@ const routes: Routes = [
   { path: 'finishlaunch', component: LauncherComponent},
   { path: 'cancellaunch', component: LauncherComponent},
   { path: 'sshauthz_callback', component: KeygenComponent},
-  { path: 'connecting', component: ConnectingComponent }
+  { path: 'connecting', component: ConnectingComponent },
+  { path: 'transfer', component: TransferComponent },
+  { path: 'shareconnect', component: ShareconnectComponent }
+
 
   // { path: 'sshauthz_callback', component: LauncherComponent}
 
diff --git a/src/app/app.component.css b/src/app/app.component.css
index 31ef901..8b13789 100644
--- a/src/app/app.component.css
+++ b/src/app/app.component.css
@@ -1,10 +1 @@
-.fill-remaining-space {
-  /* This fills the remaining space, by using flexbox.
-     Every toolbar row uses a flexbox row layout. */
-  flex: 1 1 auto;
-}
-.fill-horizontal-space {
-  /* This fills the remaining space, by using flexbox.
-     Every toolbar row uses a flexbox row layout. */
-  flex: 0 1 auto;
-}
+
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 11d6bd5..a54e574 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,5 +1,2 @@
-
-
-<router-outlet class="fill-remaining-space"></router-outlet>
-
-<!-- <app-teserrors></app-teserrors> -->
+<!-- <router-outlet class="fill-remaining-space"></router-outlet> -->
+<router-outlet></router-outlet>
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 355c770..6077c5e 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -1,6 +1,10 @@
 import { Component } from '@angular/core';
 import { TesService } from './tes.service';
 import { AuthorisationService} from './authorisation.service';
+import { ComputesitesService} from './computesites.service';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import { MatSnackBar } from '@angular/material';
+
 
 @Component({
   selector: 'app-root',
@@ -12,16 +16,39 @@ export class AppComponent {
   private loggedin: Boolean = false;
   private errmsg: string='';
   public authorised: boolean;
+  public statusMsg: BehaviorSubject<string>;
+  private snackBarRef: any;
+
   // private testingAuth: boolean;
 
-  constructor(private tesService: TesService, private authService: AuthorisationService) {
+  constructor(private tesService: TesService,
+              private authService: AuthorisationService,
+              private computesitesService: ComputesitesService,
+              public snackBar: MatSnackBar,) {
+
    };
 
   ngOnInit() {
     // this.tesService.testingAuth.subscribe(testingAuth => { this.testingAuth = testingAuth; console.log('testingAuth updated'+this.testingAuth) });
     // this.testingAuth = false;
+    this.statusMsg = new BehaviorSubject<any>('');
+    this.tesService.setStatusMsg(this.statusMsg);
+    this.computesitesService.setStatusMsg(this.statusMsg);
+    this.authService.setStatusMsg(this.statusMsg);
+    this.statusMsg.subscribe(msg => this.displayMessage(msg));
   }
 
+
+  private displayMessage(msg) {
+   if (msg === null || msg == "") {
+     console.log('attempting to dismis snackbar');
+     if (this.snackBarRef != undefined) {
+       this.snackBarRef.dismiss()
+     }
+   } else {
+     this.snackBarRef = this.snackBar.open(msg,'Dismiss');
+   }
+ }
   // login() {
   //   console.log("login");
   //   this.authService.login()
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 44d79aa..4c53c93 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -17,17 +17,22 @@ import { MatToolbarModule } from '@angular/material';
 import { MatDialogModule, MatDialog } from '@angular/material';
 import { MatSnackBarModule } from "@angular/material";
 import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatProgressBarModule } from '@angular/material/progress-bar';
 import { NoopAnimationsModule } from '@angular/platform-browser/animations';
 import { ComputesitesService } from './computesites.service';
 import { StrudelappsService } from './strudelapps.service';
 import { AuthorisationService } from './authorisation.service';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { FileExplorerModule } from './file-explorer/file-explorer.module';
+
 
 import { TesService} from './tes.service';
+import { SubmitAppService } from './submit-app.service';
 import { FormsModule } from '@angular/forms';
 import { HttpClientModule } from '@angular/common/http';
 import { JobComponent } from './job/job.component';
 // import { SiteselectionComponent } from './siteselection/siteselection.component';
-import { TeserrorsComponent } from './teserrors/teserrors.component';
+// import { TeserrorsComponent } from './teserrors/teserrors.component';
 import { AppRoutingModule } from './/app-routing.module';
 import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
 // import { LogindialogComponent } from './logindialog/logindialog.component';
@@ -39,6 +44,10 @@ import { MatSidenavModule, MatExpansionModule, MatIconModule } from '@angular/ma
 import { StrudelapplistComponent } from './strudelapplist/strudelapplist.component';
 import { KeygenComponent } from './keygen/keygen.component';
 import { ConnectingComponent } from './connecting/connecting.component';
+import { TransferComponent } from './transfer/transfer.component';
+import { Sv2SideNavComponent } from './sv2-side-nav/sv2-side-nav.component';
+import { ShareconnectComponent } from './shareconnect/shareconnect.component';
+// import { FileExplorerModule } from './file-explorer/file-explorer.module';
 
 
 
@@ -50,14 +59,17 @@ import { ConnectingComponent } from './connecting/connecting.component';
     JoblistComponent,
     JobComponent,
     // SiteselectionComponent,
-    TeserrorsComponent,
+    // TeserrorsComponent,
     TokenextractorComponent,
     LogoutdialogComponent,
     ModaldialogComponent,
     // LaunchdialogComponent,
     StrudelapplistComponent,
     KeygenComponent,
-    ConnectingComponent
+    ConnectingComponent,
+    TransferComponent,
+    Sv2SideNavComponent,
+    ShareconnectComponent
   ],
   imports: [
   BrowserModule,
@@ -80,10 +92,14 @@ import { ConnectingComponent } from './connecting/connecting.component';
   MatExpansionModule,
   MatIconModule,
   MatSnackBarModule,
+  FileExplorerModule,
+  FlexLayoutModule,
+  MatProgressBarModule,
+
 
   ],
   entryComponents: [ ModaldialogComponent, LogoutdialogComponent,  ],
-  providers: [ StrudelappsService, ComputesitesService, TesService, MatDialog, AuthorisationService],
+  providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService],
   bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/src/app/authorisation.service.ts b/src/app/authorisation.service.ts
index 51fa587..0047aa7 100644
--- a/src/app/authorisation.service.ts
+++ b/src/app/authorisation.service.ts
@@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Observable';
 import { of } from 'rxjs/observable/of';
 import { catchError, map, tap } from 'rxjs/operators';
 import { ActivatedRoute, Router } from '@angular/router';
-import {LocationStrategy} from '@angular/common';
+import {LocationStrategy, Location} from '@angular/common';
 // import { keypair } from 'keypair';
 import * as keypair from 'keypair';
 import * as forge from "node-forge";
@@ -13,6 +13,8 @@ import {BehaviorSubject} from 'rxjs/BehaviorSubject';
 import {Subject} from 'rxjs/Subject';
 import {TesService} from './tes.service';
 import { throwError } from 'rxjs';
+import { environment } from '../environments/environment';
+
 
 
 export class SshauthzServer {}
@@ -24,26 +26,38 @@ export class AuthorisationService {
   // public token: BehaviorSubject<AuthToken>;
   public token: AuthToken
   public SshAuthzServers: BehaviorSubject<SshAuthzServer[]>;
-  public readyToNavigate: Subject<Boolean>;
+  public readyToNavigate: Subject<[Boolean,string]>;
   public progress: Subject<string>;
   // private keyCert: Subject<KeyCert>;
+  private backendURI: string;
+  public statusMsg: BehaviorSubject<string>;
+  public agentContents: BehaviorSubject<any>;
+  public keys: KeyCert[];
 
 
   constructor(private http: HttpClient,
               private locationStrategy: LocationStrategy,
               private route: ActivatedRoute,
               private router: Router,
-              private tesService: TesService) {
+              private location: Location) {
                 console.log('created AuthorisationService');
                 // this.token = new BehaviorSubject<AuthToken>(new AuthToken('',''));
-                this.readyToNavigate = new Subject<Boolean>();
-                this.readyToNavigate.next(false);
+                this.readyToNavigate = new Subject<[Boolean,string]>();
+                this.readyToNavigate.next([false,'']);
                 this.progress = new Subject<string>();
                 this.progress.next("");
                 // this.token.subscribe(token => this.getCert(token));
                 this.route.fragment.subscribe(frag => this.storeToken(frag));
                 this.SshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]);
+                this.backendURI = environment.tesurl;
+                this.agentContents = new BehaviorSubject(null);
+                this.keys = [];
                 this.getSshAuthzServers();
+                this.keys = [];
+ }
+
+ public setStatusMsg(statusMsg: BehaviorSubject<any>) {
+   this.statusMsg = statusMsg;
  }
 
  getSshAuthzServers() {
@@ -56,6 +70,7 @@ export class AuthorisationService {
 
  updateSshAuthzServers(resp) {
    this.SshAuthzServers.next(<SshAuthzServer[]>resp);
+   console.log('sshauthzservers set to',this.SshAuthzServers.value);
  }
 
  storeToken(frag: string) {
@@ -109,60 +124,163 @@ export class AuthorisationService {
 
     let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.sshauthzservice.sign};
 
-    console.log('posting to getcert',this.tesService.Base);
-    this.http.post<any>(this.tesService.Base+'/getcert',data, options)
-                       .pipe(catchError(this.handleError('getCert',[])))
+    console.log('posting to getcert',this.backendURI);
+    this.http.post<any>(this.backendURI+'/getcert',data, options)
+                       // .pipe(catchError(this.handleError([])))
                        .subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.sshauthzservice),
                                   error => this.httperrorLogout(error,token.sshauthzservice));
    console.log('getcert complete');
  }
 
+public getKeys(id?: Identity) {
+  try{
+    return JSON.parse(sessionStorage.getItem('keys'));
+  } catch {
+    return [];
+  }
+}
 
  makeKeyCert(key: string, resp, sshauthzservice: SshAuthzServer) {
    let keyCert = new KeyCert()
    keyCert.key = key;
    keyCert.cert = resp['cert'];
+   console.log('in make KeyCert',resp);
    console.log('updating keycert',keyCert);
-   this.tesService.sshAdd(keyCert);
+   var keys: KeyCert[] = [];
+   try{
+     keys = JSON.parse(sessionStorage.getItem('keys'));
+   } catch {
+     keys = [];
+   }
+   if (keys === null) {
+     keys = [];
+   }
+   keys.push(keyCert);
+   sessionStorage.setItem('keys',JSON.stringify(keys))
+   this.sshAdd(keyCert);
+   let path=localStorage.getItem('path');
+   console.log('authorisation complete, ready to navigate back to the correct view',path);
+
    // this.tesService.keyCert.next(keyCert);
    // As soon as the certificate has been generated, we log back out of the signing server
    if (!(sshauthzservice.logout === null)) {
      window.open(sshauthzservice.logout);
    }
-   this.readyToNavigate.next(true);
+   this.readyToNavigate.next([true,path]);
+ }
+
+ public querySshAgentError(error: any) {
+   this.agentContents.next([]);
+   if (error.status == 0) {
+     this.statusMsg.next("A network error occured. Are you connected to the internet?")
+   }
+ }
+
+ public updateAgentContents() {
+   this.statusMsg.next("Updating the list of available accounts");
+   let headers = new HttpHeaders();
+   let options = { headers: headers, withCredentials: true};
+   var anyvar: any;
+   this.http.get<any>(this.backendURI+'/sshagent',options)
+                     // .pipe(catchError(this.handleError(anyvar)))
+                     .subscribe(resp => { console.log('sshagent returned',resp); this.agentContents.next(resp); this.statusMsg.next("") },
+                                error => this.querySshAgentError(error));
+                     // .subscribe(resp => this.computeSitesService.updateIdentities(resp),
+                     //            error => this.httperror(error))
+ }
+
+ private killAgent() {
+   this.statusMsg.next("Logging out")
+   let headers = new HttpHeaders();
+   let options = { headers: headers, withCredentials: true};
+   var anyvar: any;
+   this.http.delete<any>(this.backendURI+'/sshagent',options)
+                     .pipe(catchError(this.handleError(anyvar)))
+                     .subscribe(resp => this.updateAgentContents(),
+                                error => this.httperror(error));
+ }
+
+ public logout(): Boolean {
+   this.killAgent();
+   return true;
+ }
+
+
+
+ public sshAdd(keyCert: KeyCert) {
+   console.log('in authorisation service sshAdd');
+   if (keyCert.key == undefined) {
+     return;
+   }
+   let headers = new HttpHeaders();
+   let options = { headers: headers, withCredentials: true};
+   var anyvar: any;
+
+   this.statusMsg.next("Authorising ...")
+   let data = {'key': keyCert.key, 'cert': keyCert.cert};
+   this.http.post<any>(this.backendURI+'/sshagent',data,options)
+                       .pipe(catchError(this.handleError(anyvar)))
+                       .subscribe(resp => this.updateAgentContents(),
+                                  error => this.httperror(error))
  }
 
  public login(authservice: SshAuthzServer) {
    let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback";
    let nonce="asdfzxcv";
+
+   console.log(this.location.path());
+
+
    localStorage.setItem('authservice', JSON.stringify([authservice,nonce]));
+   localStorage.setItem('path', this.location.path());
+   console.log('login activated, pathname stored as',window.location.pathname);
    if (authservice.scope == null) {
      window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id);
    } else {
      window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id+"&scope="+authservice.scope);
-
    }
  }
 
+ // private handleError(error: HttpErrorResponse) {
+ //   if (error.error instanceof ErrorEvent) {
+ //     console.log('instance of ErrorEvent');
+ //     console.log(error);
+ //     // A client-side or network error occurred. Handle it accordingly.
+ //     return throwError("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running.");
+ //   } else {
+ //     console.log('NOT an instance of ErrorEvent');
+ //     console.log(error);
+ //     // Not sure if this code works correctly. It should update identities in case the error is
+ //     // that the user isn't allowed to run the job
+ //
+ //     var re = /identity/gi;
+ //     let searchresult = error.error.message.search(re);
+ //     if (searchresult != -1) {
+ //       // this.getIdentities();
+ //       return throwError('login expired, refreshing');
+ //     }
+ //     return throwError(error.error.message)
+ //     // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message);
+ //
+ //   }
+
  private httperror(error: any) {
-   this.tesService.statusMsg.next(error);
+   this.statusMsg.next(error);
    console.log(error);
  }
  private httperrorLogout(error: any,sshauthzservice: SshAuthzServer) {
-   this.tesService.statusMsg.next(error);
+   this.statusMsg.next(error);
    console.log(error);
    if (!(sshauthzservice.logout === null)) {
      window.open(sshauthzservice.logout);
    }
  }
 
- private handleError<T> (operation = 'operation', result?: T) {
+ private handleError<T> (result?: T) {
    return (error: any): Observable<T> => {
-     console.log('in handle error',operation);
-     console.log(error.status);
-     console.error(error);
+     console.log(error)
      if (error.status == 500) {
-       return throwError("The authorisation server encountered and error. Please try again in a few minutes")
+       return throwError("The backend server encountered and error. Please try again in a few minutes")
      }
      return throwError(error.message);
      // return of(result as T);
diff --git a/src/app/batchinterface.ts b/src/app/batchinterface.ts
index 68f2435..c7d78d4 100644
--- a/src/app/batchinterface.ts
+++ b/src/app/batchinterface.ts
@@ -2,5 +2,4 @@ export class BatchInterface {
   cancelcmd: string;
   submitcmd: string;
   statcmd: string;
-  internalfirewall: boolean;
 }
diff --git a/src/app/computesite.ts b/src/app/computesite.ts
index 695f355..f5d81fc 100644
--- a/src/app/computesite.ts
+++ b/src/app/computesite.ts
@@ -6,11 +6,15 @@ export class Computesite {
                 // submit stat and cancel
   name: string; // Human readable name
   host: string; // Login host
+  dtn: string;
+  dtnport: string;
+  lscmd: string;
   cafingerprint: string;  // Certificates contain a CA fingerprint. We use this
                           // to figure out which compute site a certificate is valid
                           // for
   appCatalog: BehaviorSubject<Strudelapp[]>;
   appCatalogUri: string;
+  internalfirewall: boolean; // Does a firewall exist within the site necessitating an extra level of ssh tunnel
 
 }
 
diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts
index 4577d55..a8d0dcd 100644
--- a/src/app/computesites.service.ts
+++ b/src/app/computesites.service.ts
@@ -5,6 +5,9 @@ import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@a
 import { Observable } from 'rxjs/Observable';
 import { of } from 'rxjs/observable/of';
 import { catchError, map, tap } from 'rxjs/operators';
+import { Identity } from './identity';
+import { TesService } from './tes.service';
+import { AuthorisationService} from './authorisation.service';
 
 
 
@@ -13,24 +16,54 @@ import { catchError, map, tap } from 'rxjs/operators';
 })
 export class ComputesitesService {
   public computesites: BehaviorSubject<Computesite[]>;
+  public identities: BehaviorSubject<Identity[]>;
+  public appidentities: BehaviorSubject<Identity[]>;
+  public ftidentities: BehaviorSubject<Identity[]>;
+  private statusMsg: BehaviorSubject<any>;
 
-  constructor(private http: HttpClient,) {
+  constructor(private http: HttpClient, private authorisationService: AuthorisationService) {
     this.computesites = new BehaviorSubject<Computesite[]>([]);
+    this.identities = new BehaviorSubject<Identity[]>([]);
+    this.appidentities = new BehaviorSubject<Identity[]>([]);
+    this.ftidentities = new BehaviorSubject<Identity[]>([]);
+
     this.computesites.subscribe(computesites => this.getStrudelApps(computesites))
     this.getComputeSites();
+    this.authorisationService.agentContents.subscribe(ac => this.updateIdentities(ac));
+  }
+
+  public setStatusMsg(statusMsg: BehaviorSubject<any>) {
+    this.statusMsg = statusMsg;
   }
 
   getStrudelApps(computesites: Computesite[]) {
     for (let s of computesites) {
       let headers = new HttpHeaders();
       let options = { headers: headers, withCredentials: false};
-      this.http.get<Strudelapp[]>(s.appCatalogUri,options)
-                       .pipe(catchError(this.handleError('getStrudelApps')))
-                       .subscribe(resp => this.updateStrudelApps(s.appCatalog,resp));
+      if (s.appCatalogUri !== null) {
+        this.http.get<Strudelapp[]>(s.appCatalogUri,options)
+                         .pipe(catchError(this.handleError('getStrudelApps')))
+                         .subscribe(resp => this.updateStrudelApps(s.appCatalog,resp,s.name));
+      }
     }
 
   }
 
+
+  updateStrudelApps(appCatalog: BehaviorSubject<Strudelapp[]>,apps, sitename) {
+    var sapps: Strudelapp[];
+    var localapps: Strudelapp[];
+    sapps = <Strudelapp[]>apps;
+
+    localapps = JSON.parse(localStorage.getItem(sitename+'-apps'))
+    if (localapps !== null)  {
+      for (let a of localapps) {
+        sapps.push(a);
+      }
+    }
+    appCatalog.next(sapps);
+  }
+
   private handleError<T> (operation = 'operation', result?: T) {
     return (error: any): Observable<T> => {
       console.error(error);
@@ -38,9 +71,6 @@ export class ComputesitesService {
     };
   }
 
-  updateStrudelApps(appCatalog: BehaviorSubject<Strudelapp[]>,apps) {
-    appCatalog.next(<Strudelapp[]>apps);
-  }
 
   getComputeSites() {
     let headers = new HttpHeaders();
@@ -51,12 +81,21 @@ export class ComputesitesService {
   }
 
   updateComputeSites(resp) {
+    var localcomputesites: Computesite[] = [];
     var computesites: Computesite[] = []
+    localcomputesites = JSON.parse(localStorage.getItem('localcomputesites'))
+    if (localcomputesites !== null ) {
+      for (let cs of localcomputesites) {
+        cs.appCatalog  = new BehaviorSubject<Strudelapp[]>([]);
+        computesites.push(cs);
+      }
+    }
     for (let cs of resp) {
       let computesite = <Computesite>cs;
       computesite.appCatalog = new BehaviorSubject<Strudelapp[]>([])
       computesites.push(computesite);
     }
+    console.log('updating compute sites');
     this.computesites.next(computesites);
   }
 
@@ -64,4 +103,47 @@ export class ComputesitesService {
     return this.computesites.value;
   }
 
+  private siteMatch(cert: any, cs: Computesite): string {
+    var fp: string = (<Computesite>cs).cafingerprint;
+    if ('Signing CA' in cert) {
+      for (let ca of cert['Signing CA']) {
+        if (ca == fp) {
+          return cert['Principals'][0]
+        }
+      }
+    }
+    return null;
+  }
+
+  private updateIdentities(resp) {
+    let certs = resp;
+    var cs: Computesite;
+    var identities: Identity[] = [];
+    var appidentities: Identity[] = [];
+    var ftidentities: Identity[] = [];
+    console.log('compute sites value', this.computesites.value);
+    console.log('certs',certs);
+    for (cs of this.computesites.value) {
+      for (let i in certs) {
+        let principal = this.siteMatch(certs[i],cs);
+        if (principal != null) {
+            let id = new Identity(principal,cs);
+            identities.push(id);
+            if (cs.appCatalogUri != null) {
+              appidentities.push(id);
+            }
+            if (cs.dtn != null ) {
+              ftidentities.push(id);
+            }
+          }
+        }
+      }
+      this.identities.next(identities);
+      this.ftidentities.next(ftidentities);
+      console.log('setting ftidentities');
+      console.log(this.ftidentities.value);
+      this.appidentities.next(appidentities);
+
+    }
+
 }
diff --git a/src/app/teserrors/teserrors.component.css b/src/app/connecting/connecting.component.css
similarity index 100%
rename from src/app/teserrors/teserrors.component.css
rename to src/app/connecting/connecting.component.css
diff --git a/src/app/connecting/connecting.component.html b/src/app/connecting/connecting.component.html
new file mode 100644
index 0000000..de96f16
--- /dev/null
+++ b/src/app/connecting/connecting.component.html
@@ -0,0 +1,11 @@
+<mat-toolbar color="primary">
+  <mat-toolbar-row>
+    <button mat-icon-button><mat-icon>menu</mat-icon></button>
+
+    <span>Strudel v2.0</span>
+    <span class="fill-horizontal-space"></span>
+    </mat-toolbar-row>
+  </mat-toolbar>
+  <mat-card>
+    Creating a secure connection ... please wait
+  </mat-card>
diff --git a/src/app/connecting/connecting.component.spec.ts b/src/app/connecting/connecting.component.spec.ts
new file mode 100644
index 0000000..9992bb0
--- /dev/null
+++ b/src/app/connecting/connecting.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ConnectingComponent } from './connecting.component';
+
+describe('ConnectingComponent', () => {
+  let component: ConnectingComponent;
+  let fixture: ComponentFixture<ConnectingComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ConnectingComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ConnectingComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/connecting/connecting.component.ts b/src/app/connecting/connecting.component.ts
new file mode 100644
index 0000000..90ee12b
--- /dev/null
+++ b/src/app/connecting/connecting.component.ts
@@ -0,0 +1,73 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http';
+import { catchError } from 'rxjs/operators';
+import { throwError } from 'rxjs';
+
+
+
+@Component({
+  selector: 'app-connecting',
+  templateUrl: './connecting.component.html',
+  styleUrls: ['./connecting.component.css']
+})
+export class ConnectingComponent implements OnInit {
+
+  constructor( private router: Router, private activatedRoute: ActivatedRoute, private http: HttpClient ) { }
+
+  ngOnInit() {
+    this.activatedRoute
+      .queryParams
+      .subscribe(params => {
+          this.doConnect(params);
+      });
+    }
+
+  buildParams(params: { [key: string]: any;}): string {
+    let newparams = new URLSearchParams;
+    Object.entries(params).forEach( ([key,value]) => newparams.set(key,value));
+    return newparams.toString();
+  }
+
+  doConnect(params: { [key: string]: any;}) {
+    console.log('doing connection');
+    console.log(params);
+    let url = params['connecturl'];
+    let headers = new HttpHeaders();
+    let options = { headers: headers, withCredentials: true};
+    let paramstr = this.buildParams(params);
+    // let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]);
+    this.http.get<HttpResponse<any>>(url+'?'+paramstr,options)
+                     .pipe(catchError(this.handleError))
+                     .subscribe(resp => this.redirect(resp),
+                                error => this.httperror(error));
+
+
+  }
+  redirect(resp: HttpResponse<any>) {
+    let loc = resp['location'];
+    console.log('updating window location to',loc);
+    window.location.assign(loc);
+  };
+
+
+  private httperror(errorstr: string) {
+    console.log(errorstr);
+  }
+
+  private handleError(error: HttpErrorResponse) {
+    if (error.error instanceof ErrorEvent) {
+      console.log('instance of ErrorEvent');
+      console.log(error);
+      // A client-side or network error occurred. Handle it accordingly.
+      return throwError("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running.");
+    } else {
+      console.log('NOT an instance of ErrorEvent');
+      console.log(error);
+      return throwError(error.error.message)
+      // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message);
+
+    }
+  }
+
+}
diff --git a/src/app/file-explorer/CREDIT b/src/app/file-explorer/CREDIT
new file mode 100644
index 0000000..e6c5700
--- /dev/null
+++ b/src/app/file-explorer/CREDIT
@@ -0,0 +1 @@
+With credit to https://github.com/LukasMarx/angular-file-manager
diff --git a/src/app/file-explorer/file-explorer.component.css b/src/app/file-explorer/file-explorer.component.css
new file mode 100644
index 0000000..e2645d5
--- /dev/null
+++ b/src/app/file-explorer/file-explorer.component.css
@@ -0,0 +1,39 @@
+:host {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+.file-or-folder {
+  padding: 8px;
+  overflow: hidden;
+}
+
+.file-or-folder-icon {
+  width: 50px;
+  height: 50px;
+  font-size: 50px;
+}
+
+.pointer {
+  cursor: pointer;
+}
+
+.spacer {
+  flex: 1 1 auto;
+}
+.file-table {
+  /* flex: 1 1 auto; */
+  height: 400px;
+  /* width: 100%; */
+}
+
+.basic-container {
+  height: 400px;
+}
diff --git a/src/app/file-explorer/file-explorer.component.html b/src/app/file-explorer/file-explorer.component.html
new file mode 100644
index 0000000..9407f32
--- /dev/null
+++ b/src/app/file-explorer/file-explorer.component.html
@@ -0,0 +1,139 @@
+<!--
+<mat-toolbar>
+  <mat-icon *ngIf="canNavigateUp" class="pointer" (click)="navigateUp()">arrow_upwards</mat-icon>
+  <span style="margin-left: 8px">{{path || 'Files'}}</span>
+  <span class="spacer"></span>
+</mat-toolbar> -->
+
+
+<!-- <div class="container" fxFlex fxLayout="row" fxLayoutAlign="space-between stretch"> -->
+  <!-- <div class="content" fxFlex fxLayout="row"> -->
+    <!-- <mat-grid-list cols="4" rowHeight="100px" fxFlex>
+      <mat-grid-tile *ngFor="let element of fileElements" class=file-or-folder>
+        <span [matMenuTriggerFor]="rootMenu" [matMenuTriggerData]="{element: element}" #menuTrigger="matMenuTrigger"></span>
+        <div fxLayout="column" fxLayoutAlign="space-between center" (click)="navigate(element)" (contextmenu)="openMenu($event, menuTrigger)">
+          <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="element.isFolder">folder</mat-icon>
+          <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="!element.isFolder">insert_drive_file</mat-icon>
+          <span>{{element.name}}</span>
+        </div>
+      </mat-grid-tile>
+    </mat-grid-list> -->
+
+
+    <!-- <cdk-virtual-scroll-viewport itemSize="50px" style="height: 400px"> -->
+    <!-- <cdk-virtual-scroll-viewport itemSize="50px">
+
+      <div *cdkVirtualFor="let element of fileElements">
+        <div fxFlex="row">
+        <div fxFlex="10%">
+          <mat-icon *ngIf="element.isFolder">folder</mat-icon>
+          <mat-icon *ngIf="!element.isFolder">insert_drive_file</mat-icon>
+        </div>
+        <div fxFlex>
+        {{ element.name }}
+      </div>
+        <div fxFlex="10%">
+        {{ element.size }}
+        </div>
+        </div>
+      </div>
+    </cdk-virtual-scroll-viewport> -->
+
+
+    <table mat-table [dataSource]="fileElements" style="width: 100%">
+      <ng-container matColumnDef="icon">
+        <th mat-header-cell *matHeaderCellDef></th>
+        <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)">
+          <mat-icon *ngIf="element.isFolder">folder</mat-icon>
+          <mat-icon *ngIf="!element.isFolder">insert_drive_file</mat-icon>
+          <div style="position: fixed"
+              [style.left]="contextMenuPosition.x"
+              [style.top]="contextMenuPosition.y"
+              [matMenuTriggerFor]="contextMenu"
+              [matMenuTriggerData]="{element: element}">
+          </div>
+        </td>
+      </ng-container>
+      <ng-container matColumnDef="name">
+        <th mat-header-cell *matHeaderCellDef>Name</th>
+        <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)">
+          {{ element.name }}
+          <div style="position: fixed"
+              [style.left]="contextMenuPosition.x"
+              [style.top]="contextMenuPosition.y"
+              [matMenuTriggerFor]="contextMenu"
+              [matMenuTriggerData]="{element: element}">
+          </div>
+        </td>
+      </ng-container>
+      <ng-container matColumnDef="size">
+        <th mat-header-cell *matHeaderCellDef>Size</th>
+        <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)">
+          {{ element.size }}
+          <div style="position: fixed"
+              [style.left]="contextMenuPosition.x"
+              [style.top]="contextMenuPosition.y"
+              [matMenuTriggerFor]="contextMenu"
+              [matMenuTriggerData]="{element: element}">
+          </div>
+        </td>
+      </ng-container>
+      <ng-container matColumnDef="online">
+        <th mat-header-cell *matHeaderCellDef>Online</th>
+        <td mat-cell *matCellDef="let element" (contextmenu)="onContextMenu($event, element)">
+          <div *ngIf="element.online !== undefined">
+            <mat-icon *ngIf="element.online">check</mat-icon>
+            <mat-icon *ngIf="!element.online">close</mat-icon>
+          </div>
+          <div style="position: fixed"
+              [style.left]="contextMenuPosition.x"
+              [style.top]="contextMenuPosition.y"
+              [matMenuTriggerFor]="contextMenu"
+              [matMenuTriggerData]="{element: element}">
+          </div>
+        </td>
+      </ng-container>
+      <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
+      <tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="navigate(row)"></tr>
+    </table>
+
+
+
+
+
+    <!-- <mat-list>
+      <mat-list-item *ngFor="let element of fileElements" class=file-or-folder>
+        <span [matMenuTriggerFor]="rootMenu" [matMenuTriggerData]="{element: element}" #menuTrigger="matMenuTrigger"></span>
+        <div fxFlex="100%">
+          <div fxLayout="row" fxLayoutAlign="start none" (click)="navigate(element)" (contextmenu)="openMenu($event, menuTrigger)">
+              <div>
+                <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="element.isFolder">folder</mat-icon>
+                <mat-icon color="primary" class="file-or-folder-icon pointer" *ngIf="!element.isFolder">insert_drive_file</mat-icon>
+              </div>
+              <span fxFlex>{{element.name}}</span>
+              <span>{{element.size}}</span>
+          </div>
+        </div>
+
+      </mat-list-item>
+    </mat-list> -->
+
+  <!-- </div> -->
+<!-- </div> -->
+
+<mat-menu #contextMenu="matMenu" [overlapTrigger]="false">
+  <ng-template matMenuContent let-element="element">
+    <!-- <button mat-menu-item [matMenuTriggerFor]="moveToMenu" [matMenuTriggerData]="{self: element}">
+      <mat-icon>open_with</mat-icon>
+      <span>Move To</span>
+    </button> -->
+    <button mat-menu-item (click)="emitSendFile(element)">
+      <mat-icon>send</mat-icon>
+      <span>Send file</span>
+    </button>
+    <button mat-menu-item (click)="openNewFolderDialog()">
+      <mat-icon>send</mat-icon>
+      <span>New directory</span>
+    </button>
+  </ng-template>
+</mat-menu>
diff --git a/src/app/file-explorer/file-explorer.component.ts b/src/app/file-explorer/file-explorer.component.ts
new file mode 100644
index 0000000..bd3751a
--- /dev/null
+++ b/src/app/file-explorer/file-explorer.component.ts
@@ -0,0 +1,109 @@
+import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
+import { FileElement } from './model/element';
+import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
+import { Observable } from 'rxjs/Observable';
+import { MatDialog } from '@angular/material/dialog';
+import { NewFolderDialogComponent } from './modals/newFolderDialog/newFolderDialog.component';
+import { RenameDialogComponent } from './modals/renameDialog/renameDialog.component';
+
+@Component({
+  selector: 'file-explorer',
+  templateUrl: './file-explorer.component.html',
+  styleUrls: ['./file-explorer.component.css']
+})
+export class FileExplorerComponent {
+  constructor(public dialog: MatDialog) {}
+
+  @ViewChild(MatMenuTrigger)
+  contextMenu: MatMenuTrigger;
+
+  contextMenuPosition = { x: '0px', y: '0px' };
+
+  @Input() fileElements: FileElement[];
+  @Input() canNavigateUp: string;
+  @Input() path: string;
+  @Input() disableSend: Boolean;
+  @Input() identities: any;
+
+  @Output() folderAdded = new EventEmitter<{ name: string }>();
+  @Output() elementRemoved = new EventEmitter<FileElement>();
+  @Output() elementRenamed = new EventEmitter<FileElement>();
+  @Output() elementMoved = new EventEmitter<{ element: FileElement; moveTo: FileElement }>();
+  @Output() navigatedDown = new EventEmitter<FileElement>();
+  @Output() navigatedUp = new EventEmitter();
+  @Output() sendFile = new EventEmitter<FileElement>();
+  @Output() idSelected = new EventEmitter<any>();
+  displayedColumns: string[] = ['icon','name','size','online'];
+  fixedSizeData = Array(10000).fill(30);
+
+  onContextMenu(event: MouseEvent, element: any) {
+    event.preventDefault();
+    console.log('called onContextMenu with element',element);
+    this.contextMenuPosition.x = event.clientX + 'px';
+    this.contextMenuPosition.y = event.clientY + 'px';
+    this.contextMenu.menuData = { 'element': element };
+    this.contextMenu.openMenu();
+  }
+
+  deleteElement(element: FileElement) {
+    this.elementRemoved.emit(element);
+  }
+
+  navigate(element: FileElement) {
+    console.log('attempting navigate down');
+
+    if (element.isFolder) {
+      console.log('attempting navigate down',element.name);
+
+      this.navigatedDown.emit(element);
+    }
+  }
+
+  navigateUp() {
+    console.log('attempting navigate up');
+    this.navigatedUp.emit();
+    console.log('emitted navigateUp');
+  }
+
+  moveElement(element: FileElement, moveTo: FileElement) {
+    this.elementMoved.emit({ element: element, moveTo: moveTo });
+  }
+
+  rowClicked(row) {
+    console.log('row clicked',row);
+  }
+
+  openNewFolderDialog() {
+    let dialogRef = this.dialog.open(NewFolderDialogComponent);
+    dialogRef.afterClosed().subscribe(res => {
+      if (res) {
+        this.folderAdded.emit({ name: res });
+      }
+    });
+  }
+
+  openRenameDialog(element: FileElement) {
+    let dialogRef = this.dialog.open(RenameDialogComponent);
+    dialogRef.afterClosed().subscribe(res => {
+      if (res) {
+        element.name = res;
+        this.elementRenamed.emit(element);
+      }
+    });
+  }
+
+  emitSendFile(element: FileElement) {
+    console.log('called emitSendFile with element',element);
+    this.sendFile.emit(element);
+  }
+
+  emitIdSelected(id: any) {
+    console.log('Im going to emit',id);
+    this.idSelected.emit(id);
+  }
+
+  openMenu(event: MouseEvent, viewChild: MatMenuTrigger) {
+    event.preventDefault();
+    viewChild.openMenu();
+  }
+}
diff --git a/src/app/file-explorer/file-explorer.module.ts b/src/app/file-explorer/file-explorer.module.ts
new file mode 100644
index 0000000..c8ee87e
--- /dev/null
+++ b/src/app/file-explorer/file-explorer.module.ts
@@ -0,0 +1,48 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { MatToolbarModule } from '@angular/material/toolbar';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { MatIconModule } from '@angular/material/icon';
+import { MatGridListModule } from '@angular/material/grid-list';
+import { MatListModule } from '@angular/material';
+import { MatMenuModule } from '@angular/material/menu';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MatDialogModule } from '@angular/material/dialog';
+import { MatInputModule } from '@angular/material/input';
+import { NewFolderDialogComponent } from './modals/newFolderDialog/newFolderDialog.component';
+import { MatButtonModule } from '@angular/material/button';
+import { FormsModule } from '@angular/forms';
+import { RenameDialogComponent } from './modals/renameDialog/renameDialog.component';
+import { FileExplorerComponent } from './file-explorer.component';
+import {MatTableModule} from '@angular/material/table';
+import {ScrollingModule} from '@angular/cdk/scrolling';
+import {MatSidenavModule} from '@angular/material/sidenav'
+import { MatSelectModule } from '@angular/material';
+
+
+// import {cdkScrollable} from '@angular/'
+
+@NgModule({
+  imports: [
+    CommonModule,
+    MatToolbarModule,
+    FlexLayoutModule,
+    MatIconModule,
+    MatGridListModule,
+    MatListModule,
+    MatMenuModule,
+    BrowserAnimationsModule,
+    MatDialogModule,
+    MatInputModule,
+    FormsModule,
+    MatButtonModule,
+    MatTableModule,
+    ScrollingModule,
+    MatSidenavModule,
+    MatSelectModule,
+  ],
+  declarations: [FileExplorerComponent, NewFolderDialogComponent, RenameDialogComponent],
+  exports: [FileExplorerComponent],
+  entryComponents: [NewFolderDialogComponent, RenameDialogComponent]
+})
+export class FileExplorerModule {}
diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.css b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html
new file mode 100644
index 0000000..275a3a6
--- /dev/null
+++ b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.html
@@ -0,0 +1,12 @@
+<h1 mat-dialog-title>Create a new folder</h1>
+
+<mat-dialog-content>
+  <mat-form-field class="example-full-width">
+    <input matInput placeholder="Folder Name" [(ngModel)]="folderName">
+  </mat-form-field>
+</mat-dialog-content>
+
+<mat-dialog-actions>
+  <button mat-raised-button mat-dialog-close>Cancel</button>
+  <button mat-raised-button [mat-dialog-close]="folderName" color="primary">OK</button>
+</mat-dialog-actions>
\ No newline at end of file
diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts
new file mode 100644
index 0000000..eb055a6
--- /dev/null
+++ b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.spec.ts
@@ -0,0 +1,28 @@
+/* tslint:disable:no-unused-variable */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { NewFolderDialogComponent } from './newFolderDialog.component';
+
+describe('NewFolderDialogComponent', () => {
+  let component: NewFolderDialogComponent;
+  let fixture: ComponentFixture<NewFolderDialogComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ NewFolderDialogComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(NewFolderDialogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts
new file mode 100644
index 0000000..5cb47ea
--- /dev/null
+++ b/src/app/file-explorer/modals/newFolderDialog/newFolderDialog.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-newFolderDialog',
+  templateUrl: './newFolderDialog.component.html',
+  styleUrls: ['./newFolderDialog.component.css']
+})
+export class NewFolderDialogComponent implements OnInit {
+  constructor(public dialogRef: MatDialogRef<NewFolderDialogComponent>) {}
+
+  folderName: string;
+
+  ngOnInit() {}
+}
diff --git a/src/app/file-explorer/modals/renameDialog/renameDialog.component.css b/src/app/file-explorer/modals/renameDialog/renameDialog.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/file-explorer/modals/renameDialog/renameDialog.component.html b/src/app/file-explorer/modals/renameDialog/renameDialog.component.html
new file mode 100644
index 0000000..dcc611c
--- /dev/null
+++ b/src/app/file-explorer/modals/renameDialog/renameDialog.component.html
@@ -0,0 +1,12 @@
+<h1 mat-dialog-title>Rename Element</h1>
+
+<mat-dialog-content>
+  <mat-form-field class="example-full-width">
+    <input matInput placeholder="Folder Name" [(ngModel)]="folderName">
+  </mat-form-field>
+</mat-dialog-content>
+
+<mat-dialog-actions>
+  <button mat-raised-button mat-dialog-close>Cancel</button>
+  <button mat-raised-button [mat-dialog-close]="folderName" color="primary">OK</button>
+</mat-dialog-actions>
\ No newline at end of file
diff --git a/src/app/file-explorer/modals/renameDialog/renameDialog.component.ts b/src/app/file-explorer/modals/renameDialog/renameDialog.component.ts
new file mode 100644
index 0000000..ccac0c2
--- /dev/null
+++ b/src/app/file-explorer/modals/renameDialog/renameDialog.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+
+@Component({
+  selector: 'app-renameDialog',
+  templateUrl: './renameDialog.component.html',
+  styleUrls: ['./renameDialog.component.css']
+})
+export class RenameDialogComponent implements OnInit {
+  constructor(public dialogRef: MatDialogRef<RenameDialogComponent>) {}
+
+  folderName: string;
+
+  ngOnInit() {}
+}
diff --git a/src/app/file-explorer/model/element.ts b/src/app/file-explorer/model/element.ts
new file mode 100644
index 0000000..9b91920
--- /dev/null
+++ b/src/app/file-explorer/model/element.ts
@@ -0,0 +1,6 @@
+export class FileElement {
+  isFolder: boolean;
+  name: string;
+  size: number;
+  online: boolean;
+}
diff --git a/src/app/identity.ts b/src/app/identity.ts
index 63a079b..4247872 100644
--- a/src/app/identity.ts
+++ b/src/app/identity.ts
@@ -5,9 +5,11 @@ export class Identity {
   username: string;
   site: Computesite;
   authservice: SshAuthzServer;
+  keyCerts: KeyCert[];
   constructor( username: string, site: Computesite) {
     this.username = username;
     this.site = site;
+    this.keyCerts = [];
   }
 
   copy_skip_catalog(): Identity {
@@ -18,6 +20,7 @@ export class Identity {
     id.site.host = this.site.host;
     id.site.name = this.site.name;
     id.site.cafingerprint = this.site.cafingerprint;
+    id.site.dtn = this.site.dtn;
     return id;
   }
   displayName(): string {
@@ -52,4 +55,5 @@ export class SshAuthzServer {
   icon: string;
   scope: string;
   logout: string;
+  userdefined: boolean;
 }
diff --git a/src/app/job.ts b/src/app/job.ts
index c4673f9..ec75181 100644
--- a/src/app/job.ts
+++ b/src/app/job.ts
@@ -1,6 +1,7 @@
 import {Computesite} from './computesite';
 import {Identity} from './identity';
-import {Strudelapp} from './strudelapp';
+import {Strudelapp, StrudelappInstance } from './strudelapp';
+
 export class Job {
   public name: string;
   public jobid: string;
@@ -10,4 +11,6 @@ export class Job {
   public batch_host: string;
   public identity: Identity;
   public app: Strudelapp;
+  public appinst: string;
+  public connectionState: number;
 }
diff --git a/src/app/job/job.component.html b/src/app/job/job.component.html
index 2fbde39..0bdb89c 100644
--- a/src/app/job/job.component.html
+++ b/src/app/job/job.component.html
@@ -1,5 +1,6 @@
 <mat-card>
   <mat-card-title>{{ jobdata.name }}</mat-card-title>
+  <div *ngIf="(jobdata.connectionState === undefined || jobdata.connectionState == 0)">
   <table>
     <tr>
       <td width="1000%">
@@ -24,4 +25,18 @@
       </td>
   </tr>
 </table>
+</div>
+<div *ngIf="jobdata.connectionState == 1">
+  Getting app parameters
+  <mat-progress-bar mode="determinate" value=30></mat-progress-bar>
+</div>
+<div *ngIf="jobdata.connectionState == 2">
+  Creating secure tunnels
+  <mat-progress-bar mode="determinate" value=60></mat-progress-bar>
+
+</div>
+<div *ngIf="jobdata.connectionState == 3">
+  Determining correct URL
+  <mat-progress-bar mode="determinate" value=90></mat-progress-bar>
+</div>
 </mat-card>
diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts
index 0998980..c7d191e 100644
--- a/src/app/job/job.component.ts
+++ b/src/app/job/job.component.ts
@@ -26,8 +26,12 @@ export class JobComponent implements OnInit {
       this.available = false;
     }
     this.tesService.busy.subscribe(busy => this.busy = busy);
-    if (this.jobdata.app.startscript === null) {
-      this.nocancel = true;
+    if (this.jobdata.app != null) {
+      if (this.jobdata.app.startscript === null) {
+        this.nocancel = true;
+      } else {
+        this.nocancel = false;
+      }
     } else {
       this.nocancel = false;
     }
diff --git a/src/app/joblist/joblist.component.html b/src/app/joblist/joblist.component.html
index ac77ade..be296f3 100644
--- a/src/app/joblist/joblist.component.html
+++ b/src/app/joblist/joblist.component.html
@@ -4,13 +4,13 @@
   </mat-card>
 </div>
 <div *ngFor="let id of identities"> -->
-
-  <mat-card class="fill-remaining-space">
+<mat-card style="width: 100%">
     <div *ngIf="identity == undefined" >
       Select your username an compute site on the left to see running jobs or start new jobs.
     </div>
     <div *ngIf="!(identity == undefined)">
-      <div *ngFor="let job of jobs[identity.repr()]">
+      <button mat-button (click)="refreshJobs()" style="width: 100%; text-align: left">Refresh</button>
+      <div *ngFor="let job of tesService.joblist | async">
         <app-job [jobdata]=job></app-job>
       </div>
     </div>
diff --git a/src/app/joblist/joblist.component.ts b/src/app/joblist/joblist.component.ts
index 5f2d2f1..883fec0 100644
--- a/src/app/joblist/joblist.component.ts
+++ b/src/app/joblist/joblist.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit, Input } from '@angular/core';
 import {TesService} from '../tes.service';
 import { Job } from '../job';
 import { Observable } from 'rxjs/Observable';
+import { Subscription, interval } from 'rxjs';
 import { Identity } from '../identity';
 
 
@@ -14,41 +15,41 @@ import { Identity } from '../identity';
 
 export class JoblistComponent implements OnInit {
   @Input() identity: Identity;
-  public jobs: {[id: string]: Job[] } = {};
-  public identities: Identity[];
+  // public jobs: {[id: string]: Job[] } = {};
   private displayedColumns = ['id'];
-  private jobsSubscription: any;
-  private idSubscription: any;
+  private timer
+  // private jobsSubscription: Subscription;
+  // private idSubscription: any;
+  private timerSub: Subscription;
 
 
   constructor(private tesService: TesService,) {
 }
 
   ngOnInit() {
-    this.jobsSubscription = this.tesService.joblist.subscribe(jobs => this.updateJobs(jobs));
-    // this.idSubscription = this.tesService.identities.subscribe(ids => this.updateIds(ids));
-
+    if (!(this.identity === undefined)) {
+      console.log("ini joblistComponent with id",this.identity);
+      this.tesService.getJobs(this.identity);
+    }
+    this.timerSub = interval(5000).subscribe(() => this.refreshJobs())
+    // this.jobsSubscription = this.tesService.joblist.subscribe(jobs => this.updateJobs(jobs));
   }
 
   public ngOnDestroy(): void {
-      if (this.jobsSubscription) {
-          this.jobsSubscription.unsubscribe();
+      if (this.timerSub) {
+          this.timerSub.unsubscribe();
       }
 
   }
-  // updateIds(identities: Identity[]) {
-  //   this.identities = identities;
+  //
+  //
+  // updateJobs(jobs) {
+  //   console.log('update jobs',jobs);
+  //   this.jobs = jobs;
   // }
 
-  updateJobs(jobs) {
-    console.log('update jobs',jobs);
-    this.jobs = jobs;
-
+  public refreshJobs(): void {
+    console.log('hit refresh with identitiy', this.identity);
+    this.tesService.getJobs(this.identity);
   }
-
-  // setId(identity) {
-  //   this.identity = identity;
-  // }
-
-
 }
diff --git a/src/app/keygen/keygen.component.css b/src/app/keygen/keygen.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/keygen/keygen.component.html b/src/app/keygen/keygen.component.html
new file mode 100644
index 0000000..c5a8e0b
--- /dev/null
+++ b/src/app/keygen/keygen.component.html
@@ -0,0 +1,11 @@
+<mat-toolbar color="primary">
+  <mat-toolbar-row>
+    <button mat-icon-button><mat-icon>menu</mat-icon></button>
+
+    <span>Strudel v2.0</span>
+    <span class="fill-horizontal-space"></span>
+    </mat-toolbar-row>
+  </mat-toolbar>
+  <mat-card>
+    Generating cryptographic tokens ... this should only take a few seconds
+  </mat-card>
diff --git a/src/app/teserrors/teserrors.component.spec.ts b/src/app/keygen/keygen.component.spec.ts
similarity index 55%
rename from src/app/teserrors/teserrors.component.spec.ts
rename to src/app/keygen/keygen.component.spec.ts
index 4a31e33..65b68d5 100644
--- a/src/app/teserrors/teserrors.component.spec.ts
+++ b/src/app/keygen/keygen.component.spec.ts
@@ -1,20 +1,20 @@
 import { async, ComponentFixture, TestBed } from '@angular/core/testing';
 
-import { TeserrorsComponent } from './teserrors.component';
+import { KeygenComponent } from './keygen.component';
 
-describe('TeserrorsComponent', () => {
-  let component: TeserrorsComponent;
-  let fixture: ComponentFixture<TeserrorsComponent>;
+describe('KeygenComponent', () => {
+  let component: KeygenComponent;
+  let fixture: ComponentFixture<KeygenComponent>;
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [ TeserrorsComponent ]
+      declarations: [ KeygenComponent ]
     })
     .compileComponents();
   }));
 
   beforeEach(() => {
-    fixture = TestBed.createComponent(TeserrorsComponent);
+    fixture = TestBed.createComponent(KeygenComponent);
     component = fixture.componentInstance;
     fixture.detectChanges();
   });
diff --git a/src/app/keygen/keygen.component.ts b/src/app/keygen/keygen.component.ts
new file mode 100644
index 0000000..d944f96
--- /dev/null
+++ b/src/app/keygen/keygen.component.ts
@@ -0,0 +1,46 @@
+import { Component, OnInit } from '@angular/core';
+import { AuthorisationService } from '../authorisation.service';
+import { Router, NavigationStart } from '@angular/router';
+import { timer } from 'rxjs/observable/timer';
+
+
+
+
+@Component({
+  selector: 'app-keygen',
+  templateUrl: './keygen.component.html',
+  styleUrls: ['./keygen.component.css']
+})
+export class KeygenComponent implements OnInit {
+
+  constructor(private router: Router, private authService: AuthorisationService) { }
+
+  ngOnInit() {
+    // The sequence is tricky here:
+    // 1) the auth service loads, examines the fragment and stores the OAuth2 Token required to generate certificates
+    // 2) The KeyGen component becomes visible providing a Spinner
+    // 3) The timer(0) executes once the component is visiable causing the authService to move onto the next phase
+    // 4) The authservice generates ssh keys in javascript (slow) and gets them signed (also slow)
+    // 5) The authservice indicates its done by setting readtToNavigate => true
+    // 6) the router navigates to the main page (removing the spinner etc)
+    // ready to navigate will be set once the keys are generated
+    this.authService.readyToNavigate.subscribe(readyToNavigate => this.navigate(readyToNavigate));
+    // use of a timer(0) means this will be executed AFTER the component is visiable (hopefully)
+    timer(0).subscribe( () => this.genKeys() );
+  }
+
+  genKeys() {
+    this.authService.getCert(this.authService.token)
+  }
+
+  activateToken(token) {
+    console.log('activate token called');
+  }
+
+  navigate(readyToNavigate: [Boolean,string]) {
+    if (readyToNavigate[0]) {
+      this.router.navigate([readyToNavigate[1]]);
+    }
+  }
+
+}
diff --git a/src/app/launcher/launcher.component.css b/src/app/launcher/launcher.component.css
index 67e3986..683ae23 100644
--- a/src/app/launcher/launcher.component.css
+++ b/src/app/launcher/launcher.component.css
@@ -1,10 +1,3 @@
-.wrapper {
-  /* display: flex;
-  flex-direction: column; */
-  flex: 1 1 auto;
-}
-.vspace {
-  /* display: flex;
-  flex-direction: column; */
-height: 500px;
+:host {
+  height: 100%;
 }
diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html
index 0315cba..642b748 100644
--- a/src/app/launcher/launcher.component.html
+++ b/src/app/launcher/launcher.component.html
@@ -1,3 +1,5 @@
+<div  fxFlex style="flex: 1 1 0%; box-sizing: border-box">
+<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" >
 <mat-toolbar color="primary">
   <mat-toolbar-row>
     <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button>
@@ -6,23 +8,15 @@
     <span class="fill-horizontal-space"></span>
     </mat-toolbar-row>
   </mat-toolbar>
-
-<mat-sidenav-container autosize class="fill-remaining-space">
+<mat-sidenav-container autosize autosize style="height: 100%; width: 100%">
   <mat-sidenav #idSideNav mode="side" opened>
-		<mat-accordion>
-			<mat-expansion-panel (click)=selectId(undefined)>
-				<mat-expansion-panel-header>
-          <mat-panel-title>
-          Login
-        </mat-panel-title>
-				</mat-expansion-panel-header>
-        <div *ngFor="let sshauthzserver of sshauthzservers">
-					<button mat-button (click)=login(sshauthzserver)>Login to {{ sshauthzserver.name }} </button>
-        </div>
-				<button mat-button (click)=logout()>Logout</button>
-			</mat-expansion-panel>
-			<div *ngFor="let id of identities">
-				<mat-expansion-panel>
+    <mat-accordion>
+      <app-sv2-side-nav></app-sv2-side-nav>
+      </mat-accordion>
+      <mat-divider></mat-divider>
+      <mat-accordion>
+			<div *ngFor="let id of computeSitesService.appidentities | async">
+				<mat-expansion-panel (click)=selectId(id)>
 					<mat-expansion-panel-header>
 						{{ id.displayName() }}
 					</mat-expansion-panel-header>
@@ -31,17 +25,17 @@
 						  <button mat-button (click)=openLaunchWindow(app,id)>{{ app.name }}</button>
             </div>
 					</div> -->
-          <app-strudelapplist [applist]=id.site.appCatalog [identity]=id></app-strudelapplist>
+          <app-strudelapplist [applist]=id.site.appCatalog [identity]="id"></app-strudelapplist>
 				</mat-expansion-panel>
 			</div>
 	</mat-accordion>
-
 	</mat-sidenav>
-  <mat-sidenav-content>
-		<app-joblist [identity]=identity></app-joblist>
-    <div class="vspace"></div>
 
-	</mat-sidenav-content>
+  <app-joblist [identity]="identitySubject | async"></app-joblist>
+  <div fxFlex></div>
+
 </mat-sidenav-container>
+</div>
+</div>
 
 <!-- <app-joblist></app-joblist> -->
diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts
index de59d57..dea07c4 100644
--- a/src/app/launcher/launcher.component.ts
+++ b/src/app/launcher/launcher.component.ts
@@ -18,6 +18,10 @@ import { LogoutdialogComponent } from '../logoutdialog/logoutdialog.component';
 // import { LaunchdialogComponent } from '../launchdialog/launchdialog.component';
 import { BatchInterface} from '../batchinterface';
 import { SshAuthzServer } from '../identity';
+import { ComputesitesService } from '../computesites.service';
+import { Router } from '@angular/router';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+
 
 
 
@@ -33,6 +37,8 @@ export class LauncherComponent implements OnInit {
   public app: Strudelapp;
   public authorised: boolean;
   public identity: Identity;
+  public identitySubject: BehaviorSubject<Identity>;
+
   public identities: Identity[];
   public sshauthzservers: SshAuthzServer[];
   private launchwindow: any;
@@ -40,33 +46,37 @@ export class LauncherComponent implements OnInit {
 
   constructor( public dialog: MatDialog,
                 private tesService: TesService,
-                private authService: AuthorisationService) {
+                private authService: AuthorisationService,
+                public computeSitesService: ComputesitesService,
+                private router: Router) {
     authService.SshAuthzServers.subscribe(o => {this.sshauthzservers = o});
     this.launchwindowWatcher = null;
+    this.identitySubject = new BehaviorSubject(undefined);
   }
 
   ngOnInit() {
     this.strudelapps = [];
-    this.identities = [];
-    this.tesService.identities.subscribe(identities => this.updateIdentities(identities));
+    // this.identities = [];
+    // this.computeSitesService.identities.subscribe(identities => this.updateIdentities(identities)
+    // this.tesService.identities.subscribe(identities => this.updateIdentities(identities));
     // setTimeout( () => { this.tesService.identities.subscribe(identities => this.updateIdentities(identities)); });
-    setTimeout( () => this.tesService.getIdentities() )
+    setTimeout( () => this.authService.updateAgentContents() )
   }
 
-  compareFn = (o1, o2) => {
-    // Funny "=>" function syntax gives me access to "this" as LauncherComponent
-    // otherwise "this" would be set to the mat-select object.
-    if (o2 === undefined) {
-
-      if (o1 === this.identity) {
-        return true;
-      }
-    }
-    if (o1 === o2) {
-      return true;
-    }
-    return false;
-  }
+  // compareFn = (o1, o2) => {
+  //   // Funny "=>" function syntax gives me access to "this" as LauncherComponent
+  //   // otherwise "this" would be set to the mat-select object.
+  //   if (o2 === undefined) {
+  //
+  //     if (o1 === this.identity) {
+  //       return true;
+  //     }
+  //   }
+  //   if (o1 === o2) {
+  //     return true;
+  //   }
+  //   return false;
+  // }
 
   updateIdentities(identities) {
     this.identities = identities;
@@ -84,6 +94,10 @@ login (sshauthzserver) {
   this.authService.login(sshauthzserver);
 }
 
+navTransfer() {
+  this.router.navigate(['transfer'])
+}
+
 logout() {
     let dialogRef = this.dialog.open(LogoutdialogComponent, {
       width: '250px',
@@ -106,6 +120,9 @@ logout() {
 
   selectId(id: Identity) {
     this.identity=id;
+    console.log('set identity subject');
+    this.identitySubject.next(id);
+    this.tesService.getJobs(id);
   }
 
 
diff --git a/src/app/logoutdialog/logoutdialog.component.ts b/src/app/logoutdialog/logoutdialog.component.ts
index 53a5c8a..e01f5d5 100644
--- a/src/app/logoutdialog/logoutdialog.component.ts
+++ b/src/app/logoutdialog/logoutdialog.component.ts
@@ -1,5 +1,5 @@
 import { Component, OnInit, Inject } from '@angular/core';
-import { TesService } from '../tes.service';
+import { AuthorisationService } from '../authorisation.service';
 import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
 
 
@@ -13,14 +13,14 @@ export class LogoutdialogComponent implements OnInit {
   constructor(
     public dialogRef: MatDialogRef<LogoutdialogComponent>,
     @Inject(MAT_DIALOG_DATA) public data: any,
-    private tesService: TesService) {
+    private authService: AuthorisationService) {
    }
 
   ngOnInit() {
   }
 
   onLogout() {
-    this.tesService.logout();
+    this.authService.logout();
     this.dialogRef.close();
   }
   onCancel() {
diff --git a/src/app/modaldialog/modaldialog.component.css b/src/app/modaldialog/modaldialog.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/modaldialog/modaldialog.component.html b/src/app/modaldialog/modaldialog.component.html
new file mode 100644
index 0000000..30c2523
--- /dev/null
+++ b/src/app/modaldialog/modaldialog.component.html
@@ -0,0 +1,10 @@
+<p>
+<mat-card>
+  <mat-card-title>Application help</mat-card-title>
+If you're having difficultly connecting to your Application please:
+<mat-list>
+  <mat-list-item>Only open one application at a time</mat-list-item>
+  <mat-list-item>Try holding shift and clicking the reload button in your browser</mat-list-item>
+  <mat-list-item>Close the application tab and use the connect button a second time.</mat-list-item>
+</mat-list>
+</mat-card>
diff --git a/src/app/modaldialog/modaldialog.component.spec.ts b/src/app/modaldialog/modaldialog.component.spec.ts
new file mode 100644
index 0000000..5da07fd
--- /dev/null
+++ b/src/app/modaldialog/modaldialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ModaldialogComponent } from './modaldialog.component';
+
+describe('ModaldialogComponent', () => {
+  let component: ModaldialogComponent;
+  let fixture: ComponentFixture<ModaldialogComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ModaldialogComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ModaldialogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/modaldialog/modaldialog.component.ts b/src/app/modaldialog/modaldialog.component.ts
new file mode 100644
index 0000000..760256f
--- /dev/null
+++ b/src/app/modaldialog/modaldialog.component.ts
@@ -0,0 +1,20 @@
+import { Component, OnInit, Inject  } from '@angular/core';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
+
+@Component({
+  selector: 'app-modaldialog',
+  templateUrl: './modaldialog.component.html',
+  styleUrls: ['./modaldialog.component.css']
+})
+export class ModaldialogComponent implements OnInit {
+
+  constructor(
+    public dialogRef: MatDialogRef<ModaldialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: any,
+) {
+   }
+
+  ngOnInit() {
+  }
+
+}
diff --git a/src/app/shareconnect/shareconnect.component.css b/src/app/shareconnect/shareconnect.component.css
new file mode 100644
index 0000000..683ae23
--- /dev/null
+++ b/src/app/shareconnect/shareconnect.component.css
@@ -0,0 +1,3 @@
+:host {
+  height: 100%;
+}
diff --git a/src/app/shareconnect/shareconnect.component.html b/src/app/shareconnect/shareconnect.component.html
new file mode 100644
index 0000000..c2ab412
--- /dev/null
+++ b/src/app/shareconnect/shareconnect.component.html
@@ -0,0 +1,38 @@
+<div fxFlex style="flex: 1 1 0%; box-sizing: border-box">
+<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" >
+    <mat-toolbar color="primary">
+      <mat-toolbar-row>
+        <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button>
+
+        <span>Strudel v2.0</span>
+        <span class="fill-horizontal-space"></span>
+      </mat-toolbar-row>
+    </mat-toolbar>
+
+<mat-sidenav-container #idSideNav autosize style="height: 100%; width: 100%">
+  <mat-sidenav #idSideNav mode="side" opened>
+      <app-sv2-side-nav></app-sv2-side-nav>
+  </mat-sidenav>
+
+    <div fxLayout="column" fxLayoutAlign="none" style="height: 100%; width: 100%">
+
+    <!-- <div style="height: 600px"></div> -->
+      <form>
+        <mat-form-field style="width: 100%; height: 100%">
+          <textarea fxFlex matInput placeholder="Paste the data sent via email" style="width: 100%; height: 100%" [(ngModel)]="data" name="data"></textarea>
+        </mat-form-field>
+      </form>
+
+    <div  style="width: 100%" fxLayout="row" fxLayoutAlign="none">
+      <div fxFlex></div>
+      <button mat-button (click)="onSubmit()">Connect</button>
+      <div fxFlex></div>
+    </div>
+  </div>
+
+
+
+    <!-- <mat-card style="width: 100%; box-sizing: border-box">Transfer tasks</mat-card> -->
+</mat-sidenav-container>
+</div>
+</div>
diff --git a/src/app/shareconnect/shareconnect.component.spec.ts b/src/app/shareconnect/shareconnect.component.spec.ts
new file mode 100644
index 0000000..8ee2c65
--- /dev/null
+++ b/src/app/shareconnect/shareconnect.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ShareconnectComponent } from './shareconnect.component';
+
+describe('ShareconnectComponent', () => {
+  let component: ShareconnectComponent;
+  let fixture: ComponentFixture<ShareconnectComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ShareconnectComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ShareconnectComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/shareconnect/shareconnect.component.ts b/src/app/shareconnect/shareconnect.component.ts
new file mode 100644
index 0000000..feb1dbb
--- /dev/null
+++ b/src/app/shareconnect/shareconnect.component.ts
@@ -0,0 +1,71 @@
+import { Component, OnInit } from '@angular/core';
+import { Strudelapp } from '../strudelapp';
+import { StrudelappsService } from '../strudelapps.service';
+import { TesService } from '../tes.service';
+import { AuthorisationService } from '../authorisation.service';
+import { Computesite } from '../computesite';
+import { ComputesitesService } from '../computesites.service';
+import { Router } from '@angular/router';
+import { Job } from '../job';
+import { Subscription } from 'rxjs';
+import {Identity, KeyCert} from '../identity';
+
+
+@Component({
+  selector: 'app-shareconnect',
+  templateUrl: './shareconnect.component.html',
+  styleUrls: ['./shareconnect.component.css']
+})
+export class ShareconnectComponent implements OnInit {
+
+  data: string = "";
+  invalid: boolean = false;
+  subscription: Subscription;
+  constructor(private computesitesService: ComputesitesService,
+              private tesService: TesService,
+              private authService: AuthorisationService,
+              private router: Router) { }
+
+  ngOnInit() {
+    setTimeout( () => this.authService.updateAgentContents() )
+  }
+
+  connect(jobdata: Job) {
+    this.tesService.connect(jobdata);
+  }
+
+  onSubmit() {
+    var dataobject: Object;
+    var jobdata: Job;
+    var site: Computesite;
+    var id: Identity;
+    try {
+      dataobject = JSON.parse(this.data);
+      console.log('json parsed');
+      jobdata = <Job>dataobject['job'];
+      site = <Computesite>dataobject['site'];
+      id = <Identity>dataobject['identity'];
+      id.site = site;
+      jobdata.identity = new Identity(id.username, site);
+
+      let keycert = dataobject['keycert'];
+      console.log('jobdata and keycert extracted');
+      if (keycert !== null) {
+        this.subscription = this.authService.agentContents.subscribe(() => this.connect(jobdata))
+        this.authService.sshAdd(keycert);
+      } else {
+        console.log('calling connect');
+        this.connect(jobdata);
+      }
+      console.log('data validated');
+      this.invalid=false;
+    } catch {
+      if (this.subscription !== undefined) {
+        this.subscription.unsubscribe();
+      }
+      this.invalid=true;
+      console.log('invalid json input')
+    }
+  }
+
+}
diff --git a/src/app/strudelapp.ts b/src/app/strudelapp.ts
index 59b85b0..e420976 100644
--- a/src/app/strudelapp.ts
+++ b/src/app/strudelapp.ts
@@ -11,3 +11,8 @@ export class Strudelapp {
                       /// how we create tunnels
   applist: Strudelapp[] = null;
 }
+
+export class StrudelappInstance {
+  port: number;
+  password: string;
+}
diff --git a/src/app/strudelapplist/strudelapplist.component.css b/src/app/strudelapplist/strudelapplist.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/strudelapplist/strudelapplist.component.html b/src/app/strudelapplist/strudelapplist.component.html
new file mode 100644
index 0000000..36b9098
--- /dev/null
+++ b/src/app/strudelapplist/strudelapplist.component.html
@@ -0,0 +1,16 @@
+<div *ngFor="let app of applist | async">
+  <div *ngIf="app.startscript != null">
+    <button mat-button (click)=openLaunchWindow(app)>{{ app.name }}</button>
+  </div>
+  <!-- The following allows for a nested set of apps -->
+  <div *ngIf="app.applist != null">
+    <mat-accordion>
+    <mat-expansion-panel>
+      <mat-expansion-panel-header>
+      {{ app.name }}
+      </mat-expansion-panel-header>
+      <app-strudelapplist [applist]=app.applist [identity]=identity></app-strudelapplist>
+    </mat-expansion-panel>
+  </mat-accordion>
+  </div>
+</div>
diff --git a/src/app/strudelapplist/strudelapplist.component.spec.ts b/src/app/strudelapplist/strudelapplist.component.spec.ts
new file mode 100644
index 0000000..8214575
--- /dev/null
+++ b/src/app/strudelapplist/strudelapplist.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { StrudelapplistComponent } from './strudelapplist.component';
+
+describe('StrudelapplistComponent', () => {
+  let component: StrudelapplistComponent;
+  let fixture: ComponentFixture<StrudelapplistComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ StrudelapplistComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(StrudelapplistComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/strudelapplist/strudelapplist.component.ts b/src/app/strudelapplist/strudelapplist.component.ts
new file mode 100644
index 0000000..33bf719
--- /dev/null
+++ b/src/app/strudelapplist/strudelapplist.component.ts
@@ -0,0 +1,38 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import {BehaviorSubject} from 'rxjs/BehaviorSubject';
+import {Strudelapp} from '../strudelapp';
+import { SubmitAppService } from '../submit-app.service';
+import { Identity } from '../identity';
+import { repeat } from 'rxjs/operators';
+import { BatchInterface} from '../batchinterface';
+import { timer } from 'rxjs/observable/timer';
+
+
+
+
+
+@Component({
+  selector: 'app-strudelapplist',
+  templateUrl: './strudelapplist.component.html',
+  styleUrls: ['./strudelapplist.component.css']
+})
+export class StrudelapplistComponent implements OnInit {
+  @Input() applist: Strudelapp[];
+  @Input() identity: Identity;
+
+
+  constructor(private submitAppService: SubmitAppService ) {
+  }
+
+  ngOnInit() {
+  }
+
+  openLaunchWindow(app, Strudelapp) {
+    console.log('strudel applist openLaunchWindow');
+    console.log(this.submitAppService);
+    this.submitAppService.launchApp(app,this.identity);
+  }
+
+
+}
diff --git a/src/app/submit-app.service.spec.ts b/src/app/submit-app.service.spec.ts
new file mode 100644
index 0000000..0428a32
--- /dev/null
+++ b/src/app/submit-app.service.spec.ts
@@ -0,0 +1,15 @@
+import { TestBed, inject } from '@angular/core/testing';
+
+import { SubmitAppService } from './submit-app.service';
+
+describe('SubmitAppService', () => {
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [SubmitAppService]
+    });
+  });
+
+  it('should be created', inject([SubmitAppService], (service: SubmitAppService) => {
+    expect(service).toBeTruthy();
+  }));
+});
diff --git a/src/app/submit-app.service.ts b/src/app/submit-app.service.ts
new file mode 100644
index 0000000..0efaccb
--- /dev/null
+++ b/src/app/submit-app.service.ts
@@ -0,0 +1,158 @@
+import { Injectable } from '@angular/core';
+import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
+import { repeat } from 'rxjs/operators';
+import { timer } from 'rxjs/observable/timer';
+
+import {Strudelapp} from './strudelapp';
+import { TesService } from './tes.service';
+import { Identity } from './identity';
+import { BatchInterface} from './batchinterface';
+
+// This service is responsible for starting applications
+// Along the way it opens new windows/tabs to
+/** This service is repsonible for starting applications
+Along the way it opens new windows/tabs to determine
+    1) Any configuration parameters for the application itself (if the app suports This
+    2) The batch command to submit the application to a cluster (if any)
+The windows are "watched" for URL changes and parameters are extracted from the
+query string
+*/
+
+
+@Injectable({
+  providedIn: 'root'
+})
+export class SubmitAppService {
+  private configwindow: any;
+  private appparams: string;
+  private submitcmd: string;
+
+
+  private windowWatcher: any;
+  private watchingForSubmit: boolean;
+
+
+  constructor(private tesService: TesService) {
+    this.appparams = null;
+    this.submitcmd = null;
+    this.watchingForSubmit = false;
+  }
+
+  public launchApp(app: Strudelapp, identity: Identity) {
+    if (!(app.url === null) && !(app.url =='')) {
+      this.openWindow(app,app.url,identity);
+    } else {
+      this.appparams = JSON.stringify([]);
+      let redirect = window.location.toString();
+      redirect = redirect.replace(/launch/,'finishlaunch');
+      let paramstr = this.buildParams(app,identity,redirect);
+      let windowloc = identity.site.url+'configure/'+app.name+"?"+paramstr;
+      this.openWindow(app,windowloc,identity);
+    }
+    // this.openLaunchWindow(app, identitiy);
+  }
+
+
+  public openWindow(app: any,url: string, identity: Identity) {
+    console.log('in openLaunchDialog');
+    console.log(app);
+
+    let headers = new HttpHeaders();
+    let options = { headers: headers, withCredentials: true};
+    let redirect = window.location.toString();
+    redirect = redirect.replace(/launch/,'finishlaunch');
+    let paramstr = this.buildParams(app,identity,redirect);
+    this.configwindow = window.open(url);
+    if (!(this.windowWatcher === null) && !(this.windowWatcher === undefined)) {
+      this.windowWatcher.unsubscribe()
+    }
+    this.windowWatcher = timer(500).pipe(repeat()).subscribe(() => this.watchWindow(this.configwindow, identity, app,redirect));
+    this.configwindow.focus();
+
+  }
+
+  public watchWindow(configwindow, id, app, redirect) {
+    // Watch another window for url changes
+    // if the url changes to include the submit parameters, submit the job
+    // if the url changes to include appparameters, save them and open
+    // a new window to get the submit parameters
+    var location: string;
+    try {
+      location = configwindow.location.href;
+      // if we can read the location and it contains a submitcmd arameter save it
+      // and close the window
+      if (configwindow.closed) { // if the window is closed without our help, presumable the user wanted to cancel the launch
+        return
+      }
+      let match = location.match(/submitcmd\=([^&]+)(&|$)/);
+      if (match) {
+          this.submitcmd = decodeURIComponent(match[1]);
+          if (!(configwindow.closed)) {
+            configwindow.close()
+          }
+          this.windowWatcher.unsubscribe();
+      }
+      // if we can read the location and it contains an appparams parameter, save it
+      // and close the window
+      match = location.match(/appparams\=([^&]+)(&|$)/);
+      if (match) {
+          this.appparams = decodeURIComponent(match[1]);
+          if (!(configwindow.closed)) {
+            configwindow.close()
+          }
+          this.windowWatcher.unsubscribe();
+      }
+      // if we can read the location and it contains the redireURI, close the window
+      let idx = location.indexOf(redirect);
+      if (idx == 0) {
+        configwindow.close();
+      }
+    } catch {
+      location = null;
+    }
+    if (configwindow.closed) {
+      this.windowWatcher.unsubscribe();
+      // if the window is closed, see if we need to override appparams from localStorage
+      try {
+        let appparams = localStorage.getItem('appparams');
+        if (appparams != null) {
+          localStorage.removeItem('appparams');
+          this.appparams = appparams;
+        }
+      } catch {
+      }
+      // if the window is closed and we have an appparams value but not a submitcmd
+      // open a new window to get the submitcmd
+
+
+      if (!(this.appparams === null) && (this.submitcmd === null)) {
+        let paramstr = this.buildParams(app,id,redirect);
+        let windowloc = id.site.url+'configure/'+app.name+"?"+paramstr;
+        this.windowWatcher.unsubscribe();
+        this.openWindow(app,windowloc,id);
+      }
+      // if the window is closed and we have an appparams valud AND a submitcmd
+      // submit
+      if (!(this.appparams == null) && !(this.submitcmd == null )) {
+
+        let bi = new BatchInterface();
+        bi.submitcmd = this.submitcmd;
+        this.tesService.submit(app,id,bi,this.appparams);
+        this.appparams = null;
+        this.submitcmd = null;
+      }
+    }
+  }
+
+  private buildParams(app: Strudelapp, identity: Identity, redirect): string {
+    let params = new URLSearchParams();
+    let id = identity.copy_skip_catalog();
+    id.site.appCatalog = null;
+
+    params.set('app',JSON.stringify(app));
+    params.set('identity',JSON.stringify(id));
+    params.set('redirect',redirect);
+    return params.toString();
+  }
+
+}
diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.css b/src/app/sv2-side-nav/sv2-side-nav.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.html b/src/app/sv2-side-nav/sv2-side-nav.component.html
new file mode 100644
index 0000000..216506d
--- /dev/null
+++ b/src/app/sv2-side-nav/sv2-side-nav.component.html
@@ -0,0 +1,16 @@
+<!-- <mat-accordion> -->
+<mat-expansion-panel>
+  <mat-expansion-panel-header>
+    <mat-panel-title>
+    Login
+  </mat-panel-title>
+  </mat-expansion-panel-header>
+  <div *ngFor="let sshauthzserver of (authService.SshAuthzServers | async)">
+    <div style="width: 100%">
+    <button mat-button (click)=login(sshauthzserver) fxFlex style="text-align: left">Login to {{ sshauthzserver.name }} </button>
+    <button mat-icon-button *ngIf="sshauthzserver.userdefined === true"><mat-icon>remove</mat-icon></button>
+    </div>
+  </div>
+  <button mat-button (click)=logout()  style="width: 100%; text-align: left" >Logout</button>
+</mat-expansion-panel>
+<!-- </mat-accordion> -->
diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts b/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts
new file mode 100644
index 0000000..f8efea9
--- /dev/null
+++ b/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { Sv2SideNavComponent } from './sv2-side-nav.component';
+
+describe('Sv2SideNavComponent', () => {
+  let component: Sv2SideNavComponent;
+  let fixture: ComponentFixture<Sv2SideNavComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ Sv2SideNavComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(Sv2SideNavComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.ts b/src/app/sv2-side-nav/sv2-side-nav.component.ts
new file mode 100644
index 0000000..c36203d
--- /dev/null
+++ b/src/app/sv2-side-nav/sv2-side-nav.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit } from '@angular/core';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material';
+
+import { AuthorisationService } from '../authorisation.service';
+import { LogoutdialogComponent } from '../logoutdialog/logoutdialog.component';
+
+
+@Component({
+  selector: 'app-sv2-side-nav',
+  templateUrl: './sv2-side-nav.component.html',
+  styleUrls: ['./sv2-side-nav.component.css']
+})
+export class Sv2SideNavComponent implements OnInit {
+
+  constructor(public authService: AuthorisationService,  public dialog: MatDialog,) { }
+
+  ngOnInit() {
+    console.log('sv2-side-nav init');
+  }
+  logout() {
+      let dialogRef = this.dialog.open(LogoutdialogComponent, {
+        width: '250px',
+        height: '400px',
+      });
+  }
+
+  login (sshauthzserver) {
+    this.authService.login(sshauthzserver);
+  }
+
+}
diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts
index 2c66105..3ef42fc 100644
--- a/src/app/tes.service.ts
+++ b/src/app/tes.service.ts
@@ -7,7 +7,7 @@ import { catchError, map, tap } from 'rxjs/operators';
 import { Job } from './job';
 import {BehaviorSubject} from 'rxjs/BehaviorSubject';
 import { Subject } from 'rxjs/Subject';
-import { Strudelapp } from './strudelapp';
+import { Strudelapp, StrudelappInstance } from './strudelapp';
 import { Computesite } from './computesite';
 import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity';
 import { BatchInterface} from './batchinterface';
@@ -15,12 +15,15 @@ import {ComputesitesService} from './computesites.service';
 import { StrudelappsService } from './strudelapps.service';
 import { timer } from 'rxjs/observable/timer';
 import { repeat } from 'rxjs/operators';
-import {LocationStrategy, Location} from '@angular/common';
+import { LocationStrategy, Location } from '@angular/common';
 import { ActivatedRoute, Router } from '@angular/router';
 import { ModaldialogComponent } from './modaldialog/modaldialog.component';
 import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
-import { MatSnackBar } from '@angular/material';
+import { AuthorisationService } from './authorisation.service';
+import { environment } from '../environments/environment';
 
+/** The TES service contains ways to start Tunnels, and Execute programs
+Its also responsible for querying a compute site for running jobs */
 
 
 
@@ -28,56 +31,48 @@ import { MatSnackBar } from '@angular/material';
   providedIn: 'root',
 })
 export class TesService {
-// public Base='https://vm-118-138-240-255.erc.monash.edu.au/tes'
-public Base='http://localhost:5000';
+public Base=environment.tesurl;
+// public Base='http://localhost:5000';
 public statusMsg: BehaviorSubject<any>;
 public jobs: any[];
 public busy: BehaviorSubject<boolean> ;
-public joblist: BehaviorSubject<{ [id: string ]: Job[]}>;
+// public joblist: BehaviorSubject<{ [id: string ]: Job[]}>;
+public joblist: BehaviorSubject<Job[]>;
+
 private timerSubscription: any;
-// public keyCert: Subject<KeyCert>;
-public identities: BehaviorSubject<Identity[]>;
 private appwindow: any;
 private appwindowWatcher: any;
 public batchinterface: {[id: string] : BatchInterface};
-private snackBarRef: any;
 
   constructor(private http: HttpClient,
               public dialog: MatDialog,
-              private computesite: ComputesitesService,
+              private computesitesService: ComputesitesService,
+              private authorisationService: AuthorisationService,
               private strudelappsService: StrudelappsService,
-              public snackBar: MatSnackBar,
               private location: Location ) {
-    this.statusMsg = new BehaviorSubject<any>('');
-    this.statusMsg.subscribe(msg => this.displayMessage(msg));
+
     this.busy = new BehaviorSubject<boolean>(false);
-    this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({});
+    // this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({});
+    this.joblist = new BehaviorSubject<Job[]>([]);
+
     this.timerSubscription = null;
     this.appwindowWatcher = null;
-    // this.keyCert = new Subject<KeyCert>();
-    this.identities= new BehaviorSubject<Identity[]>([]);
-    // this.keyCert.subscribe(keyCert => this.sshAdd(keyCert));
     this.batchinterface = {};
-    // this.getIdentities();
+    // this.computesitesService.identities.subscribe(identities => this.startPolling(identities));
  }
 
-  private displayMessage(msg) {
-   if (msg === null || msg == "") {
-     console.log('attempting to dismis snackbar');
-     if (this.snackBarRef != undefined) {
-       this.snackBarRef.dismiss()
-     }
-   } else {
-     this.snackBarRef = this.snackBar.open(msg,'Dismiss');
-   }
- }
+public setStatusMsg(statusMsg: BehaviorSubject<any>) {
+  this.statusMsg = statusMsg;
+}
 
- private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface): string {
+ private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appinst?: any): string {
    let params = new URLSearchParams();
-   let params2 = new URLSearchParams();
    let id = identity.copy_skip_catalog();
    id.site.appCatalog = null;
 
+   if (appinst!== null)  {
+     params.set('appinstance',JSON.stringify(appinst));
+   }
    params.set('app',JSON.stringify(app));
    params.set('interface',JSON.stringify(batchinterface));
    params.set('identity',JSON.stringify(id));
@@ -92,78 +87,84 @@ private snackBarRef: any;
      j.app = this.strudelappsService.getApp(j.name,identity.site.appCatalog.value);
      j.identity = identity;
    }
-   alljobs[identity.repr()] = joblist;
-   this.joblist.next(alljobs);
+   this.joblist.next(joblist)
+   // alljobs[identity.repr()] = joblist;
+   // this.joblist.next(alljobs);
  }
 
- getJobs() {
-   let headers = new HttpHeaders();
-   let options = { headers: headers, withCredentials: true};
-   // remove from the job list any jobs for identities that we don't know about
-   let oldjobs = this.joblist.value;
-   let oldjobkeys = Object.keys(oldjobs);
-   let ids = []
-   for (let identity of this.identities.value) {
-     ids.push(identity.repr());
-   }
-   for (let id of oldjobkeys) {
-     if (!ids.includes(id)) {
-       delete oldjobs[id];
-     }
-   }
-   this.joblist.next(oldjobs);
-   // for identities that we do know about, query the job list
-   for (let identity of this.identities.value) {
-     if (this.batchinterface[identity.repr()] === undefined) {
-       this.getconfig(new Strudelapp(),identity)
-        .pipe(catchError(this.handleError))
-        .subscribe(resp => this.batchinterface[identity.repr()] = resp,
-                   error => this.httperror(error));
-        continue;
-     }
-     let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]);
-     this.http.get<Job[]>(this.Base+'/stat'+'?'+paramstr,options)
-                      .pipe(catchError(this.handleError))
-                      .subscribe(resp => this.updateJoblist(resp,identity),
-                                 error => this.httperror(error));
-                    }
+ private getBatchInterfaceError(error: any) {
+   console.log('in getBatchInterfaceError');
+   console.error(error);
  }
 
- private startPolling() {
-   if (!(this.timerSubscription === null)) {
-     this.timerSubscription.unsubscribe()
+ private getJobsError(error: any) {
+   this.joblist.next([]);
+   if (error.status == 0) {
+     this.statusMsg.next("A network error occured. Please try again latter");
+     return
    }
-   this.timerSubscription = timer(5000).pipe(repeat()).subscribe(() => this.getJobs());
-   this.getJobs();
- }
-
- private stopPolling() {
-   if (!(this.timerSubscription === null)) {
-     this.timerSubscription.unsubscribe()
+   console.log(error);
+   if (error.status == 404) {
+     this.statusMsg.next("Login expired. Please log in again.");
+     return
    }
  }
 
+ getJobs(identity: Identity) {
+  let headers = new HttpHeaders();
+  let options = { headers: headers, withCredentials: true};
+  // remove from the job list any jobs for identities that we don't know about
+  let oldjobs = this.joblist.value;
+  if ( identity === undefined) {
+    this.joblist.next([]);
+    return
+  }
+  if (this.batchinterface[identity.repr()] === undefined) {
+         this.getconfig(new Strudelapp(),identity)
+          // .pipe(catchError(this.handleError))
+          .subscribe(resp => { this.batchinterface[identity.repr()] = resp; this.getJobs(identity) },
+                     error => this.getBatchInterfaceError(error));
+                     // error => this.getJobsError(error,identity));
+
+
+  } else {
+    let paramstr = this.buildParams(null,identity,this.batchinterface[identity.repr()]);
+    this.http.get<Job[]>(this.Base+'/stat'+'?'+paramstr,options)
+                      // .pipe(catchError(this.networkError))
+                      .subscribe(resp => this.updateJoblist(resp, identity),
+                                 error => this.getJobsError(error));
+  }
+}
+
  public getconfig(app: Strudelapp, identity: Identity): Observable<any> {
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    return this.http.get<any>(identity.site.url+'getconfig/'+app.name,options)
-                                        .pipe(catchError(this.handleError))
+                                        // .pipe(catchError(this.handleError))
 }
 
- submissionError(message: string) {
-   this.statusMsg.next(message);
-   this.busy.next(false);
-   console.log(message);
+ submissionError(error: any) {
+   if (error.status != 0) {
+     this.statusMsg.next(error.error.message);
+     this.busy.next(false);
+   }
+ }
+
+ buildBody(app: Strudelapp, appparams?: string) {
+   return JSON.stringify({'app': app, 'appparams': appparams});
  }
 
- submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface) {
+ submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appparams?: string) {
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
    this.statusMsg.next('Submitting job');
    this.busy.next(true);
    let paramstr = this.buildParams(app,identity,batchinterface);
-   this.http.post<any>(this.Base+'/submit'+'?'+paramstr,{}, options)
-                                         .pipe(catchError(this.handleError))
+   // let body = this.buildBody(app,appparams)
+   let keys = this.authorisationService.getKeys();
+   let body = {'app': app, 'appparams': appparams, 'keys': keys}
+   this.http.post<any>(this.Base+'/submit'+'?'+paramstr, body, options)
+                                         // .pipe(catchError(this.handleError))
                                          .subscribe(resp => {
                                                               this.busy.next(false);
                                                               this.statusMsg.next(null)
@@ -171,10 +172,10 @@ private snackBarRef: any;
                                                     error => this.submissionError(error));
  }
 
- submitted(resp: any ) {
+ submitted(resp: any, identity: Identity ) {
    this.busy.next(false);
    // this.statusMsg.next(null);
-   this.getJobs();
+   this.getJobs(identity);
  }
 
  cancel(job: Job) {
@@ -185,7 +186,7 @@ private snackBarRef: any;
    let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]);
    this.http.delete<any>(this.Base+'/cancel/'+job.jobid+'?'+paramstr, options)
                       .pipe(catchError(this.handleError))
-                      .subscribe(resp => this.submitted(resp));
+                      .subscribe(resp => this.submitted(resp,job.identity));
  }
 
  public watchAppwindow(appwindow, dialogRef) {
@@ -194,17 +195,63 @@ private snackBarRef: any;
    }
  }
 
- public connect(job: Job) {
-   // this.statusMsg.next(null);
+ public getAppInstance(job: Job) {
+   let username = job.identity.username;
+   let loginhost = job.identity.site.host;
+   let batchhost = job.batch_host;
+   let params = new URLSearchParams;
    let headers = new HttpHeaders();
    let options = { headers: headers, withCredentials: true};
-   this.busy.next(true);
-   let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]);
-   // let appwindow = window.open(this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr);
+   params.set('cmd',JSON.stringify(job.app.paramscmd));
+   let paramstr = params.toString();
+   job.connectionState = 1;
+   this.http.get<string>(this.Base+'/appinstance/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, options)
+                // .pipe(catchError(this.handleError))
+                .subscribe(resp =>  { job.appinst = resp; this.createTunnel(job) ; console.log('got app inst data'+job.appinst)} )
+   // let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]);
+   // let headers = new HttpHeaders();
+   // let options = { headers: headers, withCredentials: true};
+   // this.http.get<any>(this.Base+'/getappinst/'+paramstr,options)
+   //                                      .pipe(catchError(this.handleError))
+   //                                      .subscribe(resp =>  { job.appinst = <StrudelappInstance>resp; this.openAppWindow(job);});
+ }
+
+ public createTunnel(job: Job) {
+   let username = job.identity.username;
+   let loginhost = job.identity.site.host;
+   let batchhost = job.batch_host;
+   let params = new URLSearchParams;
+   let headers = new HttpHeaders();
+   job.connectionState = 2;
+   let options = { headers: headers, withCredentials: true};
+   params.set('cmd',JSON.stringify(job.app.paramscmd));
+   let paramstr = params.toString();
+   this.http.post<string>(this.Base+'/createtunnel/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, job.appinst, options)
+                // .pipe(catchError(this.handleError))
+                .subscribe(() =>  { this.getAppUrl(job) } )
+ }
 
-   // let windowloc = this.Base+"/connect/"+job.jobid+"/"+job.batch_host+"?"+paramstr;
-   let url = this.Base+"/connect/"+job.jobid+"/"+job.batch_host;
-   let windowloc = './connecting?connecturl='+url+"&"+paramstr;
+ public getAppUrl(job: Job) {
+   let params = new URLSearchParams;
+   let headers = new HttpHeaders();
+   let options = { headers: headers, withCredentials: true};
+   params.set('app',JSON.stringify(job.app));
+   params.set('appinst',JSON.stringify(job.appinst));
+   let paramstr = params.toString();
+   job.connectionState = 3;
+   this.http.get<string>(this.Base+'/appurl?'+paramstr,options)
+                .pipe(catchError(this.handleError))
+                .subscribe(resp => { console.log(resp); job.connectionState = 0; this.openAppWindow(resp)});
+
+
+ }
+
+ public getInterface(job: Job) {
+   this.getAppInstance(job);
+ }
+
+ public openAppWindow(url: string) {
+   let windowloc = url;
    // let windowloc = this.router.config
    this.appwindow = window.open(windowloc);
    if (!(this.appwindowWatcher === null)) {
@@ -217,77 +264,57 @@ private snackBarRef: any;
    this.appwindowWatcher = timer(500).pipe(repeat()).subscribe(() => this.watchAppwindow(this.appwindow,dialogRef));
    // this.appwindow.location.assign(windowloc);
    this.appwindow.focus();
-   this.busy.next(false);
  }
 
+ public connect(job: Job, appinst?: any) {
+   // this.statusMsg.next(null);
+   let headers = new HttpHeaders();
+   let options = { headers: headers, withCredentials: true};
+   // this.busy.next(true);
+   this.getInterface(job); // getInterface will subsequently called getAppInstance, which will call createTunnel, which will openAppWindow
+ }
 
-public sshAdd(keyCert: KeyCert) {
-  if (keyCert.key == undefined) {
-    return;
-  }
+
+
+
+public postAgentData(keyCert: KeyCert) {
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
 
   this.statusMsg.next("Authorising ...")
   let data = {'key': keyCert.key, 'cert': keyCert.cert};
-  this.http.post<any>(this.Base+'/sshagent',data,options)
+  return this.http.post<any>(this.Base+'/sshagent',data,options)
                       .pipe(catchError(this.handleError))
-                      .subscribe(resp => this.getIdentities(),
-                                 error => this.httperror(error))
+
 }
 
-public getIdentities() {
-  this.statusMsg.next("Updating the list of available accounts");
+public postMkDir(id: Identity, path: string, name: string ) {
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
-  this.http.get<any>(this.Base+'/sshagent',options)
-                    .pipe(catchError(this.handleError))
-                    .subscribe(resp => this.updateIdentities(resp),
-                               error => this.httperror(error))
+  let params = new URLSearchParams;
+  params.set('identity',JSON.stringify(id));
+  params.set('path',JSON.stringify(path));
+
+  return this.http.post<any>(this.Base+'/mkdir?'+params.toString(),name, options)
+                      .pipe(catchError(this.handleError))
+
 }
 
-private killAgent() {
-  this.statusMsg.next("Updating the list of available accounts")
+public getSftpData(id: Identity, path: string, cd: string ) {
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
-  this.http.delete<any>(this.Base+'/sshagent',options)
-                    .pipe(catchError(this.handleError))
-                    .subscribe(resp => this.updateIdentities(resp),
-                               error => this.httperror(error));
-}
+  let params = new URLSearchParams;
+  params.set('identity',JSON.stringify(id));
+  params.set('path',JSON.stringify(path));
+  params.set('cd',JSON.stringify(cd));
 
-private updateIdentities(resp) {
-  let certcontents = resp;
-  let identities = [];
-  var cs: any;
-  let idsShort = []
-  for (cs of this.computesite.returnComputeSites()) {
-    let computesite = <Computesite>cs;
-    for (let i in certcontents) {
-      var fp: string = (<Computesite>cs).cafingerprint;
-      for (let ca of certcontents[i]['Signing CA']) {
-        if (ca == fp) {
-          identities.push(new Identity(certcontents[i].Principals[0],<Computesite>cs))
-        }
-      }
-    }
-  }
-  this.identities.next(identities);
-  this.statusMsg.next(null);
-  if (identities.length == 0) {
-    return;
-  }
-  this.startPolling();
-}
+  return this.http.get<any>(this.Base+'/ls?'+params.toString(),options)
+                      .pipe(catchError(this.handleError))
 
-public logout(): Boolean {
-  for (let id of this.identities.value) {
-    console.log('logout identitiy',id.authservice);
-  }
-  this.killAgent();
-  return true;
 }
 
+
+
 public getstatusMsgSubject(): BehaviorSubject<any> {
   return this.statusMsg;
 }
@@ -297,11 +324,20 @@ private httperror(errorstr: string) {
   let searchresult = errorstr.search(re);
   if (searchresult != -1) {
     this.statusMsg.next("Some authentication tokens have expired, you may need to log in again");
-    this.getIdentities();
+    this.authorisationService.updateAgentContents();
   }
   console.log(errorstr);
 }
 
+// private networkError(error: HttpErrorResponse) {
+//   if (error.error instanceof ErrorEvent) {
+//     console.log('network error contacting TES backend');
+//   } else {
+//     return error;
+//   }
+// }
+
+
 private handleError(error: HttpErrorResponse) {
   if (error.error instanceof ErrorEvent) {
     console.log('instance of ErrorEvent');
@@ -315,12 +351,16 @@ private handleError(error: HttpErrorResponse) {
     // that the user isn't allowed to run the job
 
     var re = /identity/gi;
-    let searchresult = error.error.message.search(re);
-    if (searchresult != -1) {
-      // this.getIdentities();
-      return throwError('login expired, refreshing');
+    if (error.error.message != undefined) {
+      let searchresult = error.error.message.search(re);
+      if (searchresult != -1) {
+        // this.getIdentities();
+        return throwError('login expired, refreshing');
+      }
+      return throwError(error.error.message)
+    } else {
+      return throwError('An error occured but I\'m not sure exactly what. Please try again latter or contact the sys admins');
     }
-    return throwError(error.error.message)
     // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message);
 
   }
diff --git a/src/app/teserrors/teserrors.component.html b/src/app/teserrors/teserrors.component.html
deleted file mode 100644
index 7899878..0000000
--- a/src/app/teserrors/teserrors.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<!-- <p>
-{{ statusMsg }}
-</p> -->
diff --git a/src/app/teserrors/teserrors.component.ts b/src/app/teserrors/teserrors.component.ts
deleted file mode 100644
index 386fc59..0000000
--- a/src/app/teserrors/teserrors.component.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { TesService } from '../tes.service';
-import { MatSnackBar } from '@angular/material';
-
-
-@Component({
-  selector: 'app-teserrors',
-  templateUrl: './teserrors.component.html',
-  styleUrls: ['./teserrors.component.css']
-})
-export class TeserrorsComponent implements OnInit {
-
-  // public statusMsg: string;
-  private snackBarRef: any;
-  constructor(private tesService: TesService, public snackBar: MatSnackBar,) { }
-  ngOnInit() {
-    // this.tesService.getstatusMsgSubject().subscribe( msg => this.getNewMessage(msg));
-  }
-
-  getNewMessage(msg) {
-    if (msg === null) {
-      console.log('attempting to dismis snackbar');
-      if (this.snackBarRef != undefined) {
-        this.snackBarRef.dismiss()
-      }
-    } else {
-      this.snackBarRef = this.snackBar.open(msg,'Dismiss');
-    }
-  }
-
-}
diff --git a/src/app/transfer/transfer.component.css b/src/app/transfer/transfer.component.css
new file mode 100644
index 0000000..683ae23
--- /dev/null
+++ b/src/app/transfer/transfer.component.css
@@ -0,0 +1,3 @@
+:host {
+  height: 100%;
+}
diff --git a/src/app/transfer/transfer.component.html b/src/app/transfer/transfer.component.html
new file mode 100644
index 0000000..4259e24
--- /dev/null
+++ b/src/app/transfer/transfer.component.html
@@ -0,0 +1,54 @@
+<div  fxFlex style="flex: 1 1 0%; box-sizing: border-box">
+<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" >
+    <mat-toolbar color="primary">
+      <mat-toolbar-row>
+        <button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button>
+
+        <span>Strudel v2.0</span>
+        <span class="fill-horizontal-space"></span>
+      </mat-toolbar-row>
+    </mat-toolbar>
+
+<mat-sidenav-container #idSideNav autosize style="height: 100%; width: 100%">
+  <mat-sidenav #idSideNav mode="side" opened>
+      <app-sv2-side-nav></app-sv2-side-nav>
+  </mat-sidenav>
+  <div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" >
+
+    <div fxLayout="row" fxLayout="space-between stretch" fxLayoutGap="1%" style="height: 90%">
+      <!-- <div fxFlex *ngFor="let idx of [0,1]" style="height: 100%; overflow-y: auto"> -->
+        <div fxFlex *ngFor="let idx of [0,1]" style="height: 100%">
+
+        <!-- <div style="height: 800px; background-color: #00ffff; overflow-y: scroll"></div> -->
+
+          <mat-form-field>
+            <mat-select (selectionChange)="setId(idx,$event)" placeholder="Choose a server">
+              <mat-option *ngFor="let s of computesitesService.ftidentities | async" [value]="s">
+                {{ s.username }}@{{s.site.name }}
+              </mat-option>
+            </mat-select>
+          </mat-form-field>
+          <span class="mat-body"> {{ path[idx] }}</span>
+          <div *ngIf="working[idx] | async">
+            <mat-spinner></mat-spinner>
+          </div>
+          <div *ngIf="!(working[idx] | async)" style="height: 90%; overflow-y: auto">
+          <file-explorer [fileElements]="fileElements[idx] | async"
+                          [path]="pathSubject[idx] | async"
+                          (navigatedDown)="navigateToFolder(idx,$event)"
+                          (navigatedUp)="navigateUp(idx,$event)"
+                          [canNavigateUp]="canNavigateUp[idx] | async"
+                          (sendFile)="sendFile(idx,$event)"
+                          (folderAdded)="newDirectory(idx,$event)" >
+          </file-explorer>
+          </div>
+      </div>
+    </div>
+  </div>
+
+
+
+    <!-- <mat-card style="width: 100%; box-sizing: border-box">Transfer tasks</mat-card> -->
+</mat-sidenav-container>
+</div>
+</div>
diff --git a/src/app/transfer/transfer.component.spec.ts b/src/app/transfer/transfer.component.spec.ts
new file mode 100644
index 0000000..73f4b41
--- /dev/null
+++ b/src/app/transfer/transfer.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TransferComponent } from './transfer.component';
+
+describe('TransferComponent', () => {
+  let component: TransferComponent;
+  let fixture: ComponentFixture<TransferComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TransferComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TransferComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/transfer/transfer.component.ts b/src/app/transfer/transfer.component.ts
new file mode 100644
index 0000000..66a33ab
--- /dev/null
+++ b/src/app/transfer/transfer.component.ts
@@ -0,0 +1,175 @@
+import { Component, OnInit } from '@angular/core';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+import { Observable } from 'rxjs/Observable';
+import { FileElement } from '../file-explorer/model/element';
+import { TesService } from '../tes.service';
+import { ComputesitesService } from '../computesites.service';
+import { AuthorisationService } from '../authorisation.service';
+import { Computesite } from '../computesite';
+import { Identity } from '../identity';
+import { Router } from '@angular/router';
+
+
+
+
+
+@Component({
+  selector: 'app-transfer',
+  templateUrl: './transfer.component.html',
+  styleUrls: ['./transfer.component.css']
+})
+export class TransferComponent implements OnInit {
+
+  fileElements: BehaviorSubject<FileElement[]>[];
+  pathSubject: BehaviorSubject<string>[];
+  path: string[];
+  working: BehaviorSubject<Boolean>[];
+  canNavigateUp: BehaviorSubject<Boolean>[];
+  id: Identity[];
+  site: Computesite[];
+  disableSend: Boolean;
+  statusMsg: BehaviorSubject<string>;
+
+
+  constructor(private computesitesService: ComputesitesService,
+              private tesService: TesService,
+              private authService: AuthorisationService,
+              private router: Router) {
+    this.path = ['',''];
+    this.fileElements = [new BehaviorSubject([]), new BehaviorSubject([])];
+    this.working = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)];
+    this.canNavigateUp = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)];
+    this.pathSubject = [ new BehaviorSubject(""),new BehaviorSubject("")];
+    this.id = [null,null];
+    this.disableSend = true;
+  }
+
+  ngOnInit() {
+    var fel: FileElement[] = []
+    // this.fileElements = [new BehaviorSubject([]), new BehaviorSubject([])];
+    // this.working = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)];
+    // this.canNavigateUp = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)];
+    // this.pathSubject = [ new BehaviorSubject(""),new BehaviorSubject("")];
+    // this.id = [null,null];
+    // this.disableSend = true;
+    setTimeout( () => this.authService.updateAgentContents() )
+
+  }
+
+compareObjects(o1: any, o2: any) {
+  if(o1.name == o2.name) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+newDirectory(idx: number, name: string) {
+  console.log('attempt to create directory',name,'on',this.id[idx]);
+  this.working[idx].next(true);
+  this.tesService.postMkDir(this.id[idx],this.path[idx],name).subscribe(resp => this. getSftpls(idx,'.'));
+}
+
+getSftpls(idx,cd) {
+  console.log('getSftpls');
+  console.log(this.id);
+  console.log(idx);
+  this.working[idx].next(true);
+  this.tesService.getSftpData(this.id[idx],this.path[idx],cd).subscribe(resp => this.updateSftpls(idx,resp));
+}
+
+makeFileElements(data) {
+  console.log('making file elements');
+  console.log(data);
+  var rv: FileElement[] = [];
+  var fe: FileElement;
+  for (let f of data) {
+    fe = new FileElement();
+    if (f.isdir !== undefined) {
+      fe.isFolder = f.isdir;
+    } else {
+      if (f.mode.indexOf('d')== 0) {
+        fe.isFolder=true;
+      }
+    }
+    fe.name = f.name;
+    fe.size = f.size;
+    fe.online = f.online;
+    rv.push(fe);
+  }
+  return rv;
+}
+
+ updateSftpls(idx,resp) {
+   let files = this.makeFileElements(resp.files);
+   console.log(resp);
+   console.log('got the current path',resp.pwd);
+   console.log('got a list files', resp.files);
+   if (resp.pwd.length > 1) {
+     this.canNavigateUp[idx].next(true);
+   } else {
+     this.canNavigateUp[idx].next(false);
+
+   }
+   this.path[idx] = resp.pwd;
+   console.log('full uri',this.id[idx].username+"@"+this.id[idx].site.name+":"+this.path[idx]);
+   this.pathSubject[idx].next(this.path[idx]);
+   this.fileElements[idx].next(files);
+   this.working[idx].next(false);
+
+ }
+
+
+  setId(idx,event) {
+    console.log('setid');
+    console.log(event);
+    this.id[idx] = event.value;
+    if ((this.id[0] != null) && (this.id[1] != null)) {
+      console.log('enabling the send function, two ids are selected');
+      this.disableSend = false;
+    }
+    this.path[idx]=".";
+    this.pathSubject[idx].next(this.id[idx].username+"@"+this.id[idx].site.name+":"+this.path[idx]);
+    this.getSftpls(idx,'.');
+
+  }
+
+  navLaunch() {
+    console.log('attempting navLaunch');
+    this.router.navigate(['/launch']);
+  }
+
+  navigateToFolder(idx,element: FileElement) {
+    this.getSftpls(idx,element.name);
+  }
+
+  navigateUp(idx: number, event: any) {
+    console.log('attempting navigate up',idx);
+    this.getSftpls(idx,'..');
+  }
+
+
+
+  sendFile(idx: number, element: FileElement) {
+    let src = idx;
+    let dest = (idx+1) % 2;
+    this.id[src].keyCerts = this.authService.getKeys(this.id[src]);
+    this.id[dest].keyCerts = this.authService.getKeys(this.id[dest]);
+    if ((this.id[src] != null) && (this.id[dest] != null)) {
+      let appparams = JSON.stringify(
+        {'src': {
+          'id': this.id[src],
+          'path': this.path[src],
+          'file': element.name },
+        'dest': {
+          'id': this.id[dest],
+          'path': this.path[dest]
+        }});
+      localStorage.setItem('appparams',appparams);
+      window.close();
+    } else {
+      console.log('Dont send file not enough servers'+element.name+' on ');
+    }
+  }
+
+}
diff --git a/src/assets/config/authservers.json b/src/assets/config/authservers.json
new file mode 100644
index 0000000..376edea
--- /dev/null
+++ b/src/assets/config/authservers.json
@@ -0,0 +1,34 @@
+[
+
+    {
+      "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/aafcentral",
+      "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/",
+      "client_id": "Q96kt2Vtw6S78dpORktM81DH",
+      "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/monash_hpcid/api/v1/sign_key",
+      "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout",
+      "name": "M3 via AAF Central",
+      "icon": null,
+      "scope": "user:email"
+    },
+    {
+      "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/google",
+      "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/",
+      "client_id": "Q96kt2Vtw6S78dpORktM81DH",
+      "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/monash_hpcid/api/v1/sign_key",
+      "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout",
+      "name": "M3 via Google",
+      "icon": null,
+      "scope": "user:email"
+    },
+    {
+      "authorise": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/oauth/authorize/aafcentral",
+      "base": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/",
+      "client_id": "Q96kt2Vtw6S78dpORktM81DH",
+      "sign": "https://sshauthz.cloud.cvl.org.au/pysshauthz/sign/swinburne/api/v1/sign_key",
+      "logout": "https://sshauthz.cloud.cvl.org.au/pysshauthz/oauth2/logout",
+      "name": "OzStar via AAF Central",
+      "icon": null,
+      "scope": "user:email"
+    }
+
+]
diff --git a/src/assets/config/computesites.json b/src/assets/config/computesites.json
new file mode 100644
index 0000000..ee5d818
--- /dev/null
+++ b/src/assets/config/computesites.json
@@ -0,0 +1,40 @@
+[
+  {
+    "url": "http://localhost:8080/",
+    "name": "M3",
+    "host": "m3-login2.massive.org.au",
+    "dtn": "m3-dtn1.massive.org.au",
+    "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
+    "appCatalog": [],
+    "appCatalogUri": "./assets/config/m3apps.json"
+  },
+  {
+    "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig/",
+    "name": "OzStar",
+    "host": "ozstar.swin.edu.au",
+    "dtn": "ozstar.swin.edu.au",
+    "cafingerprint": "RSA SHA256:uRYwTJSLksZbwSXYYmOjXh6JzSXpwVnY6o7BYFJc60g",
+    "appCatalog": [],
+    "appCatalogUri": "./assets/config/m3apps.json"
+  },
+  {
+    "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig_dev/",
+    "name": "CVL@UWA",
+    "host": "146.118.65.246",
+    "dtn": "146.118.65.246",
+    "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
+    "appCatalog": [],
+    "appCatalogUri": "./assets/config/cvluwaapps.json"
+  },
+  {
+    "url": null ,
+    "name": "Vault-V2@Monash",
+    "host": "dtn-2.erc.monash.edu.au",
+    "dtn": "dtn-2.erc.monash.edu.au",
+    "lscmd": "/srv/home/chines/hsmls.py",
+    "dtnport": "33001",
+    "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
+    "appCatalog": [],
+    "appCatalogUri": null
+  }
+]
diff --git a/src/assets/config/computesites.json~ b/src/assets/config/computesites.json~
new file mode 100644
index 0000000..11ff902
--- /dev/null
+++ b/src/assets/config/computesites.json~
@@ -0,0 +1,26 @@
+[
+  {
+    "url": "http://localhost:8080/",
+    "name": "M3",
+    "host": "m3-login2.massive.org.au",
+    "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
+    "appCatalog": [],
+    "appCatalogUri": "assets/config/m3apps.json"
+  },
+  {
+    "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3staticsiteconfig/",
+    "name": "M3 Static Services",
+    "host": "m3-login2.massive.org.au",
+    "cafingerprint": "RSA SHA256:cmDxHrZQSPlBMUUcI/BWmruXho1XOzfXPDHSqVTwV2I",
+    "appCatalog": [],
+    "appCatalogUri": "assets/config/m3staticapps.json"
+  },
+  {
+    "url": "https://vm-118-138-240-255.erc.monash.edu.au/m3siteconfig/",
+    "name": "OzStar",
+    "host": "ozstar.swin.edu.au",
+    "cafingerprint": "RSA SHA256:uRYwTJSLksZbwSXYYmOjXh6JzSXpwVnY6o7BYFJc60g",
+    "appCatalog": [],
+    "appCatalogUri": "assets/config/m3apps.json"
+  }
+]
diff --git a/src/assets/config/cvluwaapps.json b/src/assets/config/cvluwaapps.json
new file mode 100644
index 0000000..e97e7c9
--- /dev/null
+++ b/src/assets/config/cvluwaapps.json
@@ -0,0 +1,62 @@
+[
+  { "url": null,
+    "name": "Desktop",
+    "startscript": "#!/bin/bash\n/home/chines/sv2/desktop_novnc/desktop.sh\n ",
+    "paramscmd": "/home/chines/sv2/desktop_novnc/params.py",
+    "client": {"cmd": null, "redir": "vnc.html?host=vm-118-138-240-255.erc.monash.edu.au&port=443&password={password}" },
+    "localbind": true,
+    "applist": null
+  },
+  { "url": null,
+    "name": "Guacamole Desktop",
+    "startscript": "#!/bin/bash\n/home/chines/desktop.sh\n ",
+    "paramscmd": "/home/chines/vdiparam.py",
+    "client": {"cmd": null, "redir": "guacamole?username={username}&password={password}" },
+    "localbind": true,
+    "applist": null
+  },
+ {
+   "url": null,
+   "name": "Jupyter Lab",
+   "startscript": "#!/bin/bash\n/home/chines/jupyter.slurm\n",
+   "paramscmd": "/home/chines/jupyter_params.py",
+   "client": {"cmd": null, "redir": "?token={token}"},
+   "localbind": true,
+   "applist": null
+  },
+  {
+    "url": null,
+    "name": "LiberTEM",
+    "startscript": "eval $(cat /home/chines/libertembatch)",
+    "paramscmd": "/projects/pMOSP/chines/libertem_venv/bin/python /projects/pMOSP/chines/libertem_venv/bin/server_stat.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true,
+    "applist": null
+  },
+  {
+    "url": null,
+    "name": "CryoSPARC",
+    "startscript": null,
+    "paramscmd": "/home/chines/cryosparc_params.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true,
+    "applist": null
+  },
+  {
+    "url": null,
+    "name": "R Studio Server",
+    "startscript": "#!/bin/bash\n/usr/local/sv2/rstudioserver/run.sh\n",
+    "paramscmd": "/usr/local/sv2/rstudioserver/params.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true,
+    "applist": null
+  },
+  { "url": "transfer",
+    "name": "Transfer files",
+    "startscript": "#!/bin/bash\n echo '{appparams}' > ft.json",
+    "paramscmd": "/home/chines/sv2/desktop_novnc/params.py",
+    "client": {"cmd": null, "redir": null },
+    "localbind": true,
+    "applist": null
+  }
+]
diff --git a/src/assets/config/m3apps.json b/src/assets/config/m3apps.json
new file mode 100644
index 0000000..e97e7c9
--- /dev/null
+++ b/src/assets/config/m3apps.json
@@ -0,0 +1,62 @@
+[
+  { "url": null,
+    "name": "Desktop",
+    "startscript": "#!/bin/bash\n/home/chines/sv2/desktop_novnc/desktop.sh\n ",
+    "paramscmd": "/home/chines/sv2/desktop_novnc/params.py",
+    "client": {"cmd": null, "redir": "vnc.html?host=vm-118-138-240-255.erc.monash.edu.au&port=443&password={password}" },
+    "localbind": true,
+    "applist": null
+  },
+  { "url": null,
+    "name": "Guacamole Desktop",
+    "startscript": "#!/bin/bash\n/home/chines/desktop.sh\n ",
+    "paramscmd": "/home/chines/vdiparam.py",
+    "client": {"cmd": null, "redir": "guacamole?username={username}&password={password}" },
+    "localbind": true,
+    "applist": null
+  },
+ {
+   "url": null,
+   "name": "Jupyter Lab",
+   "startscript": "#!/bin/bash\n/home/chines/jupyter.slurm\n",
+   "paramscmd": "/home/chines/jupyter_params.py",
+   "client": {"cmd": null, "redir": "?token={token}"},
+   "localbind": true,
+   "applist": null
+  },
+  {
+    "url": null,
+    "name": "LiberTEM",
+    "startscript": "eval $(cat /home/chines/libertembatch)",
+    "paramscmd": "/projects/pMOSP/chines/libertem_venv/bin/python /projects/pMOSP/chines/libertem_venv/bin/server_stat.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true,
+    "applist": null
+  },
+  {
+    "url": null,
+    "name": "CryoSPARC",
+    "startscript": null,
+    "paramscmd": "/home/chines/cryosparc_params.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true,
+    "applist": null
+  },
+  {
+    "url": null,
+    "name": "R Studio Server",
+    "startscript": "#!/bin/bash\n/usr/local/sv2/rstudioserver/run.sh\n",
+    "paramscmd": "/usr/local/sv2/rstudioserver/params.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true,
+    "applist": null
+  },
+  { "url": "transfer",
+    "name": "Transfer files",
+    "startscript": "#!/bin/bash\n echo '{appparams}' > ft.json",
+    "paramscmd": "/home/chines/sv2/desktop_novnc/params.py",
+    "client": {"cmd": null, "redir": null },
+    "localbind": true,
+    "applist": null
+  }
+]
diff --git a/src/assets/config/m3staticapps.json b/src/assets/config/m3staticapps.json
new file mode 100644
index 0000000..c784493
--- /dev/null
+++ b/src/assets/config/m3staticapps.json
@@ -0,0 +1,10 @@
+[
+  {
+    "url": null,
+    "name": "CryoSPARC",
+    "startscript": null,
+    "paramscmd": "/home/chines/cryosparc_params.py",
+    "client": {"cmd": null, "redir": ""},
+    "localbind": true
+  }
+]
diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts
index 3612073..358d47b 100644
--- a/src/environments/environment.prod.ts
+++ b/src/environments/environment.prod.ts
@@ -1,3 +1,5 @@
 export const environment = {
-  production: true
+  production: true,
+  tesurl: "https://vm-118-138-240-255.erc.monash.edu.au/tes"
+
 };
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index b7f639a..39a4192 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -4,5 +4,6 @@
 // The list of which env maps to which file can be found in `.angular-cli.json`.
 
 export const environment = {
-  production: false
+  production: false,
+  tesurl: "http://localhost:5000"
 };
diff --git a/src/index.html b/src/index.html
index 46ca962..c1a1056 100644
--- a/src/index.html
+++ b/src/index.html
@@ -8,7 +8,7 @@
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="icon" type="image/x-icon" href="favicon.ico">
 </head>
-<body>
-  <app-root></app-root>
+<body style="height: 100vh; margin-left: 0px; margin-right: 0px; margin-top: 0px; margin-bottom: 0px  ">
+    <app-root></app-root>
 </body>
 </html>
-- 
GitLab