From 1a0491f2462d2c309bd8e310b22c11019a79ce1e Mon Sep 17 00:00:00 2001
From: rsanchez <rsanchez@curisit.net>
Date: Mon, 20 Mar 2017 16:02:14 +0000
Subject: [PATCH] #3527 fix - Added applications forms and metadata component

---
 securis/src/main/webapp/src/app/common/utils.ts                         |  101 +++--
 securis/src/main/webapp/src/app/resources/organizations.ts              |    5 
 securis/src/main/webapp/src/app/forms/base.ts                           |  212 +++++++++++
 securis/src/main/webapp/src/app/resources/applications.ts               |    5 
 securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java   |   76 ++--
 securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java   |  106 ++--
 securis/src/main/webapp/src/app/listing/pack.list.component.ts          |    4 
 securis/src/main/webapp/src/app/common/i18n.ts                          |    2 
 securis/src/main/webapp/src/app/forms/application.form.html             |  124 +-----
 securis/src/main/webapp/src/app/forms/pack.form.component.ts            |   43 -
 securis/src/main/webapp/src/app/resources/license_types.ts              |    5 
 securis/src/main/webapp/src/app/forms/license.form.component.ts         |   22 
 securis/src/main/java/net/curisit/securis/db/PackMetadata.java          |  116 +++---
 securis/src/main/webapp/src/app/forms/license.form.html                 |   21 -
 securis/src/main/webapp/src/app/forms/application.form.component.ts     |   22 
 securis/src/main/webapp/src/app/resources/licenses.ts                   |    4 
 securis/src/main/webapp/src/app/resources/base.ts                       |   20 
 securis/src/main/webapp/src/app/resources/users.ts                      |    5 
 securis/src/main/java/net/curisit/securis/DevFilter.java                |    2 
 /dev/null                                                               |   54 ---
 securis/src/main/webapp/src/app/listing/application.list.component.html |    1 
 securis/src/main/webapp/src/app/resources/packs.ts                      |    4 
 securis/src/main/webapp/src/app/app.module.ts                           |    9 
 securis/src/main/webapp/src/app/user.service.ts                         |   35 -
 securis/src/main/webapp/src/app/forms/pack.form.html                    |   21 -
 securis/src/main/webapp/src/lang/messages_en.json                       |    4 
 26 files changed, 523 insertions(+), 500 deletions(-)

diff --git a/securis/src/main/java/net/curisit/securis/DevFilter.java b/securis/src/main/java/net/curisit/securis/DevFilter.java
index 8b01f33..80f6a3b 100644
--- a/securis/src/main/java/net/curisit/securis/DevFilter.java
+++ b/securis/src/main/java/net/curisit/securis/DevFilter.java
@@ -33,7 +33,7 @@
 		// For dev. using JS in different server
 		res.addHeader("Access-Control-Allow-Origin", "*");
 		//res.addHeader("Access-Control-Request-Headers", "*");
-		res.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
+		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");
 
diff --git a/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java b/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java
index c777d7f..4b81924 100644
--- a/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java
+++ b/securis/src/main/java/net/curisit/securis/db/ApplicationMetadata.java
@@ -17,6 +17,7 @@
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -29,78 +30,77 @@
 @JsonInclude(Include.NON_NULL)
 @Entity
 @Table(name = "application_metadata")
