From 60c65f2110f65221bc3a71b2887667e78c53c53e Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Wed, 22 Mar 2017 17:44:16 +0000
Subject: [PATCH] #3527 fix - Added pack/license actions and a lot of bugfixing

---
 securis/src/main/webapp/src/app/forms/user.form.component.ts        |   61 +++++++
 securis/src/main/webapp/src/app/resources/licenses.ts               |   76 ++++++--
 securis/src/main/webapp/src/app/forms/base.ts                       |    6 
 securis/src/main/webapp/src/app/listing/license.list.component.ts   |   65 ++++---
 securis/src/main/java/net/curisit/securis/DevFilter.java            |    2 
 securis/src/main/webapp/package.json                                |    2 
 securis/src/main/webapp/src/app/listing/pack.list.component.ts      |   44 ++--
 securis/src/main/webapp/src/app/forms/user.form.html                |   71 +++++---
 securis/src/main/webapp/src/app/resources/packs.ts                  |   15 +
 securis/src/main/webapp/src/app/listing/pack.list.component.html    |    7 
 securis/src/main/webapp/src/app/common/default.requests.options.ts  |   11 
 securis/src/main/webapp/src/app/forms/pack.form.component.ts        |   24 ++
 securis/src/main/webapp/src/app/app.module.ts                       |    4 
 securis/src/main/webapp/src/app/forms/license.form.component.ts     |   15 +
 securis/src/main/webapp/src/app/forms/pack.form.html                |   16 +
 securis/src/main/webapp/systemjs.config.js                          |    1 
 securis/src/main/webapp/src/app/listing/license.list.component.html |    8 
 securis/src/main/webapp/src/app/forms/license.form.html             |   13 +
 securis/src/main/webapp/src/lang/messages_en.json                   |    8 
 19 files changed, 317 insertions(+), 132 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/DevFilter.java b/securis/src/main/java/net/curisit/securis/DevFilter.java
index 80f6a3b..1e049c0 100644
--- a/securis/src/main/java/net/curisit/securis/DevFilter.java
+++ b/securis/src/main/java/net/curisit/securis/DevFilter.java
@@ -35,7 +35,7 @@
 		//res.addHeader("Access-Control-Request-Headers", "*");
 		res.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
 		res.addHeader("Access-Control-Allow-Headers", "X-SECURIS-TOKEN, Content-Type");
-		res.addHeader("Access-Control-Expose-Headers", "X-SECURIS-ERROR-MSG, X-SECURIS-ERROR-CODE, Content-Type");
+		res.addHeader("Access-Control-Expose-Headers", "X-SECURIS-ERROR-MSG, X-SECURIS-ERROR-CODE, Content-Type, Content-Disposition");
 
 		// LOG.info("Added header to: " + res.getHeaderNames());
 		if (!req.getMethod().equals("OPTIONS")) {
diff --git a/securis/src/main/webapp/package.json b/securis/src/main/webapp/package.json
index f953d0d..b294000 100644
--- a/securis/src/main/webapp/package.json
+++ b/securis/src/main/webapp/package.json
@@ -39,6 +39,7 @@
     "angular-2-local-storage": "^1.0.1",
     "angular-in-memory-web-api": "~0.2.4",
     "core-js": "^2.4.1",
+    "file-saver": "^1.3.3",
     "hammerjs": "^2.0.8",
     "ng2-toastr": "^1.5.1",
     "rxjs": "^5.0.1",
@@ -46,6 +47,7 @@
     "zone.js": "^0.7.4"
   },
   "devDependencies": {
+    "@types/file-saver": "0.0.0",
     "@types/jasmine": "2.5.36",
     "@types/node": "^6.0.46",
     "canonical-path": "0.0.2",
diff --git a/securis/src/main/webapp/src/app/app.module.ts b/securis/src/main/webapp/src/app/app.module.ts
index 57df58a..fe86958 100644
--- a/securis/src/main/webapp/src/app/app.module.ts
+++ b/securis/src/main/webapp/src/app/app.module.ts
@@ -35,11 +35,11 @@
 
 import { LoginFormComponent } from './forms/login.form.component';
 import { LicenseFormComponent } from './forms/license.form.component';
+import { ApplicationFormComponent } from './forms/application.form.component';
+import { PackFormComponent } from "./forms/pack.form.component";
 import { LicenseTypeFormComponent } from './forms/licensetype.form.component';
 import { OrganizationFormComponent } from './forms/organization.form.component';
 import { UserFormComponent } from './forms/user.form.component';
-import { ApplicationFormComponent } from './forms/application.form.component';
-import { PackFormComponent } from "./forms/pack.form.component";
 
 
 import { appRoutes, appRoutingProviders } from './app.routes';
diff --git a/securis/src/main/webapp/src/app/common/default.requests.options.ts b/securis/src/main/webapp/src/app/common/default.requests.options.ts
index 5b3cba2..ce35fb0 100644
--- a/securis/src/main/webapp/src/app/common/default.requests.options.ts
+++ b/securis/src/main/webapp/src/app/common/default.requests.options.ts
@@ -23,11 +23,6 @@
 
     // Set the default 'Content-Type' header
     this.headers.set('Content-Type', 'application/json');
-    let token = this.store.get<string>('token');
-    if (token) {
-        this.headers.set('X-SECURIS-TOKEN', token);
-        
-    }    
     
   }
 }
