From 04afd774aecc70dca37559fdd8b9a716829c18cd Mon Sep 17 00:00:00 2001
From: Roberto Sánchez <roberto.sanchez@curisit.net>
Date: Fri, 17 Jan 2014 12:27:16 +0000
Subject: [PATCH] #396 feature - Added LocalStorage support and http interceptor for unauthorized access

---
 securis/src/main/resources/static/js/store.min.js          |    2 
 securis/src/main/resources/static/main.html                |   14 
 securis/src/main/resources/static/js/store.js              |  165 +++++++++
 securis/src/main/resources/static/js/licenses.js           |   81 ++++
 securis/src/main/resources/static/js/catalogs.js           |  473 +++++++++++++++----------
 securis/src/main/resources/static/js/main.js               |   77 +++
 securis/src/main/resources/static/admin.html               |   25 -
 securis/src/main/resources/static/css/font-awesome.min.css |    4 
 securis/src/main/resources/static/js/i18n.js               |    2 
 securis/src/main/resources/static/licenses.html            |  165 +++++++-
 securis/src/main/resources/static/js/login.js              |   12 
 securis/src/main/resources/static/js/admin.js              |   16 
 securis/src/main/resources/db/schema.sql                   |   12 
 securis/src/main/resources/static/header.html              |   19 +
 14 files changed, 788 insertions(+), 279 deletions(-)

diff --git a/securis/src/main/resources/db/schema.sql b/securis/src/main/resources/db/schema.sql
index e13879a..ee3bda6 100644
--- a/securis/src/main/resources/db/schema.sql
+++ b/securis/src/main/resources/db/schema.sql
@@ -68,16 +68,20 @@
 CREATE TABLE IF NOT EXISTS license (
   id INT NOT NULL,
   code VARCHAR(100) NOT NULL ,
+  request_data VARCHAR(1024) NULL ,
+  license_data VARCHAR(1024) NULL ,
   pack_id INT NOT NULL,  
-  user_name INT NULL,  
-  user_email INT NOT NULL,  
+  full_name VARCHAR(150) NULL,  
+  email VARCHAR(100)  NOT NULL,  
   creation_timestamp DATETIME NOT NULL ,  
-  sent_timestamp DATETIME NULL ,  
+  send_timestamp DATETIME NULL ,  
   modification_timestamp DATETIME NULL ,  
+  activation_timestamp DATETIME NULL ,  
   cancelation_timestamp DATETIME NULL ,  
+  last_access_timestamp DATETIME NULL ,  
   canceled_by varchar(45) NULL ,  
   created_by varchar(45) NULL ,  
-  status VARCHAR(3) NULL ,  
+  status VARCHAR(3) NOT NULL default 0,  
   PRIMARY KEY (id));
   
   
diff --git a/securis/src/main/resources/static/admin.html b/securis/src/main/resources/static/admin.html
index 2053ec3..bc71148 100644
--- a/securis/src/main/resources/static/admin.html
+++ b/securis/src/main/resources/static/admin.html
@@ -1,21 +1,5 @@
 
-	<div class="navbar navbar-inverse navbar-fixed-top">
-		<div class="container">
-			<div class="navbar-header">
-				<ul class="nav navbar-nav navbar-left">
-					<li style="color: white; padding-top: 15px;">SeCuris</li>
-					<li><a href="#licenses">Licenses</a></li>
-					<li><a href="#admin">Admin</a></li>
-				</ul>
-			</div>
-			<div class="navbar-collapse collapse">
-				<ul class="nav navbar-nav navbar-right">
-					<li><a href="#about">About</a></li>
-					<li><a href="#contact">Contact</a></li>
-				</ul>
-			</div>
-		</div>
-	</div>
+	<div ng-include="'header.html'" ></div>
 
 	<div class="container">
 		<div class="col-md-12">&nbsp;</div>
@@ -38,9 +22,9 @@
 					<div class="collapse navbar-collapse"
 						id="bs-example-navbar-collapse-1">
 						<ul class="nav navbar-nav">
-							<li><a ng-click="editNew()"><span class="glyphicon glyphicon-plus"></span>
+							<li><a i18n ng-click="editNew()"><span class="glyphicon glyphicon-plus"></span>
 									New</a></li>
-							<li><a ng-click="cancel()"> <span
+							<li><a i18n ng-click="cancel()"> <span
 									class="glyphicon glyphicon-ban-circle"></span> Cancel
 							</a></li>
 						</ul>
@@ -53,6 +37,7 @@
 						</div>
 					</div>
 				</nav>
+				
 				<div class="panel panel-default animate-show ng-hide" ng-show="showForm">
 					<form role="form" class="form-horizontal " name="catalogForm" id="catalogForm" ng-submit="saveCatalog()" >
 <!-- 					<pre>formu: {{formu | json}}</pre>-->
@@ -86,7 +71,7 @@
 						<div class="form-group">
 							<div class="col-md-offset-3 col-md-10" id="saveContainer">
 								<button id="save" type="submit" class="btn btn-primary" >
-									<span class="glyphicon glyphicon-floppy-disk"></span> Save
+									<span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
 								</button>
 							</div>
 						</div>
diff --git a/securis/src/main/resources/static/css/font-awesome.min.css b/securis/src/main/resources/static/css/font-awesome.min.css
new file mode 100644
index 0000000..449d6ac
--- /dev/null
+++ b/securis/src/main/resources/static/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ *  Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome
+ *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}
\ No newline at end of file
diff --git a/securis/src/main/resources/static/header.html b/securis/src/main/resources/static/header.html
new file mode 100644
index 0000000..a8233f4
--- /dev/null
+++ b/securis/src/main/resources/static/header.html
@@ -0,0 +1,19 @@
+
+	<div class="navbar navbar-inverse navbar-fixed-top">
+		<div class="container">
+			<div class="navbar-header">
+				<ul class="nav navbar-nav navbar-left">
+					<li i18n style="color: white; padding-top: 15px;">SeCuris</li>
+					<li><a i18n href="/licenses">Licenses</a></li>
+					<li><a i18n href="/admin">Admin</a></li>
+				</ul>
+			</div>
+			<div class="navbar-collapse collapse">
+				<ul class="nav navbar-nav navbar-right">
+					<li><a i18n href="#about">About</a></li>
+					<li><a i18n href="#contact">Contact</a></li>
+					<li><a i18n ng-click="logout()">Logout</a></li>
+				</ul>
+			</div>
+		</div>
+	</div>
diff --git a/securis/src/main/resources/static/js/admin.js b/securis/src/main/resources/static/js/admin.js
index 6d806ac..fc1857a 100644
--- a/securis/src/main/resources/static/js/admin.js
+++ b/securis/src/main/resources/static/js/admin.js
@@ -33,7 +33,9 @@
 			'$http',
 			'toaster',
 			'Catalogs',