-@NamedQueries({
-    @NamedQuery(name = "list-application-metadata", query = "SELECT a FROM ApplicationMetadata a where a.application.id = :applicationId")
-})
+@JsonIgnoreProperties(value = { "readonly" })
+@NamedQueries({ @NamedQuery(name = "list-application-metadata", query = "SELECT a FROM ApplicationMetadata a where a.application.id = :applicationId") })
 public class ApplicationMetadata implements Serializable {
 
-    private static final Logger LOG = LogManager.getLogger(ApplicationMetadata.class);
+	private static final Logger LOG = LogManager.getLogger(ApplicationMetadata.class);
 
-    private static final long serialVersionUID = 1L;
+	private static final long serialVersionUID = 1L;
 
-    @Id
-    @ManyToOne
-    @JoinColumn(name = "application_id")
-    @JsonBackReference
-    private Application application;
+	@Id
+	@ManyToOne
+	@JoinColumn(name = "application_id")
+	@JsonBackReference
+	private Application application;
 
-    @Id
-    @Column(name = "\"key\"")
-    private String key;
+	@Id
+	@Column(name = "\"key\"")
+	private String key;
 
-    private String value;
+	private String value;
 
-    private boolean mandatory;
+	private boolean mandatory;
 
-    @Column(name = "creation_timestamp")
-    @JsonProperty("creation_timestamp")
-    private Date creationTimestamp;
+	@Column(name = "creation_timestamp")
+	@JsonProperty("creation_timestamp")
+	private Date creationTimestamp;
 
-    public String getKey() {
-        return key;
-    }
+	public String getKey() {
+		return key;
+	}
 
-    public void setKey(String key) {
-        this.key = key;
-    }
+	public void setKey(String key) {
+		this.key = key;
+	}
 
-    public Application getApplication() {
-        LOG.info("Getting application from app metadata: {}", application);
-        return application;
-    }
+	public Application getApplication() {
+		LOG.info("Getting application from app metadata: {}", application);
+		return application;
+	}
 
-    public void setApplication(Application application) {
-        this.application = application;
-    }
+	public void setApplication(Application application) {
+		this.application = application;
+	}
 
-    public Date getCreationTimestamp() {
-        return creationTimestamp;
-    }
+	public Date getCreationTimestamp() {
+		return creationTimestamp;
+	}
 
-    public void setCreationTimestamp(Date creationTimestamp) {
-        this.creationTimestamp = creationTimestamp;
-    }
+	public void setCreationTimestamp(Date creationTimestamp) {
+		this.creationTimestamp = creationTimestamp;
+	}
 
-    public String getValue() {
-        return value;
-    }
+	public String getValue() {
+		return value;
+	}
 
-    public void setValue(String value) {
-        this.value = value;
-    }
+	public void setValue(String value) {
+		this.value = value;
+	}
 
-    public boolean isMandatory() {
-        return mandatory;
-    }
+	public boolean isMandatory() {
+		return mandatory;
+	}
 
-    public void setMandatory(boolean mandatory) {
-        this.mandatory = mandatory;
-    }
+	public void setMandatory(boolean mandatory) {
+		this.mandatory = mandatory;
+	}
 
-    @Override
-    public String toString() {
+	@Override
+	public String toString() {
 
-        return String.format("ApplicationMetadata (%s)", this.key);
-    }
+		return String.format("ApplicationMetadata (%s)", this.key);
+	}
 
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java b/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java
index 1ea4b99..c88e894 100644
--- a/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java
+++ b/securis/src/main/java/net/curisit/securis/db/LicenseTypeMetadata.java
@@ -13,6 +13,7 @@
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 
@@ -24,56 +25,55 @@
 @JsonInclude(Include.NON_NULL)
 @Entity
 @Table(name = "licensetype_metadata")
-@NamedQueries({
-    @NamedQuery(name = "list-licensetype-metadata", query = "SELECT a FROM LicenseTypeMetadata a where a.licenseType.id = :licenseTypeId")
-})
+@JsonIgnoreProperties(value = { "readonly" })
+@NamedQueries({ @NamedQuery(name = "list-licensetype-metadata", query = "SELECT a FROM LicenseTypeMetadata a where a.licenseType.id = :licenseTypeId") })
 public class LicenseTypeMetadata implements Serializable {
 
-    private static final long serialVersionUID = 1L;
+	private static final long serialVersionUID = 1L;
 
-    @Id
-    @ManyToOne
-    @JsonBackReference
-    @JoinColumn(name = "license_type_id")
-    private LicenseType licenseType;
+	@Id
+	@ManyToOne
+	@JsonBackReference
+	@JoinColumn(name = "license_type_id")
+	private LicenseType licenseType;
 
-    @Id
-    @Column(name = "\"key\"")
-    private String key;
+	@Id
+	@Column(name = "\"key\"")
+	private String key;
 
-    private String value;
+	private String value;
 
-    private boolean mandatory;
+	private boolean mandatory;
 
-    public LicenseType getLicenseType() {
-        return licenseType;
-    }
+	public LicenseType getLicenseType() {
+		return licenseType;
+	}
 
-    public void setLicenseType(LicenseType licenseType) {
-        this.licenseType = licenseType;
-    }
+	public void setLicenseType(LicenseType licenseType) {
+		this.licenseType = licenseType;
+	}
 
-    public String getValue() {
-        return value;
-    }
+	public String getValue() {
+		return value;
+	}
 
-    public void setValue(String value) {
-        this.value = value;
-    }
+	public void setValue(String value) {
+		this.value = value;
+	}
 
-    public String getKey() {
-        return key;
-    }
+	public String getKey() {
+		return key;
+	}
 
-    public void setKey(String key) {
-        this.key = key;
-    }
+	public void setKey(String key) {
+		this.key = key;
+	}
 
-    public boolean isMandatory() {
-        return mandatory;
-    }
+	public boolean isMandatory() {
+		return mandatory;
+	}
 
-    public void setMandatory(boolean mandatory) {
-        this.mandatory = mandatory;
-    }
+	public void setMandatory(boolean mandatory) {
+		this.mandatory = mandatory;
+	}
 }
diff --git a/securis/src/main/java/net/curisit/securis/db/PackMetadata.java b/securis/src/main/java/net/curisit/securis/db/PackMetadata.java
index 5bb9ab3..8a73abe 100644
--- a/securis/src/main/java/net/curisit/securis/db/PackMetadata.java
+++ b/securis/src/main/java/net/curisit/securis/db/PackMetadata.java
@@ -13,6 +13,7 @@
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonInclude.Include;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -25,82 +26,81 @@
 @JsonInclude(Include.NON_NULL)
 @Entity
 @Table(name = "pack_metadata")
-@NamedQueries({
-    @NamedQuery(name = "list-pack-metadata", query = "SELECT a FROM PackMetadata a where a.pack.id = :packId")
-})
+@JsonIgnoreProperties(value = { "readonly" })
+@NamedQueries({ @NamedQuery(name = "list-pack-metadata", query = "SELECT a FROM PackMetadata a where a.pack.id = :packId") })
 public class PackMetadata implements Serializable {
 
-    private static final long serialVersionUID = 1L;
+	private static final long serialVersionUID = 1L;
 
-    @Id
-    @JsonIgnore
-    @ManyToOne
-    @JoinColumn(name = "pack_id")
-    private Pack pack;
+	@Id
+	@JsonIgnore
+	@ManyToOne
+	@JoinColumn(name = "pack_id")
+	private Pack pack;
 
-    @Id
-    @Column(name = "\"key\"")
-    private String key;
+	@Id
+	@Column(name = "\"key\"")
+	private String key;
 
-    private String value;
+	private String value;
 
-    private boolean readonly;
+	private boolean readonly;
 
-    private boolean mandatory;
+	private boolean mandatory;
 
-    @JsonProperty("pack_id")
-    public Integer getPackId() {
-        return pack == null ? null : pack.getId();
-    }
+	@JsonProperty("pack_id")
+	public Integer getPackId() {
+		return pack == null ? null : pack.getId();
+	}
 
-    @JsonProperty("pack_id")
-    public void setLicenseTypeId(Integer idPack) {
-        if (idPack == null) {
-            pack = null;
-        } else {
-            pack = new Pack();
-            pack.setId(idPack);
-        }
-    }
+	@JsonProperty("pack_id")
+	public void setLicenseTypeId(Integer idPack) {
+		if (idPack == null) {
+			pack = null;
+		} else {
+			pack = new Pack();
+			pack.setId(idPack);
+		}
+	}
 
-    public Pack getPack() {
-        return pack;
-    }
+	public Pack getPack() {
+		return pack;
+	}
 
-    public void setPack(Pack pack) {
-        this.pack = pack;
-    }
+	public void setPack(Pack pack) {
+		this.pack = pack;
+	}
 
-    public String getValue() {
-        return value;
-    }
+	public String getValue() {
+		return value;
+	}
 
-    public void setValue(String value) {
-        this.value = value;
-    }
+	public void setValue(String value) {
+		this.value = value;
+	}
 
-    public String getKey() {
-        return key;
-    }
+	public String getKey() {
+		return key;
+	}
 
-    public void setKey(String key) {
-        this.key = key;
-    }
+	public void setKey(String key) {
+		this.key = key;
+	}
 
-    public boolean isReadonly() {
-        return readonly;
-    }
+	public boolean isReadonly() {
+		return readonly;
+	}
 
-    public void setReadonly(boolean readonly) {
-        this.readonly = readonly;
-    }
+	public void setReadonly(boolean readonly) {
+		this.readonly = readonly;
+	}
 
-    public boolean isMandatory() {
-        return mandatory;
-    }
+	public boolean isMandatory() {
+		return mandatory;
+	}
 
-    public void setMandatory(boolean mandatory) {
-        this.mandatory = mandatory;
-    }
+	public void setMandatory(boolean mandatory) {
+		this.mandatory = mandatory;
+	}
 
 }
diff --git a/securis/src/main/webapp/src/app/app.module.ts b/securis/src/main/webapp/src/app/app.module.ts
index dd0d917..d065fd0 100644
--- a/securis/src/main/webapp/src/app/app.module.ts
+++ b/securis/src/main/webapp/src/app/app.module.ts
@@ -1,4 +1,3 @@
-import { ErrorCheckerComponent } from './common/error.checker';
 import { NgModule }      from '@angular/core';
 import { BrowserModule } from '@angular/platform-browser';
 import { CommonModule } from '@angular/common';
@@ -13,6 +12,10 @@
 
 import { HomeComponent }  from './home.component';
 import { I18nDirective }  from './common/i18n';
+import { MetadataManagerComponent }  from './forms/base';
+import { FieldReadonlyComponent }  from './forms/base';
+import { ErrorCheckerComponent } from './forms/base';
+
 import { UserService }  from './user.service';
 import { PacksService }  from './resources/packs';
 import { LicenseTypesService }  from './resources/license_types';
@@ -23,7 +26,6 @@
 import { MenuComponent }  from './menu.component';
 import { NoMenuComponent }  from './nomenu.component';
 import { FooterComponent }  from './footer.component';
-import { FieldReadonlyComponent }  from './common/utils';
 import { LicenseListComponent } from './listing/license.list.component';
 import { PackListComponent }  from './listing/pack.list.component';
 import { LoginFormComponent } from './forms/login.form.component';
@@ -68,7 +70,8 @@
     MenuComponent,
     NoMenuComponent,
     FieldReadonlyComponent,
-    FooterComponent
+    FooterComponent,
+    MetadataManagerComponent
   ],
   bootstrap: [ HomeComponent ],
   entryComponents: [  ],