@@ -35,11 +30,15 @@
 @Injectable()
 export class ApiXHRBackend extends XHRBackend {
     
-    constructor(_browserXHR: BrowserXhr,  _baseResponseOptions: ResponseOptions, _xsrfStrategy: XSRFStrategy) {
+    constructor(_browserXHR: BrowserXhr,  _baseResponseOptions: ResponseOptions, _xsrfStrategy: XSRFStrategy, private store: LocalStorageService) {
           super(_browserXHR, _baseResponseOptions, _xsrfStrategy);
     }
 
     createConnection(request: Request): XHRConnection {
+        let token = this.store.get<string>('token');
+        if (token) {
+            request.headers.set('X-SECURIS-TOKEN', token);
+        }    
         if (!request.url.endsWith('.js') && !request.url.endsWith('.json') && !request.url.endsWith('.html') && !request.url.endsWith('.svg')){
            request.url = 'http://localhost:8080/securis/' + request.url;     // prefix base url
         }
diff --git a/securis/src/main/webapp/src/app/forms/base.ts b/securis/src/main/webapp/src/app/forms/base.ts
index b29548d..1fc2ca1 100644
--- a/securis/src/main/webapp/src/app/forms/base.ts
+++ b/securis/src/main/webapp/src/app/forms/base.ts
@@ -56,8 +56,8 @@
   	protected abstract init(): void;
   	protected abstract goBack(): void;
 
-	save() {
-		var command = this.isNew ? this.resourceServices.create(this.data) : this.resourceServices.modify(this.data.id, this.data);
+	save(fieldId : string = 'id') {
+		var command = this.isNew ? this.resourceServices.create(this.data) : this.resourceServices.modify(this.data[fieldId], this.data);
 		command.subscribe(
 			data => {
 				this.toaster.success(this.$L.get('{} saved sucessfully', this.resourceName.capitalize()));
@@ -94,7 +94,7 @@
 		this.form_title = this.$L.get('{} data', this.resourceName.capitalize());
 		this.isNew = true;
 		!!this.route && this.route.params.subscribe(params => {
-			var eleId = +params[idparam]; // (+) converts string 'id' to a number
+			var eleId = params[idparam];
 			if (!eleId) {
 				this.data = {};
 				Object.keys(default_values).forEach((k : string) => this.data[k] = default_values[k]);
diff --git a/securis/src/main/webapp/src/app/forms/license.form.component.ts b/securis/src/main/webapp/src/app/forms/license.form.component.ts
index 90c3ef3..6414714 100644
--- a/securis/src/main/webapp/src/app/forms/license.form.component.ts
+++ b/securis/src/main/webapp/src/app/forms/license.form.component.ts
@@ -50,7 +50,22 @@
     reader.readAsText(file); 
     this.requestFileUploader.clear();
   } 
+
+
+  licenseAction(action: string) {
+    return this.licenses[action](this.data.id).subscribe(
+      (actionResponse : any) => {
+        this.toaster.success(this.$L.get('Action "{}" executed successfully', action));
+        this.reload();
+      },
+      (err : any) => this.toaster.error(this.$L.get('Action "{}" failed', action))
+    );
+  }
+
   
+  canBeDeleted() : boolean {
+    return !this.isNew && this.licenses.isActionAvailable('delete', this.data);
+  }
 
   requestFileUploaded(file: File) : void {
     console.log(file);
diff --git a/securis/src/main/webapp/src/app/forms/license.form.html b/securis/src/main/webapp/src/app/forms/license.form.html
index 083adcf..84d78b6 100644
--- a/securis/src/main/webapp/src/app/forms/license.form.html
+++ b/securis/src/main/webapp/src/app/forms/license.form.html
@@ -39,7 +39,7 @@
 								<field-readonly [value]="data.activation_code" label="field.activation_code" flex></field-readonly>
 							</div>
 							<div layout="row" layout-fill layout-padding *ngIf="!isNew">
-								<field-readonly [value]="data.expiration_date | date: 'dateMedium'" label="field.expiration_date" flex></field-readonly>
+								<field-readonly [value]="data.expiration_date | date: 'mediumDate'" label="field.expiration_date" flex></field-readonly>
 								<field-readonly [value]="licenses.getStatusName(data.status)" label="field.status" flex></field-readonly>
 							</div>
 							<div layout="row" layout-fill layout-padding>
@@ -59,7 +59,7 @@
 											<span i18n="field.email"></span>
 										</md-placeholder>
 									</md-input-container>
-									<error-checker [fieldName]="$L.get('field.full_name')" [formField]="form.controls.email"></error-checker>
+									<error-checker [fieldName]="$L.get('field.email')" [formField]="form.controls.email"></error-checker>
 								</div>
 							</div>
 							<div layout="row" layout-fill layout-padding>
@@ -99,7 +99,16 @@
 				<md-divider></md-divider>
 				<md-card-actions>
 					<div layout="row" layout-align="start center" class="margin">
+						<button *ngIf="canBeDeleted()" md-raised-button color="warn" (click)="delete(data.id)">Delete</button>
 						<span flex></span>
+						<button md-icon-button [mdMenuTriggerFor]="licMenu" aria-label="License menu">
+							<md-icon>more_vert</md-icon>
+						</button>
+						<md-menu #licMenu="mdMenu">
+							<button md-menu-item *ngFor="let action of license_menu_options" (click)="licenseAction(action.command)" [disabled]="!licenses.isActionAvailable(action.command, data)">
+								<md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
+							</button>
+						</md-menu>
 						<button [disabled]="!form.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
 						<button md-button (click)="goBack()">Cancel</button>
 					</div>
diff --git a/securis/src/main/webapp/src/app/forms/pack.form.component.ts b/securis/src/main/webapp/src/app/forms/pack.form.component.ts
index 0ac5044..9b7c195 100644
--- a/securis/src/main/webapp/src/app/forms/pack.form.component.ts
+++ b/securis/src/main/webapp/src/app/forms/pack.form.component.ts
@@ -1,7 +1,7 @@
 import { Http } from '@angular/http';
 import { ToastsManager } from 'ng2-toastr/ng2-toastr';
 
-import { PacksService, PACK_STATUS } from '../resources/packs';
+import { PacksService, PACK_STATUS, PACK_ACTIONS } from '../resources/packs';
 import { LicenseTypesService } from '../resources/license_types';
 import { LocaleService } from '../common/i18n';
 import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
@@ -21,7 +21,7 @@
 
   organizations : IComboOption[];
   licensetypes : IComboOption[];
-  
+  pack_menu_options = PACK_ACTIONS;
   constructor(private http: Http,
               private licenseTypes: LicenseTypesService,
               private packs: PacksService,
@@ -65,11 +65,27 @@
         );
   }
 
+  packAction(action: string) {
+    return this.packs[action](this.data.id).subscribe(
+      (actionResponse : any) => {
+        this.toaster.success(this.$L.get('Action "{}" executed successfully', action));
+        this.reload();
+      },
+      (err : any) => this.toaster.error(this.$L.get('Action "{}" failed', action))
+    );
+  }
+
   changeOrg(event: any) {
-    console.log(event);
     console.log(this.data.organization_id);
   } 
-  
+
+  canBeDeleted(event: any) : boolean{
+    return !this.isNew && this.packs.isActionAvailable('delete', this.data);
+  } 
+
+  convertToEpoch(str: string) : number {
+    return new Date(str).getTime();
+  }
 
   ngAfterViewInit(): void {
     this.init();
diff --git a/securis/src/main/webapp/src/app/forms/pack.form.html b/securis/src/main/webapp/src/app/forms/pack.form.html
index afbdb05..42a4bbf 100644
--- a/securis/src/main/webapp/src/app/forms/pack.form.html
+++ b/securis/src/main/webapp/src/app/forms/pack.form.html
@@ -48,7 +48,8 @@
 						<div layout="row" layout-fill layout-padding>
 							<div layout="column" layout-fill flex>
 								<md-input-container flex>
-									<input mdInput type="date" [(ngModel)]="data.init_valid_date" name="init_valid_date" required />
+									<input mdInput type="date" [ngModel]="data.init_valid_date | date:'yyyy-MM-dd'" 
+										   (ngModelChange)="data.init_valid_date = $event" name="init_valid_date" required />
 									<md-placeholder>
 										<span i18n="field.end_valid_date"></span>
 									</md-placeholder>
@@ -57,7 +58,8 @@
 							</div>
 							<div layout="column" layout-fill flex>
 								<md-input-container flex>
-									<input mdInput type="date" [(ngModel)]="data.end_valid_date" name="end_valid_date" required />
+									<input mdInput type="date" [ngModel]="data.end_valid_date | date:'yyyy-MM-dd'" 
+									      (ngModelChange)="data.end_valid_date = $event" name="end_valid_date" required />
 									<md-placeholder>
 										<span i18n="field.end_valid_date"></span>
 									</md-placeholder>
@@ -133,7 +135,17 @@
 			<md-divider></md-divider>
 			<md-card-actions>
 				<div layout="row" layout-align="start center" class="margin">
+					<button *ngIf="canBeDeleted()" md-raised-button color="warn" (click)="delete(data.id)">Delete</button>
 					<span flex></span>
+
+					<button md-icon-button [mdMenuTriggerFor]="packMenu" aria-label="Pack menu">
+						<md-icon>more_vert</md-icon>
+					</button>
+					<md-menu #packMenu="mdMenu">
+						<button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command)" [disabled]="!packs.isActionAvailable(action.command, data)">
+							<md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
+						</button>
+					</md-menu>
 					<button [disabled]="!form.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
 					<button md-button (click)="goBack()">Cancel</button>
 				</div>
diff --git a/securis/src/main/webapp/src/app/forms/user.form.component.ts b/securis/src/main/webapp/src/app/forms/user.form.component.ts
index a709db6..c579366 100644
--- a/securis/src/main/webapp/src/app/forms/user.form.component.ts
+++ b/securis/src/main/webapp/src/app/forms/user.form.component.ts
@@ -9,6 +9,7 @@
 import { TdMediaService } from '@covalent/core';
 import { FormBase, IComboOption } from './base';
 import { ActivatedRoute, Router } from '@angular/router';
+import { OrganizationsService } from "../resources/organizations";
 
 var user_example = { 
     username: 'rym',
@@ -27,10 +28,14 @@
   templateUrl: 'src/app/forms/user.form.html'
 })
 export class UserFormComponent extends FormBase {
-
+  allOrganizations: IComboOption[];
+  orgNames: string[] = [];
+  allRoles: any[] = [{"id":1, "code": "advance", "label":"Advance"}, {"id":2, "code": "admin","label":"Admin"}];
+  user_orgs: string[] = [];
+  user_roles: any = {};
   constructor(private http: Http,
               private users: UsersService,
-              private applications: ApplicationsService,
+              private organizations: OrganizationsService,
               router: Router,
               toaster: ToastsManager,
               route: ActivatedRoute,              
@@ -39,16 +44,62 @@
     super($L, router, route, toaster, users, $L.get('user'), dialogs);
   }
 
- 
+  save() : void {
+    this.data.organizations_ids = [];
+    this.data.roles = [];
+    this.user_orgs.forEach(orgName => {
+      var selectedOrg = this.allOrganizations.find(org => org.label === orgName);
+      this.data.organizations_ids.push(selectedOrg.id);
+    });
+    this.user_roles.advance && this.data.roles.push(1);
+    this.user_roles.admin && this.data.roles.push(2);
+    super.save('username');
+  }
+
+  canBeDeleted() {
+    return this.data && this.data.username !== 'admin' && this.data.username !== '_client';
+  }
+
+  loadCombos() : void {
+      this.organizations.get()
+        .map(list => list.map((org : any) => <IComboOption>{id: org.id, label: org.name}))
+        .subscribe(
+          data => {
+            this.allOrganizations = (<IComboOption[]>data).sort((e1, e2) => e1.label.localeCompare(e2.label));
+            this.orgNames = this.allOrganizations.map(org => org.label);
+            this._loadOrgs();
+          },
+          err => console.error('Error loading organizations')
+        );
+  }
+
   goBack(): void {
     this.router.navigate([`users`]);
   }
-
+  _loadOrgs() {
+    if (this.data && this.data.organizations_ids && this.allOrganizations && this.allOrganizations.length > 0) { 
+      this.data.organizations_ids.forEach((orgId : number) => {
+          var selectedOrg = this.allOrganizations.find(org => org.id === orgId);
+          this.user_orgs.push(selectedOrg.label);
+      });
+    }
+  }
   init() : void {
+    this.loadCombos();
+    this.user_orgs = [];
+    this.user_roles = {};
     super.setFirstFocus();
     super.reset();
     super.prepareInitialData('username', {
-      metadata: []
+      organizations_ids: [],
+      roles: []
+    }, (data) => {
+       this._loadOrgs();
+        data.roles.forEach((roleId : number) => {
+            var selectedRole = this.allRoles.find(r => r.id === roleId);
+            this.user_roles[selectedRole.code] = true;
+        });        
+
     });
   }
  
diff --git a/securis/src/main/webapp/src/app/forms/user.form.html b/securis/src/main/webapp/src/app/forms/user.form.html
index 6299a7c..1a3caa4 100644
--- a/securis/src/main/webapp/src/app/forms/user.form.html
+++ b/securis/src/main/webapp/src/app/forms/user.form.html
@@ -8,17 +8,15 @@
 		<button md-icon-button (click)="save()"><md-icon>save</md-icon></button>
 	</md-toolbar>
 	<!--
-			code: 'CICS',
-  creation_timestamp: 1418384439000,
-  description: 'Wellbore integrity analysis software',
-  id: 1,
-  license_filename: 'config_server.lic',
-  name: 'CurisIntegrity',
-  metadata: 
-   [ { key: 'max_docs',
-       value: '250000',
-       readonly: true,
-       mandatory: true } ] 
+			 username: 'rym',
+    roles: [ 1 ],
+    lastLogin: 1488885433000,
+    modificationTimestamp: 1479898458000,
+    email: 'rbouchair@curistec.com',
+    first_name: 'Rym',
+    last_name: 'Bouchair',
+    creation_timestamp: 1479898458000,
+    organizations_ids: [ 1, 2, 5, 6, 7, 8 ]
 }
 		-->
 	<div class="margin" layout-align-gt-xs="center start" layout-fill="" layout-gt-xs="row">
@@ -31,59 +29,80 @@
 				<form #form="ngForm" class="inset">
 					<div layout="column" layout-align="start center">
 						<div layout="row" layout-fill layout-padding>
-							<field-readonly [value]="data.id" label="field.id" flex="15" *ngIf="!isNew"></field-readonly>
 							<div layout="column" layout-fill flex>
 								<md-input-container>
-									<input #firstField mdInput maxLength="50" type="text" [(ngModel)]="data.code" name="code" required [readonly]="!isNew" />
+									<input #firstField mdInput maxLength="50" type="text" [(ngModel)]="data.username" name="username" required [readonly]="!isNew" />
 									<md-placeholder>
-										<span i18n="field.code"></span>
+										<span i18n="field.username"></span>
 									</md-placeholder>
 								</md-input-container>
-								<error-checker [fieldName]="getFieldName('code')" [formField]="form.controls.code"></error-checker>
+								<error-checker [fieldName]="getFieldName('username')" [formField]="form.controls.username"></error-checker>
+							</div>
+							<div layout="column" layout-fill flex>
+								<md-input-container flex>
+									<input mdInput type="password" [(ngModel)]="data.password" name="password" [required]="isNew" />
+									<md-placeholder>
+										<span i18n="field.password"></span>
+									</md-placeholder>
+								</md-input-container>
+								<error-checker [fieldName]="getFieldName('password')" [formField]="form.controls.password"></error-checker>
 							</div>
 						</div>
 						<div layout="row" layout-fill layout-padding>
 							<div layout="column" layout-fill flex>
 								<md-input-container flex>
-									<input mdInput type="text" [(ngModel)]="data.name" name="name" required />
+									<input mdInput type="text" [(ngModel)]="data.first_name" name="first_name" required />
 									<md-placeholder>
-										<span i18n="field.name"></span>
+										<span i18n="field.first_name"></span>
 									</md-placeholder>
 								</md-input-container>
-								<error-checker [fieldName]="getFieldName('name')" [formField]="form.controls.name"></error-checker>
+								<error-checker [fieldName]="getFieldName('first_name')" [formField]="form.controls.first_name"></error-checker>
 							</div>
 							<div layout="column" layout-fill flex>
 								<md-input-container flex>
-									<input mdInput type="text" [(ngModel)]="data.license_filename" name="license_filename" required />
+									<input mdInput type="text" [(ngModel)]="data.last_name" name="last_name"  />
 									<md-placeholder>
-										<span i18n="field.license_filename"></span>
+										<span i18n="field.last_name"></span>
 									</md-placeholder>
 								</md-input-container>
-								<error-checker [fieldName]="getFieldName('license_filename')" [formField]="form.controls.license_filename"></error-checker>
+								<error-checker [fieldName]="getFieldName('last_name')" [formField]="form.controls.last_name"></error-checker>
 							</div>
 						</div>
 						<div layout="row" layout-fill layout-padding>
 							<div layout="column" layout-fill flex>
 								<md-input-container flex>
-									<textarea mdInput type="text" type="text" [(ngModel)]="data.description" name="description" maxlength="1024"></textarea>
+									<input mdInput type="email" [(ngModel)]="data.email" name="email" required maxlength="200" />
 									<md-placeholder>
-										<span i18n="field.description"></span>
+										<span i18n="field.email"></span>
 									</md-placeholder>
-									<md-hint align="end">(max 1024)</md-hint>
 								</md-input-container>
+								<error-checker [fieldName]="$L.get('field.email')" [formField]="form.controls.email"></error-checker>
+							</div>
+						</div>
+							<div layout="row" layout-fill layout-padding >
+							<td-chips flex [mdTooltip]="$L.get('Organizations that user can access')" [placeholder]="$L.get('Select organizations')" 
+									[items]="orgNames" [(ngModel)]="user_orgs" name="user_orgs" requireMatch>
+							</td-chips>
+							<div layout="column" layout-fill flex="25">
+								<md-checkbox [(ngModel)]="user_roles.advance" name="advance_role" [mdTooltip]="$L.get('Role {}', 'advance')">
+									<span i18n>Advance</span>
+								</md-checkbox>
+								<md-checkbox [(ngModel)]="user_roles.admin" name="admin_role" [mdTooltip]="$L.get('Role {}', 'admin')">
+									<span i18n>Admin</span>
+								</md-checkbox>
 							</div>
 						</div>
 						<div layout="row" layout-fill layout-padding *ngIf="!isNew">
+							<field-readonly [value]="data.lastLogin || '' | timeAgo" label="field.lastLogin" flex></field-readonly>
 							<field-readonly [value]="data.creation_timestamp | date: 'medium'" label="field.creation_timestamp" flex></field-readonly>
 						</div>
-						<metadata-manager addOrDelete="true" editKeys="true" [metadata]="data.metadata" ></metadata-manager>
 					</div>
 				</form>
 			</md-card-content>
 			<md-divider></md-divider>
 			<md-card-actions>
 				<div layout="row" layout-align="start center" class="margin">
-					<button *ngIf="!isNew" md-raised-button color="warn" (click)="delete(data.id)">Delete</button>
+					<button *ngIf="!isNew" [disabled]="!canBeDeleted()" md-raised-button color="warn" (click)="delete(data.username)">Delete</button>
 					<span flex></span>
 					<button [disabled]="!form.form.valid" md-raised-button color="primary" (click)="save()">Save</button>
 					<button md-button (click)="goBack()">Cancel</button>
diff --git a/securis/src/main/webapp/src/app/listing/license.list.component.html b/securis/src/main/webapp/src/app/listing/license.list.component.html
index 1a6ad85..e2b0c30 100644
--- a/securis/src/main/webapp/src/app/listing/license.list.component.html
+++ b/securis/src/main/webapp/src/app/listing/license.list.component.html
@@ -40,9 +40,9 @@
           <span style="white-space: nowrap">{{value}}</span>
         </div>
       </template>
-      <template tdDataTableTemplate="status" let-row="row">
+      <template tdDataTableTemplate="status" let-row="row"  let-value="value">
         <div layout="row" layout-align="start center">
-            <md-icon [style.color]="licenses.getStatusColor(row.status)">brightness_1</md-icon>&nbsp;<span>{{licenses.getStatusName(row.status)}}</span>
+          <md-chip selected [mdTooltip]="licenses.getStatusName(value)" [style.background-color]="licenses.getStatusColor(value)"  >{{value}}</md-chip>
         </div>
       </template>
       <template tdDataTableTemplate="email" let-row="row" let-value="value">
@@ -63,7 +63,7 @@
             <md-icon>more_vert</md-icon>
           </button>
           <md-menu #licenseMenu="mdMenu">
-              <button md-menu-item *ngFor="let action of license_menu_options" (click)="licenseAction(action.command, row)" [disabled]="!isActionAvailable(row)">
+              <button md-menu-item *ngFor="let action of license_menu_options" (click)="licenseAction(action.command, row)" [disabled]="!isActionAvailable(action.command, row)">
                 <md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
               </button>
           </md-menu>
@@ -74,4 +74,4 @@
       <span i18n td-paging-bar-label hide-xs>Rows per page:</span> {{pagingBar.range}} <span hide-xs>of {{pagingBar.total}}</span>
     </td-paging-bar>
   </div>
-</td-layout-card-over>
\ No newline at end of file
+</td-layout-card-over>
diff --git a/securis/src/main/webapp/src/app/listing/license.list.component.ts b/securis/src/main/webapp/src/app/listing/license.list.component.ts
index 4783054..843f985 100644
--- a/securis/src/main/webapp/src/app/listing/license.list.component.ts
+++ b/securis/src/main/webapp/src/app/listing/license.list.component.ts
@@ -5,9 +5,10 @@
 import { Component, AfterViewInit, ViewChild } from '@angular/core';
 import { TdMediaService } from '@covalent/core';
 import { Location } from '@angular/common';
+import { ToastsManager } from 'ng2-toastr/ng2-toastr';
 
 import { LocaleService } from '../common/i18n';
-import { LicensesService } from '../resources/licenses';
+import { LicensesService, LICENSE_ACTIONS } from '../resources/licenses';
 import { PacksService } from '../resources/packs';
 import { LicenseFormComponent } from '../forms/license.form.component';
 import { ListingBase } from './base';
@@ -43,22 +44,30 @@
     { name: 'menu', label: '' }
   ];
 
-  license_menu_options : any[] = [{
-    icon: 'edit',
-    command: 'edit',
-    name: 'Edit'
-  },{
-    icon: 'cancel',
-    command: 'cancel',
-    name: 'Cancel'
-  }]
+  license_menu_options = LICENSE_ACTIONS;
   
-  licenseAction(action: any) {
-    console.log(action.command);
+  licenseAction(action: string, license: any) {
+    return this.licenses[action](license.id).subscribe(
+      (actionResponse : any) => {
+        this.toaster.success(this.$L.get('Action "{}" executed successfully', action));
+        this.reload(this.pack.id);
+      },
+      (err : any) => this.toaster.error(this.$L.get('Action "{}" failed', action))
+    );
   }
 
-  isActionAvailable(pack : any) : boolean {
-    return true;
+  reload(packId: number) : void  {
+      this.licenses.getByPack(packId).subscribe(
+        list => {
+          this.data = list;
+          this.refresh();
+        },
+        err => console.error(err)
+      );
+  }
+
+  isActionAvailable(action: string, license : any) : boolean {
+    return this.licenses.isActionAvailable(action, license);
   }
 
   constructor( _dataTableService: TdDataTableService,
@@ -66,6 +75,7 @@
     private $L: LocaleService,
     private router: Router,
     private location: Location,
+    private toaster: ToastsManager,
     private route: ActivatedRoute,
     private dialog: MdDialog,
     private licenseForm: LicenseFormComponent,
@@ -75,22 +85,6 @@
   }
 
   ngOnInit(): void {
-    this.route.params.subscribe(params => {
-        var packId = +params['packId']; // (+) converts string 'id' to a number
-        this.licenses.getByPack(packId).subscribe(
-          list => {
-            this.data = list;
-            this.refresh();
-          },
-          err => console.error(err)
-        );
-        this.packs.get(packId).subscribe(
-          packData => {
-            this.pack = packData;
-          },
-          err => console.error(err)
-        );
-      });
   }
 
   goBack() : void {
@@ -111,7 +105,16 @@
 
   ngAfterViewInit(): void {
     this.media.broadcast();
-
+    this.route.params.subscribe(params => {
+        var packId = +params['packId']; // (+) converts string 'id' to a number
+        this.reload(packId);
+        this.packs.get(packId).subscribe(
+          packData => {
+            this.pack = packData;
+          },
+          err => console.error(err)
+        );
+      });
   }
 }
 
diff --git a/securis/src/main/webapp/src/app/listing/pack.list.component.html b/securis/src/main/webapp/src/app/listing/pack.list.component.html
index 0399351..f08c574 100644
--- a/securis/src/main/webapp/src/app/listing/pack.list.component.html
+++ b/securis/src/main/webapp/src/app/listing/pack.list.component.html
@@ -33,6 +33,11 @@
           <span style="white-space: nowrap">{{value}}</span>
         </div>
       </template>
+      <template tdDataTableTemplate="status" let-row="row" let-value="value">
+        <div layout="row" layout-align="start center">
+          <md-chip selected [mdTooltip]="$L.get('pack.status.' +value)" [style.background-color]="packs.getStatusColor(value)"  >{{value}}</md-chip>
+        </div>
+      </template>
       <template tdDataTableTemplate="menu" let-row="row" let-index="index">
         <div layout="row" layout-align="end center">
           <button md-icon-button (click)="edit(row.id)" color="primary"><md-icon>edit</md-icon></button>
@@ -41,7 +46,7 @@
           </button>
           
           <md-menu #packMenu="mdMenu">
-              <button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command, row)" [disabled]="!isActionAvailable(row)">
+              <button md-menu-item *ngFor="let action of pack_menu_options" (click)="packAction(action.command, row)" [disabled]="!packs.isActionAvailable(action.command, row)">
                 <md-icon *ngIf="!!action.icon">{{ action.icon }}</md-icon> {{ action.name }}
               </button>
           </md-menu>
diff --git a/securis/src/main/webapp/src/app/listing/pack.list.component.ts b/securis/src/main/webapp/src/app/listing/pack.list.component.ts
index 3060af9..42927b1 100644
--- a/securis/src/main/webapp/src/app/listing/pack.list.component.ts
+++ b/securis/src/main/webapp/src/app/listing/pack.list.component.ts
@@ -1,4 +1,5 @@
 import { Router, ActivatedRoute } from '@angular/router';
+import { ToastsManager } from 'ng2-toastr/ng2-toastr';
 import { MdDialog, MdDialogConfig } from '@angular/material';
 import {
     ITdDataTableColumn,
@@ -10,7 +11,7 @@
 import { IPageChangeEvent } from '@covalent/core';
 import { Component, ViewChild, AfterViewInit } from '@angular/core';
 import { TdMediaService } from '@covalent/core';
-import { PacksService } from '../resources/packs';
+import { PacksService, PACK_ACTIONS } from '../resources/packs';
 import { PackFormComponent } from '../forms/pack.form.component';
 import { LocaleService } from '../common/i18n';
 import { ListingBase } from './base';
@@ -57,17 +58,11 @@
     { name: 'licensetype_code', label: 'License type' },
     { name: 'organization_name', label: 'Organization' },
     { name: 'used_licenses', label: 'Licenses', tooltip: 'Initial/Available pack licenses' },
+    { name: 'status', label: 'Status', tooltip: 'Pack status' },
     { name: 'menu', label: '' }
   ];
 
-  pack_menu_options : any[] = [{
-    command: 'edit',
-    name: 'Edit'
-  },{
-    command: 'cancel',
-    name: 'Cancel'
-  }]
-  
+  pack_menu_options = PACK_ACTIONS;
 
   constructor(_dataTableService: TdDataTableService,
     private media: TdMediaService,
@@ -75,25 +70,30 @@
     private route: ActivatedRoute,
     private dialog: MdDialog,
     private $L: LocaleService,
+    private toaster: ToastsManager,
     private packForm: PackFormComponent,
     private packs: PacksService) {
       super(_dataTableService);
-      this.packs.get().subscribe(
-        (list : any[]) => {
-          this.data = list;
-          this.refresh();
-        },
-        (err: any) => console.error(err)
-      );
   }
 
-
-  packAction(action: any) {
-    console.log(action.command);
+  reload() : void  {
+    this.packs.get().subscribe(
+      (list : any[]) => {
+        this.data = list;
+        this.refresh();
+      },
+      (err: any) => console.error(err)
+    );
   }
 
-  isActionAvailable(pack : any) : boolean {
-    return true;
+  packAction(action: string, pack: any) {
+    return this.packs[action](pack.id).subscribe(
+      (actionResponse : any) => {
+        this.toaster.success(this.$L.get('Action "{}" executed successfully', action));
+        this.reload();
+      },
+      (err : any) => this.toaster.error(this.$L.get('Action "{}" failed', action))
+    );
   }
 
   showLicenses(pack: any) : void {
@@ -115,8 +115,8 @@
   }
 
   ngAfterViewInit(): void {
+    this.reload();
     this.media.broadcast();
-
   }
 }
 
diff --git a/securis/src/main/webapp/src/app/resources/licenses.ts b/securis/src/main/webapp/src/app/resources/licenses.ts
index a82d910..87de7bd 100644
--- a/securis/src/main/webapp/src/app/resources/licenses.ts
+++ b/securis/src/main/webapp/src/app/resources/licenses.ts
@@ -1,26 +1,9 @@
 import { LocaleService } from '../common/i18n';
 import { Observable } from 'rxjs/Rx';
 import { Injectable } from '@angular/core';
-import { Http, RequestOptions } from '@angular/http';
+import { Http, RequestOptions, ResponseContentType, Response } from '@angular/http';
 import { SeCurisResourceServices } from './base';
-
-var lic_example = {
-	id: 101,
-	activation_code: 'f3e2d27e-7f81-4ac7-87ba-b9e38b4fdbb8',
-	code: 'CITR01-436-1',
-	code_suffix: 1,
-	created_by_id: '_client',
-	creation_timestamp: 1445898088000,
-	email: 'GQuercia@trican.ca',
-	expiration_date: 1487523828000,
-	full_name: 'George Quercia',
-	licenseData: '{"appCode":"CICS","appName":"CurisIntegrity","licenseCode":"CITR01-436-1","activationCode":"f3e2d27e-7f81-4ac7-87ba-b9e38b4fdbb8","expirationDate":1487523828416,"arch":"amd64","osName":"Windows 7","macAddresses":["C4-D9-87-5D-53-72","C4-D9-87-5D-53-76","FC-15-B4-EB-70-F9"],"crcLogo":"10f6379e0e1c00ebc403160307e3c5d0aba0727c9cae0bf1ac7cd19d84fdc80f","metadata":{"a2Mode":"false","datasetPrefix":"TR","extendedMode":"false","maxConcurrentInstances":"-1","maxInstances":"3","maxUsers":"0","maxWellLifeLines":"50","timeThreshold":"0"},"signature":"cjyLYFyhXpWWsMNnG6ER9mtCREgw02aQDnXPSQQWZtiLWbu/GyHZzK+1msLhwuKMGYG6I90s5wp82HVIqhIheHOsov3JfnHgNtYzf3BdkqUinwPFuDqPqkXz5Sjb6bouWkmvTI1TN/s4U2DJOXVnYN4FnYl0/dBTcU9RP4NZlQxMu6oFuRrZSMfdMCxEJYZAU62SWgTSurkdmHhFgwRjIwsOXRWHYsr6vGT//yILI7UvMGbMc6dRCGwyJLPNi4nXwF9PRMLinB7fYK8HxKylTJx2O7bvWCZd6EOdwi6gRI/0HhOqZ7E4DzBDrqEnsHeuH4L47DfRdIMGDnA492F+mg=="}',
-	modification_timestamp: 1484931828000,
-	pack_code: 'CITR01',
-	pack_id: 12,
-	request_data: '{"appCode":"CICS","activationCode":"f3e2d27e-7f81-4ac7-87ba-b9e38b4fdbb8","arch":"amd64","osName":"Windows 7","macAddresses":["C4-D9-87-5D-53-72","C4-D9-87-5D-53-76","FC-15-B4-EB-70-F9"],"crcLogo":"10f6379e0e1c00ebc403160307e3c5d0aba0727c9cae0bf1ac7cd19d84fdc80f"}',
-	status: 'AC'
-}
+import * as saveAsFile from "file-saver";
 
 export const LIC_STATUS = {
 	CREATED: 'CR',
@@ -41,6 +24,7 @@
 		'CA': '#a21717'
 };
 
+
 /**
  * These transitions could be get from server, class License.Status, but
  * we copy them for simplicity, this info won't change easily
@@ -56,6 +40,37 @@
 	cancel: [LIC_STATUS.REQUESTED, LIC_STATUS.EXPIRED, LIC_STATUS.PREACTIVE, LIC_STATUS.ACTIVE],
 	'delete': [LIC_STATUS.CREATED, LIC_STATUS.CANCELLED, LIC_STATUS.BLOCKED]
 }
+
+export const LICENSE_ACTIONS : any[] = [{
+/*    command: 'add_request',
+    icon: 'add_to_photos',
+    name: 'Add request'
+  },{  */
+    command: 'activate',
+    icon: 'play_circle_outline',
+    name: 'Activate'
+  },{
+    command: 'send',
+    icon: 'send',
+    name: 'Send'
+  },{
+    command: 'download',
+    icon: 'file_download',
+    name: 'Download'
+  },{
+    command: 'block',
+    icon: 'do_not_disturb_on',
+    name: 'Block'
+  },{
+    command: 'unblock',
+    icon: 'do_not_disturb_off',
+    name: 'Unblock'
+  },{
+    command: 'cancel',
+    icon: 'cancel',
+    name: 'Cancel'
+  }]
+
 
 @Injectable()
 export class LicensesService extends SeCurisResourceServices {
@@ -73,19 +88,36 @@
 		return super.action(id, "activate");
 	}
 
+	public block(id: number) {
+		return super.action(id, "block");
+	}
+
+	public unblock(id: number) {
+		return super.action(id, "unblock");
+	}
+
+	public send(id: number) {
+		return super.action(id, "send");
+	}
+
 	public cancel(id: number) {
 		return super.action(id, "cancel");
 	}
 
-	public putonhold(id: number) {
-		return super.action(id, "putonhold");
+	public download(id: number) {
+	    let url = `${this.resource}/${id}/download`;
+        return this.http.get(url).map((response : Response) => {
+			let filename =  JSON.parse(response.headers.get('Content-Disposition').match(/".*"$/g)[0]);
+			let content = JSON.stringify(response.json(), null, 2);
+			saveAsFile( new Blob([ content ], { type : 'application/octet-stream' }), filename);
+			return Observable.of(true);
+		}).catch(err => super.processErrorResponse(err));
 	}
 
 	public isActionAvailable(action:string, lic:any) {
 		var validStatuses = LIC_ACTIONS_BY_STATUS[action];
 		return lic && validStatuses && validStatuses.indexOf(lic.status) !== -1;
 	}
-
 
 	getStatusName(statusCode: string): string {
 		return this.$L.get(`pack.status.${statusCode}`, this.$L.get('Unknown'));
diff --git a/securis/src/main/webapp/src/app/resources/packs.ts b/securis/src/main/webapp/src/app/resources/packs.ts
index 951db3b..17ce29b 100644
--- a/securis/src/main/webapp/src/app/resources/packs.ts
+++ b/securis/src/main/webapp/src/app/resources/packs.ts
@@ -57,6 +57,21 @@
 				'delete': [PACK_STATUS.CREATED, PACK_STATUS.CANCELLED]
 		}
 
+export const PACK_ACTIONS : any[] = [{
+    command: 'activate',
+    icon: 'play_circle_outline',
+    name: 'Activate'
+  },{
+    command: 'putonhold',
+    icon: 'pause_circle_outline',
+    name: 'Put on hold'
+  },{
+    command: 'cancel',
+    icon: 'cancel',
+    name: 'Cancel'
+  }]
+
+
 
 @Injectable()
 export class PacksService extends SeCurisResourceServices {
diff --git a/securis/src/main/webapp/src/lang/messages_en.json b/securis/src/main/webapp/src/lang/messages_en.json
index 1147470..acb0550 100644
--- a/securis/src/main/webapp/src/lang/messages_en.json
+++ b/securis/src/main/webapp/src/lang/messages_en.json
@@ -20,11 +20,17 @@
 	"field.metadata": "Metadata",
 	"field.key": "Parameter",
 	"field.value": "Value",
+	"field.username": "Username",
+	"field.password": "Password",
+	"field.first_name": "Firstname",
+	"field.last_name": "Lastname",
 	"field.created_by": "Created by",
 	"field.creation_date": "Creation date",
+	"field.expiration_date": "Expiration date",
 	"field.request_data": "Request data",
-	"field.full_name": "Full name",
+	"field.full_name": "Fullname",
 	"field.email": "Email",
+	"field.lastLogin": "Last login",
 	"field.creation_timestamp": "Creation timestamp",
 	"field.comments": "Comments",
 	"field.mandatory": "Required",
diff --git a/securis/src/main/webapp/systemjs.config.js b/securis/src/main/webapp/systemjs.config.js
index 4567190..476858d 100644
--- a/securis/src/main/webapp/systemjs.config.js
+++ b/securis/src/main/webapp/systemjs.config.js
@@ -16,6 +16,7 @@
     // other libraries
     'rxjs': 'npm:rxjs',
     'ng2-toastr': 'npm:ng2-toastr',
+    'file-saver': 'npm:file-saver/FileSaver.js',
     'angular-2-local-storage': 'npm:angular-2-local-storage/dist',
     'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
   }

--
Gitblit v1.3.2