-			function($scope, $http, toaster, Catalogs) {
+			'$store',
+			'$L',
+			function($scope, $http, toaster, Catalogs, $store, $L) {
 				$scope.showForm = false;
 				$scope.isNew = false;
 				$scope.formu = {};
@@ -78,16 +80,16 @@
 				}
 
 				$scope.delete = function(data) {
-					BootstrapDialog.confirm('The record will be deleted, are you sure?', function(result){
+					BootstrapDialog.confirm($L.get('The record will be deleted, are you sure?'), function(result){
 			            if(result) {
 							var promise = Catalogs.remove(data).$promise;
 							promise.then(function(data) {
 								$scope.list = Catalogs.query();
 								Catalogs.refreshRef($scope.refs, Catalogs.getMetadata().resource, $scope.list);
-								toaster.pop('success', Catalogs.getName(), "Element deleted successfully");
+								toaster.pop('success', Catalogs.getName(), $L.get("Element deleted successfully"));
 							},function(error) {
 								console.log(error);
-								toaster.pop('error', Catalogs.getName(), "Error deleting element, reason: " + HTTP_ERRORS[error.status] + ". Details: " + error.headers('X-SECURIS-ERROR'), 10000);
+								toaster.pop('error', Catalogs.getName(), $L.get("Error deleting element, reason: {0}. Details: {1}", $L.get(HTTP_ERRORS[error.status]), error.headers('X-SECURIS-ERROR')), 10000);
 							});
 			            }
 			        });
@@ -136,7 +138,7 @@
 
 				$scope.saveCatalog = function() {
 					if ($scope.catalogForm.$invalid) {
-						toaster.pop('error', Catalogs.getName(), "There are wrong data in current form, please fix it before to save");
+						toaster.pop('error', Catalogs.getName(), $L.get("There are wrong data in current form, please fix it before to save"));
 					} else {
 						var promise = Catalogs.save($scope.formu).$promise;
 						promise.then(function(data, otro) {
@@ -150,10 +152,10 @@
 							$scope.$parent.list = Catalogs.query();
 							Catalogs.refreshRef($scope.refs, Catalogs.getMetadata().resource, $scope.$parent.list);
 
-							toaster.pop('success', Catalogs.getName(), "Element saved successfully");
+							toaster.pop('success', Catalogs.getName(), $L.get("Element saved successfully"));
 						}, function(error) {
 							console.log(error);
-							toaster.pop('error', Catalogs.getName(), "Error saving element, reason: " + HTTP_ERRORS[error.status] + ". Details: " + error.headers('X-SECURIS-ERROR'), 10000);
+							toaster.pop('error', Catalogs.getName(), $L.get("Error saving element, reason: {0}. Details: {1}", $L.get(HTTP_ERRORS[error.status]), error.headers('X-SECURIS-ERROR')), 10000);
 						});
 						
 					}
diff --git a/securis/src/main/resources/static/js/catalogs.js b/securis/src/main/resources/static/js/catalogs.js
index 77ad975..7dc6614 100644
--- a/securis/src/main/resources/static/js/catalogs.js
+++ b/securis/src/main/resources/static/js/catalogs.js
@@ -5,209 +5,294 @@
 	 * Catalogs module
 	 */
 
-	angular.module('catalogs', ['ngResource'])
+	angular
+			.module('catalogs', [ 'ngResource' ])
 
-	.service('Catalogs', ['$rootScope', '$http', '$resource', '$q', function ($rootScope, $http, $resource, $q) {
-		var resources = {
-				application : $resource('/application/:appId', {
-					appId : '@id'
-				}),
-				user : $resource('/user/:userId', {
-					userId : '@username'
-				}),
-				organization : $resource('/organization/:orgId', {
-					orgId : '@id'
-				}),
-				licensetype : $resource('/licensetype/:licenseTypeId', {
-					licenseTypeId : '@id'
-				})
-		}
+			.service(
+					'Catalogs',
+					[
+							'$rootScope',
+							'$http',
+							'$resource',
+							'$q',
+							function($rootScope, $http, $resource, $q) {
+								var resources = {
+									application : $resource(
+											'/application/:appId', {
+												appId : '@id'
+											}),
+									user : $resource('/user/:userId', {
+										userId : '@username'
+									}),
+									organization : $resource(
+											'/organization/:orgId', {
+												orgId : '@id'
+											}),
+									licensetype : $resource(
+											'/licensetype/:licenseTypeId', {
+												licenseTypeId : '@id'
+											})
+								}
 
-	var _metadata = null;
-	var _current = null;
+								var _metadata = null;
+								var _current = null;
 
-	var _list = function() {
-		return $http.get('/js/catalogs.json').success(function(data) {
-			_metadata = data;
-		})
-	}
-	this.init = function() {
-		return _list();
-	}
-	this.getList = function() {
-		return _metadata;
-	}
-	this.getName = function(index) {
-		if (index === undefined)
-			return _current ? _current.name : '';
-		return _metadata ? _metadata[index].name : '';
-	}
-	this.getResource = function(res) {
-		if (res === undefined)
-			return _current ? resources[_current.resource] : null;
-		return _current ? resources[res] : null;		
-	}
-	this.getPk = function(catalogMetadata) {
-		if (!catalogMetadata) catalogMetadata = _current;
-		
-		for(var i = 0; i < catalogMetadata.fields.length; i++)
-			if (catalogMetadata.fields[i].pk) return catalogMetadata.fields[i].name; 
-			
-		return null;		
-	}
-	/**
-	 * Returns catalog metadata
-	 * @param index: Return current catalog if undefined, if string It find the catalog by resoource name if number it find it by position
-	 */
-	this.getMetadata = function(index) {
-		if (!_metadata) throw new Error('There is no catalog metadata info');
-		if (index === undefined)
-			return _current;
-		if (typeof index === 'string') {
-			for (var i = _metadata.length - 1; i >= 0 && _metadata[i].resource !== index; i--);
-			index = i;
-		} 
+								var _list = function() {
+									return $http.get('/js/catalogs.json')
+											.success(function(data) {
+												_metadata = data;
+											})
+								}
+								this.init = function() {
+									return _list();
+								}
+								this.getList = function() {
+									return _metadata;
+								}
+								this.getName = function(index) {
+									if (index === undefined)
+										return _current ? _current.name : '';
+									return _metadata ? _metadata[index].name
+											: '';
+								}
+								this.getResource = function(res) {
+									if (res === undefined)
+										return _current ? resources[_current.resource]
+												: null;
+									return _current ? resources[res] : null;
+								}
+								this.getPk = function(catalogMetadata) {
+									if (!catalogMetadata)
+										catalogMetadata = _current;
 
-		return _metadata[index];
-	}
-	this.setCurrent = function(index) {
-		if (!_metadata) throw new Error('There is no catalog metadata info');
-		if (index === undefined)
-			_current = null;
-		else
-			_current = _metadata[index];
-	}
-	/********************************************
-	 * Catalog fields methods                   *
-	 ********************************************/
+									for (var i = 0; i < catalogMetadata.fields.length; i++)
+										if (catalogMetadata.fields[i].pk)
+											return catalogMetadata.fields[i].name;
 
-	/**
-	 * Returns the first field in form that should get the focus. We find the first field that is not read only 
-	 */
-	this.getFFF = this.getFirstFocusableField = function() {
-		if (!_current) throw new Error('There is no current catalog selected');
-		
-		for(var i = 0; i < _current.fields.length; i++)
-			if (!_current.fields[i].readOnly) return _current.fields[i].name;
-		
-		return null;
-	}
+									return null;
+								}
+								/**
+								 * Returns catalog metadata
+								 * 
+								 * @param index:
+								 *            Return current catalog if
+								 *            undefined, if string It find the
+								 *            catalog by resoource name if
+								 *            number it find it by position
+								 */
+								this.getMetadata = function(index) {
+									if (!_metadata)
+										throw new Error(
+												'There is no catalog metadata info');
+									if (index === undefined)
+										return _current;
+									if (typeof index === 'string') {
+										for (var i = _metadata.length - 1; i >= 0
+												&& _metadata[i].resource !== index; i--)
+											;
+										index = i;
+									}
 
-	/**
-	 * Find the field by name or position 
-	 */
-	this.getField = function(key) {
-		if (!_current) throw new Error('There is no current catalog selected');
-		var index = -1;
-		if (typeof key === 'string') {
-			for (var i = _current.fields.length - 1; i >= 0 && _current.fields[i].name !== key; i--);
-			index = i;
-		} else {
-			index = key; // In this case key === field position
-		}
+									return _metadata[index];
+								}
+								this.setCurrent = function(index) {
+									if (!_metadata)
+										throw new Error(
+												'There is no catalog metadata info');
+									if (index === undefined)
+										_current = null;
+									else
+										_current = _metadata[index];
+								}
+								/***********************************************
+								 * Catalog fields methods *
+								 **********************************************/
 
-		return index === -1 ? {} : _current.fields[index];
-	}
-	
-	/********************************************
-	 * Catalog resource operations on server    *
-	 ********************************************/
+								/**
+								 * Returns the first field in form that should
+								 * get the focus. We find the first field that
+								 * is not read only
+								 */
+								this.getFFF = this.getFirstFocusableField = function() {
+									if (!_current)
+										throw new Error(
+												'There is no current catalog selected');
 
-	function _success(response) {
-		console.log('$resource')
-		console.log(response)
-	}
-	function _fail(response) {
-		console.error('Error trying to get data, HTTP error code: ' + response.status)
-	}
-	
-	
-	this.save = function(data) {
-		if (!_current) throw new Error('There is no current catalog selected');
+									for (var i = 0; i < _current.fields.length; i++)
+										if (!_current.fields[i].readOnly)
+											return _current.fields[i].name;
 
-		var resource = this.getResource();
-		return resource.save(data, _success, _fail);
-	}
-	this.remove = function(data) {
-		return this.getResource().remove({}, data, _success, _fail)
-	}
-	this.query = function() {
-		return this.getResource().query({}, _success, _fail);
-	}
-	this.refreshRef = function(refs, res, preloadedData) {
-		// We check if there is some field for the resource passed as parameter
-		var field = (function() {
-			for (var i = _current.fields.length - 1; i >= 0; i--) {
-				if (_current.fields[i].resource === res)
-					return _current.fields[i];				
-			}
-			return null;
-		})();
-		
-		// If field for that resource is not found there is nothing to refresh
-		if (!field) return;
-		var resource = this.getResource(res);
-		var data = preloadedData || resource.query({}, _success, _fail);
-		var that = this;
-		data.$promise.then(function(responseData) {
-			var pk = that.getPk(that.getMetadata(field.resource))
-			var comboData = []
-			responseData.forEach(function(row) {
-				comboData.push({
-					id: row[pk],
-					label: row.label || row.name || row.code || row.first_name + ' ' + row.last_name
-				});
-			})
-			refs[field.name] = comboData;
-		})
-	}
-	this.loadRefs = function(refs) {
-		if (!_current) throw new Error('There is no current catalog selected');
-		var refsFields = [];
-		_current.fields.forEach(function(f) {
-			if (f.resource)
-				refsFields.push(f)
+									return null;
+								}
 
-		});
-		
-		var that = this;
-		var promises = []
-		refsFields.forEach(function(f) {
-			var resource = that.getResource(f.resource);
-			refs[f.name] = resource.query({}, _success, _fail);
-			promises.push(refs[f.name].$promise);
-		});
-		
-		console.log('promises: ' + promises.length + ' ')
-		console.log(promises)
-		$q.all(promises).then(function() {
-			
-			for (var k in refs) {
-				var field = that.getField(k);
-				var pk = that.getPk(that.getMetadata(field.resource))
-				console.log('PK field for ' + k + ' is ' + pk)
-				var comboData = []
-				refs[k].forEach(function(row) {
-					console.log('field.resource !== _current.resource: ' + field.resource +' '+ _current.resource)
-					comboData.push({
-						id: row[pk],
-						label: row.label || row.name || row.code || row.first_name + ' ' + (row.last_name || '')
-					});
-				})
-				refs[k] = comboData;
-				console.log('Ready for combo for ' + k)
-				console.log(comboData);
-			}
-			_current.fields.forEach(function(f) {
-				if (f.values)
-					refs[f.name] = f.values;
-			});
-		})
-		
-		console.log(refs);
-		return refs;
-	} 
-	
-	}])
+								/**
+								 * Find the field by name or position
+								 */
+								this.getField = function(key) {
+									if (!_current)
+										throw new Error(
+												'There is no current catalog selected');
+									var index = -1;
+									if (typeof key === 'string') {
+										for (var i = _current.fields.length - 1; i >= 0
+												&& _current.fields[i].name !== key; i--)
+											;
+										index = i;
+									} else {
+										index = key; // In this case key ===
+														// field position
+									}
+
+									return index === -1 ? {}
+											: _current.fields[index];
+								}
+
+								/***********************************************
+								 * Catalog resource operations on server *
+								 **********************************************/
+
+								function _success(response) {
+									console.log('$resource')
+									console.log(response)
+								}
+								function _fail(response) {
+									console
+											.error('Error trying to get data, HTTP error code: '
+													+ response.status)
+								}
+
+								this.save = function(data) {
+									if (!_current)
+										throw new Error(
+												'There is no current catalog selected');
+
+									var resource = this.getResource();
+									return resource.save(data, _success, _fail);
+								}
+								this.remove = function(data) {
+									return this.getResource().remove({}, data,
+											_success, _fail)
+								}
+								this.query = function() {
+									return this.getResource().query({},
+											_success, _fail);
+								}
+								this.refreshRef = function(refs, res,
+										preloadedData) {
+									// We check if there is some field for the
+									// resource passed as parameter
+									var field = (function() {
+										for (var i = _current.fields.length - 1; i >= 0; i--) {
+											if (_current.fields[i].resource === res)
+												return _current.fields[i];
+										}
+										return null;
+									})();
+
+									// If field for that resource is not found
+									// there is nothing to refresh
+									if (!field)
+										return;
+									var resource = this.getResource(res);
+									var data = preloadedData
+											|| resource.query({}, _success,
+													_fail);
+									var that = this;
+									data.$promise.then(function(responseData) {
+										var pk = that.getPk(that
+												.getMetadata(field.resource))
+										var comboData = []
+										responseData.forEach(function(row) {
+											comboData.push({
+												id : row[pk],
+												label : row.label || row.name
+														|| row.code
+														|| row.first_name + ' '
+														+ row.last_name
+											});
+										})
+										refs[field.name] = comboData;
+									})
+								}
+								this.loadRefs = function(refs) {
+									if (!_current)
+										throw new Error(
+												'There is no current catalog selected');
+									var refsFields = [];
+									_current.fields.forEach(function(f) {
+										if (f.resource)
+											refsFields.push(f)
+
+									});
+
+									var that = this;
+									var promises = []
+									refsFields.forEach(function(f) {
+										var resource = that
+												.getResource(f.resource);
+										refs[f.name] = resource.query({},
+												_success, _fail);
+										promises.push(refs[f.name].$promise);
+									});
+
+									console.log('promises: ' + promises.length
+											+ ' ')
+									console.log(promises)
+									$q
+											.all(promises)
+											.then(
+													function() {
+
+														for ( var k in refs) {
+															var field = that
+																	.getField(k);
+															var pk = that
+																	.getPk(that
+																			.getMetadata(field.resource))
+															console
+																	.log('PK field for '
+																			+ k
+																			+ ' is '
+																			+ pk)
+															var comboData = []
+															refs[k]
+																	.forEach(function(
+																			row) {
+																		console
+																				.log('field.resource !== _current.resource: '
+																						+ field.resource
+																						+ ' '
+																						+ _current.resource)
+																		comboData
+																				.push({
+																					id : row[pk],
+																					label : row.label
+																							|| row.name
+																							|| row.code
+																							|| row.first_name
+																							+ ' '
+																							+ (row.last_name || '')
+																				});
+																	})
+															refs[k] = comboData;
+															console
+																	.log('Ready for combo for '
+																			+ k)
+															console
+																	.log(comboData);
+														}
+														_current.fields
+																.forEach(function(
+																		f) {
+																	if (f.values)
+																		refs[f.name] = f.values;
+																});
+													})
+
+									console.log(refs);
+									return refs;
+								}
+
+							} ])
 
 })();
\ No newline at end of file
diff --git a/securis/src/main/resources/static/js/i18n.js b/securis/src/main/resources/static/js/i18n.js
index c895497..be4eed6 100644
--- a/securis/src/main/resources/static/js/i18n.js
+++ b/securis/src/main/resources/static/js/i18n.js
@@ -72,7 +72,7 @@
 					restrict : 'A', // only activate on element attribute
 					require : '',
 					link : function(scope, element, attrs) {
-						var txt = attrs.i18n || element[0].innerText;
+						var txt = attrs.i18n || element.text();
 						element.text($L.get(txt));
 					}
 				};
diff --git a/securis/src/main/resources/static/js/licenses.js b/securis/src/main/resources/static/js/licenses.js
index 286500f..3db4b48 100644
--- a/securis/src/main/resources/static/js/licenses.js
+++ b/securis/src/main/resources/static/js/licenses.js
@@ -8,8 +8,87 @@
 			'$http',
 			'toaster',
 			'Catalogs',
-			function($scope, $http, toaster, Catalogs) {
+			'$store',
+			'$L',
+			function($scope, $http, toaster, Catalogs, $store, $L) {
+				$scope.currentPack = $store.get('currentPack');
+				$scope.packs = [
+				                {id: 1,
+				                	"organization_name": "BP-Spain",
+				                	"application_name": "CurisIntegrity",
+				                	"licensetype_code": "CIBS",
+				                	"code": "BP-SA-0001",
+				                	"licenses": 50,
+				                	"lic_available": 23},
+				                {id: 2,
+				                	"organization_name": "Exxon",
+				                	"application_name": "CurisData",
+				                	"licensetype_code": "CDL1",
+				                	"code": "EX-SA-0001",
+				                	"licenses": 1,
+				                	"lic_available": 0},
+				                {id: 3,
+				                	"organization_name": "Repsol S.A. empresa con nombre muy largo",
+				                	"application_name": "CurisData",
+				                	"licensetype_code": "CDL2",
+				                	"code": "RE-SA-0001",
+				                	"licenses": 5,
+				                	"lic_available": 2},
+				                {id: 4,
+				                	"organization_name": "BP-Spain",
+				                	"application_name": "CurisIntegrity v3.0",
+				                	"code": "BP-SA-0002",
+				                	"licensetype_code": "CISA",
+				                	"licenses": 150,
+				                	"lic_available": 13},
+				                ];
 
+				$scope.licenses = [
+				                {id: 1,
+				                	"code": "BP-SA-001-AKSJMS234",
+				                	"user_fullname": "Johnny Belmonte",
+				                	"user_email": "jb@curisit.net",
+				                	"status": 3},
+					                {id: 2,
+					                	"code": "BP-SA-001-KAJSDHAJS",
+					                	"user_fullname": "Walter Simons",
+					                	"user_email": "ws@curisit.net",
+					                	"status": 1},
+					                {id: 3,
+					                	"code": "BP-SA-001-ASKDGHKA",
+					                	"user_fullname": "Frank Belmonte",
+					                	"user_email": "fb@curisit.net",
+					                	"status": 2},
+						                {id: 4,
+						                	"code": "BP-SA-001-BBBGGGG",
+						                	"user_fullname": "John Dalton",
+						                	"user_email": "jd@curisit.net",
+						                	"status": 3},
+						                {id: 5,
+						                	"code": "BP-SA-001-AKADNAJANA",
+						                	"user_fullname": "Walter Martins",
+						                	"user_email": "wm@curisit.net",
+						                	"status": 3},
+						                {id: 6,
+						                	"code": "BP-SA-001-AKANDAKS",
+						                	"user_fullname": "Joe Bolton",
+						                	"user_email": "jbol@curisit.net",
+						                	"status": 2}
+				                ];
+				
+
+				
+				$scope.ellipsis = function(txt, len) {
+					if (!txt || txt.length <= len) return txt;
+					return txt.substring(0, len) + '...';
+				}
+				
+				$scope.selectPack = function(pack) {
+					console.log('Pack selected: ' + JSON.stringify(pack));
+					$scope.currentPack = pack;
+					$store.put('currentPack', pack);
+				}
+				
 			} ]);
 
 })();