diff --git a/securis/src/main/webapp/src/app/common/error.checker.ts b/securis/src/main/webapp/src/app/common/error.checker.ts
deleted file mode 100644
index 3c651c3..0000000
--- a/securis/src/main/webapp/src/app/common/error.checker.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Http } from '@angular/http';
-import { PacksService } from '../resources/packs';
-import { LocaleService } from '../common/i18n';
-import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
-import { IPageChangeEvent } from '@covalent/core';
-import { Component, Input, AfterViewInit } from '@angular/core';
-import { TdMediaService } from '@covalent/core';
-
-@Component({
-  selector: 'error-checker',
-  template: `
-    <div *ngIf="formField?.touched && formField.invalid" layout="column">
-      <span *ngFor="let err of getFieldErrors()" align="end">{{err}}</span>
-    </div>`
-})
-export class ErrorCheckerComponent {
-
-  @Input() formField: any;
-  @Input() fieldName: string;
-
-  constructor(private $L: LocaleService) {
-  }
-  
-  getFieldErrors() : string[] {
-    if (this.formField.valid) {
-      return []
-    } else {
-      return (<string[]>Object.keys(this.formField.errors)).map((err:string) => this.getErrorMsg(err));
-    }
-  }
-
-  private updateFieldErrors() {
-  }
-
-  private getErrorMsg(err: string) : string{
-    switch(err) { 
-      case 'required': { 
-        return this.fieldName + ' '+ this.$L.get('is required');
-      } 
-      case 'number': { 
-        return this.fieldName + ' '+ this.$L.get('should be a number');
-      } 
-      default: { 
-        return this.fieldName + ' '+ this.$L.get('unknown error') + ' ' + err;
-      } 
-    } 
-  }
-
-  log(obj: any) {
-    console.log(obj)
-  }
-  
-}
-
diff --git a/securis/src/main/webapp/src/app/common/i18n.ts b/securis/src/main/webapp/src/app/common/i18n.ts
index 7e71f18..1d81d6f 100644
--- a/securis/src/main/webapp/src/app/common/i18n.ts
+++ b/securis/src/main/webapp/src/app/common/i18n.ts
@@ -93,7 +93,7 @@
      * $L.get('hello'); // This returns "hola"
      * $L.get('Hello {0}!!', 'John'); // This returns: "Hola John!!" if language is spanish
      */
-    get(msg: string, ...params: string[] ) : string {
+    get(msg: string, ...params: any[] ) : string {
         if (msg == null) {
             return '';
         }
diff --git a/securis/src/main/webapp/src/app/common/utils.ts b/securis/src/main/webapp/src/app/common/utils.ts
index 7620961..25ce5d9 100644
--- a/securis/src/main/webapp/src/app/common/utils.ts
+++ b/securis/src/main/webapp/src/app/common/utils.ts
@@ -1,50 +1,65 @@
 
-import {Component, Input} from '@angular/core';
+import { Component, Injectable, Input } from '@angular/core';
 import {LocaleService} from './i18n';
+import { Observable } from 'rxjs/Observable';
 
-@Component({
-  selector: 'field-readonly',
-  template: `
-  <div layout="column" class="mat-input-container readonly" >
-		<div class="mat-input-wrapper">
-			<div class="mat-input-table">
-				<div class="mat-input-prefix"></div>
-				<div class="mat-input-infix">
-					<div class="label-value mat-input-element">{{value}}</div>
-					<span class="mat-input-placeholder-wrapper" >
-						<label class="mat-input-placeholder mat-float">
-							<span class="placeholder">{{label}}</span>
-						</label>
-					</span>
-				</div>
-				<div class="mat-input-suffix"></div>
-			</div>
-			<div class="mat-input-underline"></div>
-		</div>
-	</div>`,
-	styles: [`.readonly .mat-input-element {
-				margin-top: 0px !important;
-				color: rgba(0, 0, 0, 0.50);
-			}`,
-			`.readonly .mat-input-element {
-				margin-top: 0px;
-				color: rgba(0, 0, 0, 0.50);
-			}`,
-			`.readonly.mat-input-container {
-				width: 100%;
-			}`]
-})
-export class FieldReadonlyComponent {
-	@Input('value') value: any;
 
-	private _label : string;
-    @Input('label')
-	set label(txt: string) {
-		this._label = this.$L.get(txt);
-	}
-	get label(): string { return this._label; }
 
-	constructor(private $L : LocaleService) {
+export interface IError {
+	code: string | number,
+	message: string,
+	httpCode?: number
+}
 
-	}
+export const DEFAULT_APP_ERROR_STATUS_CODE = 418;
+export const ERROR_MESSAGE_HEADER = "X-SECURIS-ERROR-MSG";
+export const ERROR_CODE_MESSAGE_HEADER = "X-SECURIS-ERROR-CODE";
+
+export class ErrorCodes {
+	public static UNEXPECTED_ERROR = 1000;
+	public static INVALID_CREDENTIALS = 1001;
+	public static UNAUTHORIZED_ACCESS = 1002;
+	public static NOT_FOUND = 1003;
+	public static INVALID_FORMAT = 1004;
+	public static WRONG_STATUS = 1005;
+
+	public static INVALID_LICENSE_REQUEST_DATA = 1100;
+	public static LICENSE_NOT_READY_FOR_RENEW = 1101;
+	public static LICENSE_DATA_IS_NOT_VALID = 1102;
+	public static LICENSE_IS_EXPIRED = 1103;
+
+	public static INVALID_REQUEST_DATA = 1201;
+	public static INVALID_REQUEST_DATA_FORMAT = 1202;
+	public static BLOCKED_REQUEST_DATA = 1203;
+	public static DUPLICATED_REQUEST_DATA = 1204;
+	public static NO_AVAILABLE_LICENSES = 1205;
+
+	public static INVALID_DATA = 1301;
+}
+
+@Injectable()
+export class BasicService {
+
+	constructor(protected $L: LocaleService) {}
+
+	public processErrorResponse(errorResponse: Response | any) {
+      // In a real world app, we might use a remote logging infrastructure
+      var error: IError = <IError>{};
+      if (errorResponse instanceof Response) {
+		error.httpCode = errorResponse.status;
+      }
+
+	  error.code = errorResponse.headers.get(ERROR_CODE_MESSAGE_HEADER) || error.httpCode;
+
+      if (errorResponse.status === 403 /* forbidden */ || errorResponse.status === 401 /* unauthorized */) {
+        error.message = this.$L.get('Invalid credentials');
+		error.code = ErrorCodes.INVALID_CREDENTIALS;
+      } else if (errorResponse.status === 418 /* Teapot */) {
+        error.message = errorResponse.headers.get(ERROR_MESSAGE_HEADER) || errorResponse.statusText || this.$L.get('Unknown');
+      } else {
+        error.message = this.$L.get(`Unexpected error HTTP (${error.httpCode}) accessing to server. Contact with the administrator.`);
+      }
+
+      return Observable.throw(error);
+    }
 }
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/forms/application.form.component.ts b/securis/src/main/webapp/src/app/forms/application.form.component.ts
index baed64a..c9b4395 100644
--- a/securis/src/main/webapp/src/app/forms/application.form.component.ts
+++ b/securis/src/main/webapp/src/app/forms/application.form.component.ts
@@ -4,7 +4,7 @@
 import { ApplicationsService } from '../resources/applications';
 import { LicenseTypesService } from '../resources/license_types';
 import { LocaleService } from '../common/i18n';
-import { IPageChangeEvent } from '@covalent/core';
+import { TdDialogService } from '@covalent/core';
 import { Component, AfterViewInit } from '@angular/core';
 import { TdMediaService } from '@covalent/core';
 import { FormBase, IComboOption } from './base';
@@ -30,25 +30,15 @@
 })
 export class ApplicationFormComponent extends FormBase {
 
-  fields: any = {
-    'id': '',
-    'code': '',
-    'name': '',
-    'creation_timestamp': '',
-    'license_filename': '',
-    'description': '',
-    'metadata': '',
-    'key': '',
-    'value': '',
-  }
   constructor(private http: Http,
               private licenseTypes: LicenseTypesService,
               private router: Router,
               private applications: ApplicationsService,
               toaster: ToastsManager,
               route: ActivatedRoute,              
-              $L: LocaleService) {
-    super($L, route, toaster, applications, $L.get('application'));
+              $L: LocaleService,
+              dialogs: TdDialogService) {
+    super($L, route, toaster, applications, $L.get('application'), dialogs);
   }
 
  
@@ -58,7 +48,9 @@
 
  
   ngAfterViewInit(): void {
-    super.prepareData('applicationId');
+    super.prepareInitialData('applicationId', {
+      metadata: []
+    });
   }
 }
 
diff --git a/securis/src/main/webapp/src/app/forms/application.form.html b/securis/src/main/webapp/src/app/forms/application.form.html
index f346418..afff290 100644
--- a/securis/src/main/webapp/src/app/forms/application.form.html
+++ b/securis/src/main/webapp/src/app/forms/application.form.html
@@ -7,6 +7,20 @@
 		<span flex></span>
 		<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 } ] 
