Roberto Sánchez
2014-01-17 04afd774aecc70dca37559fdd8b9a716829c18cd
#396 feature - Added LocalStorage support and http interceptor for
unauthorized access
4 files added
10 files modified
changed files
securis/src/main/resources/db/schema.sql patch | view | blame | history
securis/src/main/resources/static/admin.html patch | view | blame | history
securis/src/main/resources/static/css/font-awesome.min.css patch | view | blame | history
securis/src/main/resources/static/header.html patch | view | blame | history
securis/src/main/resources/static/js/admin.js patch | view | blame | history
securis/src/main/resources/static/js/catalogs.js patch | view | blame | history
securis/src/main/resources/static/js/i18n.js patch | view | blame | history
securis/src/main/resources/static/js/licenses.js patch | view | blame | history
securis/src/main/resources/static/js/login.js patch | view | blame | history
securis/src/main/resources/static/js/main.js patch | view | blame | history
securis/src/main/resources/static/js/store.js patch | view | blame | history
securis/src/main/resources/static/js/store.min.js patch | view | blame | history
securis/src/main/resources/static/licenses.html patch | view | blame | history
securis/src/main/resources/static/main.html patch | view | blame | history
securis/src/main/resources/db/schema.sql
....@@ -68,16 +68,20 @@
6868 CREATE TABLE IF NOT EXISTS license (
6969 id INT NOT NULL,
7070 code VARCHAR(100) NOT NULL ,
71
+ request_data VARCHAR(1024) NULL ,
72
+ license_data VARCHAR(1024) NULL ,
7173 pack_id INT NOT NULL,
72
- user_name INT NULL,
73
- user_email INT NOT NULL,
74
+ full_name VARCHAR(150) NULL,
75
+ email VARCHAR(100) NOT NULL,
7476 creation_timestamp DATETIME NOT NULL ,
75
- sent_timestamp DATETIME NULL ,
77
+ send_timestamp DATETIME NULL ,
7678 modification_timestamp DATETIME NULL ,
79
+ activation_timestamp DATETIME NULL ,
7780 cancelation_timestamp DATETIME NULL ,
81
+ last_access_timestamp DATETIME NULL ,
7882 canceled_by varchar(45) NULL ,
7983 created_by varchar(45) NULL ,
80
- status VARCHAR(3) NULL ,
84
+ status VARCHAR(3) NOT NULL default 0,
8185 PRIMARY KEY (id));
8286
8387
securis/src/main/resources/static/admin.html
....@@ -1,21 +1,5 @@
11
2
- <div class="navbar navbar-inverse navbar-fixed-top">
3
- <div class="container">
4
- <div class="navbar-header">
5
- <ul class="nav navbar-nav navbar-left">
6
- <li style="color: white; padding-top: 15px;">SeCuris</li>
7
- <li><a href="#licenses">Licenses</a></li>
8
- <li><a href="#admin">Admin</a></li>
9
- </ul>
10
- </div>
11
- <div class="navbar-collapse collapse">
12
- <ul class="nav navbar-nav navbar-right">
13
- <li><a href="#about">About</a></li>
14
- <li><a href="#contact">Contact</a></li>
15
- </ul>
16
- </div>
17
- </div>
18
- </div>
2
+ <div ng-include="'header.html'" ></div>
193
204 <div class="container">
215 <div class="col-md-12">&nbsp;</div>
....@@ -38,9 +22,9 @@
3822 <div class="collapse navbar-collapse"
3923 id="bs-example-navbar-collapse-1">
4024 <ul class="nav navbar-nav">
41
- <li><a ng-click="editNew()"><span class="glyphicon glyphicon-plus"></span>
25
+ <li><a i18n ng-click="editNew()"><span class="glyphicon glyphicon-plus"></span>
4226 New</a></li>
43
- <li><a ng-click="cancel()"> <span
27
+ <li><a i18n ng-click="cancel()"> <span
4428 class="glyphicon glyphicon-ban-circle"></span> Cancel
4529 </a></li>
4630 </ul>
....@@ -53,6 +37,7 @@
5337 </div>
5438 </div>
5539 </nav>
40
+
5641 <div class="panel panel-default animate-show ng-hide" ng-show="showForm">
5742 <form role="form" class="form-horizontal " name="catalogForm" id="catalogForm" ng-submit="saveCatalog()" >
5843 <!-- <pre>formu: {{formu | json}}</pre>-->
....@@ -86,7 +71,7 @@
8671 <div class="form-group">
8772 <div class="col-md-offset-3 col-md-10" id="saveContainer">
8873 <button id="save" type="submit" class="btn btn-primary" >
89
- <span class="glyphicon glyphicon-floppy-disk"></span> Save
74
+ <span i18n class="glyphicon glyphicon-floppy-disk"></span> Save
9075 </button>
9176 </div>
9277 </div>
securis/src/main/resources/static/css/font-awesome.min.css
....@@ -0,0 +1,4 @@
1
+/*!
2
+ * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome
3
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
4
+ */@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"}
securis/src/main/resources/static/header.html
....@@ -0,0 +1,19 @@
1
+
2
+ <div class="navbar navbar-inverse navbar-fixed-top">
3
+ <div class="container">
4
+ <div class="navbar-header">
5
+ <ul class="nav navbar-nav navbar-left">
6
+ <li i18n style="color: white; padding-top: 15px;">SeCuris</li>
7
+ <li><a i18n href="/licenses">Licenses</a></li>
8
+ <li><a i18n href="/admin">Admin</a></li>
9
+ </ul>
10
+ </div>
11
+ <div class="navbar-collapse collapse">
12
+ <ul class="nav navbar-nav navbar-right">
13
+ <li><a i18n href="#about">About</a></li>
14
+ <li><a i18n href="#contact">Contact</a></li>
15
+ <li><a i18n ng-click="logout()">Logout</a></li>
16
+ </ul>
17
+ </div>
18
+ </div>
19
+ </div>
securis/src/main/resources/static/js/admin.js
....@@ -33,7 +33,9 @@
3333 '$http',
3434 'toaster',
3535 'Catalogs',
36
- function($scope, $http, toaster, Catalogs) {
36
+ '$store',
37
+ '$L',
38
+ function($scope, $http, toaster, Catalogs, $store, $L) {
3739 $scope.showForm = false;
3840 $scope.isNew = false;
3941 $scope.formu = {};
....@@ -78,16 +80,16 @@
7880 }
7981
8082 $scope.delete = function(data) {
81
- BootstrapDialog.confirm('The record will be deleted, are you sure?', function(result){
83
+ BootstrapDialog.confirm($L.get('The record will be deleted, are you sure?'), function(result){
8284 if(result) {
8385 var promise = Catalogs.remove(data).$promise;
8486 promise.then(function(data) {
8587 $scope.list = Catalogs.query();
8688 Catalogs.refreshRef($scope.refs, Catalogs.getMetadata().resource, $scope.list);
87
- toaster.pop('success', Catalogs.getName(), "Element deleted successfully");
89
+ toaster.pop('success', Catalogs.getName(), $L.get("Element deleted successfully"));
8890 },function(error) {
8991 console.log(error);
90
- toaster.pop('error', Catalogs.getName(), "Error deleting element, reason: " + HTTP_ERRORS[error.status] + ". Details: " + error.headers('X-SECURIS-ERROR'), 10000);
92
+ 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);
9193 });
9294 }
9395 });
....@@ -136,7 +138,7 @@
136138
137139 $scope.saveCatalog = function() {
138140 if ($scope.catalogForm.$invalid) {
139
- toaster.pop('error', Catalogs.getName(), "There are wrong data in current form, please fix it before to save");
141
+ toaster.pop('error', Catalogs.getName(), $L.get("There are wrong data in current form, please fix it before to save"));
140142 } else {
141143 var promise = Catalogs.save($scope.formu).$promise;
142144 promise.then(function(data, otro) {
....@@ -150,10 +152,10 @@
150152 $scope.$parent.list = Catalogs.query();
151153 Catalogs.refreshRef($scope.refs, Catalogs.getMetadata().resource, $scope.$parent.list);
152154
153
- toaster.pop('success', Catalogs.getName(), "Element saved successfully");
155
+ toaster.pop('success', Catalogs.getName(), $L.get("Element saved successfully"));
154156 }, function(error) {
155157 console.log(error);
156
- toaster.pop('error', Catalogs.getName(), "Error saving element, reason: " + HTTP_ERRORS[error.status] + ". Details: " + error.headers('X-SECURIS-ERROR'), 10000);
158
+ 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);
157159 });
158160
159161 }
securis/src/main/resources/static/js/catalogs.js
....@@ -5,209 +5,294 @@
55 * Catalogs module
66 */
77
8
- angular.module('catalogs', ['ngResource'])
8
+ angular
9
+ .module('catalogs', [ 'ngResource' ])
910
10
- .service('Catalogs', ['$rootScope', '$http', '$resource', '$q', function ($rootScope, $http, $resource, $q) {
11
- var resources = {
12
- application : $resource('/application/:appId', {
13
- appId : '@id'
14
- }),
15
- user : $resource('/user/:userId', {
16
- userId : '@username'
17
- }),
18
- organization : $resource('/organization/:orgId', {
19
- orgId : '@id'
20
- }),
21
- licensetype : $resource('/licensetype/:licenseTypeId', {
22
- licenseTypeId : '@id'
23
- })
24
- }
11
+ .service(
12
+ 'Catalogs',
13
+ [
14
+ '$rootScope',
15
+ '$http',
16
+ '$resource',
17
+ '$q',
18
+ function($rootScope, $http, $resource, $q) {
19
+ var resources = {
20
+ application : $resource(
21
+ '/application/:appId', {
22
+ appId : '@id'
23
+ }),
24
+ user : $resource('/user/:userId', {
25
+ userId : '@username'
26
+ }),
27
+ organization : $resource(
28
+ '/organization/:orgId', {
29
+ orgId : '@id'
30
+ }),
31
+ licensetype : $resource(
32
+ '/licensetype/:licenseTypeId', {
33
+ licenseTypeId : '@id'
34
+ })
35
+ }
2536
26
- var _metadata = null;
27
- var _current = null;
37
+ var _metadata = null;
38
+ var _current = null;
2839
29
- var _list = function() {
30
- return $http.get('/js/catalogs.json').success(function(data) {
31
- _metadata = data;
32
- })
33
- }
34
- this.init = function() {
35
- return _list();
36
- }
37
- this.getList = function() {
38
- return _metadata;
39
- }
40
- this.getName = function(index) {
41
- if (index === undefined)
42
- return _current ? _current.name : '';
43
- return _metadata ? _metadata[index].name : '';
44
- }
45
- this.getResource = function(res) {
46
- if (res === undefined)
47
- return _current ? resources[_current.resource] : null;
48
- return _current ? resources[res] : null;
49
- }
50
- this.getPk = function(catalogMetadata) {
51
- if (!catalogMetadata) catalogMetadata = _current;
52
-
53
- for(var i = 0; i < catalogMetadata.fields.length; i++)
54
- if (catalogMetadata.fields[i].pk) return catalogMetadata.fields[i].name;
55
-
56
- return null;
57
- }
58
- /**
59
- * Returns catalog metadata
60
- * @param index: Return current catalog if undefined, if string It find the catalog by resoource name if number it find it by position
61
- */
62
- this.getMetadata = function(index) {
63
- if (!_metadata) throw new Error('There is no catalog metadata info');
64
- if (index === undefined)
65
- return _current;
66
- if (typeof index === 'string') {
67
- for (var i = _metadata.length - 1; i >= 0 && _metadata[i].resource !== index; i--);
68
- index = i;
69
- }
40
+ var _list = function() {
41
+ return $http.get('/js/catalogs.json')
42
+ .success(function(data) {
43
+ _metadata = data;
44
+ })
45
+ }
46
+ this.init = function() {
47
+ return _list();
48
+ }
49
+ this.getList = function() {
50
+ return _metadata;
51
+ }
52
+ this.getName = function(index) {
53
+ if (index === undefined)
54
+ return _current ? _current.name : '';
55
+ return _metadata ? _metadata[index].name
56
+ : '';
57
+ }
58
+ this.getResource = function(res) {
59
+ if (res === undefined)
60
+ return _current ? resources[_current.resource]
61
+ : null;
62
+ return _current ? resources[res] : null;
63
+ }
64
+ this.getPk = function(catalogMetadata) {
65
+ if (!catalogMetadata)
66
+ catalogMetadata = _current;
7067
71
- return _metadata[index];
72
- }
73
- this.setCurrent = function(index) {
74
- if (!_metadata) throw new Error('There is no catalog metadata info');
75
- if (index === undefined)
76
- _current = null;
77
- else
78
- _current = _metadata[index];
79
- }
80
- /********************************************
81
- * Catalog fields methods *
82
- ********************************************/
68
+ for (var i = 0; i < catalogMetadata.fields.length; i++)
69
+ if (catalogMetadata.fields[i].pk)
70
+ return catalogMetadata.fields[i].name;
8371
84
- /**
85
- * Returns the first field in form that should get the focus. We find the first field that is not read only
86
- */
87
- this.getFFF = this.getFirstFocusableField = function() {
88
- if (!_current) throw new Error('There is no current catalog selected');
89
-
90
- for(var i = 0; i < _current.fields.length; i++)
91
- if (!_current.fields[i].readOnly) return _current.fields[i].name;
92
-
93
- return null;
94
- }
72
+ return null;
73
+ }
74
+ /**
75
+ * Returns catalog metadata
76
+ *
77
+ * @param index:
78
+ * Return current catalog if
79
+ * undefined, if string It find the
80
+ * catalog by resoource name if
81
+ * number it find it by position
82
+ */
83
+ this.getMetadata = function(index) {
84
+ if (!_metadata)
85
+ throw new Error(
86
+ 'There is no catalog metadata info');
87
+ if (index === undefined)
88
+ return _current;
89
+ if (typeof index === 'string') {
90
+ for (var i = _metadata.length - 1; i >= 0
91
+ && _metadata[i].resource !== index; i--)
92
+ ;
93
+ index = i;
94
+ }
9595
96
- /**
97
- * Find the field by name or position
98
- */
99
- this.getField = function(key) {
100
- if (!_current) throw new Error('There is no current catalog selected');
101
- var index = -1;
102
- if (typeof key === 'string') {
103
- for (var i = _current.fields.length - 1; i >= 0 && _current.fields[i].name !== key; i--);
104
- index = i;
105
- } else {
106
- index = key; // In this case key === field position
107
- }
96
+ return _metadata[index];
97
+ }
98
+ this.setCurrent = function(index) {
99
+ if (!_metadata)
100
+ throw new Error(
101
+ 'There is no catalog metadata info');
102
+ if (index === undefined)
103
+ _current = null;
104
+ else
105
+ _current = _metadata[index];
106
+ }
107
+ /***********************************************
108
+ * Catalog fields methods *
109
+ **********************************************/
108110
109
- return index === -1 ? {} : _current.fields[index];
110
- }
111
-
112
- /********************************************
113
- * Catalog resource operations on server *
114
- ********************************************/
111
+ /**
112
+ * Returns the first field in form that should
113
+ * get the focus. We find the first field that
114
+ * is not read only
115
+ */
116
+ this.getFFF = this.getFirstFocusableField = function() {
117
+ if (!_current)
118
+ throw new Error(
119
+ 'There is no current catalog selected');
115120
116
- function _success(response) {
117
- console.log('$resource')
118
- console.log(response)
119
- }
120
- function _fail(response) {
121
- console.error('Error trying to get data, HTTP error code: ' + response.status)
122
- }
123
-
124
-
125
- this.save = function(data) {
126
- if (!_current) throw new Error('There is no current catalog selected');
121
+ for (var i = 0; i < _current.fields.length; i++)
122
+ if (!_current.fields[i].readOnly)
123
+ return _current.fields[i].name;
127124
128
- var resource = this.getResource();
129
- return resource.save(data, _success, _fail);
130
- }
131
- this.remove = function(data) {
132
- return this.getResource().remove({}, data, _success, _fail)
133
- }
134
- this.query = function() {
135
- return this.getResource().query({}, _success, _fail);
136
- }
137
- this.refreshRef = function(refs, res, preloadedData) {
138
- // We check if there is some field for the resource passed as parameter
139
- var field = (function() {
140
- for (var i = _current.fields.length - 1; i >= 0; i--) {
141
- if (_current.fields[i].resource === res)
142
- return _current.fields[i];
143
- }
144
- return null;
145
- })();
146
-
147
- // If field for that resource is not found there is nothing to refresh
148
- if (!field) return;
149
- var resource = this.getResource(res);
150
- var data = preloadedData || resource.query({}, _success, _fail);
151
- var that = this;
152
- data.$promise.then(function(responseData) {
153
- var pk = that.getPk(that.getMetadata(field.resource))
154
- var comboData = []
155
- responseData.forEach(function(row) {
156
- comboData.push({
157
- id: row[pk],
158
- label: row.label || row.name || row.code || row.first_name + ' ' + row.last_name
159
- });
160
- })
161
- refs[field.name] = comboData;
162
- })
163
- }
164
- this.loadRefs = function(refs) {
165
- if (!_current) throw new Error('There is no current catalog selected');
166
- var refsFields = [];
167
- _current.fields.forEach(function(f) {
168
- if (f.resource)
169
- refsFields.push(f)
125
+ return null;
126
+ }
170127
171
- });
172
-
173
- var that = this;
174
- var promises = []
175
- refsFields.forEach(function(f) {
176
- var resource = that.getResource(f.resource);
177
- refs[f.name] = resource.query({}, _success, _fail);
178
- promises.push(refs[f.name].$promise);
179
- });
180
-
181
- console.log('promises: ' + promises.length + ' ')
182
- console.log(promises)
183
- $q.all(promises).then(function() {
184
-
185
- for (var k in refs) {
186
- var field = that.getField(k);
187
- var pk = that.getPk(that.getMetadata(field.resource))
188
- console.log('PK field for ' + k + ' is ' + pk)
189
- var comboData = []
190
- refs[k].forEach(function(row) {
191
- console.log('field.resource !== _current.resource: ' + field.resource +' '+ _current.resource)
192
- comboData.push({
193
- id: row[pk],
194
- label: row.label || row.name || row.code || row.first_name + ' ' + (row.last_name || '')
195
- });
196
- })
197
- refs[k] = comboData;
198
- console.log('Ready for combo for ' + k)
199
- console.log(comboData);
200
- }
201
- _current.fields.forEach(function(f) {
202
- if (f.values)
203
- refs[f.name] = f.values;
204
- });
205
- })
206
-
207
- console.log(refs);
208
- return refs;
209
- }
210
-
211
- }])
128
+ /**
129
+ * Find the field by name or position
130
+ */
131
+ this.getField = function(key) {
132
+ if (!_current)
133
+ throw new Error(
134
+ 'There is no current catalog selected');
135
+ var index = -1;
136
+ if (typeof key === 'string') {
137
+ for (var i = _current.fields.length - 1; i >= 0
138
+ && _current.fields[i].name !== key; i--)
139
+ ;
140
+ index = i;
141
+ } else {
142
+ index = key; // In this case key ===
143
+ // field position
144
+ }
145
+
146
+ return index === -1 ? {}
147
+ : _current.fields[index];
148
+ }
149
+
150
+ /***********************************************
151
+ * Catalog resource operations on server *
152
+ **********************************************/
153
+
154
+ function _success(response) {
155
+ console.log('$resource')
156
+ console.log(response)
157
+ }
158
+ function _fail(response) {
159
+ console
160
+ .error('Error trying to get data, HTTP error code: '
161
+ + response.status)
162
+ }
163
+
164
+ this.save = function(data) {
165
+ if (!_current)
166
+ throw new Error(
167
+ 'There is no current catalog selected');
168
+
169
+ var resource = this.getResource();
170
+ return resource.save(data, _success, _fail);
171
+ }
172
+ this.remove = function(data) {
173
+ return this.getResource().remove({}, data,
174
+ _success, _fail)
175
+ }
176
+ this.query = function() {
177
+ return this.getResource().query({},
178
+ _success, _fail);
179
+ }
180
+ this.refreshRef = function(refs, res,
181
+ preloadedData) {
182
+ // We check if there is some field for the
183
+ // resource passed as parameter
184
+ var field = (function() {
185
+ for (var i = _current.fields.length - 1; i >= 0; i--) {
186
+ if (_current.fields[i].resource === res)
187
+ return _current.fields[i];
188
+ }
189
+ return null;
190
+ })();
191
+
192
+ // If field for that resource is not found
193
+ // there is nothing to refresh
194
+ if (!field)
195
+ return;
196
+ var resource = this.getResource(res);
197
+ var data = preloadedData
198
+ || resource.query({}, _success,
199
+ _fail);
200
+ var that = this;
201
+ data.$promise.then(function(responseData) {
202
+ var pk = that.getPk(that
203
+ .getMetadata(field.resource))
204
+ var comboData = []
205
+ responseData.forEach(function(row) {
206
+ comboData.push({
207
+ id : row[pk],
208
+ label : row.label || row.name
209
+ || row.code
210
+ || row.first_name + ' '
211
+ + row.last_name
212
+ });
213
+ })
214
+ refs[field.name] = comboData;
215
+ })
216
+ }
217
+ this.loadRefs = function(refs) {
218
+ if (!_current)
219
+ throw new Error(
220
+ 'There is no current catalog selected');
221
+ var refsFields = [];
222
+ _current.fields.forEach(function(f) {
223
+ if (f.resource)
224
+ refsFields.push(f)
225
+
226
+ });
227
+
228
+ var that = this;
229
+ var promises = []
230
+ refsFields.forEach(function(f) {
231
+ var resource = that
232
+ .getResource(f.resource);
233
+ refs[f.name] = resource.query({},
234
+ _success, _fail);
235
+ promises.push(refs[f.name].$promise);
236
+ });
237
+
238
+ console.log('promises: ' + promises.length
239
+ + ' ')
240
+ console.log(promises)
241
+ $q
242
+ .all(promises)
243
+ .then(
244
+ function() {
245
+
246
+ for ( var k in refs) {
247
+ var field = that
248
+ .getField(k);
249
+ var pk = that
250
+ .getPk(that
251
+ .getMetadata(field.resource))
252
+ console
253
+ .log('PK field for '
254
+ + k
255
+ + ' is '
256
+ + pk)
257
+ var comboData = []
258
+ refs[k]
259
+ .forEach(function(
260
+ row) {
261
+ console
262
+ .log('field.resource !== _current.resource: '
263
+ + field.resource
264
+ + ' '
265
+ + _current.resource)
266
+ comboData
267
+ .push({
268
+ id : row[pk],
269
+ label : row.label
270
+ || row.name
271
+ || row.code
272
+ || row.first_name
273
+ + ' '
274
+ + (row.last_name || '')
275
+ });
276
+ })
277
+ refs[k] = comboData;
278
+ console
279
+ .log('Ready for combo for '
280
+ + k)
281
+ console
282
+ .log(comboData);
283
+ }
284
+ _current.fields
285
+ .forEach(function(
286
+ f) {
287
+ if (f.values)
288
+ refs[f.name] = f.values;
289
+ });
290
+ })
291
+
292
+ console.log(refs);
293
+ return refs;
294
+ }
295
+
296
+ } ])
212297
213298 })();
securis/src/main/resources/static/js/i18n.js
....@@ -72,7 +72,7 @@
7272 restrict : 'A', // only activate on element attribute
7373 require : '',
7474 link : function(scope, element, attrs) {
75
- var txt = attrs.i18n || element[0].innerText;
75
+ var txt = attrs.i18n || element.text();
7676 element.text($L.get(txt));
7777 }
7878 };
securis/src/main/resources/static/js/licenses.js
....@@ -8,8 +8,87 @@
88 '$http',
99 'toaster',
1010 'Catalogs',
11
- function($scope, $http, toaster, Catalogs) {
11
+ '$store',
12
+ '$L',
13
+ function($scope, $http, toaster, Catalogs, $store, $L) {
14
+ $scope.currentPack = $store.get('currentPack');
15
+ $scope.packs = [
16
+ {id: 1,
17
+ "organization_name": "BP-Spain",
18
+ "application_name": "CurisIntegrity",
19
+ "licensetype_code": "CIBS",
20
+ "code": "BP-SA-0001",
21
+ "licenses": 50,
22
+ "lic_available": 23},
23
+ {id: 2,
24
+ "organization_name": "Exxon",
25
+ "application_name": "CurisData",
26
+ "licensetype_code": "CDL1",
27
+ "code": "EX-SA-0001",
28
+ "licenses": 1,
29
+ "lic_available": 0},
30
+ {id: 3,
31
+ "organization_name": "Repsol S.A. empresa con nombre muy largo",
32
+ "application_name": "CurisData",
33
+ "licensetype_code": "CDL2",
34
+ "code": "RE-SA-0001",
35
+ "licenses": 5,
36
+ "lic_available": 2},
37
+ {id: 4,
38
+ "organization_name": "BP-Spain",
39
+ "application_name": "CurisIntegrity v3.0",
40
+ "code": "BP-SA-0002",
41
+ "licensetype_code": "CISA",
42
+ "licenses": 150,
43
+ "lic_available": 13},
44
+ ];
1245
46
+ $scope.licenses = [
47
+ {id: 1,
48
+ "code": "BP-SA-001-AKSJMS234",
49
+ "user_fullname": "Johnny Belmonte",
50
+ "user_email": "jb@curisit.net",
51
+ "status": 3},
52
+ {id: 2,
53
+ "code": "BP-SA-001-KAJSDHAJS",
54
+ "user_fullname": "Walter Simons",
55
+ "user_email": "ws@curisit.net",
56
+ "status": 1},
57
+ {id: 3,
58
+ "code": "BP-SA-001-ASKDGHKA",
59
+ "user_fullname": "Frank Belmonte",
60
+ "user_email": "fb@curisit.net",
61
+ "status": 2},
62
+ {id: 4,
63
+ "code": "BP-SA-001-BBBGGGG",
64
+ "user_fullname": "John Dalton",
65
+ "user_email": "jd@curisit.net",
66
+ "status": 3},
67
+ {id: 5,
68
+ "code": "BP-SA-001-AKADNAJANA",
69
+ "user_fullname": "Walter Martins",
70
+ "user_email": "wm@curisit.net",
71
+ "status": 3},
72
+ {id: 6,
73
+ "code": "BP-SA-001-AKANDAKS",
74
+ "user_fullname": "Joe Bolton",
75
+ "user_email": "jbol@curisit.net",
76
+ "status": 2}
77
+ ];
78
+
79
+
80
+
81
+ $scope.ellipsis = function(txt, len) {
82
+ if (!txt || txt.length <= len) return txt;
83
+ return txt.substring(0, len) + '...';
84
+ }
85
+
86
+ $scope.selectPack = function(pack) {
87
+ console.log('Pack selected: ' + JSON.stringify(pack));
88
+ $scope.currentPack = pack;
89
+ $store.put('currentPack', pack);
90
+ }
91
+
1392 } ]);
1493
1594 })();
securis/src/main/resources/static/js/login.js
....@@ -3,9 +3,10 @@
33
44 var app = angular.module('securis');
55
6
- app.controller('LoginCtrl', ['$scope', '$http', '$window', '$location', 'toaster', '$L',
7
- function($scope, $http, $window, $location, toaster, $L) {
6
+ app.controller('LoginCtrl', ['$scope', '$http', '$location', 'toaster', '$L', '$store',
7
+ function($scope, $http, $location, toaster, $L, $store) {
88
9
+
910 $('#username').focus();
1011
1112 $scope.submit = function() {
....@@ -21,14 +22,17 @@
2122 })
2223 }).
2324 success(function(data, status, headers, config) {
24
- toaster.pop('success', $L.get('Login successful'), $L.get('User {0} has logged in SeCuris', $scope.username), 1500);
25
+ toaster.pop('success', $L.get('Login successful'), $L.get('User {0} has logged in application', $scope.username), 1500);
2526 $location.path('/licenses');
27
+ $store.put('username', $scope.username);
28
+ $store.put('token', data.token);
29
+ $http.defaults.headers.common['X-SECURIS-TOKEN'] = data.token;
2630 }).
2731 error(function(data, status, headers, config) {
28
- console.error(data + " status: "+ status);
2932 if (status === 403 /* forbidden */) {
3033 toaster.pop('error', $L.get('Login error'), $L.get('Invalid credentials'), 3000);
3134 } else {
35
+ console.error(data + " status: "+ status);
3236 toaster.pop('error', $L.get('Unexpected Login error'), $L.get('Unexpected error HTTP ({0}) accessing to server. Contact with the administrator.', status), 5000);
3337 }
3438 $('#username').focus();
securis/src/main/resources/static/js/main.js
....@@ -2,35 +2,84 @@
22 'use strict';
33
44 var m = angular.module('securis', [ 'ngRoute', 'ngResource', 'toaster', 'localytics.directives', 'catalogs', 'i18n' ]);
5
+
6
+ m.service('$store', function() {
7
+ this.get = function(key, defaultValue) {
8
+ return store.get(key) || defaultValue;
9
+ }
10
+ this.set = this.put = function(key, value) {
11
+ store.set(key, value);
12
+ }
13
+ this.remove = this.delete = function(key) {
14
+ return store.remove(key);
15
+ }
16
+ this.clear = this.clearAll = function() {
17
+ store.clear();
18
+ }
19
+ this.getAll = function() {
20
+ return store.getAll();
21
+ }
22
+ });
523
6
- m.config(function($routeProvider, $locationProvider) {
24
+ m.factory('securisHttpInterceptor', function($q, $location, $store) {
25
+ var isUnauthorizedAccess = function(rejection) {
26
+ console.log('rejection -----------------------');
27
+ console.log(rejection);
28
+ return rejection.status === 401 /* Unauthorized */;
29
+ }
30
+ return {
31
+
32
+ 'responseError': function(rejection) {
33
+ // do something on error
34
+ if (isUnauthorizedAccess(rejection)) {
35
+ if ($location.path() !== '/login') {
36
+ $store.clear();
37
+ $location.path('/login');
38
+ console.error('There was an unathorized access to url {0}, method: {1}'.$i18n(rejection.config.url, rejection.config.method));
39
+ } else {
40
+ // console.log('Error on login ...')
41
+ }
42
+ }
43
+ return $q.reject(rejection);
44
+ }
45
+ };
46
+ });
47
+
48
+ m.config(function($routeProvider, $locationProvider, $httpProvider) {
749 console.log('Configuring routes...');
850 $routeProvider.when('/login', {
951 templateUrl: 'login.html',
10
- controller: 'LoginCtrl',
11
- controllerAs: 'login'
52
+ controller: 'LoginCtrl'
1253 });
1354 $routeProvider.when('/licenses', {
1455 templateUrl: 'licenses.html',
15
- controller: 'LicensesCtrl',
16
- controllerAs: 'licenses'
56
+ controller: 'LicensesCtrl'
1757 });
1858 $routeProvider.when('/admin', {
1959 templateUrl: 'admin.html',
20
- controller: 'AdminCtrl',
21
- controllerAs: 'admin'
60
+ controller: 'AdminCtrl'
2261 });
2362
2463 // configure html5 to get links working on jsfiddle
2564 $locationProvider.html5Mode(true);
65
+ $httpProvider.interceptors.push('securisHttpInterceptor');
2666 });
27
- m.controller('MainCtrl', ['$scope', '$location', '$L',
28
- function($scope, $location, $L) {
29
- console.log('Moving to login...');
30
- console.log('Test 1 lang: ' + 'Hello {0}!! this is {1}'.$i18n('World', 'cool'));
31
- console.log('Test 2 lang: ' + $L.get('Hello Pepe!!'));
32
- $location.path('/login');
33
- }]);
67
+ m.controller('MainCtrl', ['$scope', '$http', '$location', '$L', '$store',
68
+ function($scope, $http, $location, $L, $store) {
3469
70
+ if ($store.get('token') != null) {
71
+ $http.defaults.headers.common['X-SECURIS-TOKEN'] = $store.get('token');
72
+ $location.path('/licenses');
73
+ } else {
74
+ $location.path('/login');
75
+ }
76
+
77
+ $scope.logout = function() {
78
+ $store.remove('user');
79
+ $store.remove('token');
80
+ $location.path('/login');
81
+ }
82
+
83
+ }]);
3584
3685 })();
securis/src/main/resources/static/js/store.js
....@@ -0,0 +1,165 @@
1
+;(function(win){
2
+ var store = {},
3
+ doc = win.document,
4
+ localStorageName = 'localStorage',
5
+ scriptTag = 'script',
6
+ storage
7
+
8
+ store.disabled = false
9
+ store.set = function(key, value) {}
10
+ store.get = function(key) {}
11
+ store.remove = function(key) {}
12
+ store.clear = function() {}
13
+ store.transact = function(key, defaultVal, transactionFn) {
14
+ var val = store.get(key)
15
+ if (transactionFn == null) {
16
+ transactionFn = defaultVal
17
+ defaultVal = null
18
+ }
19
+ if (typeof val == 'undefined') { val = defaultVal || {} }
20
+ transactionFn(val)
21
+ store.set(key, val)
22
+ }
23
+ store.getAll = function() {}
24
+ store.forEach = function() {}
25
+
26
+ store.serialize = function(value) {
27
+ return JSON.stringify(value)
28
+ }
29
+ store.deserialize = function(value) {
30
+ if (typeof value != 'string') { return undefined }
31
+ try { return JSON.parse(value) }
32
+ catch(e) { return value || undefined }
33
+ }
34
+
35
+ // Functions to encapsulate questionable FireFox 3.6.13 behavior
36
+ // when about.config::dom.storage.enabled === false
37
+ // See https://github.com/marcuswestin/store.js/issues#issue/13
38
+ function isLocalStorageNameSupported() {
39
+ try { return (localStorageName in win && win[localStorageName]) }
40
+ catch(err) { return false }
41
+ }
42
+
43
+ if (isLocalStorageNameSupported()) {
44
+ storage = win[localStorageName]
45
+ store.set = function(key, val) {
46
+ if (val === undefined) { return store.remove(key) }
47
+ storage.setItem(key, store.serialize(val))
48
+ return val
49
+ }
50
+ store.get = function(key) { return store.deserialize(storage.getItem(key)) }
51
+ store.remove = function(key) { storage.removeItem(key) }
52
+ store.clear = function() { storage.clear() }
53
+ store.getAll = function() {
54
+ var ret = {}
55
+ store.forEach(function(key, val) {
56
+ ret[key] = val
57
+ })
58
+ return ret
59
+ }
60
+ store.forEach = function(callback) {
61
+ for (var i=0; i<storage.length; i++) {
62
+ var key = storage.key(i)
63
+ callback(key, store.get(key))
64
+ }
65
+ }
66
+ } else if (doc.documentElement.addBehavior) {
67
+ var storageOwner,
68
+ storageContainer
69
+ // Since #userData storage applies only to specific paths, we need to
70
+ // somehow link our data to a specific path. We choose /favicon.ico
71
+ // as a pretty safe option, since all browsers already make a request to
72
+ // this URL anyway and being a 404 will not hurt us here. We wrap an
73
+ // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
74
+ // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
75
+ // since the iframe access rules appear to allow direct access and
76
+ // manipulation of the document element, even for a 404 page. This
77
+ // document can be used instead of the current document (which would
78
+ // have been limited to the current path) to perform #userData storage.
79
+ try {
80
+ storageContainer = new ActiveXObject('htmlfile')
81
+ storageContainer.open()
82
+ storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>')
83
+ storageContainer.close()
84
+ storageOwner = storageContainer.w.frames[0].document
85
+ storage = storageOwner.createElement('div')
86
+ } catch(e) {
87
+ // somehow ActiveXObject instantiation failed (perhaps some special
88
+ // security settings or otherwse), fall back to per-path storage
89
+ storage = doc.createElement('div')
90
+ storageOwner = doc.body
91
+ }
92
+ function withIEStorage(storeFunction) {
93
+ return function() {
94
+ var args = Array.prototype.slice.call(arguments, 0)
95
+ args.unshift(storage)
96
+ // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
97
+ // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
98
+ storageOwner.appendChild(storage)
99
+ storage.addBehavior('#default#userData')
100
+ storage.load(localStorageName)
101
+ var result = storeFunction.apply(store, args)
102
+ storageOwner.removeChild(storage)
103
+ return result
104
+ }
105
+ }
106
+
107
+ // In IE7, keys may not contain special chars. See all of https://github.com/marcuswestin/store.js/issues/40
108
+ var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
109
+ function ieKeyFix(key) {
110
+ return key.replace(forbiddenCharsRegex, '___')
111
+ }
112
+ store.set = withIEStorage(function(storage, key, val) {
113
+ key = ieKeyFix(key)
114
+ if (val === undefined) { return store.remove(key) }
115
+ storage.setAttribute(key, store.serialize(val))
116
+ storage.save(localStorageName)
117
+ return val
118
+ })
119
+ store.get = withIEStorage(function(storage, key) {
120
+ key = ieKeyFix(key)
121
+ return store.deserialize(storage.getAttribute(key))
122
+ })
123
+ store.remove = withIEStorage(function(storage, key) {
124
+ key = ieKeyFix(key)
125
+ storage.removeAttribute(key)
126
+ storage.save(localStorageName)
127
+ })
128
+ store.clear = withIEStorage(function(storage) {
129
+ var attributes = storage.XMLDocument.documentElement.attributes
130
+ storage.load(localStorageName)
131
+ for (var i=0, attr; attr=attributes[i]; i++) {
132
+ storage.removeAttribute(attr.name)
133
+ }
134
+ storage.save(localStorageName)
135
+ })
136
+ store.getAll = function(storage) {
137
+ var ret = {}
138
+ store.forEach(function(key, val) {
139
+ ret[key] = val
140
+ })
141
+ return ret
142
+ }
143
+ store.forEach = withIEStorage(function(storage, callback) {
144
+ var attributes = storage.XMLDocument.documentElement.attributes
145
+ for (var i=0, attr; attr=attributes[i]; ++i) {
146
+ callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
147
+ }
148
+ })
149
+ }
150
+
151
+ try {
152
+ var testKey = '__storejs__'
153
+ store.set(testKey, testKey)
154
+ if (store.get(testKey) != testKey) { store.disabled = true }
155
+ store.remove(testKey)
156
+ } catch(e) {
157
+ store.disabled = true
158
+ }
159
+ store.enabled = !store.disabled
160
+
161
+ if (typeof module != 'undefined' && module.exports) { module.exports = store }
162
+ else if (typeof define === 'function' && define.amd) { define(store) }
163
+ else { win.store = store }
164
+
165
+})(this.window || global);
securis/src/main/resources/static/js/store.min.js
....@@ -0,0 +1,2 @@
1
+/* Copyright (c) 2010-2013 Marcus Westin */
2
+(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)
securis/src/main/resources/static/licenses.html
....@@ -1,32 +1,145 @@
11
2
- <div class="navbar navbar-inverse navbar-fixed-top">
3
- <div class="container">
4
- <div class="navbar-header">
5
- <button type="button" class="navbar-toggle" data-toggle="collapse"
6
- data-target=".navbar-collapse">
7
- <span class="icon-bar"></span> <span class="icon-bar"></span> <span
8
- class="icon-bar"></span>
9
- </button>
10
- <a class="navbar-brand" href="#">SeCuris</a>
11
- </div>
12
- <div class="navbar-collapse collapse">
13
- <ul class="nav navbar-nav navbar-right">
14
- <li><a href="#about">About</a></li>
15
- <li><a href="#contact">Contact</a></li>
16
- </ul>
17
- </div>
18
- </div>
19
- </div>
20
-
21
- <!-- Main jumbotron for a primary marketing message or call to action -->
22
- <div class="jumbotron">
23
- <div class="container">
24
- <h2>SeCuris</h2>
25
- <p>Licenses management</p>
26
- </div>
27
- </div>
2
+ <div ng-include="'header.html'" ></div>
283
294 <div class="container">
5
+ <div class="col-md-12">&nbsp;</div>
6
+ <div id="packs_section" class="col-md-6">
7
+ <nav class="navbar navbar-default navbar-static-top">
8
+ <!-- Brand and toggle get grouped for better mobile display -->
9
+ <div class="navbar-header">
10
+ <a class="navbar-brand" i18n >Packs</a>
11
+ </div>
12
+
13
+ <!-- Collect the nav links, forms, and other content for toggling -->
14
+ <div class="collapse navbar-collapse"
15
+ id="bs-example-navbar-collapse-1">
16
+ <ul class="nav navbar-nav">
17
+ <li><a i18n ng-click="editNew()"><span class="glyphicon glyphicon-plus"></span>
18
+ New</a></li>
19
+ <li><a i18n ng-click="cancel()"> <span
20
+ class="glyphicon glyphicon-ban-circle"></span> Cancel
21
+ </a></li>
22
+ </ul>
23
+ <div class="navbar-form navbar-right">
24
+ <div class="input-group input-group-sm">
25
+ <span class="input-group-addon glyphicon glyphicon-search" style="top: 0px;"></span>
26
+ <input type="text" class="form-control" placeholder="Search" ng-model="$parent.searchText" >
27
+ <span class="btn input-group-addon glyphicon glyphicon-remove" ng-click="$parent.searchText = ''" style="top: 0px;"></span>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </nav>
32
+ <div class="panel panel-default" >
33
+ <div class="panel-heading">
34
+ Packs <span class="badge pull-right" ng-bind="packs.length || 0"></span>
35
+ </div>
36
+
37
+ <table class="table table-hover table-condensed">
38
+ <thead>
39
+ <tr>
40
+ <th i18n >Organization</th>
41
+ <th i18n >Application</th>
42
+ <th i18n >Code</th>
43
+ <th i18n >Licenses</th>
44
+ <th></th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ <tr ng-repeat="pack in packs | filter:searchText" ng-dblclick="editPack(row)" ng-class="{success: currentPack.id === pack.id}" ng-click="selectPack(pack)">
49
+ <td ng-bind="ellipsis(pack.organization_name, 20)" title="{{pack.organization_name}}" ></td>
50
+ <td ng-bind="pack.application_name"></td>
51
+ <td style="white-space: nowrap;" ng-bind="pack.code"></td>
52
+ <td title="Total: {{pack.licenses}}, avaliable: {{pack.lic_available}}">{{pack.licenses}} ({{pack.lic_available}})</td>
53
+ <td><span ng-click="editPack(row)"
54
+ class="glyphicon glyphicon-pencil"></span>
55
+ <span ng-click="deletePack(row)"
56
+ class="glyphicon glyphicon-remove"></span>
57
+ </td>
58
+ </tr>
59
+ </tbody>
60
+ <tfoot>
61
+ </tfoot>
62
+ </table>
63
+ </div>
3064
65
+ </div>
66
+
67
+ <div id="licenses_section" class="col-md-6" >
68
+ <nav class="navbar navbar-default navbar-static-top" ng-disabled="!!currentPack">
69
+ <!-- Brand and toggle get grouped for better mobile display -->
70
+ <div class="navbar-header success">
71
+ <a class="navbar-brand" i18n>Licenses</a>
72
+ </div>
73
+
74
+ <!-- Collect the nav links, forms, and other content for toggling -->
75
+ <div class="collapse navbar-collapse"
76
+ id="bs-example-navbar-collapse-1">
77
+ <ul class="nav navbar-nav">
78
+ <li><a i18n ng-click="editNewLicense()"><span class="glyphicon glyphicon-plus"></span>
79
+ New</a></li>
80
+ <li><a i18n ng-click="cancelEditionLicense()"> <span
81
+ class="glyphicon glyphicon-ban-circle"></span> Cancel
82
+ </a></li>
83
+ </ul>
84
+ <div class="navbar-form navbar-right">
85
+ <div class="input-group input-group-sm">
86
+ <span class="input-group-addon glyphicon glyphicon-search" style="top: 0px;"></span>
87
+ <input type="text" class="form-control" placeholder="Search" ng-model="$searchPacksText" >
88
+ <span class="btn input-group-addon glyphicon glyphicon-remove" ng-click="$searchPacksText = ''" style="top: 0px;"></span>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </nav>
93
+
94
+ <div ng-if="!currentPack" class="well well-lg">
95
+ <h4 i18n>No pack selected</h4>
96
+ <p i18n>Please, select a pack to manage its licenses</p>
97
+ </div>
98
+
99
+ <div class="panel panel-default" ng-if="currentPack">
100
+ <div class="panel-heading">
101
+ <span i18n>Licenses for pack: </span>{{currentPack.code}}
102
+ <span style="color: lightgreen;" class="badge pull-right" ng-bind="currentPack.lic_available || 0"></span>
103
+ <span class="badge pull-right" ng-bind="licenses.length || 0"></span>
104
+ </div>
105
+
106
+
107
+ <table class="table table-hover table-condensed" >
108
+ <thead>
109
+ <tr>
110
+ <th i18n >License code</th>
111
+ <th i18n >User fullname</th>
112
+ <th i18n >Email</th>
113
+ <th i18n >Status</th>
114
+ <th></th>
115
+ </tr>
116
+ </thead>
117
+ <tbody>
118
+ <tr ng-repeat="lic in licenses | filter:searchLicenseText" ng-dblclick="editLicense(lic)" >
119
+ <td style="white-space: nowrap;" ng-bind="lic.code"></td>
120
+ <td ng-bind="ellipsis(lic.user_fullname, 20)" title="{{lic.user_fullname}}" ></td>
121
+ <td ng-bind="ellipsis(lic.user_email, 30)" title="{{lic.user_email}}" ></td>
122
+ <td ng-bind="lic.status"></td>
123
+ <td>
124
+ <div class="dropdown">
125
+ <a class="dropdown-toggle" data-toggle="dropdown" >
126
+ <span class="glyphicon glyphicon-align-justify"></span> <span class="caret"></span>
127
+ </a>
128
+ <ul class="dropdown-menu">
129
+ <li><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-pencil"></span> <span i18n>Edit</span></a></li>
130
+ <li><a ng-click="activateLicense(lic)"><span class="glyphicon glyphicon-check"></span> <span i18n>Activate</span></a></li>
131
+ <li><a ng-click="sendEmail(lic)"><span class="glyphicon glyphicon-send"></span> <span i18n>Send email</span></a></li>
132
+ <li><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-remove"></span> <span i18n>Remove</span></a></li>
133
+ </ul>
134
+ </div>
135
+ </td>
136
+ </tr>
137
+ </tbody>
138
+ <tfoot>
139
+ </tfoot>
140
+ </table>
141
+ </div>
142
+
143
+ </div>
31144 </div>
32145
securis/src/main/resources/static/main.html
....@@ -45,6 +45,8 @@
4545 <script type="text/javascript"
4646 src="/js/angular-resource.min.js"></script>
4747 <script type="text/javascript"
48
+ src="/js/angular-resource.min.js"></script>
49
+ <script type="text/javascript"
4850 src="/js/bootstrap-dialog.js"></script>
4951 <script type="text/javascript"
5052 src="/js/toaster.js"></script>
....@@ -54,21 +56,17 @@
5456 src="/js/vendor/chosen.jquery.js"></script>
5557 <script type="text/javascript"
5658 src="/js/chosen.js"></script>
59
+ <script type="text/javascript"
60
+ src="/js/store.min.js"></script>
5761
5862 <script type="text/javascript" src="js/i18n.js"></script>
5963 <script type="text/javascript" src="js/main.js"></script>
6064 <script type="text/javascript" src="js/login.js"></script>
6165 <script type="text/javascript" src="js/catalogs.js"></script>
66
+ <script type="text/javascript" src="js/licenses.js"></script>
6267 <script type="text/javascript" src="js/admin.js"></script>
6368
64
- <!-- <script src="js/main.js"></script> -->
65
- <script type="text/javascript">
66
- $(function() {
67
- });
68
-
69
- </script>
70
-
71
- <toaster-container toaster-options="{'time-out': 3000}"></toaster-container>
69
+ <toaster-container toaster-options="{'time-out': 3000}"></toaster-container>
7270 </div>
7371 </body>
7472 </html>