diff --git a/securis/src/main/resources/static/js/login.js b/securis/src/main/resources/static/js/login.js
index 29a7eae..704bdc3 100644
--- a/securis/src/main/resources/static/js/login.js
+++ b/securis/src/main/resources/static/js/login.js
@@ -3,9 +3,10 @@
 
 	var app = angular.module('securis');
 	
-	app.controller('LoginCtrl', ['$scope', '$http', '$window', '$location', 'toaster', '$L',
-                             function($scope, $http, $window, $location, toaster, $L) {
+	app.controller('LoginCtrl', ['$scope', '$http', '$location', 'toaster', '$L', '$store',
+                             function($scope, $http, $location, toaster, $L, $store) {
 
+		
 		$('#username').focus();
 		
 		$scope.submit = function() {
@@ -21,14 +22,17 @@
 					})
 			}).
 			  success(function(data, status, headers, config) {
-				  toaster.pop('success', $L.get('Login successful'), $L.get('User {0} has logged in SeCuris', $scope.username), 1500);
+				  toaster.pop('success', $L.get('Login successful'), $L.get('User {0} has logged in application', $scope.username), 1500);
 				  $location.path('/licenses');
+				  $store.put('username', $scope.username);
+				  $store.put('token', data.token);
+				  $http.defaults.headers.common['X-SECURIS-TOKEN'] = data.token; 
 			  }).
 			  error(function(data, status, headers, config) {
-				console.error(data + " status: "+ status);
 				if (status === 403 /* forbidden */) {
 					toaster.pop('error', $L.get('Login error'), $L.get('Invalid credentials'), 3000);
 				} else {
+					console.error(data + " status: "+ status);
 					toaster.pop('error', $L.get('Unexpected Login error'), $L.get('Unexpected error HTTP ({0}) accessing to server. Contact with the administrator.', status), 5000);
 				}
         		$('#username').focus();
diff --git a/securis/src/main/resources/static/js/main.js b/securis/src/main/resources/static/js/main.js
index 8428bc3..14521b6 100644
--- a/securis/src/main/resources/static/js/main.js
+++ b/securis/src/main/resources/static/js/main.js
@@ -2,35 +2,84 @@
 	'use strict';
 	
 	var m = angular.module('securis', [ 'ngRoute', 'ngResource', 'toaster', 'localytics.directives', 'catalogs', 'i18n' ]);
+
+	m.service('$store', function() {
+		this.get = function(key, defaultValue) {
+			return store.get(key) || defaultValue;
+		}
+		this.set = this.put = function(key, value) {
+			store.set(key, value);
+		}
+		this.remove = this.delete = function(key) {
+			return store.remove(key);
+		}
+		this.clear = this.clearAll = function() {
+			store.clear();
+		}
+		this.getAll = function() {
+			return store.getAll();
+		}
+	});
 	
-	m.config(function($routeProvider, $locationProvider) {
+	m.factory('securisHttpInterceptor', function($q, $location, $store) {
+		var isUnauthorizedAccess = function(rejection) {
+			console.log('rejection -----------------------');
+			console.log(rejection);
+			return rejection.status === 401 /* Unauthorized */;
+		} 
+		  return {
+
+		   'responseError': function(rejection) {
+		      // do something on error
+		      if (isUnauthorizedAccess(rejection)) {
+		        if ($location.path() !== '/login') {
+		        	$store.clear();
+		        	$location.path('/login');
+		        	console.error('There was an unathorized access to url {0}, method: {1}'.$i18n(rejection.config.url, rejection.config.method));
+		        } else {
+		        	// console.log('Error on login ...')
+		        }
+		      }
+		      return $q.reject(rejection);
+		    }
+		  };
+		});
+
+	m.config(function($routeProvider, $locationProvider, $httpProvider) {
 		console.log('Configuring routes...');
 		    $routeProvider.when('/login', {
 		      templateUrl: 'login.html',
-		      controller: 'LoginCtrl',
-		      controllerAs: 'login'
+		      controller: 'LoginCtrl'
 		    });
 		    $routeProvider.when('/licenses', {
 			      templateUrl: 'licenses.html',
-			      controller: 'LicensesCtrl',
-			      controllerAs: 'licenses'
+			      controller: 'LicensesCtrl'
 		    });
 		    $routeProvider.when('/admin', {
 			      templateUrl: 'admin.html',
-			      controller: 'AdminCtrl',
-			      controllerAs: 'admin'
+			      controller: 'AdminCtrl'
 		    });
 		 
 		    // configure html5 to get links working on jsfiddle
 		    $locationProvider.html5Mode(true);
+		    $httpProvider.interceptors.push('securisHttpInterceptor');
 		});	
-	m.controller('MainCtrl', ['$scope', '$location', '$L',
-	                             function($scope, $location, $L) {
-		console.log('Moving to login...');
-		console.log('Test 1 lang: ' + 'Hello {0}!! this is {1}'.$i18n('World', 'cool'));
-		console.log('Test 2 lang: ' + $L.get('Hello Pepe!!'));
-		$location.path('/login');
-		}]);	
+	m.controller('MainCtrl', ['$scope', '$http', '$location', '$L', '$store',
+	                             function($scope, $http, $location, $L, $store) {
 		
+		if ($store.get('token') != null) {
+			$http.defaults.headers.common['X-SECURIS-TOKEN'] = $store.get('token'); 
+			$location.path('/licenses');
+		} else {
+			$location.path('/login');
+		}
+		
+		$scope.logout = function() {
+			$store.remove('user');
+			$store.remove('token');
+			$location.path('/login');
+		}
+		
+		}]);	
 	
 })();
\ No newline at end of file
diff --git a/securis/src/main/resources/static/js/store.js b/securis/src/main/resources/static/js/store.js
new file mode 100644
index 0000000..cf38791
--- /dev/null
+++ b/securis/src/main/resources/static/js/store.js
@@ -0,0 +1,165 @@
+;(function(win){
+	var store = {},
+		doc = win.document,
+		localStorageName = 'localStorage',
+		scriptTag = 'script',
+		storage
+
+	store.disabled = false
+	store.set = function(key, value) {}
+	store.get = function(key) {}
+	store.remove = function(key) {}
+	store.clear = function() {}
+	store.transact = function(key, defaultVal, transactionFn) {
+		var val = store.get(key)
+		if (transactionFn == null) {
+			transactionFn = defaultVal
+			defaultVal = null
+		}
+		if (typeof val == 'undefined') { val = defaultVal || {} }
+		transactionFn(val)
+		store.set(key, val)
+	}
+	store.getAll = function() {}
+	store.forEach = function() {}
+
+	store.serialize = function(value) {
+		return JSON.stringify(value)
+	}
+	store.deserialize = function(value) {
+		if (typeof value != 'string') { return undefined }
+		try { return JSON.parse(value) }
+		catch(e) { return value || undefined }
+	}
+
+	// Functions to encapsulate questionable FireFox 3.6.13 behavior
+	// when about.config::dom.storage.enabled === false
+	// See https://github.com/marcuswestin/store.js/issues#issue/13
+	function isLocalStorageNameSupported() {
+		try { return (localStorageName in win && win[localStorageName]) }
+		catch(err) { return false }
+	}
+
+	if (isLocalStorageNameSupported()) {
+		storage = win[localStorageName]
+		store.set = function(key, val) {
+			if (val === undefined) { return store.remove(key) }
+			storage.setItem(key, store.serialize(val))
+			return val
+		}
+		store.get = function(key) { return store.deserialize(storage.getItem(key)) }
+		store.remove = function(key) { storage.removeItem(key) }
+		store.clear = function() { storage.clear() }
+		store.getAll = function() {
+			var ret = {}
+			store.forEach(function(key, val) {
+				ret[key] = val
+			})
+			return ret
+		}
+		store.forEach = function(callback) {
+			for (var i=0; i<storage.length; i++) {
+				var key = storage.key(i)
+				callback(key, store.get(key))
+			}
+		}
+	} else if (doc.documentElement.addBehavior) {
+		var storageOwner,
+			storageContainer
+		// Since #userData storage applies only to specific paths, we need to
+		// somehow link our data to a specific path.  We choose /favicon.ico
+		// as a pretty safe option, since all browsers already make a request to
+		// this URL anyway and being a 404 will not hurt us here.  We wrap an
+		// iframe pointing to the favicon in an ActiveXObject(htmlfile) object
+		// (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
+		// since the iframe access rules appear to allow direct access and
+		// manipulation of the document element, even for a 404 page.  This
+		// document can be used instead of the current document (which would
+		// have been limited to the current path) to perform #userData storage.
+		try {
+			storageContainer = new ActiveXObject('htmlfile')
+			storageContainer.open()
+			storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>')
+			storageContainer.close()
+			storageOwner = storageContainer.w.frames[0].document
+			storage = storageOwner.createElement('div')
+		} catch(e) {
+			// somehow ActiveXObject instantiation failed (perhaps some special
+			// security settings or otherwse), fall back to per-path storage
+			storage = doc.createElement('div')
+			storageOwner = doc.body
+		}
+		function withIEStorage(storeFunction) {
+			return function() {
+				var args = Array.prototype.slice.call(arguments, 0)
+				args.unshift(storage)
+				// See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
+				// and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
+				storageOwner.appendChild(storage)
+				storage.addBehavior('#default#userData')
+				storage.load(localStorageName)
+				var result = storeFunction.apply(store, args)
+				storageOwner.removeChild(storage)
+				return result
+			}
+		}
+
+		// In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
+		var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
+		function ieKeyFix(key) {
+			return key.replace(forbiddenCharsRegex, '___')
+		}
+		store.set = withIEStorage(function(storage, key, val) {
+			key = ieKeyFix(key)
+			if (val === undefined) { return store.remove(key) }
+			storage.setAttribute(key, store.serialize(val))
+			storage.save(localStorageName)
+			return val
+		})
+		store.get = withIEStorage(function(storage, key) {
+			key = ieKeyFix(key)
+			return store.deserialize(storage.getAttribute(key))
+		})
+		store.remove = withIEStorage(function(storage, key) {
+			key = ieKeyFix(key)
+			storage.removeAttribute(key)
+			storage.save(localStorageName)
+		})
+		store.clear = withIEStorage(function(storage) {
+			var attributes = storage.XMLDocument.documentElement.attributes
+			storage.load(localStorageName)
+			for (var i=0, attr; attr=attributes[i]; i++) {
+				storage.removeAttribute(attr.name)
+			}
+			storage.save(localStorageName)
+		})
+		store.getAll = function(storage) {
+			var ret = {}
+			store.forEach(function(key, val) {
+				ret[key] = val
+			})
+			return ret
+		}
+		store.forEach = withIEStorage(function(storage, callback) {
+			var attributes = storage.XMLDocument.documentElement.attributes
+			for (var i=0, attr; attr=attributes[i]; ++i) {
+				callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
+			}
+		})
+	}
+
+	try {
+		var testKey = '__storejs__'
+		store.set(testKey, testKey)
+		if (store.get(testKey) != testKey) { store.disabled = true }
+		store.remove(testKey)
+	} catch(e) {
+		store.disabled = true
+	}
+	store.enabled = !store.disabled
+	
+	if (typeof module != 'undefined' && module.exports) { module.exports = store }
+	else if (typeof define === 'function' && define.amd) { define(store) }
+	else { win.store = store }
+	
+})(this.window || global);
diff --git a/securis/src/main/resources/static/js/store.min.js b/securis/src/main/resources/static/js/store.min.js
new file mode 100644
index 0000000..133226c
--- /dev/null
+++ b/securis/src/main/resources/static/js/store.min.js
@@ -0,0 +1,2 @@
+/* Copyright (c) 2010-2013 Marcus Westin */
+(function(e){function o(){try{return r in e&&e[r]}catch(t){return!1}}var t={},n=e.document,r="localStorage",i="script",s;t.disabled=!1,t.set=function(e,t){},t.get=function(e){},t.remove=function(e){},t.clear=function(){},t.transact=function(e,n,r){var i=t.get(e);r==null&&(r=n,n=null),typeof i=="undefined"&&(i=n||{}),r(i),t.set(e,i)},t.getAll=function(){},t.forEach=function(){},t.serialize=function(e){return JSON.stringify(e)},t.deserialize=function(e){if(typeof e!="string")return undefined;try{return JSON.parse(e)}catch(t){return e||undefined}};if(o())s=e[r],t.set=function(e,n){return n===undefined?t.remove(e):(s.setItem(e,t.serialize(n)),n)},t.get=function(e){return t.deserialize(s.getItem(e))},t.remove=function(e){s.removeItem(e)},t.clear=function(){s.clear()},t.getAll=function(){var e={};return t.forEach(function(t,n){e[t]=n}),e},t.forEach=function(e){for(var n=0;n<s.length;n++){var r=s.key(n);e(r,t.get(r))}};else if(n.documentElement.addBehavior){var u,a;try{a=new ActiveXObject("htmlfile"),a.open(),a.write("<"+i+">document.w=window</"+i+'><iframe src="/favicon.ico"></iframe>'),a.close(),u=a.w.frames[0].document,s=u.createElement("div")}catch(f){s=n.createElement("div"),u=n.body}function l(e){return function(){var n=Array.prototype.slice.call(arguments,0);n.unshift(s),u.appendChild(s),s.addBehavior("#default#userData"),s.load(r);var i=e.apply(t,n);return u.removeChild(s),i}}var c=new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]","g");function h(e){return e.replace(c,"___")}t.set=l(function(e,n,i){return n=h(n),i===undefined?t.remove(n):(e.setAttribute(n,t.serialize(i)),e.save(r),i)}),t.get=l(function(e,n){return n=h(n),t.deserialize(e.getAttribute(n))}),t.remove=l(function(e,t){t=h(t),e.removeAttribute(t),e.save(r)}),t.clear=l(function(e){var t=e.XMLDocument.documentElement.attributes;e.load(r);for(var n=0,i;i=t[n];n++)e.removeAttribute(i.name);e.save(r)}),t.getAll=function(e){var n={};return t.forEach(function(e,t){n[e]=t}),n},t.forEach=l(function(e,n){var r=e.XMLDocument.documentElement.attributes;for(var i=0,s;s=r[i];++i)n(s.name,t.deserialize(e.getAttribute(s.name)))})}try{var p="__storejs__";t.set(p,p),t.get(p)!=p&&(t.disabled=!0),t.remove(p)}catch(f){t.disabled=!0}t.enabled=!t.disabled,typeof module!="undefined"&&module.exports?module.exports=t:typeof define=="function"&&define.amd?define(t):e.store=t})(this.window||global)
\ No newline at end of file
diff --git a/securis/src/main/resources/static/licenses.html b/securis/src/main/resources/static/licenses.html
index a3f3c43..53e911a 100644
--- a/securis/src/main/resources/static/licenses.html
+++ b/securis/src/main/resources/static/licenses.html
@@ -1,32 +1,145 @@
 
-	<div class="navbar navbar-inverse navbar-fixed-top">
-		<div class="container">
-			<div class="navbar-header">
-				<button type="button" class="navbar-toggle" data-toggle="collapse"
-					data-target=".navbar-collapse">
-					<span class="icon-bar"></span> <span class="icon-bar"></span> <span
-						class="icon-bar"></span>
-				</button>
-				<a class="navbar-brand" href="#">SeCuris</a>
-			</div>
-			<div class="navbar-collapse collapse">
-				<ul class="nav navbar-nav navbar-right">
-					<li><a href="#about">About</a></li>
-					<li><a href="#contact">Contact</a></li>
-				</ul>
-			</div>
-		</div>
-	</div>
-
-	<!-- Main jumbotron for a primary marketing message or call to action -->
-	<div class="jumbotron">
-		<div class="container">
-			<h2>SeCuris</h2>
-			<p>Licenses management</p>
-		</div>
-	</div>
+	<div ng-include="'header.html'" ></div>
 
 	<div class="container">
+		<div class="col-md-12">&nbsp;</div>
+		<div id="packs_section" class="col-md-6">
+						<nav class="navbar navbar-default navbar-static-top">
+					<!-- Brand and toggle get grouped for better mobile display -->
+					<div class="navbar-header">
+						<a class="navbar-brand" i18n >Packs</a>
+					</div>
+
+					<!-- Collect the nav links, forms, and other content for toggling -->
+					<div class="collapse navbar-collapse"
+						id="bs-example-navbar-collapse-1">
+						<ul class="nav navbar-nav">
+							<li><a i18n ng-click="editNew()"><span class="glyphicon glyphicon-plus"></span>
+									New</a></li>
+							<li><a i18n ng-click="cancel()"> <span
+									class="glyphicon glyphicon-ban-circle"></span> Cancel
+							</a></li>
+						</ul>
+						<div class="navbar-form navbar-right">
+						<div class="input-group input-group-sm">
+						  	<span class="input-group-addon glyphicon glyphicon-search" style="top: 0px;"></span>
+						  <input type="text" class="form-control" placeholder="Search" ng-model="$parent.searchText" >
+								<span class="btn input-group-addon glyphicon glyphicon-remove" ng-click="$parent.searchText = ''" style="top: 0px;"></span>
+						</div>
+						</div>
+					</div>
+				</nav>
+			<div class="panel panel-default" >
+				<div class="panel-heading">
+					Packs <span class="badge pull-right" ng-bind="packs.length || 0"></span>
+				</div>
+
+				<table class="table table-hover table-condensed">
+					<thead>
+						<tr>
+							<th i18n >Organization</th>
+							<th i18n >Application</th>
+							<th i18n >Code</th>
+							<th i18n >Licenses</th>
+							<th></th>
+						</tr>
+					</thead>
+					<tbody>
+						<tr ng-repeat="pack in packs | filter:searchText" ng-dblclick="editPack(row)" ng-class="{success: currentPack.id === pack.id}" ng-click="selectPack(pack)">
+						    <td ng-bind="ellipsis(pack.organization_name, 20)" title="{{pack.organization_name}}" ></td>
+						    <td ng-bind="pack.application_name"></td>
+						    <td style="white-space: nowrap;" ng-bind="pack.code"></td>
+						    <td title="Total: {{pack.licenses}}, avaliable: {{pack.lic_available}}">{{pack.licenses}} ({{pack.lic_available}})</td>
+							<td><span ng-click="editPack(row)"
+								class="glyphicon glyphicon-pencil"></span>
+								<span ng-click="deletePack(row)"
+								class="glyphicon glyphicon-remove"></span>
+								</td>
+						</tr>
+					</tbody>
+					<tfoot>
+					</tfoot>
+				</table>
+			</div>
 		
+		</div>
+
+		<div id="licenses_section" class="col-md-6" >
+				<nav class="navbar navbar-default navbar-static-top" ng-disabled="!!currentPack">
+					<!-- Brand and toggle get grouped for better mobile display -->
+					<div class="navbar-header success">
+						<a class="navbar-brand" i18n>Licenses</a>
+					</div>
+
+					<!-- Collect the nav links, forms, and other content for toggling -->
+					<div class="collapse navbar-collapse"
+						id="bs-example-navbar-collapse-1">
+						<ul class="nav navbar-nav">
+							<li><a i18n ng-click="editNewLicense()"><span class="glyphicon glyphicon-plus"></span>
+									New</a></li>
+							<li><a i18n ng-click="cancelEditionLicense()"> <span
+									class="glyphicon glyphicon-ban-circle"></span> Cancel
+							</a></li>
+						</ul>
+						<div class="navbar-form navbar-right">
+						<div class="input-group input-group-sm">
+						  	<span class="input-group-addon glyphicon glyphicon-search" style="top: 0px;"></span>
+						  <input type="text" class="form-control" placeholder="Search" ng-model="$searchPacksText" >
+								<span class="btn input-group-addon glyphicon glyphicon-remove" ng-click="$searchPacksText = ''" style="top: 0px;"></span>
+						</div>
+						</div>
+					</div>
+				</nav>
+				
+				<div ng-if="!currentPack" class="well well-lg">
+			      <h4 i18n>No pack selected</h4>
+			      <p i18n>Please, select a pack to manage its licenses</p>
+			    </div>
+
+				<div class="panel panel-default" ng-if="currentPack">
+					<div class="panel-heading">
+						<span i18n>Licenses for pack: </span>{{currentPack.code}} 
+						<span style="color: lightgreen;" class="badge pull-right" ng-bind="currentPack.lic_available || 0"></span>
+						<span class="badge pull-right" ng-bind="licenses.length || 0"></span>
+					</div>
+					
+				
+					<table class="table table-hover table-condensed" >
+					<thead>
+						<tr>
+							<th i18n >License code</th>
+							<th i18n >User fullname</th>
+							<th i18n >Email</th>
+							<th i18n >Status</th>
+							<th></th>
+						</tr>
+					</thead>
+					<tbody>
+						<tr ng-repeat="lic in licenses | filter:searchLicenseText" ng-dblclick="editLicense(lic)" >
+						    <td style="white-space: nowrap;" ng-bind="lic.code"></td>
+						    <td ng-bind="ellipsis(lic.user_fullname, 20)" title="{{lic.user_fullname}}" ></td>
+						    <td ng-bind="ellipsis(lic.user_email, 30)" title="{{lic.user_email}}" ></td>
+						    <td ng-bind="lic.status"></td>
+							<td>
+							<div class="dropdown">
+							    <a class="dropdown-toggle" data-toggle="dropdown" >
+							      <span class="glyphicon glyphicon-align-justify"></span> <span class="caret"></span>
+							    </a>
+							    <ul class="dropdown-menu">
+							      <li><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-pencil"></span> <span i18n>Edit</span></a></li>
+							      <li><a ng-click="activateLicense(lic)"><span class="glyphicon glyphicon-check"></span> <span i18n>Activate</span></a></li>
+							      <li><a ng-click="sendEmail(lic)"><span class="glyphicon glyphicon-send"></span> <span i18n>Send email</span></a></li>
+							      <li><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-remove"></span> <span i18n>Remove</span></a></li>
+							    </ul>
+							  </div>
+							</td>
+						</tr>
+					</tbody>
+					<tfoot>
+					</tfoot>
+				</table>
+			</div>
+		
+		</div>
 	</div>
 
diff --git a/securis/src/main/resources/static/main.html b/securis/src/main/resources/static/main.html
index aad1333..a99b9a5 100644
--- a/securis/src/main/resources/static/main.html
+++ b/securis/src/main/resources/static/main.html
@@ -45,6 +45,8 @@
 	<script type="text/javascript"
 		src="/js/angular-resource.min.js"></script>
 	<script type="text/javascript"
+		src="/js/angular-resource.min.js"></script>
+	<script type="text/javascript"
 		src="/js/bootstrap-dialog.js"></script>
 	<script type="text/javascript"
 		src="/js/toaster.js"></script>
@@ -54,21 +56,17 @@
 		src="/js/vendor/chosen.jquery.js"></script>
 	<script type="text/javascript"
 		src="/js/chosen.js"></script>
+	<script type="text/javascript"
+		src="/js/store.min.js"></script>
 		
  	<script type="text/javascript" src="js/i18n.js"></script>
  	<script type="text/javascript" src="js/main.js"></script>
  	<script type="text/javascript" src="js/login.js"></script>
  	<script type="text/javascript" src="js/catalogs.js"></script>
+ 	<script type="text/javascript" src="js/licenses.js"></script>
  	<script type="text/javascript" src="js/admin.js"></script>
 
-	<!--  <script src="js/main.js"></script>  -->
-	<script type="text/javascript">
-        	$(function() {
-        	});
-			
-        </script>
-
-        <toaster-container toaster-options="{'time-out': 3000}"></toaster-container>
+    <toaster-container toaster-options="{'time-out': 3000}"></toaster-container>
 </div>
 </body>
 </html>
\ No newline at end of file

--
Gitblit v1.3.2