+}
+		-->
 	<div class="margin" layout-align-gt-xs="center start" layout-fill="" layout-gt-xs="row">
 		<md-card flex="70">
 			<md-card-title>
@@ -27,121 +41,49 @@
 								</md-input-container>
 								<error-checker [fieldName]="getFieldName('code')" [formField]="applicationForm.controls.code"></error-checker>
 							</div>
+						</div>
+						<div layout="row" layout-fill layout-padding>
 							<div layout="column" layout-fill flex>
-								<md-input-container>
-									<input mdInput type="number" [(ngModel)]="data.num_licenses" name="num_licenses" required />
+								<md-input-container flex>
+									<input mdInput type="text" [(ngModel)]="data.name" name="name" required />
 									<md-placeholder>
-										<span i18n="field.num_licenses"></span>
+										<span i18n="field.name"></span>
 									</md-placeholder>
 								</md-input-container>
-								<error-checker [fieldName]="getFieldName('num_licenses')" [formField]="applicationForm.controls.num_licenses"></error-checker>
+								<error-checker [fieldName]="getFieldName('name')" [formField]="applicationForm.controls.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 />
+									<md-placeholder>
+										<span i18n="field.license_filename"></span>
+									</md-placeholder>
+								</md-input-container>
+								<error-checker [fieldName]="getFieldName('license_filename')" [formField]="applicationForm.controls.license_filename"></error-checker>
 							</div>
 						</div>
 						<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 />
+									<textarea mdInput type="text" type="text" [(ngModel)]="data.description" name="description" maxlength="1024"></textarea>
 									<md-placeholder>
-										<span i18n="field.end_valid_date"></span>
-									</md-placeholder>
-								</md-input-container>
-								<error-checker [fieldName]="getFieldName('init_valid_date')" [formField]="applicationForm.controls.init_valid_date"></error-checker>
-							</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 />
-									<md-placeholder>
-										<span i18n="field.end_valid_date"></span>
-									</md-placeholder>
-								</md-input-container>
-								<error-checker [fieldName]="getFieldName('end_valid_date')" [formField]="applicationForm.controls.end_valid_date"></error-checker>
-							</div>
-						</div>
-						<div layout="row" layout-fill layout-padding *ngIf="isNew">
-							<div layout="column" layout-fill flex>
-								<md-select [placeholder]="getFieldName('organization_id')" flex [(ngModel)]="data.organization_id" name="organization_id"
-								 (change)="changeOrg($event)">
-									<md-option *ngFor="let org of organizations" [value]="org.id">
-										{{org.label}}
-									</md-option>
-								</md-select>
-								<error-checker [fieldName]="getFieldName('organization_id')" [formField]="applicationForm.controls.organization_id"></error-checker>
-							</div>
-							<div layout="column" layout-fill flex>
-								<md-select flex [placeholder]="getFieldName('license_type_id')" [(ngModel)]="data.license_type_id" name="license_type_id"
-								 (change)="changeLicType($event)">
-									<md-option *ngFor="let lt of licensetypes" [value]="lt.id">
-										{{lt.label}}
-									</md-option>
-								</md-select>
-								<error-checker [fieldName]="getFieldName('license_type_id')" [formField]="applicationForm.controls.license_type_id"></error-checker>
-							</div>
-						</div>
-						<div layout="row" layout-fill layout-padding *ngIf="!isNew">
-							<field-readonly [value]="data.organization_name" label="field.organization_id" flex></field-readonly>
-							<field-readonly [value]="data.licensetype_code" label="field.license_type_id" flex></field-readonly>
-						</div>
-						<div layout="row" layout-fill layout-padding>
-							<div layout="column" layout-fill flex>
-								<md-input-container flex>
-									<input mdInput type="number" [(ngModel)]="data.preactivation_valid_period" name="preactivation_valid_period" required />
-									<md-placeholder>
-										<span i18n="field.preactivation_valid_period"></span>
-									</md-placeholder>
-									<md-hint align="end">days</md-hint>
-								</md-input-container>
-								<error-checker [fieldName]="getFieldName('preactivation_valid_period')" [formField]="applicationForm.controls.preactivation_valid_period"></error-checker>
-							</div>
-							<div layout="column" layout-fill flex>
-								<md-input-container flex>
-									<input mdInput type="number" [(ngModel)]="data.renew_valid_period" name="renew_valid_period" required />
-									<md-placeholder>
-										<span i18n="field.renew_valid_period"></span>
-									</md-placeholder>
-									<md-hint align="end">days</md-hint>
-								</md-input-container>
-								<error-checker [fieldName]="getFieldName('renew_valid_period')" [formField]="applicationForm.controls.renew_valid_period"></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.comments" name="comments" maxlength="1024"></textarea>
-									<md-placeholder>
-										<span i18n="field.comments"></span>
+										<span i18n="field.description"></span>
 									</md-placeholder>
 									<md-hint align="end">(max 1024)</md-hint>
 								</md-input-container>
 							</div>
 						</div>
 						<div layout="row" layout-fill layout-padding *ngIf="!isNew">
-							<field-readonly [value]="data.created_by_name" label="field.created_by" flex></field-readonly>
 							<field-readonly [value]="data.creation_timestamp | date: 'medium'" label="field.creation_timestamp" flex></field-readonly>
 						</div>
-						<div layout="column" layout-fill>
-							<span class="md-title" i18n>License metadata</span>
-							<div layout="row" layout-fill layout-padding *ngFor="let pair of data.metadata">
-								<md-input-container flex="40">
-									<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.key" readonly />
-									<md-placeholder>
-										<span i18n="field.key"></span>
-									</md-placeholder>
-								</md-input-container>
-								<md-input-container flex>
-									<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.value" [readonly]="pair.readonly" [required]="pair.required"
-									/>
-									<md-placeholder>
-										<span i18n="field.value"></span>
-									</md-placeholder>
-								</md-input-container>
-							</div>
-						</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>
 					<span flex></span>
 					<button [disabled]="!applicationForm.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/forms/base.ts b/securis/src/main/webapp/src/app/forms/base.ts
index 708c83e..17cba6d 100644
--- a/securis/src/main/webapp/src/app/forms/base.ts
+++ b/securis/src/main/webapp/src/app/forms/base.ts
@@ -1,11 +1,13 @@
 import { Http } from '@angular/http';
 import { ActivatedRoute } from '@angular/router';
+import { TdDialogService } from '@covalent/core';
 import { ToastsManager } from 'ng2-toastr/ng2-toastr';
 
 import { LocaleService } from '../common/i18n';
+import { BasicService } from '../common/utils';
 import { SeCurisResourceServices } from '../resources/base';
 
-import { AfterViewInit } from '@angular/core';
+import { AfterViewInit, Component, Input } from '@angular/core';
 
 
 export interface IComboOption {
@@ -14,17 +16,19 @@
 }
 
 
-export class FormBase {
+export class FormBase extends BasicService {
 	protected form_title: string = '';
 	protected form_subtitle: string = '';
 	protected data: any = {};
 	protected isNew : boolean = true;
 
-	constructor(protected $L: LocaleService, 
+	constructor($L: LocaleService, 
 	            protected route: ActivatedRoute,
 				protected toaster: ToastsManager,
 				protected resourceServices: SeCurisResourceServices, 
-				protected resourceName: string ) {
+				protected resourceName: string,
+				protected dialogs : TdDialogService ) {
+		super($L);
 	}
 
 	public getFieldName(fieldId: string) : string {
@@ -36,15 +40,30 @@
 	save() {
 		var command = this.isNew ? this.resourceServices.create(this.data) : this.resourceServices.modify(this.data.id, this.data);
 		command.subscribe(
-			data => this.toaster.success(this.$L.get('{} saved sucessfully', this.$L.get(this.resourceName).capitalize())),
-			err => {
-				console.error(err);
-				this.toaster.success(this.$L.get('Error saving {}', this.$L.get(this.resourceName)));
-			}
-			);
+			data => this.toaster.success(this.$L.get('{} saved sucessfully', this.resourceName.capitalize())),
+			err => this.toaster.error(err.message, this.$L.get('Error saving {}', this.resourceName))
+		);
 	}
 
-	protected prepareData(idparam: string, default_values: any = {}) : void {
+	delete(eleId: number | string) : void {
+		this.dialogs.openConfirm({
+			message: this.$L.get('The {} with ID {} will be deleted. Do you want to continue ?', this.resourceName, eleId),
+			disableClose: false, // defaults to false
+			title: this.$L.get('Delete {}', this.resourceName),
+			cancelButton: this.$L.get('Cancel'), 
+			acceptButton: this.$L.get('Yes, delete')
+		}).afterClosed().subscribe((accept: boolean) => {
+			if (accept) {
+				 this.resourceServices.remove(eleId)
+				 	.subscribe(
+						responseData => this.toaster.success(this.$L.get('{} was sucessfully deleted', this.resourceName.capitalize())),
+						err => this.toaster.success(err.message, this.$L.get('Error deleting the {}', this.resourceName))
+				);
+			}
+		});
+	}
+
+	protected prepareInitialData(idparam: string, default_values: any = {}) : void {
 		this.form_title = this.$L.get('{} data', this.resourceName.capitalize());
 		this.isNew = true;
 		!!this.route && this.route.params.subscribe(params => {
@@ -61,4 +80,173 @@
 		});
 	}
 
-}
\ No newline at end of file
+}
+
+@Component({
+  selector: 'error-checker',
+  template: `
+    <div *ngIf="formField?.touched && formField.invalid" layout="column">
+      <span class="error-msg" *ngFor="let err of getFieldErrors()" align="end">{{err}}</span>
+    </div>`,
+	styles: [
+		':host {margin-top: -10px;}',
+		`.error-msg {
+			color: darkred;
+			font-size: 0.8em;
+		}`
+	]
+})
+export class ErrorCheckerComponent {
+
+  @Input() formField: any;
+  @Input() fieldName: string;
+
+  constructor(private $L: LocaleService) {
+  }
+  
+  getFieldErrors() : string[] {
+    if (this.formField.valid) {
+      return []
+    } else {
+      return (<string[]>Object.keys(this.formField.errors)).map((err:string) => this.getErrorMsg(err));
+    }
+  }
+
+  private updateFieldErrors() {
+  }
+
+  private getErrorMsg(err: string) : string{
+    switch(err) { 
+      case 'required': { 
+        return this.fieldName + ' '+ this.$L.get('is required');
+      } 
+      case 'number': { 
+        return this.fieldName + ' '+ this.$L.get('should be a number');
+      } 
+      default: { 
+        return this.fieldName + ' '+ this.$L.get('unknown error') + ' ' + err;
+      } 
+    } 
+  }
+
+  log(obj: any) {
+    console.log(obj)
+  }
+  
+}
+
+
+@Component({
+  selector: 'field-readonly',
+  template: `
+  <div layout="column" class="mat-input-container readonly" >
+		<div class="mat-input-wrapper">
+			<div class="mat-input-table">
+				<div class="mat-input-prefix"></div>
+				<div class="mat-input-infix">
+					<div class="label-value mat-input-element">{{value}}</div>
+					<span class="mat-input-placeholder-wrapper" >
+						<label class="mat-input-placeholder mat-float">
+							<span class="placeholder">{{label}}</span>
+						</label>
+					</span>
+				</div>
+				<div class="mat-input-suffix"></div>
+			</div>
+			<div class="mat-input-underline"></div>
+		</div>
+	</div>`,
+	styles: [`.readonly .mat-input-element {
+				margin-top: 0px !important;
+				color: rgba(0, 0, 0, 0.50);
+			}`,
+			`.readonly .mat-input-element {
+				margin-top: 0px;
+				color: rgba(0, 0, 0, 0.50);
+			}`,
+			`.readonly.mat-input-container {
+				width: 100%;
+			}`]
+})
+export class FieldReadonlyComponent {
+	@Input('value') value: any;
+
+	private _label : string;
+    @Input('label')
+	set label(txt: string) {
+		this._label = this.$L.get(txt);
+	}
+	get label(): string { return this._label; }
+
+	constructor(private $L : LocaleService) {
+
+	}
+}
+
+interface MetadataPair {
+	key: string,
+	value: string,
+	readonly: boolean,
+	mandatory: boolean
+}
+
+@Component({
+  selector: 'metadata-manager',
+  template: `
+  <div layout="column" layout-fill flex>
+		<div layout="row" layout-align="start center">
+			<span class="md-title">{{title}}</span>
+			<span flex></span>
+			<button *ngIf="addOrDelete" type="button"  md-icon-button color="primary" (click)="newMetadata()"><md-icon>add</md-icon></button>
+		</div>
+		<div layout="row" layout-align="start center" *ngFor="let pair of metadata" class="values">
+			<md-input-container flex="35" >
+				<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.key" [readonly]="!editKeys" [mdTooltip]="pair.key" />
+				<md-placeholder>
+					<span i18n="field.key"></span>
+				</md-placeholder>
+			</md-input-container>
+			<md-input-container flex >
+				<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.value" [readonly]="pair.readonly" [required]="pair.mandatory"
+				 [mdTooltip]="pair.value" />
+				<md-placeholder>
+					<span i18n="field.value"></span>
+				</md-placeholder>
+			</md-input-container>
+			<md-checkbox *ngIf="addOrDelete" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.mandatory" name="mandatory" [mdTooltip]="$L.get('field.mandatory')">
+			</md-checkbox>
+			<button *ngIf="addOrDelete" type="button" md-icon-button color="warn" (click)="deleteMetadata(pair)"><md-icon>delete</md-icon></button>
+		</div>
+	</div>`,
+	styles: [
+		':host { width:100%; }',
+		`.values > * {
+			margin-left: 5px;
+			margin-right: 5px;
+		}`
+	]
+})
+export class MetadataManagerComponent {
+	@Input('metadata') metadata: MetadataPair[];
+	@Input('addOrDelete') addOrDelete: boolean = false;
+	@Input('editKeys') editKeys: boolean = false;
+	@Input('title') title: string;
+
+	constructor(private $L : LocaleService) {
+		this.title = $L.get('License metadata');
+	}
+
+	newMetadata() : void{
+		this.metadata.push(<MetadataPair>{
+			mandatory: false,
+			readonly: false
+		});
+	}
+
+	deleteMetadata(pair: MetadataPair) : void {
+		let indexToRemove = this.metadata.findIndex(ele => ele.key === pair.key);
+		if (indexToRemove !== -1) {
+			this.metadata.splice(indexToRemove, 1);
+		}
+	}
+}
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 e2604f9..b8089fb 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
@@ -7,7 +7,7 @@
 import { AfterViewInit, Component, ViewChild } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 
-import { TdMediaService, TdFileInputComponent } from '@covalent/core';
+import { TdMediaService, TdFileInputComponent, TdDialogService } from '@covalent/core';
 import { FormBase, IComboOption } from './base';
 import { ToastsManager } from "ng2-toastr/ng2-toastr";
 
@@ -20,11 +20,7 @@
 export class LicenseFormComponent extends FormBase {
 
   @ViewChild('requestFileUploader') requestFileUploader : TdFileInputComponent;
-  form_title: string = '';
-  form_subtitle: string = '';
-  data: any = {};
   pack: any = null;
-  isNew : boolean = true;
 
   constructor(private http: Http,
               private licenses: LicensesService,
@@ -32,8 +28,9 @@
               private packs: PacksService,
               toaster: ToastsManager,
               route: ActivatedRoute,
-              $L: LocaleService) {
-      super($L, route, toaster, licenses, $L.get('license'));
+              $L: LocaleService,
+              dialogs: TdDialogService) {
+      super($L, route, toaster, licenses, $L.get('license'), dialogs);
   }
 
   requestFileSelected(file: File) : void {
@@ -41,7 +38,7 @@
     console.log(this.requestFileUploader);
 		if (!window.FileReader) { // Browser is not
           // compatible
-          console.log(this.$L.get("Open your .req file with a text editor and copy&paste the content in the form text field?"));
+          console.log(this.$L.get("Open your .req file with a text editor and copy&paste the content in the form text field"));
           return;
   	}
     var reader = new FileReader();
@@ -54,6 +51,7 @@
     this.requestFileUploader.clear();
   } 
   
+
   requestFileUploaded(file: File) : void {
     console.log(file);
   }
@@ -82,10 +80,16 @@
 
     this.route.params.subscribe(params => {
       var packId = +params['packId']; // (+) converts string 'id' to a number
-      super.prepareData('licenseId', {
+      super.prepareInitialData('licenseId', {
         pack_id: packId,            
         activation_code: this.createActivationCode()
       });
+      this.packs.get(packId).subscribe(
+          packData => {
+            this.pack = packData;
+          },
+          err => console.error(err)
+        );
     });
   }
 }
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 0d4ec82..545f5f0 100644
--- a/securis/src/main/webapp/src/app/forms/license.form.html
+++ b/securis/src/main/webapp/src/app/forms/license.form.html
@@ -3,7 +3,7 @@
 		<button md-icon-button (click)="goBack()" color="accent">
 			<md-icon>arrow_back</md-icon>
 		</button>
-		<span>{{form_title}}</span>
+		<span [innerText]="form_title"></span>
 		<span flex></span>
 		<button md-icon-button (click)="save()"><md-icon>save</md-icon></button>
 		<md-toolbar-row class="inner-padding" *ngIf="!!pack" >
@@ -91,23 +91,8 @@
 								<field-readonly [value]="data.created_by_id" label="field.created_by" flex></field-readonly>
 								<field-readonly [value]="data.creation_date | date: 'medium'" label="field.creation_date" flex></field-readonly>
 							</div>
-							<div layout="column" layout-fill>
-								<span class="md-title" i18n>License metadata</span>
-								<div layout="row" layout-fill layout-padding *ngFor="let pair of pack?.metadata">
-									<md-input-container flex="40">
-										<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.key" readonly />
-										<md-placeholder>
-											<span i18n="field.key"></span>
-										</md-placeholder>
-									</md-input-container>
-									<md-input-container flex>
-										<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.value" readonly />
-										<md-placeholder>
-											<span i18n="field.value"></span>
-										</md-placeholder>
-									</md-input-container>
-								</div>
-							</div>
+							<metadata-manager [addOrDelete]="false" [editKeys]="false" [metadata]="data.metadata" ></metadata-manager>
+
 						</div>
 					</form>
 				</md-card-content>
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 e798be4..3652a15 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
@@ -5,7 +5,7 @@
 import { LicenseTypesService } from '../resources/license_types';
 import { LocaleService } from '../common/i18n';
 import { TdDataTableService, TdDataTableSortingOrder, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '@covalent/core';
-import { IPageChangeEvent } from '@covalent/core';
+import { TdDialogService } from '@covalent/core';
 import { Component, AfterViewInit } from '@angular/core';
 import { TdMediaService } from '@covalent/core';
 import { IComboOption, FormBase } from './base';
@@ -21,39 +21,16 @@
 
   organizations : IComboOption[];
   licensetypes : IComboOption[];
-  data: any = {};
-  isNew : boolean = true;
-  fields: any = {
-    'code': '',
-    'num_licenses': '',
-    'init_valid_date': '',
-    'end_valid_date': '',
-    'license_preactivation': '',
-    'license_type_id': '',
-    'organization_id': '',
-    'licensetype_code': '',
-    'organization_name': '',
-    'preactivation_valid_period': '',
-    'renew_valid_period': '',
-    'application_name': '',
-    'status': '',
-    'metadata': '',
-    'comments': '',
-    'key': '',
-    'value': '',
-  }
+  
   constructor(private http: Http,
-              toaster: ToastsManager,
               private licenseTypes: LicenseTypesService,
-              route: ActivatedRoute,
               private router: Router,
               private packs: PacksService,
-              $L: LocaleService) {
-    super($L, route, toaster, packs, $L.get('pack'));
-  }
-
-  public getFieldName(fieldId: string) : string {
-    return this.fields[fieldId];
+              toaster: ToastsManager,
+              route: ActivatedRoute,              
+              $L: LocaleService,
+              dialogs: TdDialogService) {
+    super($L, route, toaster, packs, $L.get('pack'), dialogs);
   }
 
   loadCombos(): void {
@@ -93,14 +70,10 @@
     console.log(this.data.organization_id);
   } 
   
-  ngOnInit(): void {
-
-  }
-
 
   ngAfterViewInit(): void {
     this.loadCombos();
-    super.prepareData('packId', {
+    super.prepareInitialData('packId', {
       status: PACK_STATUS.CREATED
     });
   }
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 e509765..5ba11e5 100644
--- a/securis/src/main/webapp/src/app/forms/pack.form.html
+++ b/securis/src/main/webapp/src/app/forms/pack.form.html
@@ -3,7 +3,7 @@
 		<button md-icon-button (click)="goBack()" color="accent">
 			<md-icon>arrow_back</md-icon>
 		</button>
-		<span class="md-title" i18n>{{form_title}}</span>
+		<span class="md-title" [innerText]="form_title"></span>
 		<span flex></span>
 		<button md-icon-button (click)="save()"><md-icon>save</md-icon></button>
 	</md-toolbar>
@@ -126,24 +126,7 @@
 							<field-readonly [value]="data.created_by_name" label="field.created_by" flex></field-readonly>
 							<field-readonly [value]="data.creation_timestamp | date: 'medium'" label="field.creation_timestamp" flex></field-readonly>
 						</div>
-						<div layout="column" layout-fill>
-							<span class="md-title" i18n>License metadata</span>
-							<div layout="row" layout-fill layout-padding *ngFor="let pair of data.metadata">
-								<md-input-container flex="40">
-									<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.key" readonly />
-									<md-placeholder>
-										<span i18n="field.key"></span>
-									</md-placeholder>
-								</md-input-container>
-								<md-input-container flex>
-									<input mdInput type="text" [ngModelOptions]="{standalone: true}" [(ngModel)]="pair.value" [readonly]="pair.readonly" [required]="pair.required"
-									/>
-									<md-placeholder>
-										<span i18n="field.value"></span>
-									</md-placeholder>
-								</md-input-container>
-							</div>
-						</div>
+						<metadata-manager [addOrDelete]="false" [editKeys]="false" [metadata]="data.metadata" ></metadata-manager>
 					</div>
 				</form>
 			</md-card-content>
diff --git a/securis/src/main/webapp/src/app/listing/application.list.component.html b/securis/src/main/webapp/src/app/listing/application.list.component.html
index ee92101..522e7b7 100644
--- a/securis/src/main/webapp/src/app/listing/application.list.component.html
+++ b/securis/src/main/webapp/src/app/listing/application.list.component.html
@@ -26,7 +26,6 @@
       <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>
-          <button md-icon-button (click)="delete(row)" color="warn"><md-icon>delete</md-icon></button>
         </div>
       </template>
     </td-data-table>
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 d359e7a..3060af9 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
@@ -104,8 +104,8 @@
     this.router.navigate([`create`], {relativeTo: this.route});
   }
 
-  edit(pack: any) : void {
-    this.router.navigate([`edit/${pack.id}`], {relativeTo: this.route});
+  edit(packId: number) : void {
+    this.router.navigate([`edit/${packId}`], {relativeTo: this.route});
   }
 
   sort(sortEvent: ITdDataTableSortChangeEvent): void {
diff --git a/securis/src/main/webapp/src/app/resources/applications.ts b/securis/src/main/webapp/src/app/resources/applications.ts
index d214bed..ecfa6a8 100644
--- a/securis/src/main/webapp/src/app/resources/applications.ts
+++ b/securis/src/main/webapp/src/app/resources/applications.ts
@@ -2,6 +2,7 @@
 import { Injectable } from '@angular/core';
 import { Http, RequestOptions } from '@angular/http';
 import { SeCurisResourceServices } from './base';
+import { LocaleService } from '../common/i18n';
 
 var app_example = { code: 'CICS',
   creation_timestamp: 1418384439000,
@@ -17,8 +18,8 @@
 
 @Injectable()
 export class ApplicationsService extends SeCurisResourceServices {
-  constructor(http: Http) {
-    super(http, 'application');
+  constructor(http: Http, $L: LocaleService) {
+    super($L, http, 'application');
   }
 
 
diff --git a/securis/src/main/webapp/src/app/resources/base.ts b/securis/src/main/webapp/src/app/resources/base.ts
index 4f79cc2..c03ed59 100644
--- a/securis/src/main/webapp/src/app/resources/base.ts
+++ b/securis/src/main/webapp/src/app/resources/base.ts
@@ -1,3 +1,5 @@
+import { LocaleService } from '../common/i18n';
+import { BasicService } from '../common/utils';
 import { Observable } from 'rxjs/Observable';
 import { Http, RequestOptionsArgs, URLSearchParams } from '@angular/http';
 
@@ -8,29 +10,31 @@
   }
 }
 
-export class SeCurisResourceServices {
-    constructor(protected http: Http,
+export class SeCurisResourceServices extends BasicService {
+    constructor($L: LocaleService,
+                protected http: Http,                
                 protected resource: string) {
+        super($L);
     }    
 
     public get(id?: any) : Observable<any> {
       let url = `${this.resource}/${id || ''}`;
-      return this.http.get(url).map(response => response.json());
+      return this.http.get(url).map(response => response.json()).catch(err => super.processErrorResponse(err));
     }
 
     public create(data: any) : Observable<any> {
       let url = `${this.resource}`;
-      return this.http.post(url, JSON.stringify(data)).map(response => response.json());
+      return this.http.post(url, JSON.stringify(data)).map(response => response.json()).catch(err => super.processErrorResponse(err));
     }
 
     public modify(id: any, data: any) : Observable<any> {
       let url = `${this.resource}/${id}`;
-      return this.http.post(url, JSON.stringify(data)).map(response => response.json());
+      return this.http.post(url, JSON.stringify(data)).map(response => response.json()).catch(err => super.processErrorResponse(err));
     }
 
     public remove(id: any) : Observable<any> {
       let url = `${this.resource}/${id}`;
-      return this.http.delete(url).map(response => response.json());
+      return this.http.delete(url).map(response => response.json()).catch(err => super.processErrorResponse(err));
     }
     
     public action(id: any, action: string, method = 'POST') : Observable<any> {
@@ -44,6 +48,8 @@
         search: (method == 'GET') && new MySearchParams(params) || undefined,
         body: (method == 'POST') && JSON.stringify(params) || undefined
       };
-      return this.http.request(url, options).map(response => response.json());
+      return this.http.request(url, options).map(response => response.json()).catch(err => super.processErrorResponse(err));
     }
+
+
 }
\ No newline at end of file
diff --git a/securis/src/main/webapp/src/app/resources/license_types.ts b/securis/src/main/webapp/src/app/resources/license_types.ts
index cc6b700..c12c90e 100644
--- a/securis/src/main/webapp/src/app/resources/license_types.ts
+++ b/securis/src/main/webapp/src/app/resources/license_types.ts
@@ -2,6 +2,7 @@
 import { Injectable } from '@angular/core';
 import { Http, RequestOptions } from '@angular/http';
 import { SeCurisResourceServices } from './base';
+import { LocaleService } from '../common/i18n';
 
 var lictype_example = { application_name: 'CurisIntegrity',
   code: 'CIA2',
@@ -14,8 +15,8 @@
 
 @Injectable()
 export class LicenseTypesService extends SeCurisResourceServices {
-  constructor(http: Http) {
-    super(http, 'licensetype');
+  constructor(http: Http, $L: LocaleService) {
+    super($L, http, 'licensetype');
   }
 
 
diff --git a/securis/src/main/webapp/src/app/resources/licenses.ts b/securis/src/main/webapp/src/app/resources/licenses.ts
index 459f2f5..a82d910 100644
--- a/securis/src/main/webapp/src/app/resources/licenses.ts
+++ b/securis/src/main/webapp/src/app/resources/licenses.ts
@@ -60,8 +60,8 @@
 @Injectable()
 export class LicensesService extends SeCurisResourceServices {
 	constructor(http: Http,
-				private $L: LocaleService) {
-		super(http, 'license');
+				$L: LocaleService) {
+		super($L, http, 'license');
 	}
 
 	public getByPack(packId: number) {
diff --git a/securis/src/main/webapp/src/app/resources/organizations.ts b/securis/src/main/webapp/src/app/resources/organizations.ts
index 20b902a..286428d 100644
--- a/securis/src/main/webapp/src/app/resources/organizations.ts
+++ b/securis/src/main/webapp/src/app/resources/organizations.ts
@@ -2,6 +2,7 @@
 import { Injectable } from '@angular/core';
 import { Http, RequestOptions } from '@angular/http';
 import { SeCurisResourceServices } from './base';
+import { LocaleService } from '../common/i18n';
 
 var org_example = { code: 'NPS',
   creation_timestamp: 1488269722000,
@@ -11,8 +12,8 @@
 
 @Injectable()
 export class OrganizationsService extends SeCurisResourceServices {
-  constructor(http: Http) {
-    super(http, 'organization');
+  constructor(http: Http, $L: LocaleService) {
+    super($L, http, 'organization');
   }
 
 
diff --git a/securis/src/main/webapp/src/app/resources/packs.ts b/securis/src/main/webapp/src/app/resources/packs.ts
index f92292f..951db3b 100644
--- a/securis/src/main/webapp/src/app/resources/packs.ts
+++ b/securis/src/main/webapp/src/app/resources/packs.ts
@@ -60,8 +60,8 @@
 
 @Injectable()
 export class PacksService extends SeCurisResourceServices {
-  constructor(http: Http, private $L: LocaleService) {
-    super(http, 'pack');
+  constructor(http: Http, $L: LocaleService) {
+    super($L, http, 'pack');
   }
 
   public activate(id: number) {
diff --git a/securis/src/main/webapp/src/app/resources/users.ts b/securis/src/main/webapp/src/app/resources/users.ts
index b0f57ac..09eebb3 100644
--- a/securis/src/main/webapp/src/app/resources/users.ts
+++ b/securis/src/main/webapp/src/app/resources/users.ts
@@ -2,6 +2,7 @@
 import { Injectable } from '@angular/core';
 import { Http, RequestOptions } from '@angular/http';
 import { SeCurisResourceServices } from './base';
+import { LocaleService } from '../common/i18n';
 
 var user_example = { 
   creation_timestamp: 1479898458000,
@@ -17,8 +18,8 @@
 
 @Injectable()
 export class UsersService extends SeCurisResourceServices {
-  constructor(http: Http) {
-    super(http, 'user');
+  constructor(http: Http, $L: LocaleService) {
+    super($L, http, 'user');
   }
 
 
diff --git a/securis/src/main/webapp/src/app/user.service.ts b/securis/src/main/webapp/src/app/user.service.ts
index a0656a7..ee0c0b1 100644
--- a/securis/src/main/webapp/src/app/user.service.ts
+++ b/securis/src/main/webapp/src/app/user.service.ts
@@ -1,4 +1,5 @@
 import { LocaleService } from './common/i18n';
+import { BasicService } from './common/utils';
 import { Injectable } from '@angular/core';
 import { Router } from '@angular/router';
 import { Location } from '@angular/common';
@@ -10,15 +11,15 @@
 const SECURIS_TOKEN = "X-SECURIS-TOKEN";
 
 @Injectable()
-export class UserService {
+export class UserService extends BasicService {
 
   count : number = 0;
 
-  constructor(private $L: LocaleService,
+  constructor($L: LocaleService,
               private router: Router,
               private store: LocalStorageService, 
               private http: Http) {
-    
+    super($L);
   }
 
   public login(username: string, password: string) : Observable<string> {
@@ -28,7 +29,7 @@
     let options = new RequestOptions({ headers: new Headers({ "Content-Type": "application/x-www-form-urlencoded" })});
     return this.http.post('user/login', params.toString(), options)
                     .map((resp) => this.mapLogin(resp))
-                    .catch((err) => this.handleError(err));
+                    .catch((err) => super.processErrorResponse(err));
   }
 
   private mapLogin(res : Response) : string {
@@ -36,6 +37,7 @@
     this.store.set('user_full_name', data.full_name);
     this.store.set('username', data.username);
 		this.store.set('token', data.token);
+    console.log('New login token: ' + data.token);
     return <string>data.token;
   }    
 
@@ -47,7 +49,7 @@
     let option = new RequestOptions({ headers: new Headers({ 'X-SECURIS-TOKEN': token }) });
     return this.http.get('check', option)
                     .map((resp) => this.mapCheck(resp))
-                    .catch((err) => this.handleError(err));
+                    .catch((err) => super.processErrorResponse(err));
   }
 
   private mapCheck(res : Response) : boolean {
@@ -67,29 +69,6 @@
     this.router.navigate(['public/login']);
   }
 
-
-  private handleError (error: Response | any) {
-    // In a real world app, we might use a remote logging infrastructure
-    let errMsg: string;
-    if (error instanceof Response) {
-      const err = JSON.stringify(error);
-      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
-    } else {
-      errMsg = error.message ? error.message : error.toString();
-    }
-
-    if (error.status === 403 /* forbidden */ || error.status === 401 /* unauthorized */) {
-      errMsg = this.$L.get('Invalid credentials');
-    } else if (error.status === 418 /* Teapot */) {
-      errMsg = this.$L.get(error.headers.get('X-SECURIS-ERROR-MSG'));
-    } else {
-      console.error(error);
-      errMsg = this.$L.get(`Unexpected error HTTP (${error.status}) accessing to server. Contact with the administrator.`);
-    }
-
-    console.error(errMsg);
-    return Observable.throw(errMsg);
-  }
 
 }
 
diff --git a/securis/src/main/webapp/src/lang/messages_en.json b/securis/src/main/webapp/src/lang/messages_en.json
index bb03671..ca6244b 100644
--- a/securis/src/main/webapp/src/lang/messages_en.json
+++ b/securis/src/main/webapp/src/lang/messages_en.json
@@ -26,6 +26,10 @@
 	"field.email": "Email",
 	"field.creation_timestamp": "Creation timestamp",
 	"field.comments": "Comments",
+	"field.mandatory": "Required",
+	"field.name": "Name",
+	"field.license_filename": "License filename",
+	"field.description": "Description",
 	"pack.status.CR": "Created",
 	"pack.status.AC": "Active",
 	"pack.status.OH": "On Hold",

--
Gitblit v1.3.2