summaryrefslogtreecommitdiffstats
path: root/ecomp-portal-FE-os/client/src
diff options
context:
space:
mode:
Diffstat (limited to 'ecomp-portal-FE-os/client/src')
-rw-r--r--ecomp-portal-FE-os/client/src/app.js122
-rw-r--r--ecomp-portal-FE-os/client/src/app.less82
-rw-r--r--ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.directive.js111
-rw-r--r--ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.less182
-rw-r--r--ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.tpl.html42
-rw-r--r--ecomp-portal-FE-os/client/src/directives/search-users/search-users.controller.js168
-rw-r--r--ecomp-portal-FE-os/client/src/directives/search-users/search-users.less159
-rw-r--r--ecomp-portal-FE-os/client/src/directives/search-users/search-users.tpl.html143
-rw-r--r--ecomp-portal-FE-os/client/src/router.js238
-rw-r--r--ecomp-portal-FE-os/client/src/services/support/session/session.service.js76
-rw-r--r--ecomp-portal-FE-os/client/src/services/userProfile/userProfile.service.js202
-rw-r--r--ecomp-portal-FE-os/client/src/styles/att-abs.less92
-rw-r--r--ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.js268
-rw-r--r--ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.spec.js19
-rw-r--r--ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.modal.html201
-rw-r--r--ecomp-portal-FE-os/client/src/views/applications/applications.controller.js123
-rw-r--r--ecomp-portal-FE-os/client/src/views/applications/applications.controller.spec.js19
-rw-r--r--ecomp-portal-FE-os/client/src/views/applications/applications.tpl.html81
-rw-r--r--ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.controller.js218
-rw-r--r--ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.modal.html113
-rw-r--r--ecomp-portal-FE-os/client/src/views/dashboard/dashboard.tpl.html173
-rw-r--r--ecomp-portal-FE-os/client/src/views/footer/footer.controller.js48
-rw-r--r--ecomp-portal-FE-os/client/src/views/footer/footer.controller.spec.js19
-rw-r--r--ecomp-portal-FE-os/client/src/views/footer/footer.less104
-rw-r--r--ecomp-portal-FE-os/client/src/views/footer/footer.tpl.html34
-rw-r--r--ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js415
-rw-r--r--ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html37
-rw-r--r--ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html103
-rw-r--r--ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.controller.js372
-rw-r--r--ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.tpl.html46
-rw-r--r--ecomp-portal-FE-os/client/src/views/functionalMenu/jqTreeContextMenu.js214
-rw-r--r--ecomp-portal-FE-os/client/src/views/search/search.controller.js198
-rw-r--r--ecomp-portal-FE-os/client/src/views/search/search.controller.spec.js19
-rw-r--r--ecomp-portal-FE-os/client/src/views/search/search.tpl.html84
-rw-r--r--ecomp-portal-FE-os/client/src/views/support/contact-us/contact-us.aux.html25
-rw-r--r--ecomp-portal-FE-os/client/src/views/support/get-access/get-accessinfo.html24
-rw-r--r--ecomp-portal-FE-os/client/src/views/tabs/tabs.tpl.html47
-rw-r--r--ecomp-portal-FE-os/client/src/views/userbar/userbar.controller.js229
-rw-r--r--ecomp-portal-FE-os/client/src/views/userbar/userbar.tpl.html20
39 files changed, 4870 insertions, 0 deletions
diff --git a/ecomp-portal-FE-os/client/src/app.js b/ecomp-portal-FE-os/client/src/app.js
new file mode 100644
index 00000000..8a1897fc
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/app.js
@@ -0,0 +1,122 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+
+var app = angular.module('ecompApp', [
+ 'ngCookies',
+ 'ngResource',
+ 'ngSanitize',
+ 'ui.router',
+ 'ecomp.conf',
+ 'ngMessages',
+ 'ui.select',
+ 'angular-cache',
+ 'ngDialog',
+ 'gridster',
+ 'angular-gestures',
+ 'uuid',
+ 'ui.bootstrap',
+ 'ngMaterial',
+ 'oc.lazyLoad',
+ 'b2b.att'
+ ])
+ .config(($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider, ngDialogProvider, $controllerProvider, hammerDefaultOptsProvider) => {
+ app.controllerProvider = $controllerProvider;
+ $urlRouterProvider.otherwise('/error404');
+ $locationProvider.html5Mode(true);
+
+ //initialize get if not there
+ if (!$httpProvider.defaults.headers.get) {
+ $httpProvider.defaults.headers.get = {};
+ }
+ var myHostName;
+ myHostName = location.host;
+
+
+ //withCredentials flag on the XHR object - add cookie to XHR requests
+ if (!(myHostName.includes("localhost"))) { // Don't load this for Mock
+ $httpProvider.defaults.withCredentials = true;
+ }
+
+ $httpProvider.defaults.useXDomain = true;
+ $httpProvider.defaults.timeout = 30000;
+
+ //default configuration for ngDialog modal
+ ngDialogProvider.setDefaults({
+ className: 'ngdialog-theme-default',
+ showClose: true,
+ closeByDocument: false,
+ closeByEscape: false
+ });
+
+ //hammer defaults for gestures
+ hammerDefaultOptsProvider.set({
+ recognizers: [[Hammer.Pan, 'enabled']]
+ });
+
+ if (!(myHostName.includes("localhost"))) {
+ $httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
+ $httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
+ $httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
+ }
+
+
+ //interceptor here:
+ var interceptor = function ($q, $injector, $log) {
+ return {
+ 'responseError': function (rejection) {
+ $log.error('Interceptor rejection: ' + JSON.stringify(rejection));
+ var $state = $injector.get('$state');
+ switch (rejection.status) {
+ case 401:
+ var globalLoginUrl = rejection.headers()['global-login-url'];
+ if (globalLoginUrl) {
+ window.location = globalLoginUrl +
+ (globalLoginUrl.indexOf('?') === -1 ? '?' : '') +
+ '&retUrl=' + encodeURI(window.location);
+ return;
+ }
+ break;
+ default:
+ //handle internal server error
+ }
+ return $q.reject(rejection);
+ }
+ };
+ };
+ $httpProvider.interceptors.push(interceptor);
+
+ }).run(($http, CacheFactory) => {
+ //default configuration for cache factory
+ $http.defaults.cache = CacheFactory('defaultCache', {
+ maxAge: 15 * 60 * 1000, // Items added to this cache expire after 15 minutes
+ cacheFlushInterval: 60 * 60 * 1000, // This cache will clear itself every hour
+ deleteOnExpire: 'aggressive' // Items will be deleted from this cache when they expire
+ });
+ });
+
+
+angular.module( 'ecompApp' ).config( [
+'$compileProvider',
+function( $compileProvider )
+{
+ $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension):/);
+}
+]); \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/app.less b/ecomp-portal-FE-os/client/src/app.less
new file mode 100644
index 00000000..1c99e063
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/app.less
@@ -0,0 +1,82 @@
+// injector
+@import 'directives/left-menu/left-menu.less';
+@import 'directives/multiple-select/multiple-select.less';
+@import 'directives/right-menu/right-menu.less';
+@import 'directives/search-users/search-users.less';
+@import 'styles/att-abs.less';
+@import 'styles/buttons.less';
+@import 'styles/ecomp-general.less';
+@import 'styles/fonts.less';
+@import 'styles/form.less';
+@import 'styles/mixins.less';
+@import 'styles/ng-dialog.less';
+@import 'styles/reset.less';
+@import 'styles/select2.less';
+@import 'styles/spinner.less';
+@import 'styles/sprites.less';
+@import 'styles/variables.less';
+@import 'views/admins/add-admin-dialogs/new-admin.modal.less';
+@import 'views/admins/admins.less';
+@import 'views/applications/application-details-dialog/application-details.modal.less';
+@import 'views/applications/applications.less';
+@import 'views/catalog/catalog.less';
+@import 'views/confirmation-box/confirmation-box.less';
+@import 'views/dashboard/dashboard-widget.controller.less';
+@import 'views/dashboard/dashboard.less';
+@import 'views/errors/error.less';
+@import 'views/footer/footer.less';
+@import 'views/functionalMenu/functionalMenu-dialog/modal-details.modal.less';
+@import 'views/functionalMenu/functionalMenu.less';
+@import 'views/header/header.less';
+@import 'views/home/applications-home/applications-home.less';
+@import 'views/notification-history/notificationhistory.less';
+@import 'views/portal-admin/new-portal-admin/new-portal-admin.modal.less';
+@import 'views/portal-admin/portal-admins.less';
+@import 'views/search/search.less';
+@import 'views/sidebar/sidebar.less';
+@import 'views/support/contact-us/contact-us-manage/contact-us-manage.controller.less';
+@import 'views/support/contact-us/contact-us.less';
+@import 'views/support/get-access/get-access.less';
+@import 'views/tabs/tabs.less';
+@import 'views/user-notifications-admin/user.notifications.less';
+@import 'views/user-notifications-admin/user.notifications.modal.less';
+@import 'views/userbar/userbar.less';
+@import 'views/users/new-user-dialogs/bulk-user.modal.less';
+@import 'views/users/new-user-dialogs/new-user.modal.less';
+@import 'views/users/users.less';
+@import 'views/widget-catalog/widget-catalog.less';
+@import 'views/widget-onboarding/widget-details-dialog/widget-details.modal.less';
+@import 'views/widget-onboarding/widget-onboarding.less';
+@import 'views/widgets/widget-details-dialog/widget-details.modal.less';
+@import 'views/widgets/widgets.less';
+// endinjector
+
+html { height: 100%; overflow-y: hidden;}
+* { box-sizing: border-box; }
+body {
+ font: normal 12px Omnes-ECOMP-W02, Arial;
+}
+
+/* Responsive: Portrait tablets and up */
+@media screen and (min-width: 768px) {
+ .container {
+ //max-width: 730px;
+ }
+}
+#page-content{
+ padding-left:230px;
+}
+
+#input-user-search{
+ width:280px;
+}
+
+.tab-cross-sign-opensrc{
+ top:-30px !important;
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+ top:-70px;
+ }
+}
+.checkbox-field-openSRC{
+ height: 15px !important;
+ }
diff --git a/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.directive.js b/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.directive.js
new file mode 100644
index 00000000..92207dfa
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.directive.js
@@ -0,0 +1,111 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 1/28/16.
+ */
+(function () {
+ class RightMenu {
+ constructor($rootScope,$window,$timeout) {
+ this.templateUrl = 'app/directives/right-menu/right-menu.tpl.html';
+ this.restrict = 'AE';
+ this.$rootScope = $rootScope;
+ this.$window = $window;
+ this.$timeout=$timeout;
+ this.link = this._link.bind(this);
+ this.scope = {
+ userList :'='
+ }
+ }
+
+ _link(scope) {
+ let init = () => {
+ scope.isOpen = true;
+ scope.rightSideToggleBtn = 'Collapse';
+
+ scope.openInNewTab = (url) => {
+ if(url == "self") {
+ alert("Cannot chat with self!");
+ } else {
+ var win = window.open(url, '_blank');
+ setCookie(url.split("chat_id=")[1], 'source', 1);
+ //window.localStorage.setItem(url.split("chat_id=")[1],'source');
+ win.focus();
+ }
+ };
+
+ };
+
+ init();
+
+
+ scope.toggleSidebar = () => {
+ scope.isOpen = !scope.isOpen;
+ if(scope.isOpen){
+ scope.rightSideToggleBtn = 'Collapse';
+ }else{
+ scope.rightSideToggleBtn = 'Expand';
+ }
+ };
+
+ scope.scrollbarWidth =function(){
+ var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
+ widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
+ $outer.remove();
+ return 100 - widthWithScroll;
+ };
+
+ scope.calculateUserbarOffset = function(){
+ var userbarWid = $("#online-userbar").width()==0?75:$("#online-userbar").width();
+ var scrollbarWid = scope.scrollbarWidth();
+ var userbarOffset = scrollbarWid+userbarWid-1;
+ /*Setting the offset for userbar*/
+ $("#online-userbar").css("left",-userbarOffset);
+ /*Setting the offset for user bar toggle button*/
+ $(".ecomp-right-sidebar-toggle-btn").css("right",scrollbarWid);
+ }
+ this.$timeout(scope.calculateUserbarOffset, 0);
+
+ scope.isBrowserInternetExplorer = false;
+ scope.browserName = bowser.name;
+
+ if (bowser.msie || bowser.msedge) {
+ scope.isBrowserInternetExplorer = true;
+ } else {
+ scope.isBrowserInternetExplorer = false;
+ }
+
+ scope.calculateUserBarHeight = () => {
+ var footerOff = $('#online-userbar').offset().top;
+ var headOff = $('#footer').offset().top;
+ var userbarHeight= parseInt($(".online-user-container").css('height'),10);
+ var defaultOffSet = 45;
+ // console.log(headOff - footerOff-defaultOffSet);
+ $(".online-user-container").css({
+ "height" : headOff - footerOff-defaultOffSet
+ });
+ };
+ angular.element(this.$window).bind('resize', function(){
+ scope.calculateUserBarHeight();
+ });
+ }
+
+ }
+ angular.module('ecompApp').directive('rightMenu', ($rootScope,$window,$timeout) => new RightMenu($rootScope,$window,$timeout));
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.less b/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.less
new file mode 100644
index 00000000..102ac228
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.less
@@ -0,0 +1,182 @@
+/**
+ * Created by nnaffar on 1/28/16.
+ */
+@sidebar-width: 200px;
+@transition-duration: 0.25s;
+@overlayer-opacity: 0.65;
+
+
+.close-button {
+ //width: @sidebar-width;
+ .blackText24m;
+ //.bg_u;
+ font-size: 3em;
+ line-height: 18px;
+ position: absolute;
+ cursor: pointer;
+ vertical-align: middle;
+ top: @second-level-top;
+ left: 0;
+ -webkit-font-smoothing: antialiased;
+ height: 53px;
+ z-index: 101;
+ //box-shadow: 0 4px 5px rgba(0, 0, 0, .2);
+
+}
+.ecomp-right-sidebar-container{
+ position: absolute;
+ display: block;
+ left: 0;
+ z-index: 100;
+ transition: left @transition-duration;
+ margin-top: -15px;
+
+ .ecomp-right-sidebar-main {
+ //background-color: ;
+ position: absolute;
+ margin-top: 115px;
+
+ height: 100vh;
+ .bg_portalWhite;//white for 1702
+ box-shadow: 0 4px 5px rgba(0, 0, 0, .2);
+ left: -90px;
+ width:75px;
+ padding:0px;
+ height: 100vh;
+
+
+
+ .accordion-container{
+ margin-top: 45px;
+ }
+ .portal-accordion-font{
+ font-size: .875rem;
+ color: #666;
+ display: inline-block;
+ font-family: Arial;
+
+ }
+
+ .portal-accordion-active{
+ color: #199DDF !important;
+ }
+
+ .sub-item{
+ .portal-accordion-font;
+ cursor: pointer;
+ height: 37px;
+ line-height: 37px;
+ padding-left: 20px;
+ padding-bottom: 10px;
+ vertical-align: middle;
+ width: 100%;
+ }
+ .sub-item:hover{
+ .portal-accordion-active;
+ }
+
+ .parent-item{
+ .portal-accordion-font;
+ border-bottom: 1px solid #bbb;
+ cursor: pointer;
+ height: 37px;
+ line-height: 37px;
+ padding-bottom: 10px;
+ vertical-align: middle;
+ width: 100%;
+ }
+ .parent-item:hover{
+ .portal-accordion-active;
+ }
+
+ }
+}
+
+.open-userbar {
+ left: 0;
+ transition: left .25s ease-in-out;
+ -moz-transition: left .25s ease-in-out;
+ -webkit-transition: left .25s ease-in-out;
+}
+
+.close-userbar {
+ left: 91px;
+ transition: left .25s ease-in-out;
+ -moz-transition: left .25s ease-in-out;
+ -webkit-transition: left .25s ease-in-out;
+}
+
+.content-overlayed {
+ position: fixed;
+ top: 110px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background: none repeat scroll 0 0 rgb(242, 242, 242);
+ z-index: 9999;
+}
+.fade-animation{
+ opacity: @overlayer-opacity;
+ transition: opacity @transition-duration ease-in-out;
+}
+.fade-animation.ng-hide {
+ opacity:0;
+ transition: opacity @transition-duration ease-in-out;
+}
+
+.activeUserIcon {
+ transition: all .2s ease-in-out;
+ display: block;
+ margin-left: auto; margin-right: auto; height:55px; width:55px; border-radius: 50%;
+
+}
+.activeUserIcon:hover { transform: scale(1.5); }
+
+.ecomp-right-sidebar-toggle{
+ position: absolute;
+ top: 400px;
+ right: 35px;
+}
+.ecomp-right-sidebar-toggle-btn{
+ position:relative;
+}
+.open-userbar-toggle {
+ // right: 0;
+ transition: right .25s ease-in-out;
+ -moz-transition: right .25s ease-in-out;
+ -webkit-transition: right .25s ease-in-out;
+}
+
+.close-userbar-toggle {
+ right: -36px;
+ transition: right .25s ease-in-out;
+ -moz-transition: right .25s ease-in-out;
+ -webkit-transition: right .25s ease-in-out;
+}
+
+.ecomp-right-sidebar-title{
+ font-family: Arial;
+ font-size: 14px;
+ color: #ef6f00;
+ margin-bottom:20px;
+ text-align: center;
+}
+
+.ecomp-right-sidebar-toggle a{
+ transform: rotate(-90deg);
+ z-index:2
+}
+.ecomp-right-sidebar-toggle button{
+ transform: rotate(-90deg);
+ z-index:2;
+ width: 100px!important;
+ height: 30px !important;
+ padding-bottom: 26px !important;
+ }
+ .btn:last-child {
+ margin-right: 6px !important;
+}
+
+.right-menu-button{
+ font-family: Omnes-ECOMP-W02,Arial
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.tpl.html b/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.tpl.html
new file mode 100644
index 00000000..d779cae5
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/directives/right-menu/right-menu.tpl.html
@@ -0,0 +1,42 @@
+<!--
+ ================================================================================
+ eCOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div id="ecomp-right-sidebar-container" class="ecomp-sidebar-container" ng-class="isOpen ? 'open-sidebar': 'close-sidebar'">
+ <div id="online-userbar" class="ecomp-sidebar-main" style="right: 0px; width:75px; padding:0px;" >
+ <div class="online-user-container" id="online-user-container" style="margin-top:45px; overflow-y:auto; overflow-x:hidden">
+ <div class="ecomp-right-sidebar-title">Online Users</div>
+ <div ng-repeat="user in userList">
+ <div class="child-row" style="height:85px;">
+ <div>
+ <a href="javascript:void(0)" ng-click="openInNewTab(user.linkQ)" ><img class="activeUserIcon" ng-src="assets/images/photo.png" alt="User Link" ></a>
+ <div style="font-family: Arial; font-size:10px; text-align: center;" >{{user.userId}}</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div id="user-sidebar-toggle" class="ecomp-right-sidebar-toggle" ng-class="isOpen ? 'open-userbar-toggle': 'close-userbar-toggle'">
+ <div id="user-sidebar-toggle-button" class="ecomp-right-sidebar-toggle-btn">
+ <button href="javascript:void(0)" ng-click="toggleSidebar()" class="btn btn-alt btn-small">
+ <span id="user-chevron-down" class="icon-chevron-down" ng-show="isOpen"></span>
+ <span id="user-chevron-up" class="icon-chevron-up" ng-hide="isOpen"><span class="right-menu-button">Users</span></span>
+ </button>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/directives/search-users/search-users.controller.js b/ecomp-portal-FE-os/client/src/directives/search-users/search-users.controller.js
new file mode 100644
index 00000000..e6e7661b
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/directives/search-users/search-users.controller.js
@@ -0,0 +1,168 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+
+'use strict';
+(function () {
+ class SearchUsersCtrl {
+
+ constructor($log, usersService,adminsService, $scope,confirmBoxService) {
+
+ $scope.UserSearchsIsNull=false;
+ $scope.userExist = false;
+ this.scrollApi = {};//scrollTop directive
+ $scope.txtResults = 'result';
+
+ this.showAddUser = false;
+ this.showSearch = true;
+ this.newUser ={
+ firstName:'',
+ lastName:'',
+ emailAddress:'',
+
+ middleName:'',
+ loginId:'',
+ loginPwd:'',
+ loginPwdCheck:''
+ };
+
+ let activeRequests = [];
+ let clearReq = (req) => {
+ activeRequests.splice(activeRequests.indexOf(req), 1);
+ };
+
+ this.showAddUserSection = () => {
+ this.showAddUser = true;
+ this.showSearch = false;
+ }
+
+ this.addNewUserFun = () => {
+ if (this.newUser.loginId =='' || this.newUser.loginPwd == '' || this.newUser.firstName == '' || this.newUser.lastName =='' || this.newUser.emailAddress ==''||this.newUser.loginPwd ==''){
+ var warningMsg = "Please enter a value for all fields marked with *.";
+ confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;});
+ return;
+ } else if (this.newUser.loginPwd != this.newUser.loginPwdCheck) {
+ var warningMsg = "Passwords do not match, please try again.";
+ confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;});
+ return;
+ }
+ else {
+ // check password length complexity.
+ var warningMsg = adminsService.isComplexPassword(this.newUser.loginPwd);
+ if (warningMsg != null) {
+ confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;});
+ return;
+ }
+ } // password
+
+ adminsService.addNewUser(this.newUser,'Yes').then(res=> {
+
+ if(res.message == 'Record already exist'){
+
+ this.showAddUser = true;
+ this.showSearch = false;
+ $scope.userExist = true;
+
+ }else{
+
+ $scope.userExist = false;
+ this.selectedUser = this.newUser;
+ this.selectedUser.orgUserId = this.newUser.loginId;
+ this.searchUsersResults = [];
+ this.searchUsersResults.push(this.newUser);
+ this.showAddUser = false;
+ this.showSearch = true;
+ this.newUser ={
+ firstName:'',
+ lastName:'',
+ emailAdress:'',
+ middleName:'',
+ loginId:'',
+ loginPwd:'',
+ loginPwdCheck:''
+ };
+ this.searchUserString ='';
+ $scope.UserSearchsIsNull = false;
+ }
+
+
+ }).catch(err=> {
+ $log.error('adminsService: addNewUser error:: ', err);
+ // $scope.errMsg=err;
+ confirmBoxService.showInformation('Add New User failed: ' + err);
+
+ }).finally(() => {
+ //this.isLoadingTable = false;
+
+ });
+ }
+
+ this.searchUsers = () => {
+ this.isLoading = true;
+ if(this.searchUsersInProgress){
+ return;
+ }
+ this.selectedUser = null;
+ this.searchUsersInProgress = true;
+ this.searchUsersResults = null;
+
+ let searchUsersReq = usersService.searchUsers(this.searchUserString);
+ activeRequests.push(searchUsersReq);
+ searchUsersReq.promise().then(usersList => {
+ $log.debug('searchUsers found the following users: ', JSON.stringify(usersList));
+ this.searchUsersResults = usersList;
+ $log.debug('searchUsersResults length: ', usersList.length);
+ if (usersList.length != 1) {
+ $scope.txtResults = 'results'
+ } else {
+ $scope.txtResults = 'result'
+ }
+ $scope.UserSearchsIsNull=false;
+ }).catch(err => {
+ $log.error('SearchUsersCtrl.searchUsers: ' + err);
+ $scope.UserSearchsIsNull=true;
+ }).finally(() => {
+ this.scrollApi.scrollTop();
+ this.searchUsersInProgress = false;
+ clearReq(searchUsersReq);
+ this.isLoading = false;
+ });
+ };
+
+ let init = () => {
+ this.isLoading = false;
+ this.searchUsersInProgress = false;
+ };
+
+ this.setSelectedUser = user => {
+ this.selectedUser = user;
+ };
+
+ init();
+
+ $scope.$on('$destroy', () => {
+ activeRequests.forEach(req => {
+ req.cancel();
+ });
+ });
+ }
+ }
+ SearchUsersCtrl.$inject = ['$log', 'usersService','adminsService', '$scope','confirmBoxService'];
+ angular.module('ecompApp').controller('SearchUsersCtrl', SearchUsersCtrl);
+})();
diff --git a/ecomp-portal-FE-os/client/src/directives/search-users/search-users.less b/ecomp-portal-FE-os/client/src/directives/search-users/search-users.less
new file mode 100644
index 00000000..2fb97867
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/directives/search-users/search-users.less
@@ -0,0 +1,159 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+ .search-users-directive{
+ .title {
+ //.n18r;
+ .dGray18r; //AT&T Dark Gray
+ border-bottom: @blue-active 3px solid;
+
+ }
+
+ .add-user-section{
+ margin-top:0px;
+ margin-left:10px;
+ }
+
+ .input-text-new-user {
+ font-style: italic;
+ padding: 7px 10px;
+ width: 200px !important;
+ display: inline-block;
+ position: relative;
+ margin-bottom: 10px;
+ border-radius: 6px;
+ border: 1px solid #d8d8d8;
+ height: 32px;
+ border-color: slategrey !important;
+ }
+
+ .input-new-user-div{
+ display:inline-block;
+ width: 240px;
+ margin-left:10px;
+ }
+
+ .add-user-button {
+ cursor: pointer;
+ float:right;
+ width: 120px;
+
+ margin-left : 5px;
+
+ .btn-blue;
+
+ line-height: 32px;
+ height: 29px;
+ text-align: center;
+ vertical-align: middle;
+ line-height: 28px;
+
+ &::before {
+ .ico_add_user;
+ content: '\f211';
+ font-family: "Ionicons";
+ vertical-align: middle;
+ display: inline-block;
+ margin-right: 8px;
+ line-height: 16px;
+ }
+ }
+
+ .main {
+ margin: 16px;
+ .search-instructions {
+ .dGray14r;
+ margin-bottom: 8px;
+ }
+ .search {
+ .input-field {
+ display: inline-block;
+ width: 250px;
+ height: 30px;
+ }
+ .search-button {
+ .btn-blue;
+ width: 90px;
+ display: inline-block;
+ float: right;
+ }
+ }
+
+ .search-results {
+ margin-top: 10px;
+ .results-title {
+ .dGray14r;
+ margin-bottom: 8px;
+ }
+
+ .results-container {
+ height: 208px;
+ overflow-y: auto;
+ .user {
+ padding-left: 10px;
+ padding-top: 6px;
+ height: 48px;
+
+ border-top: 1px solid @portalGray;
+ border-right: 1px solid @portalGray;
+ border-left: 1px solid @portalGray;
+
+ &:last-child {
+ border-bottom: 1px solid @portalGray;
+ }
+
+ cursor: pointer;
+ &:hover {
+ background-color: @funcBkgGray;
+ }
+
+ &.selected {
+ background-color: @funcBkgGray;
+ }
+
+ .main-name {
+ .dGray14r;
+
+ }
+ .sub-job-title {
+ .gray13r;
+
+ }
+ }
+
+ }
+
+ }
+ .error-text {
+ margin-top: 25px;
+ font-weight: 400;
+ font-size: 16px;
+ text-align: left;
+ color: @err;
+ }
+
+ .no-user-found {
+ margin-top: 16px;
+ .dGray14r;
+ }
+ .add-new-user-btn{
+ margin-top:10px;
+ }
+ }
+} \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/directives/search-users/search-users.tpl.html b/ecomp-portal-FE-os/client/src/directives/search-users/search-users.tpl.html
new file mode 100644
index 00000000..ef659920
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/directives/search-users/search-users.tpl.html
@@ -0,0 +1,143 @@
+<!--
+ ================================================================================
+ eCOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="search-users-directive">
+ <form name="searchUsersForm" novalidate>
+ <div class="title" ng-bind="searchUsers.searchTitle"></div>
+ <div class="main">
+ <div ng-if="searchUsers.showSearch">
+ <div class="search-instructions">Enter first name, last name or User ID</div>
+ <div class="search">
+ <input id="input-user-search"
+ autocomplete="off"
+ type="text"
+ name="searchString"
+ placeholder="Search"
+ ng-model="searchUsers.searchUserString"
+ ng-keyup="$event.keyCode===13 && searchUsersForm.searchString.$valid && searchUsers.searchUsers()"
+ pattern="[a-zA-Z0-9-'\s]{1,}"
+ maxlength="80"
+ required="true" auto-focus tabindex="0"/>
+
+ <button class="btn btn-alt btn-small" tabindex="2" id="button-search-users"
+ ng-class="{disabled: searchUsersForm.searchString.$invalid || searchUsers.searchUsersInProgress}"
+ ng-click="searchUsersForm.searchString.$valid && searchUsers.searchUsers()">Search
+ </button>
+
+ <button class="btn btn-alt btn-small" id="Create-New-User-button" ng-click="searchUsers.showAddUserSection()">
+ <i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;New User
+ </button>
+
+ </div>
+
+ <span class="ecomp-spinner" ng-show="searchUsers.isLoading"></span>
+ <div ng-show="UserSearchsIsNull===false">
+ <div class="search-results"
+ ng-show="!searchUsers.isLoading
+ && searchUsers.searchUsersResults
+ && searchUsers.searchUsersResults.length">
+ <div id="search-results" ng-show="UserSearchsIsNull===false"
+ class="results-title"
+ ng-bind="'Showing ' + searchUsers.searchUsersResults.length + ' results'"></div>
+ <div class="results-container" scroll-top="searchUsers.scrollApi">
+ <div id="search-result-{{$index}}"
+ class="user"
+ ng-repeat="user in (searchUsers.searchUsersResults | orderBy:['firstName','lastName','orgUserId']) | limitTo: 100 track by $index"
+ ng-click="searchUsers.setSelectedUser(user)"
+ ng-class="{selected: user.orgUserId === searchUsers.selectedUser.orgUserId}">
+ <div id="main-name-{{$index}}" class="main-name">
+ <span id="result-first-name-{{$index}}" ng-bind="::user.firstName"></span>
+ <span id="result-last-name-{{$index}}" ng-bind="::user.lastName"></span>
+ <span id="result-uuid-{{$index}}" ng-bind="::user.orgUserId"></span></div>
+ <div id="job-title-{{$index}}" class="sub-job-title" ng-bind="::user.jobTitle"></div>
+ <br/>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="error-text"
+ id="user-search-error-403"
+ ng-show="UserSearchsIsNull===true">
+ No match found.
+ </div>
+
+ <div id="no-user-found"
+ class="no-user-found"
+ ng-show="searchUsers.searchUsersResults && searchUsers.searchUsersResults.length===0 && !searchUsers.searchUsersInProgress">
+ No users found
+ </div>
+ </div>
+
+
+ <div id="addWidget" class="add-user-section" ng-if="searchUsers.showAddUser">
+ <div>
+ <div class="input-new-user-div" >
+ <div class="">*First Name</div>
+ <input type="text" ng-model="searchUsers.newUser.firstName"/>
+ </div>
+ <div class="input-new-user-div" >
+ <div class="">Middle Name</div>
+ <input type="text" ng-model="searchUsers.newUser.middleName"/>
+ </div>
+ <div class="input-new-user-div" >
+ <div class="">*Last Name</div>
+ <input type="text" ng-model="searchUsers.newUser.lastName"/>
+ </div>
+ <div class="input-new-user-div" >
+ <div class="">*Email Address ID</div>
+ <input type="text" ng-model="searchUsers.newUser.emailAddress"/>
+ </div>
+ <div class="input-new-user-div" >
+ <div class="">*Login ID</div>
+ <input type="text" ng-model="searchUsers.newUser.loginId"/>
+ </div>
+ <div class="input-new-user-div" >
+ <div class="">*Login Password</div>
+ <input type="password" ng-model="searchUsers.newUser.loginPwd"/>
+ </div>
+ <div class="input-new-user-div" >
+ <div class="">*Confirm Login Password</div>
+ <input type="password" ng-model="searchUsers.newUser.loginPwdCheck"/>
+ </div>
+ <div ng-show="searchUsers.newUser.loginPwdCheck.length>=searchUsers.newUser.loginPwd.length&&searchUsers.newUser.loginPwdCheck.length>0&&searchUsers.newUser.loginPwd!=searchUsers.newUser.loginPwdCheck"
+ style="color: #cf2a2a; font-size: 12px;">
+ <small
+ style="position: absolute; margin-top: -6px;">The passwords do not match. Try again.
+ </small>
+ </div>
+ <div ng-show="userExist==true"
+ style="color: #cf2a2a; font-size: 12px;">
+ <small
+ style="position: absolute; margin-top: -6px;">User with same loginId already exists. Try again.
+ </small>
+ </div>
+ </div>
+ <div class="add-new-user-btn">
+ <button class="btn btn-alt btn-small" ng-click="searchUsers.addNewUserFun()">Add New User</button>
+ </div>
+ </div>
+
+
+ </div>
+
+ </div>
+
+ </form>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/router.js b/ecomp-portal-FE-os/client/src/router.js
new file mode 100644
index 00000000..88afa39b
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/router.js
@@ -0,0 +1,238 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+
+angular.module('ecompApp')
+ .config($stateProvider => {
+ $stateProvider
+ .state('root', {
+ abstract: true,
+ views: {
+ 'header@': {
+ templateUrl: 'app/views/header/header.tpl.html',
+ controller: 'HeaderCtrl',
+ controllerAs: 'header'
+ },
+ 'tabbar@': {
+ templateUrl: 'app/views/tabs/tabs.tpl.html',
+ controller: 'TabsCtrl',
+ controllerAs: 'tabsHome'
+ },
+ 'sidebar@':{
+ templateUrl: 'app/views/sidebar/sidebar.tpl.html',
+ controller: 'SidebarCtrl',
+ controllerAs: 'sidebar'
+ },
+ 'userbar@':{
+ templateUrl: 'app/views/userbar/userbar.tpl.html',
+ controller: 'UserbarCtrl',
+ controllerAs: 'userbar'
+ },
+ 'footer@': {
+ templateUrl: 'app/views/footer/footer.tpl.html',
+ controller: 'FooterCtrl',
+ controllerAs: 'footer'
+ }
+ }
+ }).state('root.applicationsHome', {
+ url: '/applicationsHome',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/dashboard/dashboard.tpl.html',
+ controller: 'DashboardCtrl',
+ controllerAs: 'dashboard'
+ }
+ }
+ }).state('root.appCatalog', {
+ url: '/appCatalog',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/catalog/catalog.tpl.html',
+ controller: 'CatalogCtrl',
+ controllerAs: 'catalog'
+ }
+ }
+ }).state('root.widgetCatalog', {
+ url: '/widgetCatalog',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/widget-catalog/widget-catalog.tpl.html',
+ controller: 'WidgetCatalogCtrl',
+ controllerAs: 'widgetCatalog'
+ }
+ }
+ }).state('root.admins', {
+ url: '/admins',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/admins/admins.tpl.html',
+ controller: 'AdminsCtrl',
+ controllerAs: 'admins'
+ }
+ }
+ }).state('root.roles', {
+ url: '/roles',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/role/role_list.html',
+ controller: 'roleListController',
+ controllerAs: 'roles'
+ }
+ }
+ }).state('root.role', {
+ url: '/role/:roleId',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/role/role.html',
+ controller: 'roleController',
+ controllerAs: 'role'
+ }
+ }
+ }).state('root.roleFunctions', {
+ url: '/roleFunctions',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/role/role_function_list.html',
+ controller: 'roleFunctionListController',
+ controllerAs: 'roleFunctions'
+ }
+ }
+ }).state('root.users', {
+ url: '/users',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/users/users.tpl.html',
+ controller: 'UsersCtrl',
+ controllerAs: 'users'
+ }
+ }
+ }).state('root.applications', {
+ url: '/applications',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/applications/applications.tpl.html',
+ controller: 'ApplicationsCtrl',
+ controllerAs: 'apps'
+ }
+ }
+ }).state('root.microserviceOnboarding', {
+ url: '/microserviceOnboarding',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/microservice-onboarding/microservice-onboarding.tpl.html',
+ controller: 'MicroserviceOnboardingCtrl',
+ controllerAs: 'microserviceOnboarding'
+ }
+ }
+ }).state('root.widgetOnboarding', {
+ url: '/widgetOnboarding',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/widget-onboarding/widget-onboarding.tpl.html',
+ controller: 'WidgetOnboardingCtrl',
+ controllerAs: 'widgetOnboarding'
+ }
+ }
+ }).state('root.functionalMenu', {
+ url: '/functionalMenu',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/functionalMenu/functionalMenu.tpl.html',
+ controller: 'FunctionalMenuCtrl',
+ controllerAs: 'functionalMenu'
+ }
+ }
+ }).state('root.getAccess', {
+ url: '/getAccess',
+ params: {
+ appName: null,
+ },
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/support/get-access/get-access.tpl.html',
+ controller: 'GetAccessCtrl',
+ controllerAs: 'access'
+ }
+ }
+ }).state('root.contactUs', {
+ url: '/contactUs',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/support/contact-us/contact-us.tpl.html',
+ controller: 'ContactUsCtrl',
+ controllerAs: 'contact'
+ }
+ }
+ }).state('root.userNotifications', {
+ url: '/userNotifications',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/user-notifications-admin/user.notifications.tpl.html',
+ controller: 'userNotificationsCtrl',
+ controllerAs: 'userNotifications'
+ }
+ }
+ }).state('root.notificationHistory', {
+ url: '/notificationHistory',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/notification-history/notificationhistory.tpl.html',
+ controller: 'notificationHistoryCtrl',
+ controllerAs: 'notificationHistory'
+ }
+ }
+ }).state('root.portalAdmins', {
+ url: '/portalAdmins',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/portal-admin/portal-admin.tpl.html',
+ controller: 'PortalAdminsCtrl',
+ controllerAs: 'portalAdmin'
+ }
+ }
+ }).state('root.error404', {
+ url: '/error404',
+ views: {
+ 'content@': {
+ templateUrl: 'app/views/errors/error.404.tpl.html',
+ controller: 'ErrorCtrl',
+ controllerAs: 'error'
+ }
+ }
+ }).state('noUserError', {
+ url: '/noUserError',
+ views: {
+ 'error@': {
+ templateUrl: 'app/views/errors/error.tpl.html',
+ controller: 'ErrorCtrl',
+ controllerAs: 'error'
+ }
+ }
+ }).state('unKnownError', {
+ url: '/unKnownError',
+ views: {
+ 'error@': {
+ templateUrl: 'app/views/errors/error.tpl.html',
+ controller: 'ErrorCtrl',
+ controllerAs: 'error'
+ }
+ }
+ });
+ });
diff --git a/ecomp-portal-FE-os/client/src/services/support/session/session.service.js b/ecomp-portal-FE-os/client/src/services/support/session/session.service.js
new file mode 100644
index 00000000..eca3719e
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/services/support/session/session.service.js
@@ -0,0 +1,76 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+
+(function () {
+ class SessionService {
+ constructor($q, $log, $http, conf,uuid,$sce) {
+ this.$q = $q;
+ this.$log = $log;
+ this.$http = $http;
+ this.conf = conf;
+ this.uuid = uuid;
+ this.$sce = $sce;
+ }
+
+ logout(appStr) {
+ this.$log.info('SessionService::logout from App');
+ let deferred = this.$q.defer();
+ this.$log.info('SessionService appStr: ', appStr);
+
+ var eaccessPattern = '\https?\:\/\/[^/]+/[^/]+/[^/]+';
+ var standardPattern = '\https?\:\/\/[^/]+/[^/]+';
+
+
+ var contextUrl = appStr.match(new RegExp(standardPattern));
+ var logoutUrl = contextUrl + '/logout.htm' ;
+ this.$sce.trustAsResourceUrl(logoutUrl);
+ console.log('logoutUrl ' + logoutUrl);
+ //window.open(logoutUrl, '_blank','width=100,height=100');
+ jQuery('#reg-logout-div').append("<iframe style='display:none' src='" + logoutUrl + "' />");
+
+
+ /*
+ this.$http({
+ method: "POST",
+ url: logoutUrl,
+ cache: false
+ }).then( res => {
+ // If response comes back as a redirected HTML page which IS NOT a success
+ this.$log.info(res);
+ if (Object.keys(res).length === 0) {
+ deferred.reject("ecomp::session-service::logout Failed");
+ } else {
+ this.$log.debug('query results: ', res);
+ this.$log.info('ecomp::session-service::logout Succeeded');
+ deferred.resolve(res);
+ }
+ }).catch( status => {
+ this.$log.error('query error: ',status);
+ deferred.reject(status);
+ });
+ return deferred.promise;
+ */
+ }
+
+ }
+ SessionService.$inject = ['$q', '$log', '$http', 'conf','uuid4','$sce'];
+ angular.module('ecompApp').service('sessionService', SessionService)
+})();
diff --git a/ecomp-portal-FE-os/client/src/services/userProfile/userProfile.service.js b/ecomp-portal-FE-os/client/src/services/userProfile/userProfile.service.js
new file mode 100644
index 00000000..dc0022ac
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/services/userProfile/userProfile.service.js
@@ -0,0 +1,202 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+
+(function () {
+ class UserProfileService {
+
+ constructor($q, $log, $http, conf, uuid, utilsService) {
+ this.$q = $q;
+ this.$log = $log;
+ this.$http = $http;
+ this.conf = conf;
+ this.uuid = uuid;
+ this.utilsService = utilsService;
+ this.userProfile = null;
+ this.debug = false;
+ }
+
+ getUserProfile() {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getUserProfile: get logged user profile');
+ let deferred = this.$q.defer();
+ let url = this.conf.api.userProfile;
+ this.$http({
+ method: "GET",
+ url: url,
+ cache: false,
+ headers: {
+ 'X-ECOMP-RequestID':this.uuid.generate()
+ }
+ })
+ .then( res => {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getUserProfile: result is ' + JSON.stringify(res));
+ // Res is always JSON, but the data object might be an HTML error page.
+ if (! this.utilsService.isValidJSON(res.data)) {
+ var msg = 'UserProfileService::getUserProfile: result data is not JSON';
+ if (this.debug)
+ this.$log.debug(msg);
+ deferred.reject(msg);
+ } else {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getUserProfile: success');
+ this.userProfile = res.data;
+ deferred.resolve(res.data);
+ }
+ })
+ .catch( status => {
+ this.$log.error('UserProfileService::getUserProfile caught exception: ' + JSON.stringify(status));
+ deferred.reject(status);
+ });
+ return deferred.promise;
+ }
+
+ getSortedUserIdCombination(user1, user2) {
+
+ var combination = "";
+ if(user1<user2) {
+ combination = user1+"-"+user2;
+ } else if (user1>user2){
+ combination = user2+"-"+user1;
+ } else {
+
+ return "self";
+ }
+
+ var collaborateUrl = 'opencollaboration?chat_id=' + combination ;
+
+ return collaborateUrl;
+
+
+
+ }
+
+ getFunctionalMenuStaticInfo() {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getFunctionalMenuStaticInfo: start');
+ let deferred = this.$q.defer();
+ let url = this.conf.api.functionalMenuStaticInfo;
+ this.$http({
+ method: "GET",
+ url : url,
+ cache: false,
+ headers: {
+ 'X-ECOMP-RequestID':this.uuid.generate()
+ }
+ }).then( res => {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getFunctionalMenuStaticInfo: result is ' + JSON.stringify(res));
+ // Res is always JSON, but the data object might be an HTML error page.
+ if (! this.utilsService.isValidJSON(res.data)) {
+ var msg = 'UserProfileService::getFunctionalMenuStaticInfo: result data is not JSON';
+ if (this.debug)
+ this.$log.debug(msg);
+ deferred.reject(msg);
+ } else {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getFunctionalMenuStaticInfo Succeeded');
+ deferred.resolve(res.data);
+ }
+ }).catch( status => {
+ this.$log.error('UserProfileService::getFunctionalMenuStaticInfo caught exception: ' + JSON.stringify(status));
+ deferred.reject(status);
+ });
+ return deferred.promise;
+ }
+
+ getActiveUser() {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getActiveUser: start');
+ let deferred = this.$q.defer();
+ let url = this.conf.api.getActiveUser;
+ this.$http({
+ method: 'GET',
+ url : url,
+ cache: false,
+ headers: {
+ 'X-ECOMP-RequestID': this.uuid.generate()
+ }
+ }).then( res => {
+ if (this.debug) {
+ this.$log.debug('UserProfileService::getActiveUser: result is ' + JSON.stringify(res));
+ this.$log.debug('UserProfileService::getActiveUser: isValidJSON is ' + this.utilsService.isValidJSON(res.data));
+ }
+ // Res is always JSON, but the data object might be an HTML error page.
+ // res.data should be a list of Org IDs; an empty list is NOT an error.
+ if (! this.utilsService.isValidJSON(res.data)) {
+ var msg = 'UserProfileService::getActiveUser: result data is not JSON';
+ if (this.debug)
+ this.$log.debug(msg);
+ deferred.reject(msg);
+ }
+ else {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getActiveUser: success');
+ deferred.resolve(res.data);
+ }
+ }).catch( status => {
+ this.$log.error('UserProfileService::getActiveUser caught exception: ' + JSON.stringify(status));
+ deferred.reject(status);
+ });
+ return deferred.promise;
+ }
+
+
+ getUserAppRoles(userId) {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getUserAppRoles: get logged user profile');
+ let deferred = this.$q.defer();
+ this.$http({
+ method: "GET",
+ url: this.conf.api.userApplicationRoles,
+ params: {userId:userId},
+ cache: false,
+ headers: {
+ 'X-ECOMP-RequestID':this.uuid.generate()
+ }
+ })
+ .then( res => {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getUserAppRoles: result is ' + JSON.stringify(res));
+ // Res is always JSON, but the data object might be an HTML error page.
+ if (! this.utilsService.isValidJSON(res.data)) {
+ var msg = 'UserProfileService::getUserAppRoles: result data is not JSON';
+ if (this.debug)
+ this.$log.debug(msg);
+ deferred.reject(msg);
+ } else {
+ if (this.debug)
+ this.$log.debug('UserProfileService::getUserAppRoles: success');
+ deferred.resolve(res.data);
+ }
+ })
+ .catch( status => {
+ this.$log.error('UserProfileService::getUserAppRoles caught exception: ' + JSON.stringify(status));
+ deferred.reject(status);
+ });
+
+ return deferred.promise;
+ }
+
+ }
+ UserProfileService.$inject = ['$q', '$log', '$http', 'conf', 'uuid4', 'utilsService'];
+ angular.module('ecompApp').service('userProfileService', UserProfileService)
+})();
diff --git a/ecomp-portal-FE-os/client/src/styles/att-abs.less b/ecomp-portal-FE-os/client/src/styles/att-abs.less
new file mode 100644
index 00000000..3cf10449
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/styles/att-abs.less
@@ -0,0 +1,92 @@
+//portal abs table component
+.c-ecomp-portal-abs-table{
+ width: 100%;
+ height: 600px;
+ overflow-x: auto;
+ overflow-y: auto;
+ background-color: @portalLightGray;
+
+ .tablesorter-default {
+ //border: 1px solid @q;
+
+ th,
+ thead td {
+ .dGray16m;
+ color: #333333;
+ background-color: @portalLightGray;
+ border-collapse: collapse;
+ //border-top-style: none;
+ border: 1px solid @q;
+ padding: 0;
+ font-weight: 700;
+ height: 38px;
+ }
+
+ tbody tr td {
+ .dGray16m;
+ border-left: 1px solid @portalLGray;
+ background-color: @portalWhite;
+ border-bottom: 1px solid @portalLGray;
+ height: 32px;
+ div {
+ line-height: 32px;
+ }
+ }
+
+ .td-first {
+ border-left: none;
+ }
+
+ .tablesorter-search-highlight {
+ font-weight: bold;
+ }
+
+
+ tr:first-child {
+ border-bottom: 1px solid @portalLGray;
+ }
+
+ th:first-child{
+ border-right: 1px solid @portalLGray;
+ }
+ th:nth-child(2){
+ border-right: 1px solid @portalLGray;
+ }
+ th:nth-child(3){
+ border-right: 1px solid @portalLGray;
+ }
+
+ tr:last-child {
+ td {
+ border-bottom-style: none;
+ }
+ }
+
+
+ }
+}
+
+//portal abs select component
+.c-ecomp-portal-abs-select{
+ width: @select-width;
+ display: inline-block;
+ margin-right: 10px;
+ .select2-chosen{
+ width: @select-width;
+ line-height: 20px;
+ }
+ .select2-container .select2-choice {
+ width: @select-width;
+ border-radius: 6px;
+ border-color: @portalLGray;
+ }
+
+ .select2-results {
+ width: @select-width;
+ background-color: @portalWhite;
+ }
+
+ .form-field, .form-field-input-container {
+ width: @select-width;
+ }
+}
diff --git a/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.js b/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.js
new file mode 100644
index 00000000..da7cd4a4
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.js
@@ -0,0 +1,268 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class AppDetailsModalCtrl {
+ constructor($scope, $log, applicationsService, errorMessageByCode,
+ ECOMP_URL_REGEX,userProfileService, $cookies, confirmBoxService) {
+// let emptyImg = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+ // empty image should really be empty, or it causes problems for the back end
+ let emptyImg = null;
+ this.emptyImgForPreview = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+
+ let newAppModel = {
+ 'id': null,
+ 'name': null,
+ 'imageUrl': null,
+ 'description': null,
+ 'notes': null,
+ 'url': null,
+ 'alternateUrl': null,
+ 'restUrl': null,
+ 'isOpen': false,
+ 'username': null,
+ 'appPassword': null,
+ 'thumbnail': emptyImg,
+ 'isEnabled': true,
+ 'restrictedApp': false
+ };
+
+ let init = () => {
+ $log.info('AppDetailsModalCtrl::init');
+ this.isSaving = false;
+ if($scope.ngDialogData && $scope.ngDialogData.app){
+ $log.debug('AppDetailsModalCtrl:init:: Edit app mode for', $scope.ngDialogData.app);
+ this.isEditMode = true;
+ this.app = _.clone($scope.ngDialogData.app);
+ }else{
+ $log.debug('AppDetailsModalCtrl:init:: New app mode');
+ this.isEditMode = false;
+ this.app = _.clone(newAppModel);
+ }
+ this.originalImage = null
+ };
+
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+
+ this.imageApi = {};
+ this.removeImage = () => {
+ $log.debug('AppDetailsModalCtrl:removeImage:: entering removeImage');
+
+ confirmBoxService.confirm("Are you sure you want to remove the image?").then(isConfirmed => {
+ if(isConfirmed){
+ this.imageApi.clearFile();
+ this.app.thumbnail = emptyImg;
+ this.originalImage = null;
+ this.app.imageUrl = null;
+ }
+ }).catch(err => {
+ $log.error('AppDetailsModalCtrl:removeImage error:: ',err);
+ });
+ };
+
+ //This part handles conflict errors (409)
+ this.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){ //support objects
+ err.data = [err.data]
+ }
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ //set conflict message
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ //set field as invalid
+ $scope.appForm[field.name].$setValidity('conflict', false);
+ //set watch once to clear error after user correction
+ watchOnce[field.name]();
+ });
+ });
+ this.scrollApi.scrollTop();
+ };
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ if($scope.appForm[fieldName]){
+ $scope.appForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+
+
+ let emptyCookies = () => {
+ $log.debug('AppDetailsModalCtrl:emptyCookies:: entering emptyCookies');
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ // $log.info(profile);
+ $scope.orgUserId = profile.attuid;
+ $log.debug('AppDetailsModalCtrl:emptyCookies for the following attuid: ' + profile.attuid);
+ if ($cookies.getObject($scope.orgUserId + '_apps') != undefined && $cookies.getObject($scope.orgUserId + '_apps') != null) {
+ $cookies.remove($scope.orgUserId + '_apps');
+ $log.debug('AppDetailsModalCtrl:emptyCookies removed: ' + $scope.orgUserId + '_apps');
+ }
+ if ($cookies.getObject($scope.orgUserId + '_widget') != undefined && $cookies.getObject($scope.orgUserId + '_widget') != null) {
+ $cookies.remove($scope.orgUserId + '_widget');
+ $log.debug('AppDetailsModalCtrl:emptyCookies removed: ' + $scope.orgUserId + '_widget');
+ }
+ }).catch(err => {
+ $log.error('AppDetailsModalCtrl:emptyCookies error:: '+ JSON.stringify(err));
+ });
+ };
+
+
+ let watchOnce = {
+ name: () => {
+ let unregisterName = $scope.$watch('appDetails.app.name', (newVal, oldVal) => {
+ // $log.debug('newVal, oldVal = ' + newVal.toLowerCase() + " | " + oldVal.toLowerCase())
+ if(newVal.toLowerCase() !== oldVal.toLowerCase()){
+ resetConflict('name');
+ unregisterName();
+ }
+ });
+ },
+ url: () => {
+ let unregisterUrl = $scope.$watch('appDetails.app.url', (newVal, oldVal) => {
+ if(newVal.toLowerCase() !== oldVal.toLowerCase()) {
+ resetConflict('url');
+ unregisterUrl();
+ }
+ });
+ }
+ };
+ //***************************
+
+ this.saveChanges = () => {
+ //if valid..
+ if($scope.appForm.$invalid){
+ return;
+ }
+ this.isSaving = true;
+ // For a restricted app, null out all irrelevant fields
+ if (this.app.restrictedApp) {
+ this.app.restUrl = null;
+ this.app.isOpen = true;
+ this.app.username = null;
+ this.app.appPassword = null;
+ this.app.uebTopicName = null;
+ this.app.uebKey = null;
+ this.app.uebSecret = null;
+ }
+ if(this.isEditMode){
+ applicationsService.updateOnboardingApp(this.app)
+ .then(() => {
+ $log.debug('AppDetailsModalCtrl:updateOnboardingApp:: App update succeeded!');
+ $scope.closeThisDialog(true);
+ emptyCookies();
+ }).catch(err => {
+ switch (err.status) {
+ case '409': // Conflict
+ handleConflictErrors(err);
+ break;
+ case '500': // Internal Server Error
+ confirmBoxService.showInformation('There was a problem updating the application changes. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => {});
+ break;
+ case '403': // Forbidden... possible webjunction error to try again
+ confirmBoxService.showInformation('There was a problem updating the application changes. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' + err.status).then(isConfirmed => {});
+ break;
+ default:
+ confirmBoxService.showInformation('There was a problem updating the application changes. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' + err.status).then(isConfirmed => {});
+ }
+ $log.error('applicationsService:updateOnboardingApp error:: '+ JSON.stringify(err));
+ }).finally(()=>{
+ this.isSaving = false;
+ // for bug in IE 11
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('AppDetailsModalCtrl:updateOnboardingApp:: Browser is IE, forcing Refresh');
+ $window.location.reload(); // for bug in IE 11
+ }
+ // for bug in IE 11
+ });
+ }else{
+ applicationsService.addOnboardingApp(this.app)
+ .then(() => {
+ $log.debug('App creation succeeded!');
+ $scope.closeThisDialog(true);
+ emptyCookies();
+ }).catch(err => {
+ switch (err.status) {
+ case '409': // Conflict
+ handleConflictErrors(err);
+ break;
+ case '500': // Internal Server Error
+ confirmBoxService.showInformation('There was a problem adding the application information. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => {});
+ break;
+ default:
+ confirmBoxService.showInformation('There was a problem updating the application changes. ' +
+ 'Please try again. If the problem persists, then try again later. Error: ' +
+ err.status).then(isConfirmed => {});
+ }
+ $log.error('applicationsService:addOnboardingApp error:: '+ JSON.stringify(err));
+ }).finally(()=>{
+ this.isSaving = false;
+ // for bug in IE 11
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('applicationsService:addOnboardingApp:: Browser is IE, forcing Refresh');
+ $window.location.reload(); // for bug in IE 11
+ }
+ // for bug in IE 11
+ });
+ }
+ };
+
+
+ init();
+
+ $scope.$watch('appDetails.originalImage', (newVal, oldVal) => {
+ if((!newVal || !newVal.resized) && !this.app.imageUrl){
+ if (!newVal) {
+ $log.debug('applicationsService:$scope.$watch:: originalImage: newVal is null');
+ } else {
+ $log.debug('applicationsService:$scope.$watch:: originalImage: newVal is not resized and no imageUrl');
+ }
+ this.app.imageUrl = null;
+ this.app.thumbnail = emptyImg;
+ return;
+ }
+
+ if(!(_.isEqual(newVal, oldVal))){
+ $log.debug('applicationsService:$scope.$watch:: thumbnail updated!');
+ this.app.imageUrl = null;
+ this.app.thumbnail = newVal.resized.dataURL;
+ }
+ });
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ e.preventDefault();
+ });
+ }
+ }
+ AppDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'errorMessageByCode',
+ 'ECOMP_URL_REGEX','userProfileService','$cookies', 'confirmBoxService'];
+ angular.module('ecompApp').controller('AppDetailsModalCtrl', AppDetailsModalCtrl);
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.spec.js b/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.controller.spec.js
@@ -0,0 +1,19 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
diff --git a/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.modal.html b/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.modal.html
new file mode 100644
index 00000000..ad659aa8
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/applications/application-details-dialog/application-details.modal.html
@@ -0,0 +1,201 @@
+<!--
+ ================================================================================
+ eCOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="application-details-modal">
+ <div id="title" class="title">Application Details</div>
+
+ <div class="app-properties-main" scroll-top="appDetails.scrollApi">
+ <form name="appForm" novalidate autocomplete="off">
+ <!-- We can remove this script once we get to AT&T Corporate Firefox version 47
+ autocomplete="off" won't work until v47 -->
+ <script type="text/javascript">
+ document.getElementById("appForm").reset();
+ </script>
+ <div id="app-left-container" class="left-container">
+ <div class="property">
+ <input id="checkbox-app-is-restricted" type="checkbox" class="checkbox-field checkbox-field-openSRC" ng-disabled="appDetails.isEditMode" ng-model="appDetails.app.restrictedApp" ng-checked="appDetails.app.restrictedApp"/>
+ <div class="property-label checkbox-label">Hyperlink only application</div>
+ </div>
+ <div class="property required">
+ <div class="property-label">Application Name</div>
+ <input id="input-app-name" type="text"
+ ng-model="appDetails.app.name"
+ maxlength="100"
+ name="name"
+ ng-pattern="/^[a-zA-Z0-9_\-\s\&]*$/"
+ required/>
+
+ <div id="error-container-conflict" class="error-container" ng-show="appDetails.conflictMessages.name" id="conflictMessages-name">
+ <small id="app-name-error-conflict" class="err-message" ng-bind="appDetails.conflictMessages.name"></small>
+ </div>
+ <div id="error-container-edit" class="error-container" ng-show="appForm.name.$dirty || appDetails.isEditMode">
+ <div ng-messages="appForm.name.$error" class="error-container">
+ <small id="app-name-error-required" class="err-message" ng-message="required">Application name is required</small>
+ <small id="app-name-error-alpha-num" class="err-message" ng-message="pattern">Application name must be alphanumeric</small>
+ </div>
+ </div>
+ </div>
+ <!--<div class="property">-->
+ <!--<div class="property-label">Description</div>-->
+ <!--<input class="input-field" type="text" ng-model="appDetails.app.description"/>-->
+ <!--</div>-->
+ <!--<div class="property">-->
+ <!--<div class="property-label">Notes</div>-->
+ <!--<input class="input-field" type="text" ng-model="appDetails.app.notes"/>-->
+ <!--</div>-->
+ <div class="property required">
+ <div id="url-property-label" class="property-label">URL</div>
+ <input id="input-app-url"
+ ng-model="appDetails.app.url"
+ maxlength="256"
+ name="url"
+ type="url" placeholder="https://"
+ ng-pattern="appDetails.ECOMP_URL_REGEX"
+ required />
+ <div id="app-error-url" class="error-container" ng-show="appDetails.conflictMessages.url" id="div-app-name-err-url">
+ <small class="err-message" ng-bind="appDetails.conflictMessages.url"></small>
+ </div>
+ <div id="app-error-url-message" class="error-container" ng-show="appForm.url.$dirty || appDetails.isEditMode">
+ <div ng-messages="appForm.url.$error" class="error-container">
+ <small id="error-app-url-req" class="err-message" ng-message="required">Application URL is required</small>
+ <!--<small id="error-app-url-invalid" class="err-message" ng-message="pattern">Application URL must be a valid URL</small>-->
+ <small id="error-app-url-invalid" class="err-message" ng-show="appForm.url.$error.url">Application URL must be a valid URL</small>
+ </div>
+ </div>
+ <!--<span class="err-message" ng-show="appForm.url.$error.url"> Not valid URL!</span>-->
+ </div>
+
+
+ <div class="property" ng-show="!appDetails.app.restrictedApp">
+ <div class="property-label">Rest API URL</div>
+ <input id="input-app-rest-url"
+ ng-model="appDetails.app.restUrl"
+ name="restUrl"
+ type="url" placeholder="https://"
+ ng-pattern="appDetails.ECOMP_URL_REGEX"
+ maxlength="256"/>
+ <div id="app-error-rest-message" class="error-container" ng-show="appForm.restUrl.$dirty || appDetails.isEditMode">
+ <div ng-messages="appForm.restUrl.$error" class="error-container">
+ <!--<small class="err-message" ng-message="pattern">Application REST URL must be a valid URL</small>-->
+ <small class="err-message" ng-show="appForm.restUrl.$error.url">Application REST URL must be a valid URL</small>
+ </div>
+ </div>
+ </div>
+
+ <div class="property required" ng-show="!appDetails.app.restrictedApp">
+ <div id="username-property-label" class="property-label">Username</div>
+ <input type="text"
+ ng-model="appDetails.app.username"
+ name="username"
+ maxlength="256"
+ ng-required="!appDetails.app.restrictedApp"/>
+ <div id="app-error-username-container" class="error-container" ng-show="appForm.username.$dirty || appDetails.isEditMode">
+ <div ng-messages="appForm.username.$error" class="error-container">
+ <small id="error-appusername-reqd" class="err-message" ng-message="required">My Logins App Username is required</small>
+ </div>
+ </div>
+ </div>
+ <div class="property required" ng-show="!appDetails.app.restrictedApp">
+ <div id="pwd-property-label" class="property-label">Password</div>
+ <input type="password" id="input-mylogins-password"
+ ng-model="appDetails.app.appPassword" autocomplete="new-password"
+ name="appPassword"
+ maxlength="256"
+ ng-required="!appDetails.app.restrictedApp"/>
+ <div id="app-error-password-container" class="error-container" ng-show="appForm.appPassword.$dirty || appDetails.isEditMode">
+ <div ng-messages="appForm.appPassword.$error" class="error-container">
+ <small id="error-mylogins-password-reqd" class="err-message" ng-message="required">My Logins App Password is required</small>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="right-container">
+ <div class="property">
+ <div class="property-label">Upload Image</div>
+ <input type="file" id="input-app-image-upload"
+ class="input-file-field"
+ accept="image/*"
+ ng-model="appDetails.originalImage"
+ name="appImage"
+ image-upload="appDetails.originalImage"
+ image-upload-resize-max-height="300"
+ image-upload-resize-max-width="360"
+ image-upload-resize-quality="0.7"
+ image-upload-api="appDetails.imageApi" style="height: 24px;"/>
+
+ <div id="app-error-image-upload" class="error-container" ng-show="appForm.appImage.$dirty">
+ <div ng-messages="appForm.appImage.$error" class="error-container">
+ <small id="error-app-invalid-image" class="err-message" ng-message="mimeType">Uploaded file must be an image</small>
+ <small id="error-app-invalid-image-size" class="err-message" ng-message="imageSize">Image file must be smaller than 1MB</small>
+ </div>
+ </div>
+
+ <div class="property-label preview">
+ <span class="left-label">Preview</span>
+ <span class="remove" ng-click="appDetails.removeImage()">Remove</span>
+ </div>
+ <img id="image-app-preview" class="image-preview" ng-src="{{appDetails.app.imageLink || appDetails.app.thumbnail || appDetails.emptyImgForPreview}}"/>
+ <div id="property-communication-inbox" class="property" ng-show="!appDetails.app.restrictedApp">
+ <div id="property-communication-inbox-label" class="property-label">Communication Inbox</div>
+ <input type="text" id="input-UEB-topicname"
+ ng-model="appDetails.app.uebTopicName"
+ name="uebTopicName" readonly="readonly"/>
+ </div>
+
+ <div id="property-communication-key" class="property" ng-show="!appDetails.app.restrictedApp">
+ <div id="property-communication-key-label" class="property-label">Communication Key</div>
+ <input type="text" id="input-UEB-communication-key"
+ ng-model="appDetails.app.uebKey"
+ name="uebKey" readonly="readonly" />
+ </div>
+
+ <div id="property-communication-secret" class="property" ng-show="!appDetails.app.restrictedApp">
+ <div id="property-communication-secret-label" class="property-label">Communication Secret</div>
+ <input type="text" id="input-UEB-communication-secret"
+ ng-model="appDetails.app.uebSecret"
+ name="uebSecret" readonly="readonly" />
+ </div>
+
+ <div id="property-guest-access" class="property">
+ <input id="checkbox-app-is-open" type="checkbox" class="checkbox-field checkbox-field-openSRC" ng-model="appDetails.app.isOpen" ng-checked="appDetails.app.isOpen || appDetails.app.restrictedApp" ng-disabled="appDetails.app.restrictedApp"/>
+ <div id="property-guest-checkbox-label" class="property-label checkbox-label">Allow guest access</div>
+ </div>
+ <div id="property-active" class="property">
+ <input id="checkbox-app-is-enabled" type="checkbox" class=" checkbox-field checkbox-field-openSRC " ng-model="appDetails.app.isEnabled"/>
+ <div id="property-active-checkbox-label" class="property-label checkbox-label">Active</div>
+ </div>
+
+ </div>
+
+ </div>
+ </form>
+ </div>
+
+ <div class="dialog-control">
+ <span class="ecomp-save-spinner" ng-show="appDetails.isSaving"></span>
+ <button id="button-save-app" class="btn btn-alt btn-small" ng-class="{disabled: appForm.$invalid}" ng-click="appDetails.saveChanges()">Save</button>
+ <button id="button-app-cancel" class="btn btn-alt btn-small" ng-click="closeThisDialog()">Cancel</button>
+ </div>
+</div>
+
+<script type="application/javascript">
+ $(document).ready(function(){
+ $(".ngdialog-content").css("top","-150px")
+ });
+</script> \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/views/applications/applications.controller.js b/ecomp-portal-FE-os/client/src/views/applications/applications.controller.js
new file mode 100644
index 00000000..08f00669
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/applications/applications.controller.js
@@ -0,0 +1,123 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class ApplicationsCtrl {
+
+ constructor($log, $cookies, conf, ngDialog,
+ applicationsService, confirmBoxService, userProfileService, utilsService) {
+ this.emptyImgForPreview = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+ let getOnboardingApps = () => {
+ this.isLoadingTable = true;
+ applicationsService.getOnboardingApps()
+ .then(appsList => {
+ // Use proper back-end URL to load image
+ for (var i = 0; i < appsList.length; i++) {
+ appsList[i].imageLink = '';
+ if (appsList[i].imageUrl)
+ appsList[i].imageLink = conf.api.appThumbnail.replace(':appId', appsList[i].id);
+ }
+ this.appsList = appsList;
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a problem retrieving the Applications. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ $log.error('ApplicationsCtrl:openAddNewAppModal: Error: ', err);
+ }).finally(()=> {
+ this.isLoadingTable = false;
+ });
+ };
+
+ let init = () => {
+ this.isLoadingTable = false;
+ getOnboardingApps();
+ this.searchString = '';
+ this.appsTableHeaders = [
+ {name: 'Application Name', value: 'name', isSortable: true},
+ {name: 'Active', value: 'isEnabled', isSortable: true},
+ {name: 'Integration Type', value: 'restrictedApp', isSortable: true},
+ {name: 'Guest Access', value: 'isOpen', isSortable: true},
+ {name: 'URL', value: 'url', isSortable: true},
+ {name: 'REST URL', value: 'restUrl', isSortable: true},
+ {name: 'Communication Topic', value: 'uebTopicName', isSortable: true},
+ {name: 'Communication Key', value: 'uebKey', isSortable: true},
+ {name: 'Communication Secret', value: 'uebSecret', isSortable: true},
+ ];
+ this.appsList = [];
+ };
+
+ init();
+
+ this.openAddNewAppModal = (selectedApp) => {
+ let data = null;
+ if (selectedApp) {
+ if (!selectedApp.id) {
+ $log.error('App id not found');
+ return;
+ }
+ data = {
+ app: selectedApp
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/applications/application-details-dialog/application-details.modal.html',
+ controller: 'AppDetailsModalCtrl',
+ controllerAs: 'appDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if (needUpdate.value === true) {
+ $log.debug('ApplicationsCtrl:openAddNewAppModal: updating table data...');
+ getOnboardingApps();
+ }
+ });
+
+
+ };
+
+ this.deleteApp = application => {
+ $log.debug('ApplicationsCtrl:deleteApp:: ', application.name);
+
+ confirmBoxService.deleteItem(application.name).then(isConfirmed => {
+ if(isConfirmed){
+ if(!application || !application.id){
+ $log.error('ApplicationsCtrl:deleteApp:: No application or ID... cannot delete');
+ return;
+ }
+ applicationsService.deleteOnboardingApp(application.id).then(() => {
+ this.appsList.splice(this.appsList.indexOf(application), 1);
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a problem deleting the Application. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ $log.error('ApplicationsCtrl:deleteApp: Error: ', err);
+ });
+ }
+ }).catch(err => {
+ confirmBoxService.showInformation('There was a problem deleting the Application. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ $log.error('ApplicationsCtrl:deleteApp error:: ', err);
+ });
+
+ };
+
+ }
+ }
+ ApplicationsCtrl.$inject = ['$log', '$cookies', 'conf', 'ngDialog',
+ 'applicationsService', 'confirmBoxService', 'userProfileService', 'utilsService'];
+ angular.module('ecompApp').controller('ApplicationsCtrl', ApplicationsCtrl);
+})();
diff --git a/ecomp-portal-FE-os/client/src/views/applications/applications.controller.spec.js b/ecomp-portal-FE-os/client/src/views/applications/applications.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/applications/applications.controller.spec.js
@@ -0,0 +1,19 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
diff --git a/ecomp-portal-FE-os/client/src/views/applications/applications.tpl.html b/ecomp-portal-FE-os/client/src/views/applications/applications.tpl.html
new file mode 100644
index 00000000..edded4bb
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/applications/applications.tpl.html
@@ -0,0 +1,81 @@
+<!--
+ ================================================================================
+ eCOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="w-ecomp-main">
+ <div class="w-ecomp-main-container">
+
+ <div class="applications-page-main" id="page-content">
+ <div id='app-title' class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Application Onboarding</h1>
+ </div>
+ <div class="apps-table">
+ <div class="table-control">
+ <div class="table-control-fields">
+ <div >
+ <input id="input-table-search" placeholder="Search in entire table" class="table-search-field" type="text" data-ng-model="searchAppName">
+ </div>
+ </div>
+ <div class="table-control-buttons">
+ <button class="btn btn-alt btn-small" ng-click="apps.openAddNewAppModal()"><i class="icon-people-userbookmark" aria-hidden="true"></i>&nbsp;Add App</button>
+ </div>
+ </div>
+ <span class="ecomp-spinner" ng-show="apps.isLoadingTable"></span>
+
+
+ <div b2b-table table-data="apps.appsList" ng-hide="apps.isLoadingTable" search-string="searchAppName" class="b2b-table-div">
+ <table>
+ <thead b2b-table-row type="header">
+ <th b2b-table-header id="col1" sortable="false" >Thumbnail</th>
+ <th b2b-table-header id="col2" key="name" sortable="true" >Application Name</th>
+ <th b2b-table-header id="col3" sortable="false" >Active?</th>
+ <th b2b-table-header id="col4" sortable="false" >Integration Type</th>
+ <th b2b-table-header id="col5" sortable="false" >Guest Access</th>
+ <th b2b-table-header id="app-header-URL" sortable="false">URL</th>
+ <th b2b-table-header id="app-header-RESTURL" sortable="false">REST URL</th>
+ <th b2b-table-header id="app-header-Topic" sortable="false">Communication Topic</th>
+ <th b2b-table-header id="app-header-CommKey" sortable="false">Communication Key</th>
+ <th b2b-table-header id="app-header-Secret" sortable="false">Communication Secret</th>
+ <th b2b-table-header id="app-header-delete" sortable="false">Delete</th>
+ </tr>
+ </thead>
+ <tbody b2b-table-row type="body" row-repeat="rowData in apps.appsList">
+ <tr>
+ <td b2b-table-body id="rowheader_t1_{{$index}}" headers="col1" ng-click="apps.openAddNewAppModal(rowData)">
+ <img id="{{$index}}-app-imageUrl" class="small-thumbnail" ng-src="{{rowData.imageLink || apps.emptyImgForPreview}}">
+ </td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col2" ng-bind="rowData.name" ng-click="apps.openAddNewAppModal(rowData)"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col3" ng-click="apps.openAddNewAppModal(rowData)">{{(rowData.isEnabled) ? 'yes' : 'no'}}</td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col4" ng-click="apps.openAddNewAppModal(rowData)">{{(rowData.restrictedApp) ? 'link' : 'standard'}}</td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col5" ng-click="apps.openAddNewAppModal(rowData)">{{(rowData.isOpen) ? 'yes' : 'no'}}</td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col7" ng-click="apps.openAddNewAppModal(rowData)">{{rowData.url | elipsis: 27}}</td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col8" ng-click="apps.openAddNewAppModal(rowData)">{{rowData.restUrl | elipsis: 27}}</td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col11" ng-click="apps.openAddNewAppModal(rowData)"ng-bind="rowData.uebTopicName"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col12" ng-click="apps.openAddNewAppModal(rowData)"ng-bind="rowData.uebKey"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col13" ng-click="apps.openAddNewAppModal(rowData)"ng-bind="rowData.uebSecret"></td>
+ <td b2b-table-body headers="rowheader_t1_{{$index}} col14" >
+ <span class="icon-misc-trash" ng-click="apps.deleteApp(rowData)"></span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.controller.js b/ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.controller.js
new file mode 100644
index 00000000..5372841e
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.controller.js
@@ -0,0 +1,218 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/20/15.
+ */
+'use strict';
+(function () {
+ class NewCatalogModalCtrl {
+ constructor($scope, $log, usersService, catalogService, applicationsService, confirmBoxService) {
+ let init = () => {
+ //$log.info('NewUserModalCtrl::init');
+ this.isSaving = false;
+ this.adminApps =[];
+ this.appRoles = [];
+ $scope.userAppRoles = [];
+ $scope.titleText = "Request for Access in MyLogins:";
+ $scope.title ="Request is pending in MyLogins for the following Roles";
+ this.isGettingAdminApps = false;
+ if($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState){
+ this.selectedUser = $scope.ngDialogData.selectedUser;
+ this.dialogState = $scope.ngDialogData.dialogState;
+ this.isShowBack = false;
+ if(this.dialogState === 2){
+ this.getUserAppsRoles();
+ }
+ }
+ };
+
+ this.appChanged = (index) => {
+ let myApp = this.adminApps[index];
+ //$log.debug('NewUserModalCtrl::appChanged: index: ', index, '; app id: ', myApp.id, 'app name: ',myApp.name);
+ myApp.isChanged = true;
+ this.anyChanges = true;
+ }
+
+ this.deleteApp = (app) => {
+ let appMessage = this.selectedUser.firstName + ' ' + this.selectedUser.lastName;
+ confirmBoxService.deleteItem(appMessage).then(isConfirmed => {
+ if(isConfirmed){
+ this.anyChanges = true;
+ app.isChanged = true;
+ app.isDeleted = true; // use this to hide the app in the display
+ app.appRoles.forEach(function(role){
+ role.isApplied = false;
+ });
+ }
+ }).catch(err => {
+ $log.error('NewUserModalCtrl::deleteApp error: ',err);
+ confirmBoxService.showInformation('There was a problem deleting the the applications. ' +
+ 'Please try again later. Error: ' + err.status).then(isConfirmed => {});
+ });
+ };
+
+ this.getUserAppsRoles = () => {
+
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: about to call getAdminAppsSimpler');
+ this.isGettingAdminApps = true;
+ catalogService.getAppCatalog().then((apps) => {
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: beginning of then for getAdminAppsSimpler');
+
+ this.isGettingAdminApps = false;
+ if (!apps || !apps.length) {
+ $log.error('NewUserModalCtrl::getUserApps error: no admin apps found');
+ return null;
+ }
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: then for getAdminAppsSimpler: step 2');
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: admin apps: ', apps);
+
+ this.dialogState = 2;
+ this.isLoading = true;
+ this.adminApps = apps;
+
+ catalogService.getuserAppRolesCatalog(this.selectedUser.headerText).then(
+ function(result) {
+ // $log.debug('CatalogCtrl:storeSelection result is ', result);
+
+ $scope.userAppRoles = result;
+ $scope.displyUserAppCatalogRoles = true;
+ if(result.length === 1 && result[0].requestedRoleId === null)
+ {
+ $scope.title= "Removal Request is pending in MyLogins";
+ }
+
+ if($scope.userAppRoles.length==0)
+ {
+ $scope.displyUserAppCatalogRoles = false;
+ }
+ });
+ apps.forEach(app => {
+ if(app.name === this.selectedUser.headerText){
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: app: id: ', app.id, 'name: ',app.name);
+ // Keep track of which app has changed, so we know which apps to update using a BE API
+ app.isChanged = false;
+ // Each of these specifies a state, which corresponds to a different message and style that gets displayed
+ app.isLoading = true;
+ app.isError = false;
+ app.isDeleted = false;
+ app.printNoChanges = false;
+ app.isUpdating = false;
+ app.isErrorUpdating = false;
+ app.isDoneUpdating = false;
+ app.errorMessage = "";
+ usersService.getUserAppRoles(app.id, this.selectedUser.attuid).promise().then((userAppRolesResult) => {
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: got a result for app: ',app.id,': ',app.name,': ',userAppRolesResult);
+ app.appRoles = userAppRolesResult;
+ app.isLoading = false;
+ }).catch(err => {
+ $log.error(err);
+ app.isError = true;
+ app.isLoading = false;
+ app.errorMessage = err.headers('FEErrorString');
+ //$log.debug('NewUserModalCtrl::getUserAppsRoles: in new-user.controller: response header: '+err.headers('FEErrorString'));
+ }).finally(()=>{
+ this.numberAppsProcessed++;
+ if (this.numberAppsProcessed === this.adminApps.length) {
+ this.isLoading = false;
+ }
+ });
+ }
+ })
+ return;
+ }).catch(err => {
+ $log.error(err);
+ })
+
+ }
+
+
+ /**
+ * Update the selected user apps with the new roles.
+ * If no roles remain, set the user to inactive.
+ */
+ this.updateUserAppsRoles = () => {
+ // $log.debug('NewUserModalCtrl::updateUserAppsRoles: entering updateUserAppsRoles');
+ if(!this.selectedUser || !this.selectedUser.attuid || !this.adminApps){
+ $log.error('NewUserModalCtrl::updateUserAppsRoles: mmissing arguments');
+ return;
+ }
+ this.isSaving = true;
+ confirmBoxService.makeUserAppRoleCatalogChanges('Are you sure you want to make these changes?')
+ .then(confirmed => {
+ if(confirmed === true){
+ //$log.debug('NewUserModalCtrl::updateUserAppsRoles: going to update user: ' + this.selectedUser.attuid);
+ this.numberAppsProcessed = 0;
+ this.numberAppsSucceeded = 0;
+ this.adminApps.forEach(app => {
+ if (app.isChanged) {
+ //$log.debug('NewUserModalCtrl::updateUserAppsRoles: app roles have changed; going to update: id: ', app.id, '; name: ', app.name);
+ app.isUpdating = true;
+ var UserAppRolesRequest = {
+ attuid: this.selectedUser.attuid,
+ appId: app.id,
+ appRoles: app.appRoles,
+ appName: app.name
+ };
+ this.isSaving = true;
+ $log.debug('going to update user: ' + this.selectedUser.attuid + ' with app roles: ' + JSON.stringify(this.adminAppsRoles));
+ applicationsService.saveUserAppsRoles(UserAppRolesRequest).then(res => {
+ app.isUpdating = false;
+ $scope.closeThisDialog(true);
+ }).catch(err => {
+ $log.error('NewAdminModalCtrl.updateAdminAppsRoles:: Failed - ' + err);
+ }).finally(()=> {
+ this.isSaving = false;
+ })
+ }
+ });
+ }else{
+ this.isSaving = false;
+ }
+
+
+
+ });
+
+ };
+
+ /**
+ * Navigate between dialog screens using step number: 1,2,...
+ */
+ this.navigateBack = () => {
+ if (this.dialogState === 1) {
+ //back from 1st screen?
+ }
+ if (this.dialogState === 3) {
+ this.dialogState = 1;
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ //**Nabil - note: this will cause the history back state to be replaced with current state
+ e.preventDefault();
+ });
+ }
+ }
+ NewCatalogModalCtrl.$inject = ['$scope', '$log', 'usersService', 'catalogService', 'applicationsService', 'confirmBoxService'];
+ angular.module('ecompApp').controller('NewCatalogModalCtrl', NewCatalogModalCtrl);
+})();
diff --git a/ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.modal.html b/ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.modal.html
new file mode 100644
index 00000000..1cf96f7f
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/catalog/add-catalog-dialogs/new-catalog.modal.html
@@ -0,0 +1,113 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="new-catalog-modal">
+
+ <div class="user-catalog-roles" ng-show="userInfo.dialogState===2">
+ <div >
+ <div class=display-userApp-Catalog-Roles >{{titleText}}</div>
+ <div class="title"
+ ng-bind="userInfo.selectedUser.firstName + ' ' + userInfo.selectedUser.lastName + ' (' + userInfo.selectedUser.attuid + ')'"></div>
+ </div>
+ <div class="app-roles-main">
+
+ <div class="app-catalog-roles-list">
+ <div class="app-catalog-item" ng-repeat="app in userInfo.adminApps"
+ ng-if="app.name === userInfo.selectedUser.headerText"
+ id="app-name-{{app.name.split(' ').join('-')}}">
+ <div class="app-catalog-item-left"
+ id="div-app-name-{{app.name.split(' ').join('-')}}">{{app.name
+ | elipsis: 27}}</div>
+ <div class="app-catalog-item-right"
+ id="div-app-name-dropdown-{{app.name.split(' ').join('-')}}"
+ ng-show="!app.isError && !app.isLoading && !app.noChanges && !app.isUpdating && !app.isDoneUpdating && !app.isErrorUpdating"
+ ng-if="userAppRoles.length===0">
+ <multiple-select id="app-roles" unique-data="{{$index}}"
+ placeholder="Select roles" ng-model="app.appRoles"
+ on-change="userInfo.appChanged($index)" name-attr="roleName"
+ value-attr="isApplied"></multiple-select>
+ </div>
+
+ <div class="app-catalog-item-right"
+ id="div-app-name-dropdown-{{app.name.split(' ').join('-')}}"
+ ng-show="!app.isError && !app.isLoading && !app.noChanges && !app.isUpdating && !app.isDoneUpdating && !app.isErrorUpdating"
+ ng-if="userAppRoles.length!==0">
+ <multiple-select2 id="app-roles" unique-data="{{$index}}"
+ placeholder="Select roles" ng-model="app.appRoles"
+ on-change="userInfo.appChanged($index)" name-attr="roleName"
+ value-attr="isApplied"></multiple-select2>
+ </div>
+
+
+ <div id="app-item-no-contact" class="app-catalog-item-right-error"
+ ng-show="app.isError">{{app.errorMessage | elipsis: 35}}</div>
+ <div id="app-item-contacting"
+ class="app-catalog-item-right-contacting" ng-show="app.isLoading">Contacting
+ application...</div>
+ <div id="app-item-no-changes"
+ class="app-catalog-item-right-contacting" ng-show="app.noChanges">No
+ changes</div>
+ <div id="app-item-no-updating"
+ class="app-catalog-item-right-contacting" ng-show="app.isUpdating">Updating
+ application...</div>
+ <div id="app-item-done-updating"
+ class="app-catalog-item-right-contacting"
+ ng-show="app.isDoneUpdating">Finished updating application</div>
+ <div id="app-item-cannot-update"
+ class="app-catalog-item-right-error" ng-show="app.isErrorUpdating">Could
+ not update application...</div>
+ <div id='ecomp-small-spinner' class="ecomp-small-spinner"
+ ng-show="app.isLoading"></div>
+ </div>
+
+ &nbsp;
+
+ <div class="usr-app-roles" ng-show=displyUserAppCatalogRoles>
+ <div class="userApp-Catalog-Roles-label">{{title}}</div>
+ &nbsp;
+ <div class="display-userApp-Catalog-Roles-value"
+ style="height: 150px; width: 200px; overflow-y: auto;">
+ <div ng-repeat="getUserApplicationRole in userAppRoles">
+
+ <div class="userAppCatalog-lbl-value">
+ <span class="userAppCatalog-lbl-value-spn">
+ {{getUserApplicationRole.rolename}}</span>
+ </div>
+
+ </div>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="dialog-control">
+ <span id="ecomp-save-spinner" class="ecomp-save-spinner"
+ ng-show="userInfo.isSaving || userInfo.isGettingAdminApps"></span>
+ <button id="new-user-next-button" class="next-button"
+ ng-click="userInfo.updateUserAppsRoles()"
+ ng-disabled="(!userInfo.anyChanges == true)">Submit</button>
+ <div id="new-user-cancel-button" class="cancel-button"
+ ng-click="closeThisDialog()">Cancel</div>
+ </div>
+
+ </div>
+
+ </div>
+
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/dashboard/dashboard.tpl.html b/ecomp-portal-FE-os/client/src/views/dashboard/dashboard.tpl.html
new file mode 100644
index 00000000..cf5c2ee6
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/dashboard/dashboard.tpl.html
@@ -0,0 +1,173 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="w-ecomp-dashboard-home" id="widgets">
+ <div class="dashboard-home-container" id="page-content">
+ <div align="center" id="errorInfo"
+ style="display: none; font-size: 12px; margin-left: 5px">
+ <span style="color: red">You don't have a user account in that
+ application. Please ask the system administrator. </span>
+ </div>
+ <div class="portals-list" style="margin-bottom: 0px;">
+ <div>
+ <div class="simulateGridHeader">
+ <span class="simulateGridHeaderTitle">Applications <span
+ class="dashboardSortHeader">Sort by:</span></span>
+
+ <div style="float: right; margin-right:7px; margin-top:5px">
+ <select id="sort-by-{{selectedSortType.value}}" name="dropdown1" b2b-dropdown ng-model="selectedSortType.value" ng-change="selectedSortTypeChanged(selectedSortType.value)">
+ <option b2b-dropdown-list option-repeat="d in sort_options" value="{{d.value}}">{{d.title}}</option>
+ </select>
+ </div>
+
+ </div>
+ <div class="dashboard-boarder">
+ <div class="gridster-container override_background">
+ <div ng-if="appsViewData.length==0">
+ <div class="app-error-block">
+ <i class="icon-information full-linear-icon-information"
+ style="margin-left: 50%; font-size: 90px; color: black"></i> <br>
+ <div class="activity-error-msg1">
+ You do not have access to any application or function in ECOMP
+ Portal.
+ </div>
+ </div>
+ </div>
+
+ <div ng-if="appsViewData.length>0" id="page-content"
+ class="content" gridster="dashboard.gridsterAppOpts">
+ <ul>
+ <li gridster-item="item" ng-repeat="item in appsViewData">
+ <div class="gridster-box" ng-if="item.addRemoveApps == null">
+ <div class="gridster-box-header">
+ <i class="icon-content-gridguide"></i>
+ <h3 style="cursor: context-menu">{{item.headerText |
+ elipsis: 14}}</h3>
+ </div>
+ <div class="gridster-box-content"
+ ng-style="{'cursor':'pointer',
+ 'background-image': 'url('+(item.imageLink)+')',
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px'}"
+ ng-click="dashboard.goToPortal(item);dashboard.auditLog(item)"
+ ng-hide="users.isLoadingTable && !users.getUserAppsIsDone">
+ </div>
+ </div>
+ <div class="gridster-box" ng-if="item.addRemoveApps">
+ <div class="gridster-box-header" style="cursor: pointer;"
+ ng-click="dashboard.goToCatalog()">
+ <i class="icon-content-gridguide"></i>
+ <h3>Select applications...</h3>
+
+ </div>
+ <div class="gridster-box-content"
+ ng-style="{'cursor':'pointer',
+ 'order': item.order,
+ 'text-align': 'center',
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px',
+ 'padding': '4px',
+ 'opacity': '1'}"
+ ng-click="dashboard.goToCatalog()" class="gridsterContent">
+ <label style="font-size: 12px;">Click
+ here to personalize <br> this applications page
+ </label> <i class="icon-controls-add-maximize"
+ style="font-size: 80px; color: #067ab4"></i>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+
+ <br> <br>
+
+
+ <div
+ ng-show=dashboard.isCommError>Failed to communicate with the
+ widget microservice.</div>
+
+ <div id="widget-boarder" class="content" gridster="dashboard.gridsterWidgetOpts">
+ <ul>
+ <li gridster-item="widget" ng-repeat="widget in widgetsViewData">
+ <div class="box">
+ <div class="box-header">
+ <i style="cursor:move;" class="icon-content-gridguide"></i>
+ <h3>{{ widget.headerText}}</h3>
+ <div class="box-header-btns pull-right"
+ ng-if="isAdminPortalAdmin == true" class="ng-scope"
+ ng-show="widget.widgetIdentifier != ''">
+ <a id="widgetHeaderBtns"
+ class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder"
+ ng-class="singleBtnBorder" alt="Settings"
+ ng-mousedown="showImpResOption=!showImpResOption"
+ ng-mouseleave="showImpResOption=false" title="Settings"
+ ddh-accessibility-click="13,32" tabindex="0"><span
+ style="color: #888;"><img
+ ng-src="assets/images/generic.png"
+ style="margin-bottom: 3px; margin-left: 3px;" alt="..."
+ aria-label="Tap or Click to move"
+ src="assets/images/generic.png"></span></a>
+
+ <ul class="optionsMenu"
+ style="max-width: 130px; height: 32px; display: block"
+ ng-show="showImpResOption"
+ ng-mouseenter="showImpResOption=true"
+ ng-mouseleave="showImpResOption=false">
+ <!-- ngIf: widget.duplicateAllowed -->
+ <li
+ ng-click="editWidgetModalPopup(importResData, widget.widgetIdentifier);">
+ <a class="optionsMenuLink" href="javascript:void(0)"
+ ddh-accessibility-click="13,32">Edit</a>
+ </li>
+ <!-- end ngIf: widget.duplicateAllowed -->
+ </ul>
+
+ </div>
+ </div>
+ <div class="box-content">
+ <div dyn-attr="widget.attrb"></div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+ <div></div>
+ <div class="gridster-container override_background">
+ <div id="dashboardAddWidgetPreference"
+ ng-style="{'cursor':'pointer'}"
+ ng-click="dashboard.goToWidgetCatLog()">
+ <i class="icon-controls-add-maximize"></i> Add Widget
+ </div>
+ &nbsp;
+ <div id="dashboardDefaultPreference" ng-style="{'cursor':'pointer'}"
+ ng-click="restoreSortSelected()">
+ <i class="icon-arrows-replay-restart"></i> Reset Widget Layout
+ </div>
+ </div>
+ </div>
+
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/footer/footer.controller.js b/ecomp-portal-FE-os/client/src/views/footer/footer.controller.js
new file mode 100644
index 00000000..34d3d960
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/footer/footer.controller.js
@@ -0,0 +1,48 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class FooterCtrl {
+ constructor($scope, $rootScope, manifestService, $log) {
+ this.manifestService = manifestService;
+ this.$log = $log;
+ this.$scope = $scope;
+ this.$rootScope = $rootScope;
+
+ $scope.buildinfo = null;
+
+ $rootScope.showFooter = true;
+ $scope.date = new Date();
+
+
+ manifestService.getManifest().then( jsonObj => {
+ // $log.debug('FooterCtrl.getManifest: ', JSON.stringify(jsonObj));
+ $scope.buildInfo = jsonObj;
+
+ }).catch(err=> {
+ $log.error('FooterCtrl::updateTableData error :',err);
+ });
+ };
+
+ }
+
+ FooterCtrl.$inject = ['$scope', '$rootScope', 'manifestService', '$log'];
+ angular.module('ecompApp').controller('FooterCtrl', FooterCtrl);
+})();
diff --git a/ecomp-portal-FE-os/client/src/views/footer/footer.controller.spec.js b/ecomp-portal-FE-os/client/src/views/footer/footer.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/footer/footer.controller.spec.js
@@ -0,0 +1,19 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
diff --git a/ecomp-portal-FE-os/client/src/views/footer/footer.less b/ecomp-portal-FE-os/client/src/views/footer/footer.less
new file mode 100644
index 00000000..59c8622e
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/footer/footer.less
@@ -0,0 +1,104 @@
+.w-ecomp-footer {
+ z-index: 101;
+ background-color: #222;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: @footer-height;
+ .white12r;
+
+ a {
+ .white12r;
+ font-family: Arial;
+ font-size: 11px;
+ }
+
+ .footer-image {
+ margin: auto;
+
+ }
+
+ .build-number {
+ float: right;
+ padding: 5px;
+ }
+ .footer-content {
+ align-items: center;
+ text-align: center;
+ vertical-align: middle;
+ display: flex;
+ justify-content: center;
+ //width: @table-width;
+ margin: auto;
+
+ }
+
+ [class*="icon-primary-"], .cssIcon-globe:before {
+ color: #0574ac;
+ display: inline-block;
+ font-family: 'icoPrimary' !important;
+ font-style: normal;
+ font-size: 20px;
+ font-weight: normal;
+ font-variant: normal;
+ height: 1em;
+ margin-right: 7px;
+ text-transform: none;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ position: relative;
+ speak: none;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ vertical-align: middle;
+ width: 1em;
+ }
+
+ .footer-logo {
+ color: #000 !important;
+ }
+ .footerWrapper .footer-logo .logo-title {
+ color: #fff;
+ margin-left: 10px;
+ display: inline-block;
+ font-size: 25px;
+ margin-top: 0px;
+ font-family: Arial;
+ font-weight: normal;
+ }
+ .displayInline {
+ display: inline-block;
+ margin-left: 20px;
+ margin-top: 20px;
+ }
+ .footerWrapper .copyright-text {
+ color: #fff;
+ font-size: 11px;
+ font-family: Arial;
+ margin-bottom:0px;
+ margin-top: 15px;
+ line-height: 1rem;
+ margin-top: 20px;
+ margin-left: 100px;
+ }
+ .footerText {
+ width: 59%;
+ float: left;
+ text-align: left;
+ margin-left: 50px;
+ }
+ .footerLastSection {
+ width: 1200px;
+ margin: auto;
+ }
+ .build-number{
+ margin-top:-14px;
+ margin-right: -195px;
+ }
+ .footer-link{
+ text-decoration: underline;
+ }
+}
diff --git a/ecomp-portal-FE-os/client/src/views/footer/footer.tpl.html b/ecomp-portal-FE-os/client/src/views/footer/footer.tpl.html
new file mode 100644
index 00000000..a6fbdeea
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/footer/footer.tpl.html
@@ -0,0 +1,34 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="w-ecomp-footer" id="w-ecomp-footer" ng-style="{height: tabBottom}">
+ <div id="footer" class="footer-content">
+ <div id="footer-wrapper" class="footerWrapper" id="footerWrapper">
+ <div id="footer-info" class="attFooterInfo" style="text-align: center;">
+ <div id="footer-last-section" class="footerLastSection">
+ <div id="footer-text" class="footerText">
+ OpenECOMP Portal Version {{buildInfo.manifest['Build-Number']}}.
+ </div>
+ <div id="display-inline" class="displayInline">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js
new file mode 100644
index 00000000..09a8e75a
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js
@@ -0,0 +1,415 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+/**
+ * Created by nnaffar on 12/20/15.
+ */
+'use strict';
+(function () {
+ class MenuDetailsModalCtrl {
+ constructor($scope, $log, functionalMenuService, errorMessageByCode, ECOMP_URL_REGEX,$rootScope,confirmBoxService) {
+
+
+ let newMenuModel = {
+ name: null,
+ menuId: null,
+ parentMenuId: null,
+ url: null
+ };
+
+ let getAvailableRoles = (appid) => {
+ this.isSaving = true;
+ if (appid != null) {
+ $log.debug("MenuDetailsModalCtrl::getAvailableRoles: About to call getManagedRolesMenu");
+ functionalMenuService.getManagedRolesMenu(appid).then(rolesObj => {
+ $log.debug("MenuDetailsModalCtrl::getAvailableRoles: Roles returned = " + JSON.stringify(rolesObj))
+ this.availableRoles = rolesObj;
+ this.preSelectedRoles = {roles:[]};
+
+ if(($scope.ngDialogData.source==='edit') && this.isMidLevelMenuItem()){
+ // in Edit flow , for Midlevel menu item no need to preSelect.
+ this.preSelectedRoles = {roles:[]};
+ }else if(!angular.isUndefined(this.menuItem.menuDetails) &&
+ $scope.ngDialogData.source==='edit' && this.isLeafMenuItem() &&
+ this.menuItem.menuDetails.appid!=appid) {
+ // in Edit flow , for LeafMenuItem, if appid changed then no need to preSelect.
+ this.preSelectedRoles = {roles:[]};
+ }else{
+ if((!angular.isUndefined(this.menuItem.menuDetails)) &&
+ (!angular.isUndefined(this.menuItem.menuDetails.roles))){
+ $log.debug('menuDetails.roles: ' + this.menuItem.menuDetails.roles);
+ for(var i=0; i<this.menuItem.menuDetails.roles.length; i++){
+ var role = {"roleId":this.menuItem.menuDetails.roles[i]};
+ $log.debug('MenuDetailsModalCtrl::getAvailableRoles: adding role to preselected: ' + i + ': ' + JSON.stringify(role));
+ this.preSelectedRoles.roles.push(role);
+ }
+ }
+ }
+ $rootScope.$broadcast('availableRolesReady');
+ this.isSaving = false;
+ }).catch(err => {
+ $log.error("MenuDetailsModalCtrl::getAvailableRoles: error: " + err);
+ });
+ } else {
+ $log.debug("MenuDetailsModalCtrl::getAvailableRoles: appid was null");
+ }
+ };
+
+ let getAvailableApps = () => {
+ functionalMenuService.getAvailableApplications().then(apps => {
+ $log.debug("MenuDetailsModalCtrl::getAvailableApps: Apps returned = " + JSON.stringify(apps))
+ this.availableApps = apps;
+ // Keep track of whether or not the selected app is disabled
+ if (angular.isDefined(this.selectedApp) && angular.isDefined(this.selectedApp.index)) {
+ for (var i = 0; i < apps.length; i++) {
+ if (apps[i].index === this.selectedApp.index) {
+ $log.debug("MenuDetailsModalCtrl::getAvailableApps: found app with index: " + this.selectedApp.index);
+ $log.debug("MenuDetailsModalCtrl::getAvailableApps: setting isDisabled to: " + !apps[i].enabled);
+ this.selectedApp.isDisabled = !apps[i].enabled;
+ break;
+ }
+ }
+ $log.debug("didn't find index: " + this.selectedApp.index);
+ }
+ })['catch'](function (err) {
+ confirmBoxService.showInformation('There was a problem retrieving the Applications. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ $log.error("MenuDetailsModalCtrl::getAvailableApps: getAvailableApps error: " + err);
+ }).finally(()=>{
+ this.isSaving = false;
+ });
+ };
+
+ let init = () => {
+ $log.info('MenuDetailsModalCtrl::init');
+ this.saveOrContinueBtnText = "Save";
+ this.isSaving = false;
+ this.displayRoles = $scope.ngDialogData.source==='view' ? true : false;
+ this.formEditable = $scope.ngDialogData.source==='view' ? false : true;
+ this.selectedRole = [];
+ this.availableRoles = [];
+ this.menuItem = _.clone($scope.ngDialogData.menuItem);
+ $log.info('MenuDetailsModalCtrl::getAvailableApps: Within init, about to check menuDetails for defined');
+ if(!angular.isUndefined(this.menuItem.menuDetails) &&
+ ($scope.ngDialogData.source==='view' ||
+ ($scope.ngDialogData.source==='edit') && this.isLeafMenuItem() )){
+
+ $log.debug("MenuDetailsModalCtrl::init: menuItem: ");
+ $log.debug('MenuDetailsModalCtrl::init: ',this.menuItem);
+ this.menuItem.menu.url = this.menuItem.menuDetails.url;
+ this.selectedApp={};
+ this.selectedApp.index = this.menuItem.menuDetails.appid;
+ getAvailableRoles(this.selectedApp.index);
+
+ }
+
+ if($scope.ngDialogData.source==='view' || $scope.ngDialogData.source==='edit'){
+ this.menutitle = this.menuItem.menu.name;
+ this.menuLocation = this.isParentMenuItem() ? this.menuItem.menu.name : this.menuItem.menu.parent.name;
+ }else{
+ this.menutitle = '';
+ this.menuLocation = this.menuItem.menu.name;
+ }
+ // Temporarily passing 0 as dummy for getAvailableRoles incase of this.selectedApp is not there i.e., in Add flow
+ // getAvailableRoles(angular.isUndefined(this.selectedApp) ? 0: this.selectedApp.index );
+ getAvailableApps();
+ $log.debug("MenuDetailsModalCtrl::init: Menu details: " + JSON.stringify(this.menuItem.menuDetails));
+ };
+
+
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+
+ //This part handles conflict errors (409)
+ this.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){ //support objects
+ err.data = [err.data]
+ }
+ $log.debug('MenuDetailsModalCtrl::handleConflictErrors: err.data = ' + JSON.stringify(err.data));
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ //set conflict message
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ //set field as invalid
+ $log.debug('MenuDetailsModalCtrl::handleConflictErrors: fieldName = ' + field.name);
+ $scope.functionalMenuForm[field.name].$setValidity('conflict', false);
+ //set watch once to clear error after user correction
+ watchOnce[field.name]();
+ });
+ });
+ this.scrollApi.scrollTop();
+ };
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ $log.debug('MenuDetailsModalCtrl::resetConflict: $setValidity(true) = ' + fieldName);
+ if($scope.functionalMenuForm[fieldName]){
+ $scope.functionalMenuForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+
+ let watchOnce = {
+ text: () => {
+ let unregisterName = $scope.$watch('functionalMenuDetails.menutitle', (newVal, oldVal) => {
+ // $log.debug('title:: newVal, oldVal = ' + newVal.toLowerCase() + " | " + oldVal.toLowerCase());
+ if(newVal.toLowerCase() !== oldVal.toLowerCase()){
+ resetConflict('text');
+ unregisterName();
+ }
+ });
+ },
+ url: () => {
+ let unregisterUrl = $scope.$watch('functionalMenuDetails.menuItem.menu.url', (newVal, oldVal) => {
+ if(newVal.toLowerCase() !== oldVal.toLowerCase()){
+ resetConflict('url');
+ unregisterUrl();
+ }
+ });
+ }
+ };
+
+ //***************************
+
+ this.isLeafMenuItem = () => {
+ return this.menuItem.menu.children.length>0 ? false : true;
+ };
+
+ this.isMidLevelMenuItem = () => {
+ return this.menuItem.menu.parentMenuId!=null && this.menuItem.menu.children.length>0 ? true : false;
+ };
+
+ this.isParentMenuItem = () => {
+ return this.menuItem.menu.parentMenuId!=null ? false : true;
+ };
+
+ this.updateSelectedApp = (appItem) => {
+ if (!appItem) {
+ return;
+ }
+ $log.debug('MenuDetailsModalCtrl::updateSelectedApp: drop down app item = ' + JSON.stringify(appItem.index));
+ $log.debug("MenuDetailsModalCtrl::updateSelectedApp: appItem in updateSelectedApp: ");
+ $log.debug('MenuDetailsModalCtrl::updateSelectedApp: ',appItem);
+ this.selectedApp.isDisabled = ! appItem.enabled;
+ $log.debug("MenuDetailsModalCtrl::updateSelectedApp: isDisabled: "+this.selectedApp.isDisabled);
+ getAvailableRoles(appItem.index);
+ };
+
+ this.continue = () => {
+ this.displayRoles = true;
+ this.formEditable = false;
+ };
+
+ this.saveChanges = () => {
+
+ //todo : form validation was commented as dialog message is kept for error validations
+ /*if($scope.functionalMenuForm.$invalid){
+ return;
+ }*/
+
+ if(!!this.menuItem.menu.url && (angular.isUndefined(this.selectedApp) || !this.selectedApp.index>0)) {
+ confirmBoxService.showInformation('Please select the appropriate app, or remove the url').then(isConfirmed => {});
+ return;
+ }else if(!this.menuItem.menu.url && !angular.isUndefined(this.selectedApp) && this.selectedApp.index>0){
+ confirmBoxService.showInformation('Please enter url, or select "No Application"').then(isConfirmed => {});
+ return;
+ }else if(!this.menutitle){
+ confirmBoxService.showInformation('Please enter the Menu title').then(isConfirmed => {});
+ return;
+ }
+
+ this.isSaving = true;
+ var activeMenuItem = {};
+
+ if ($scope.ngDialogData.source === 'edit') { // Edit Menu Item
+ $log.debug('MenuDetailsModalCtrl::saveChanges: Will be saving an edit menu item');
+ activeMenuItem = {
+ menuId:this.menuItem.menu.menuId,
+ column:this.menuItem.menu.column,
+ text:this.menutitle,
+ parentMenuId:this.menuItem.menu.parentMenuId,
+ url:this.menuItem.menu.url,
+ appid: angular.isUndefined(this.selectedApp) ? null:this.selectedApp.index,
+ roles:this.selectedRole
+ };
+ // If we have removed the url and appid, we must remove the roles
+ if (!activeMenuItem.appid && !activeMenuItem.url) {
+ activeMenuItem.roles = null;
+ }
+ functionalMenuService.saveEditedMenuItem(activeMenuItem)
+ .then(() => {
+ $log.debug('MenuDetailsModalCtrl::saveChanges: Menu Item saved');
+ $scope.closeThisDialog(true);
+ }).catch(err => {
+ if(err.status === 409){//Conflict
+ handleConflictErrors(err);
+ } else {
+ confirmBoxService.showInformation('There was a problem saving your change. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ }
+ $log.error('MenuDetailsModalCtrl::saveChanges: error - ',err);
+ }).finally(()=>{
+ this.isSaving = false;
+ });
+
+ $log.debug("MenuDetailsModalCtrl::saveChanges: Edit Menu output will be: " + JSON.stringify(activeMenuItem));
+ } else { // New Menu Item
+ $log.debug('MenuDetailsModalCtrl::saveChanges: Will be saving a New menu item');
+ var newMenuItem = {
+ menuId:null, // this is a new menu item
+ column:this.menuItem.menu.column,
+ text:this.menutitle,
+ // We are creating this new menu item under the menu item that was clicked on.
+ parentMenuId:this.menuItem.menu.menuId,
+ url:this.menuItem.menu.url,
+ appid: angular.isUndefined(this.selectedApp) ? null:this.selectedApp.index,
+ roles:this.selectedRole
+ };
+
+ $log.debug("MenuDetailsModalCtrl::saveChanges: New Menu output will be: " + JSON.stringify(newMenuItem));
+ functionalMenuService.saveMenuItem(newMenuItem)
+ .then(() => {
+ $log.debug('MenuDetailsModalCtrl::saveChanges: Menu Item saved');
+ $scope.closeThisDialog(true);
+ }).catch(err => {
+ if(err.status === 409){//Conflict
+ handleConflictErrors(err);
+ } else {
+ confirmBoxService.showInformation('There was a problem saving your menu. ' +
+ 'Please try again later. Error Status: '+ err.status).then(isConfirmed => {});
+ }
+ $log.error('MenuDetailsModalCtrl::saveChanges error: ', err);
+ }).finally(()=>{
+ this.isSaving = false;
+ });
+
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ //Disable navigation when modal is opened
+ e.preventDefault();
+ });
+ }
+ }
+ MenuDetailsModalCtrl.$inject = ['$scope', '$log', 'functionalMenuService', 'errorMessageByCode', 'ECOMP_URL_REGEX','$rootScope','confirmBoxService'];
+ angular.module('ecompApp').controller('MenuDetailsModalCtrl', MenuDetailsModalCtrl);
+
+ angular.module('ecompApp').directive('dropdownMultiselect', ['functionalMenuService',function(){
+ return {
+ restrict: 'E',
+ scope: {
+ model: '=',
+ options: '=',
+ populated_roles: '=preSelected',
+ dropdownTitle: '@',
+ source: '='
+ },
+ template: "<div class='btn-group' data-ng-class='{open: open}'>" +
+ "<button class='btn btn-medium'>{{dropdownTitle}}</button>" +
+ "<button class='btn dropdown-toggle' data-ng-click='open=!open;openDropDown()'><span class='caret'></span></button>" +
+ "<ul class='dropdown-menu dropdown-menu-medium' aria-labelledby='dropdownMenu'>" +
+ "<li data-ng-repeat='option in options'> <input ng-disabled='setDisable(source)' type='checkbox' data-ng-change='setSelectedItem(option.roleId)' ng-model='selectedItems[option.roleId]'>{{option.rolename}}</li>" +
+ "</ul>" +
+ "</div>",
+ controller: function ($scope) {
+ $scope.selectedItems = {};
+ $scope.checkAll = false;
+ $scope.$on('availableRolesReady', function() {
+ init();
+ });
+
+ function init() {
+ console.log('dropdownMultiselect init');
+ $scope.dropdownTitle = $scope.source ==='view' ? 'View Roles' : 'Select Roles';
+ console.log('$scope.populated_roles = ' + $scope.populated_roles);
+ }
+
+ $scope.$watch('populated_roles', function(){
+ if ($scope.populated_roles && $scope.populated_roles.length>0) {
+ for (var i = 0; i < $scope.populated_roles.length; i++) {
+ $scope.model.push($scope.populated_roles[i].roleId);
+ $scope.selectedItems[$scope.populated_roles[i].roleId] = true;
+ }
+ if ($scope.populated_roles.length === $scope.options.length) {
+ $scope.checkAll = true;
+ }
+ }else{
+ deselectAll();
+ }
+ });
+
+ $scope.openDropDown = function () {
+
+ };
+
+ $scope.checkAllClicked = function () {
+ if ($scope.checkAll) {
+ selectAll();
+ } else {
+ deselectAll();
+ }
+ };
+
+ function selectAll() {
+ $scope.model = [];
+ $scope.selectedItems = {};
+ angular.forEach($scope.options, function (option) {
+ $scope.model.push(option.roleId);
+ });
+ angular.forEach($scope.model, function (id) {
+ $scope.selectedItems[id] = true;
+ });
+ console.log($scope.model);
+ };
+
+ function deselectAll() {
+ $scope.model = [];
+ $scope.selectedItems = {};
+ console.log($scope.model);
+ };
+
+ $scope.setSelectedItem = function (id) {
+ var filteredArray = [];
+ if ($scope.selectedItems[id] === true) {
+ $scope.model.push(id);
+ } else {
+ filteredArray = $scope.model.filter(function (value) {
+ return value != id;
+ });
+ $scope.model = filteredArray;
+ $scope.checkAll = false;
+ }
+ console.log(filteredArray);
+ return false;
+ };
+
+ $scope.setDisable = function(source){
+ return source ==='view' ? true : false;
+ }
+ }
+ }
+ }]);
+
+})();
diff --git a/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html
new file mode 100644
index 00000000..f5f027c3
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html
@@ -0,0 +1,37 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="functionalMenu-details-modal">
+ <div id="title" class="title">Delete Menu Item</div>
+
+ <div id="functionalMenu-main" class="functionalMenu-properties-main" scroll-top="functionalMenuDetails.scrollApi">
+ <form name="functionalMenuForm" novalidate>
+
+ <div class="confirmation-message-wrap">
+ <div class="confirmation-message" ng-bind="confirmBox.message"></div>
+ </div>
+ </form>
+ </div>
+ <div class="dialog-control">
+ <span class="ecomp-save-spinner" ng-show="functionalMenuDetails.isSaving"></span>
+ <div id='menu-details-next-button' class="next-button"
+ ng-class="{disabled: functionalMenuForm.$invalid}" ng-click="functionalMenuDetails.saveChanges()">Delete</div>
+ <div id='menu-details-cancel-button' class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html
new file mode 100644
index 00000000..aebc6a52
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html
@@ -0,0 +1,103 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="functionalMenu-details-modal">
+ <div class="title">{{ngDialogData.title}}</div>
+
+
+ <div class="functionalMenu-properties-main" scroll-top="functionalMenuDetails.scrollApi">
+ <form name="functionalMenuForm">
+
+ <div class="item">
+ <div class="item-label">Parent</div>
+ <input id="input-parent" readonly="readonly" class="input-field" value="{{functionalMenuDetails.menuLocation}}" type="text" name="menuItem" required/>
+ </div>
+ <div class="item">
+ <div class="item-label">Title</div>
+ <input id="input-title" ng-readonly="!functionalMenuDetails.formEditable ? 'checked':'' " placeholder="Enter text" class="input-field" ng-model="functionalMenuDetails.menutitle" type="text" name="text"/>
+ <div id="div-error-conflict" class="error-container" ng-show="functionalMenuDetails.conflictMessages.text">
+ <small id="msg-error-conflict" id="functionalMenuDetails-input-title-conflict" class="err-message" ng-bind="functionalMenuDetails.conflictMessages.text"></small>
+ </div>
+ </div>
+ <div class="item">
+ <div class="left-item">
+ <div class="item-label">URL</div>
+ <input id="input-url" ng-readonly="!functionalMenuDetails.formEditable ? 'checked':'' " placeholder="http://" class="input-field" ng-model="functionalMenuDetails.menuItem.menu.url" type="url" name="url"/>
+ <span id="error-url-conflict" class="err-message" ng-show="functionalMenuForm.url.$error.url"> Not valid URL!</span>
+ </div>
+ <div class="right-item">
+ <div class="item-label">App</div>
+ <div class="custom-select-wrap" ng-readonly="checked">
+ <select id="select-app" class="select-field"
+ ng-model="functionalMenuDetails.selectedApp"
+ ng-change="functionalMenuDetails.updateSelectedApp(functionalMenuDetails.selectedApp);"
+ ng-options="app.title for app in functionalMenuDetails.availableApps track by app.index"
+ ng-disabled="!functionalMenuDetails.availableApps || !functionalMenuDetails.availableApps.length || !functionalMenuDetails.formEditable"
+ name="app"
+ required>
+ <option value="" >No Application</option>
+ </select>
+ </div>
+ <!--<span class="err-message" ng-show="functionalMenuForm.app.$dirty && functionalMenuForm.app.$error.required"> Please select App!</span>-->
+ <!-- <pre>{{functionalMenuDetails.menuItem | json}}</pre> -->
+ </div>
+ </div>
+ <div class="item" ng-show="functionalMenuDetails.displayRoles==true">
+ <div class="item-label">Roles</div>
+ <dropdown-multiselect id="dropdown-roles" dropdown-title="Select Roles" pre-selected="functionalMenuDetails.preSelectedRoles.roles"
+ model="functionalMenuDetails.selectedRole"
+ options="functionalMenuDetails.availableRoles" source="ngDialogData.source">
+ </dropdown-multiselect>
+ </div>
+ <div ng-show="functionalMenuDetails.selectedApp && functionalMenuDetails.selectedApp.isDisabled" class="err-message">Application is disabled</div>
+ </form>
+ </div>
+ <div class="dialog-control">
+ <div ng-if="ngDialogData.source!='view'">
+ <span class="ecomp-save-spinner" ng-show="functionalMenuDetails.isSaving"></span>
+ <span ng-if="ngDialogData.source=='edit' && functionalMenuDetails.isLeafMenuItem()">
+ <button class="save-button" id="add-menu-button-save"
+ ng-show="!functionalMenuDetails.menuItem.menu.url ||
+ !(functionalMenuDetails.selectedApp.index>0) ||
+ functionalMenuDetails.displayRoles"
+ ng-disabled="!functionalMenuForm.text.$valid"
+ ng-click="functionalMenuDetails.saveChanges()">Save</button>
+ <button class="save-button" id="add-menu-button-continue"
+ ng-show="!!functionalMenuDetails.menuItem.menu.url &&
+ functionalMenuDetails.selectedApp.index>0 &&
+ functionalMenuDetails.menutitle &&
+ !functionalMenuDetails.displayRoles"
+ ng-disabled="!functionalMenuForm.url.$valid" ng-click="functionalMenuDetails.continue()">Continue</button>
+ </span>
+ <span ng-if="ngDialogData.source=='add' || (ngDialogData.source=='edit' && !functionalMenuDetails.isLeafMenuItem())">
+ <button id="button-save-add"
+ class="save-button" ng-show="!functionalMenuDetails.menuItem.menu.url || !(functionalMenuDetails.selectedApp.index>0)
+ || functionalMenuDetails.selectedRole.length>0 || !functionalMenuDetails.menutitle" ng-disabled="!functionalMenuForm.text.$valid || !functionalMenuDetails.menutitle"
+ ng-click="functionalMenuDetails.saveChanges()">Save</button>
+ <button id="button-save-continue" class="save-button" ng-show="!!functionalMenuDetails.menuItem.menu.url && functionalMenuDetails.selectedApp.index>0 && functionalMenuDetails.menutitle
+ && !functionalMenuDetails.selectedRole.length>0" ng-disabled="!functionalMenuForm.url.$valid"
+ ng-click="functionalMenuDetails.continue()">Continue</button>
+ </span>
+ <button id="button-cancel-close-dialog" class="cancel-button" ng-click="closeThisDialog()">Cancel</button>
+ </div>
+ <div ng-if="ngDialogData.source=='view'">
+ <div id="div-close-dialog" class="cancel-button" ng-click="closeThisDialog()">Close</div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.controller.js b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.controller.js
new file mode 100644
index 00000000..dedc1375
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.controller.js
@@ -0,0 +1,372 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class FunctionalMenuCtrl {
+ constructor($log, functionalMenuService, $scope,ngDialog, confirmBoxService) {
+ $log.info('FunctionalMenuCtrl init');
+
+ $scope.invokeDialog = () => {
+ // alert("click dialog");
+ };
+
+ this.regenerateFunctionalMenuAncestors = () => {
+ functionalMenuService.regenerateFunctionalMenuAncestors().then(res => {
+ $log.debug("FunctionalMenuCtrl:regenerateFunctionalMenuAncestors::returned from regenerateFunctionalMenuAncestors API call");
+ confirmBoxService.showInformation('You have successfully regenerated the menu.').then(isConfirmed => {
+ });
+ })['catch'](function (err) {
+ $log.error("FunctionalMenuCtrl:regenerateFunctionalMenuAncestors:: error: " + err);
+ confirmBoxService.showInformation('There was an error while regenerating the menu.').then(isConfirmed => {
+ });
+ });
+ };
+
+ let getFunctionalMenu = () => {
+ this.isLoadingTable = true;
+ functionalMenuService.getManagedFunctionalMenu().then(res => {
+
+ let actualData=[];
+
+ //Adding children and label attribute to all objects in res
+ for(let i = 0; i < res.length; i++){
+ res[i].children=[];
+ res[i].label=res[i].text;
+ res[i].id=res[i].text;
+
+ }
+ //Adding actual child items to children array in res objects
+ for(let i = 0; i < res.length; i++){
+
+ let parentId=res[i].menuId;
+ for(let j = 0; j < res.length; j++){
+ let childId=res[j].parentMenuId;
+ if(parentId===childId){
+ res[i].children.push(res[j]);
+
+ }
+ }
+ }
+
+ // Sort the top-level menu items in order based on the column
+ res.sort(function(a, b) {
+ return a.column-b.column;
+ });
+
+ // Sort all the children in order based on the column
+ for(let i = 0; i < res.length; i++){
+ res[i].children.sort(function(a, b){
+ return a.column-b.column;
+ });
+ }
+
+ //Forming actual parent items
+ for(let i = 0; i < res.length; i++){
+ let parentId=res[i].parentMenuId;
+ if(parentId===null){
+ actualData.push(res[i]);
+ }
+ }
+
+ $scope.treedata = actualData;
+
+ }).catch(err => {
+ $log.error('FunctionalMenuCtrl:getFunctionalMenu:: error ',err);
+ }).finally(()=> {
+ this.isLoadingTable = false;
+ });
+
+ };
+
+
+ let init = () => {
+ this.isLoadingTable = false;
+ this.functionalMenu = [];
+ getFunctionalMenu();
+ this.searchString = '';
+
+
+ };
+
+ this.filterByDropdownValue = item => {
+ if(this.filterByApp.value === ''){
+ return true;
+ }
+ return item.appName === this.filterByApp.value;
+ };
+
+ let getDialogTitle = (source) => {
+ switch (source) {
+ case 'edit':
+ return "Functional Menu - Edit";
+ case 'view':
+ return "Functional Menu - View";
+ case 'add':
+ return "Functional Menu - Add";
+ default:
+ return "Functional Menu";
+ };
+ };
+
+ $scope.reloadTreeStructure = (selectedItem,source) => {
+ getFunctionalMenu();
+ };
+ $scope.openMenuDetailsModal = (selectedItem,source) => {
+ let data = null;
+ let selectedMenuDetails = null;
+ console.log('selectedItem: ', selectedItem);
+
+ functionalMenuService.getMenuDetails(selectedItem.menuId)
+ .then(function( resp ){
+ selectedMenuDetails = resp;
+ $log.info('FunctionalMenuCtrl::openMenuDetailsModal: getMenuDetails: ', resp );
+
+ if(selectedItem){
+ data = {
+ menuItem: {menu: _.clone(selectedItem),menuDetails:_.clone(selectedMenuDetails)},
+ source: source,
+ title: getDialogTitle(source)
+ };
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html',
+ controller: 'MenuDetailsModalCtrl',
+ controllerAs: 'functionalMenuDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ $log.debug('FunctionalMenuCtrl::openMenuDetailsModal: updating table data...');
+ if(source==="edit") {
+ init();
+ }
+ }
+ });
+ });
+ };
+
+
+ $scope.createNewMenuItem = (selectedItem,source) => {
+
+ if(selectedItem != null && selectedItem.getLevel() >= 4){
+ confirmBoxService.showInformation('You are not allowed to have a menu item at a level greater than 4.').then(isConfirmed => {
+
+ });
+ return ;
+ }
+
+ let data = null;
+ let selectedMenuDetails = null;
+ functionalMenuService.getMenuDetails(selectedItem.menuId)
+ .then(function( resp ){
+ selectedMenuDetails = resp;
+
+ if((selectedItem.children===null || !selectedItem.children.length>0) &&
+ (!!selectedMenuDetails.url || !!selectedMenuDetails.appid || !!selectedMenuDetails.roles)){
+ confirmBoxService.showInformation('Warning: the child menu item "' + selectedItem.name + '" is already configured with an application. You can create a new mid-level menu item, and move this item under it.').then(isConfirmed => {
+ return;
+ });
+ }else{
+ if(selectedItem){
+ data = {
+ menuItem: {menu: _.clone(selectedItem)},
+ source:source,
+ title: getDialogTitle(source)
+ };
+ }
+
+ ngDialog.open({
+ templateUrl: 'app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html',
+ controller: 'MenuDetailsModalCtrl',
+ controllerAs: 'functionalMenuDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ $log.debug('FunctionalMenuCtrl::getMenuDetails: updating table data...');
+ init();
+ //getOnboardingWidgets();
+ }
+ });
+ }
+ });
+ };
+
+ $scope.deleteMenuItem = (selectedItem,source) => {
+ $log.info('FunctionalMenuCtrl:deleteMenuItem:: delete selectedItem: ', selectedItem);
+
+ if(selectedItem.children!=null && selectedItem.children.length>0){
+ confirmBoxService.showInformation('You are not allowed to delete a menu item that has children. You can only delete leaf menu items.').then(isConfirmed => {
+
+ });
+ }else{
+ confirmBoxService.deleteItem(selectedItem.name).then(isConfirmed => {
+ if(isConfirmed){
+ $log.info('FunctionalMenuCtrl:deleteMenuItem:: Deleting Menu Item :: name: '+selectedItem.name+'; menuId: '+selectedItem.menuId);
+ $log.info('FunctionalMenuCtrl:deleteMenuItem:: selectedItem: ', selectedItem);
+
+ functionalMenuService.deleteMenuItem(selectedItem.menuId).then(() => {
+ //TODO:Have to splice menu item
+ //this.widgetsList.splice(this.widgetsList.indexOf(widget), 1);
+ $log.info('FunctionalMenuCtrl:deleteMenuItem:: Removed Menu Item :: '+selectedItem.name);
+ init();
+ }).catch(err => {
+ $log.error(err);
+ });
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ }
+ };
+
+ init();
+ }
+ }
+ FunctionalMenuCtrl.$inject = ['$log', 'functionalMenuService','$scope', 'ngDialog', 'confirmBoxService'];
+ angular.module('ecompApp').controller('FunctionalMenuCtrl', FunctionalMenuCtrl);
+
+ angular.module('ecompApp').directive('jqTree', ['functionalMenuService','$log','confirmBoxService',function(functionalMenuService,$log,confirmBoxService){
+ return {
+ templateUrl: 'jqtree-tmpl.html',
+ link: function(scope, el, attrs){
+
+ var $jqTree = el.find('#jqTree').tree({
+ data: scope.treedata,
+ autoOpen: false,
+ dragAndDrop: true,
+ onCreateLi: function(node, $li) {
+ $li.attr('id', node.id.replace(/\s+/g,'_'));
+ }
+ });
+
+ el.find('#jqTree').bind('tree.move', function(event){
+ event.preventDefault();
+ console.log('moved_node', event.move_info.moved_node);
+ console.log('target_node', event.move_info.target_node);
+ console.log('position', event.move_info.position);
+ console.log('previous_parent', event.move_info.previous_parent);
+
+
+
+ if(event.move_info.target_node != null &&
+ ((event.move_info.position === 'after' && event.move_info.target_node.getLevel() > 4) ||
+ (event.move_info.position === 'inside' && event.move_info.target_node.getLevel() > 3))){
+ confirmBoxService.showInformation('You are not allowed to have a menu item at a level greater than 4.').then(isConfirmed => {
+
+ });
+ return ;
+ }
+
+ var confMsg = 'Are you sure you want to move "'+event.move_info.moved_node.name+'" ?';
+ if ((event.move_info.position === "inside") && (event.move_info.target_node.url != "")) {
+ // If we are moving UNDER a node that has a url associated with it, warn the user
+ // that all the app information will be removed if they do this.
+ confMsg = 'Warning: You are moving "'+event.move_info.moved_node.name+'" under "'+event.move_info.target_node.name+'", which has application information associated with it. This will cause all the application information from "'+event.move_info.target_node.name+'" to be deleted.';
+ }
+ confirmBoxService.moveMenuItem(confMsg).then(isConfirmed => {
+ if(isConfirmed){
+ /*
+ {
+ "menuId": 129,
+ "column": 3,
+ "text": "",
+ "parentMenuId": 37,
+ "url": "",
+ "appid": null,
+ "roles": null
+ }
+
+ The menuId for the menu item being moved
+ The column it is being moved to
+ The parentMenuId for the parent it is being moved under
+ */
+
+ // The target_node is the node before the position we are
+ // moving to. If we are moving to a lower column number, or
+ // to a new parent, we must adjust the column to be after
+ // the target_node.
+ var new_column = event.move_info.target_node.column;
+ var old_column = event.move_info.moved_node.column;
+ if ((event.move_info.moved_node.parentMenuId !=
+ event.move_info.target_node.parentMenuId) ||
+ (new_column < old_column)
+ ) {
+ new_column += 1;
+ }
+ var activeMenuItem = {
+ menuId:event.move_info.moved_node.menuId,
+ column:new_column,
+ text:"",
+ parentMenuId:event.move_info.target_node.parentMenuId,
+ url:"",
+ appid: null,
+ roles:null
+ };
+ // When position is "inside", this is a special case,
+ // where you are moving to the first column under
+ // a parent. The target_node is the parent node.
+ // So we need to set the column to 1, and the parentMenuId to the menuId of
+ // the target move.
+ if (event.move_info.position === "inside") {
+ console.log("special case: target_node is parent");
+ activeMenuItem.column = 1;
+ activeMenuItem.parentMenuId = event.move_info.target_node.menuId;
+ }
+
+
+ functionalMenuService.saveEditedMenuItem(activeMenuItem)
+ .then(() => {
+ $log.debug(' Menu Item moved');
+ scope.reloadTreeStructure();
+ }).catch(err => {
+ $log.error(err);
+ }).finally(()=>{
+ });
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+
+ //event.move_info.do_move();
+ });
+
+
+ $jqTree.jqTreeContextMenu(el.find('ul.dropdown-menu'), {
+ "view": function (node) {scope.openMenuDetailsModal(node,'view'); },
+ "edit": function (node) {scope.openMenuDetailsModal(node,'edit'); },
+ "delete": function (node) { scope.deleteMenuItem(node,'delete') },
+ "add": function (node) { scope.createNewMenuItem(node,'add') }
+ });
+
+ scope.$watch('treedata', function(oldValue, newValue){
+ if(oldValue !== newValue){
+ console.log('FunctionalMenuCtrl:: Tree value has changed in some way');
+ $jqTree.tree('loadData', scope.treedata);
+ $jqTree.tree('reload', function() {
+ console.log('FunctionalMenuCtrl:: Tree is reloaded');
+ });
+ }
+ });
+ }
+ };
+ }]);
+
+})();
+
+
diff --git a/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.tpl.html b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.tpl.html
new file mode 100644
index 00000000..1a1252a1
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/functionalMenu/functionalMenu.tpl.html
@@ -0,0 +1,46 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="functional-menu-main">
+ <div class="functional-menu-container" id="page-content">
+ <div id="title" class="w-ecomp-main-view-title">
+ <h1 class="heading-page" >Edit Functional Menu</h1>
+ </div>
+ <div id="tree1">
+ <div id="Service_Creation"></div>
+ </div>
+ <div id="jq-tree-div" jq-tree class="tree"></div>
+ <script type="text/ng-template" id="jqtree-tmpl.html">
+ <div id="jqTree"></div>
+ <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+ <li id="functional-menu-view-li"><a id="functional-menu-view-href" ng-href="#view"><i class="icon-data"></i> View</a></li>
+ <li class="divider"></li>
+ <li id="functional-menu-add-li"><a id="functional-menu-add-href" ng-href="#add"><i class="icon-plus"></i> Add</a></li>
+ <li class="divider"></li>
+ <li id="functional-menu-edit-li"><a id="functional-menu-edit-href" ng-href="#edit"><i class="icon-edit"></i> Edit</a></li>
+ <li class="divider"></li>
+ <li id="functional-menu-delete-li"><a id="functional-menu-delete-href" ng-href="#delete"><i class="icon-misc-trash"></i> Delete</a></li>
+ </ul>
+ </script>
+ <div class="functional-admin-button-container">
+ <button id="regenrate-functionalmenu-btn" class="btn btn-alt btn-small" ng-click="functionalMenu.regenerateFunctionalMenuAncestors()">Regenerate Menu</button>
+ <span class="n16r" style="color:black">Click when you are done with your changes</span>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/functionalMenu/jqTreeContextMenu.js b/ecomp-portal-FE-os/client/src/views/functionalMenu/jqTreeContextMenu.js
new file mode 100644
index 00000000..6e2be32d
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/functionalMenu/jqTreeContextMenu.js
@@ -0,0 +1,214 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+(function ($) {
+ if (!$.fn.tree) {
+ throw "Error jqTree is not loaded.";
+ }
+
+ $.fn.jqTreeContextMenu = function (menuElement, callbacks) {
+ //
+ // TODO:
+ // * Make sure the useContextMenu option is set in jqTree, either complain or set it automatically
+ // * Make menu fade in/out
+ //
+ var self = this;
+ var $el = this;
+
+ // The jQuery object of the menu div.
+ var $menuEl = menuElement;
+
+ // This hash holds all menu items that should be disabled for a specific node.
+ var nodeToDisabledMenuItems = {};
+
+ // Hide the menu div.
+ $menuEl.hide();
+
+ // Disable system context menu from beeing displayed.
+ $el.bind("contextmenu", function (e) {
+ e.preventDefault();
+ return false;
+ });
+
+ // Handle the contextmenu event sent from jqTree when user clicks right mouse button.
+ $el.bind('tree.contextmenu', function (event) {
+ var x = event.click_event.pageX;
+ var y = event.click_event.pageY;
+ var yPadding = 5;
+ var xPadding = 5;
+ var menuHeight = $menuEl.height();
+ var menuWidth = $menuEl.width();
+ var windowHeight = $(window).height();
+ var windowWidth = $(window).width();
+
+ if (menuHeight + y + yPadding > windowHeight) {
+ // Make sure the whole menu is rendered within the viewport.
+ y = y - menuHeight;
+ }
+ if (menuWidth + x + xPadding > windowWidth) {
+ // Make sure the whole menu is rendered within the viewport.
+ x = x - menuWidth;
+ }
+
+ // Handle disabling and enabling of menu items on specific nodes.
+ if (Object.keys(nodeToDisabledMenuItems).length > 0) {
+ if (event.node.name in nodeToDisabledMenuItems) {
+ var nodeName = event.node.name;
+ var items = nodeToDisabledMenuItems[nodeName];
+ if (items.length === 0) {
+ $menuEl.find('li').addClass('disabled');
+ $menuEl.find('li > a').unbind('click');
+ } else {
+ $menuEl.find('li > a').each(function () {
+ $(this).closest('li').removeClass('disabled');
+ var hrefValue = $(this).attr('href');
+ var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length)
+ if ($.inArray(value, items) > -1) {
+ $(this).closest('li').addClass('disabled');
+ $(this).unbind('click');
+ }
+ });
+ }
+ } else {
+ $menuEl.find('li.disabled').removeClass('disabled');
+ }
+ }
+
+ // Must call show before we set the offset (offset can not be set on display: none elements).
+ $menuEl.show();
+
+ $menuEl.offset({ left: x, top: y });
+
+ var dismissContextMenu = function () {
+ $(document).unbind('click.jqtreecontextmenu');
+ $el.unbind('tree.click.jqtreecontextmenu');
+ $menuEl.hide();
+ }
+ // Make it possible to dismiss context menu by clicking somewhere in the document.
+ $(document).bind('click.jqtreecontextmenu', function () {
+ dismissContextMenu();
+ });
+
+ // Dismiss context menu if another node in the tree is clicked.
+ $el.bind('tree.click.jqtreecontextmenu', function (e) {
+ dismissContextMenu();
+ });
+
+ // Make selection follow the node that was right clicked on.
+ var selectedNode = $el.tree('getSelectedNode');
+ if (selectedNode !== event.node) {
+ $el.tree('selectNode', event.node);
+ }
+
+ // Handle click on menu items, if it's not disabled.
+ var menuItems = $menuEl.find('li:not(.disabled) a');
+ if (menuItems.length !== 0) {
+ menuItems.unbind('click');
+ menuItems.click(function (e) {
+ e.stopImmediatePropagation();
+ dismissContextMenu();
+ var hrefAnchor = e.currentTarget.attributes.href.nodeValue;
+ var funcKey = hrefAnchor.slice(hrefAnchor.indexOf("#") + 1, hrefAnchor.length)
+ var callbackFn = callbacks[funcKey];
+ if (callbackFn) {
+ callbackFn(event.node);
+ }
+ return false;
+ });
+ }
+ });
+
+ this.disable = function () {
+ if (arguments.length === 0) {
+ // Called as: api.disable()
+ $menuEl.find('li:not(.disabled)').addClass('disabled');
+ $menuEl.find('li a').unbind('click');
+ nodeToDisabledMenuItems = {};
+ } else if (arguments.length === 1) {
+ // Called as: api.disable(['edit','remove'])
+ var items = arguments[0];
+ if (typeof items !== 'object') {
+ return;
+ }
+ $menuEl.find('li > a').each(function () {
+ var hrefValue = $(this).attr('href');
+ var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length)
+ if ($.inArray(value, items) > -1) {
+ $(this).closest('li').addClass('disabled');
+ $(this).unbind('click');
+ }
+ });
+ nodeToDisabledMenuItems = {};
+ } else if (arguments.length === 2) {
+ // Called as: api.disable(nodeName, ['edit','remove'])
+ var nodeName = arguments[0];
+ var items = arguments[1];
+ nodeToDisabledMenuItems[nodeName] = items;
+ }
+ };
+
+ this.enable = function () {
+ if (arguments.length === 0) {
+ // Called as: api.enable()
+ $menuEl.find('li.disabled').removeClass('disabled');
+ nodeToDisabledMenuItems = {};
+ } else if (arguments.length === 1) {
+ // Called as: api.enable(['edit','remove'])
+ var items = arguments[0];
+ if (typeof items !== 'object') {
+ return;
+ }
+
+ $menuEl.find('li > a').each(function () {
+ var hrefValue = $(this).attr('href');
+ var value = hrefValue.slice(hrefValue.indexOf("#") + 1, hrefValue.length)
+ if ($.inArray(value, items) > -1) {
+ $(this).closest('li').removeClass('disabled');
+ }
+ });
+
+ nodeToDisabledMenuItems = {};
+ } else if (arguments.length === 2) {
+ // Called as: api.enable(nodeName, ['edit','remove'])
+ var nodeName = arguments[0];
+ var items = arguments[1];
+ if (items.length === 0) {
+ delete nodeToDisabledMenuItems[nodeName];
+ } else {
+ var disabledItems = nodeToDisabledMenuItems[nodeName];
+ for (var i = 0; i < items.length; i++) {
+ var idx = disabledItems.indexOf(items[i]);
+ if (idx > -1) {
+ disabledItems.splice(idx, 1);
+ }
+ }
+ if (disabledItems.length === 0) {
+ delete nodeToDisabledMenuItems[nodeName];
+ } else {
+ nodeToDisabledMenuItems[nodeName] = disabledItems;
+ }
+ }
+ if (Object.keys(nodeToDisabledMenuItems).length === 0) {
+ $menuEl.find('li.disabled').removeClass('disabled');
+ }
+ }
+ };
+ return this;
+ };
+} (jQuery));
diff --git a/ecomp-portal-FE-os/client/src/views/search/search.controller.js b/ecomp-portal-FE-os/client/src/views/search/search.controller.js
new file mode 100644
index 00000000..3c3c2a03
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/search/search.controller.js
@@ -0,0 +1,198 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+
+ class SearchCtrl {
+ constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService, dashboardService) {
+ $scope.firstName="";
+ $scope.lastName="";
+
+ function showHideSearchSnippet() {
+
+ setTimeout(function() {
+ jQuery("#mainSearchSnippet").click();
+ },500);
+
+ setTimeout(function() {
+ jQuery("#mainSearchText").focus();
+ },1000);
+ }
+
+ jQuery("#mainSearchDiv").keyup(function(event){
+ if(event.keyCode == 13){
+ // there is a watch on this variable which will trigger the database pull
+ dashboardService.searchString = jQuery("#mainSearchText").val();
+
+ // opens the popup
+ var popupDomObj = jQuery("[content='searchSnippet.html']");
+ if(popupDomObj.length == 0) {
+ showHideSearchSnippet();
+ } else {
+ jQuery("#mainSearchSnippet").click();
+ showHideSearchSnippet();
+ }
+
+
+
+ }
+ });
+
+
+
+ }
+
+
+ }
+
+
+ class SearchSnippetCtrl {
+ constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService, dashboardService,applicationsService, $window, $state) {
+ $scope.firstName="";
+ $scope.lastName="";
+ $scope.goToUrl = goToUrl;
+ $scope.dService = dashboardService;
+
+ $scope.searchResDialog = false;
+ $scope.searchDialogToggle = function($event){
+ var keyCode = $event.which || $event.keyCode;
+ if (keyCode === 13) {
+ $scope.searchResDialog = true;
+ dashboardService.searchString = jQuery("#mainSearchText").val();
+ }
+
+ }
+
+ window.onclick = function() {
+ if ($scope.searchResDialog) {
+ $scope.searchResDialog = false;
+ $scope.$apply();
+ }
+ };
+ function goToUrl (item, type) {
+ $log.info("goToUrl called")
+ $log.info(item + "/" + type);
+
+
+ if(type == 'intra') {
+
+ var intraSearcLink = "";
+ var intraSpecSearcLink = intraSearcLink + encodeURIComponent(dashboardService.searchString);
+ $window.open(intraSpecSearcLink, '_blank');
+
+ } else if (type == 'extra') {
+ var extraSearcLink = "";
+ var extraSpecSearcLink = extraSearcLink + encodeURIComponent(dashboardService.searchString);
+ $window.open(extraSpecSearcLink, '_blank');
+ }
+
+ let url = item.target;
+ let restrictedApp = item.uuid;
+ let getAccessState = "root.getAccess"
+ if (!url) {
+
+ applicationsService.goGetAccessAppName = item.name;
+ if($state.current.name == getAccessState)
+ $state.reload();
+ else
+ $state.go(getAccessState);
+ //$log.info('No url found for this application, doing nothing..');
+ return;
+ }
+
+ if (restrictedApp != "true") {
+ $window.open(url, '_blank');
+ } else {
+ if(item.url=="root.access"){
+ $state.go(url);
+ var tabContent = { id: new Date(), title: 'Home', url: url };
+ $cookies.putObject('addTab', tabContent );
+ } else {
+ var tabContent = { id: new Date(), title: item.name, url: url };
+ $cookies.putObject('addTab', tabContent );
+ }
+ }
+
+ }
+
+ function getItems(searchString) {
+
+ var items;
+ var itemMap = dashboardService.getSearchAllByStringResults(searchString)
+ .then(res => {
+ $scope.items = res;
+
+
+ }).catch(err => {
+ $scope.items = [];
+ $log.error('Couldnt get search results...', err)
+ });
+
+ }
+
+ $scope.$watch('dService.searchString', function(searchString) {
+ if(searchString != undefined )
+ getItems(searchString);
+
+ });
+
+
+
+ }
+
+
+
+ }
+
+
+
+ SearchCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService', 'dashboardService'];
+ SearchSnippetCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService', 'dashboardService','applicationsService', '$window','$state'];
+ angular.module('ecompApp').controller('searchCtrl', SearchCtrl);
+ angular.module('ecompApp').controller('searchSnippetCtrl', SearchSnippetCtrl);
+ angular.module('ecompApp').directive('searchBox', function() {
+ return {
+ restrict: "E",
+ templateUrl: 'app/views/search/search.tpl.html',
+ link: function(scope, element) {
+
+ //var iframeId = "#tabframe-" + scope.$parent.tab.title.split(' ').join('-');
+ // jQuery(iframeId).load(function() {
+ // alert("hello");
+ // }); //.attr("src",'{{tab.content | trusted}}' ); //src='{{tab.content | trusted}}'
+ // jQuery(iframeId).attr('src', '{{tab.content | trusted}}');
+
+ //element.childNodes[0].on('load', function() {
+ // alert('hello');
+ //});
+ }
+ }
+ });
+ angular.module( 'ecompApp' ).config( [
+ '$compileProvider',
+ function( $compileProvider )
+ {
+ $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|chrome-extension|qto):/);
+ }
+ ]);
+
+})();
+
+
diff --git a/ecomp-portal-FE-os/client/src/views/search/search.controller.spec.js b/ecomp-portal-FE-os/client/src/views/search/search.controller.spec.js
new file mode 100644
index 00000000..3841a2b3
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/search/search.controller.spec.js
@@ -0,0 +1,19 @@
+/*-
+ * ================================================================================
+ * ECOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
diff --git a/ecomp-portal-FE-os/client/src/views/search/search.tpl.html b/ecomp-portal-FE-os/client/src/views/search/search.tpl.html
new file mode 100644
index 00000000..a08d0aa6
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/search/search.tpl.html
@@ -0,0 +1,84 @@
+<!--
+ ================================================================================
+ eCOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="search-div" ng-controller="searchSnippetCtrl">
+ <div class="notification-div">
+ <div class="form-field form-field__small">
+ <div class="search-input">
+ <input id="mainSearchText" type="text"
+ ng-keypress="searchDialogToggle($event)"
+ placeholder="What are you looking for?"
+ class="form-field form-field__small" />
+ </div>
+ <span class="icon-search-span"> <i class="icon-search">&nbsp;</i>
+ </span>
+
+ </div>
+ </div>
+ <div ng-show="searchResDialog" class="search-res-dialog">
+ <div id="reg-header-snippet">
+
+ <div tabindex="0" class="reg-profileDetails" id="reg-searchPop-id">
+ <div id="contentVertical" att-scrollbar="y"
+ class="ng-scope vertical" style="position: relative;">
+ <ul class="searchUl">
+ <li><a class="icon-tiles"></a> <a id="search-app-title"
+ ng-click="goToUrl(item)" class="searchLiHeader">Applications:</a>
+ </li>
+ <li ng-repeat='item in items.application'><a
+ id="search-app-item-{{item.name.split(' ').join('-')}}-{{$index}}"
+ ng-click="goToUrl(item)" class="searchLiItems">{{item.name}}</a>
+ </li>
+ </ul>
+
+ <ul class="searchUl">
+ <li><a class="icon-tiles-small"></a> <a
+ id="seach-functional-menu-title" ng-click="goToUrl(item)"
+ class="searchLiHeader">Functional Menus:</a></li>
+ <li ng-repeat='item in items.menu'><a
+ id="search-functional-menu-item-{{item.name.split(' ').join('-')}}-{{$index}}"
+ ng-click="goToUrl(item)" class="searchLiItems">{{item.name}}</a>
+ </li>
+ </ul>
+
+
+ <ul class="searchUl">
+ <li><a class="full-linear-icon-person"
+ style="font-size: 16px;"></a> <a id="search-user-title"
+ ng-click="goToUrl(item)" class="searchLiHeader">Users:</a></li>
+ <li ng-repeat='item in items.user'><a
+ id="search-user-item-{{item.name.split(' ').join('-')}}-{{$index}}"
+ ng-href="mailto:{{item.target}}" class="searchLiItems">{{item.name}}</a>
+
+ </li>
+ </ul>
+
+ <ul class="searchUl">
+ <li><a class="full-linear-icon-person"
+ style="font-size: 16px;"></a> <a id="search-widgets-title"
+ ng-click="goToUrl(item)" class="searchLiHeader">Widgets:</a></li>
+ <li ng-repeat='item in items.widget'><a
+ id="search-widgets-item-{{item.name.split(' ').join('-')}}-{{$index}}"
+ ng-href="widgetCatalog" class="searchLiItems">{{item.name}}</a></li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/support/contact-us/contact-us.aux.html b/ecomp-portal-FE-os/client/src/views/support/contact-us/contact-us.aux.html
new file mode 100644
index 00000000..822c5099
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/support/contact-us/contact-us.aux.html
@@ -0,0 +1,25 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="contactUs-general-div">
+ <span class="contactUs-txt">To give feedback or report an issue
+ to the OpenECOMP Portal team, send email to <a
+ ng-href="mailto:{{contact.portalInfo_Address}}" target="_top">{{contact.portalInfo_Address}}</a>.
+
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/support/get-access/get-accessinfo.html b/ecomp-portal-FE-os/client/src/views/support/get-access/get-accessinfo.html
new file mode 100644
index 00000000..a8a42c1d
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/support/get-access/get-accessinfo.html
@@ -0,0 +1,24 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="get-access-general-div">
+ <span> Request access for following functions by sending email to the Application's Admin. For Admin contact details, please
+ <a href="contactUs">click here</a>.
+ </span>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/tabs/tabs.tpl.html b/ecomp-portal-FE-os/client/src/views/tabs/tabs.tpl.html
new file mode 100644
index 00000000..d11608e5
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/tabs/tabs.tpl.html
@@ -0,0 +1,47 @@
+<!--
+ ================================================================================
+ eCOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<div class="w-ecomp-tabs" ng-cloak ng-style="{bottom: tabBottom}">
+ <div class="tab-container">
+ <md-tabs md-selected="selectedIndex" md-dynamic-height="true" md-autoselect="true">
+ <md-tab ng-repeat="tab in tabs" md-on-select="selectTab(tab.title);auditLog(tab)">
+ <md-tab-label>
+ <span style="overflow: hidden;white-space: nowrap;display: block;"
+ id="tab-{{tab.title.split(' ').join('-')}}">
+ <i tooltip="{{tab.title}}" tooltip-aria-label="Tooltip with HTML Content"
+ tooltip-style="light" tooltip-placement="right" tooltip-trigger="mouseover">{{adjustTitle(tab.title)}}</i>
+ </span>
+ <span class="tab-cross-sign tab-cross-sign-opensrc">
+ <i class="ion-close-round" ng-click="removeTab($event, $index)" ng-show="'{{tab.title}}'!='Home'"></i>
+ </span>
+ <!-- the tab close button -->
+ </md-tab-label>
+ <md-tab-body>
+ <main-area id="tab-home" ng-show="'{{tab.title}}'!='Home'"></main-area>
+ </md-tab-body>
+ </md-tab> <!-- this is the open new tab button -->
+ </md-tabs>
+ </div>
+ <div style=" position: fixed; right: 290px; top: 62px;">
+
+ </div>
+ <div ng-controller="searchCtrl" style=" position: fixed; right: 0; top: 58px;">
+ <search-box id="mainSearchDiv" />
+ </div>
+</div>
diff --git a/ecomp-portal-FE-os/client/src/views/userbar/userbar.controller.js b/ecomp-portal-FE-os/client/src/views/userbar/userbar.controller.js
new file mode 100644
index 00000000..4e2b10b9
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/userbar/userbar.controller.js
@@ -0,0 +1,229 @@
+/*-
+ * ================================================================================
+ * eCOMP Portal
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ================================================================================
+ */
+'use strict';
+
+(function () {
+
+ class UserbarCtrl {
+ constructor(userProfileService,userbarUpdateService, $log, $rootScope , $interval,$scope,$timeout,dashboardService) {
+ this.$log = $log;
+ var debug = false;
+ this.userProfileService = userProfileService;
+ this.$rootScope = $rootScope;
+ $rootScope.isAdminPortalAdmin = false;
+ $scope.updateRate = 10000; //default online user bar refreshing rate
+ var intervalPromise = null;
+ $scope.myservice = userbarUpdateService;
+ $scope.userList=this.userLists;
+ var websocket = '';
+ var currentUser = '';
+ var remoteUser = '';
+ var f = '';
+
+
+ function socketSetup(initialPageVisit,_currentUser, _remoteUser, _f) {
+
+
+
+ if( true) {
+
+ var href = window.location.href;
+ var hostPatt = new RegExp(window.location.host +"/[^/]*");
+ var res = hostPatt.exec(href);
+ var protocol = window.location.protocol.replace("http","ws");
+ var signalingServerPath = protocol + "//" + res + "/opencontact";
+ var wsUri = signalingServerPath;
+ console.log("Connecting to " + wsUri);
+ websocket = new WebSocket(wsUri);
+ //localStorage.notifySocket = JSON.stringify(websocket);
+ //window.top.notifySocket = websocket;
+
+ currentUser = _currentUser;
+ remoteUser = _remoteUser;
+ f = socketSend;
+
+
+ }
+
+ //var websocket = JSON.parse(localStorage.notifySocket || "{}") ;
+ if( websocket != null) {
+ websocket.onopen = function(evt) { onOpen(evt); };
+ websocket.onmessage = function(evt) { onMessage(evt); };
+ websocket.onerror = function(evt) { onError(evt); };
+
+ }
+
+ //if(f != undefined)
+ // f();
+
+ //window.top.notifySocket.send("");
+ }
+
+ function socketSend(currentUser, remoteUser) {
+
+
+
+ websocket.send(JSON.stringify({
+ from: currentUser,
+ to: remoteUser
+ }));
+
+ //window.top.notifySocket.send("");
+ }
+
+
+
+
+ function onOpen(evt) {
+ console.log("onOpen");
+ //writeToScreen("CONNECTED");
+
+ if(f != undefined)
+ f(currentUser, remoteUser);
+
+ }
+
+ function onMessage(evt) {
+ if (typeof evt.data == "string") {
+ writeToScreen("RECEIVED (text): " + evt.data);
+ var chatObject = JSON.parse(evt.data);
+ if(confirm("User " + chatObject.from + " is requesting a chat session with you. Please click ok to accept")== true) {
+
+ var url = userProfileService.getSortedUserIdCombination(chatObject.from, chatObject.to);
+ var win = window.open(url, '_blank');
+ win.focus();
+ } else {
+ //
+ }
+
+
+ } else {
+ writeToScreen("RECEIVED (binary): " + evt.data);
+ }
+ }
+
+ function onError(evt) {
+ writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
+ }
+
+ function writeToScreen(message) {
+ console.log(message);
+ }
+
+ function updateActiveUsers() {
+ // $log.debug('UserbarCtrl::updateActiveUsers: intervalPromise is ' + intervalPromise);
+ // $log.info("UserbarCtrl:updateActiveUsers invoked.")
+ // decrement the counter every time updateActiveUser is called;
+ userbarUpdateService.decrementRefreshCount();
+ userProfileService.getActiveUser()
+ .then(res=> {
+ if (debug)
+ $log.debug('UserbarCtrl::updateActiveUsers: service returned ' + JSON.stringify(res));
+ if (res==null) {
+ $log.error('UserbarCtrl::updateActiveUsers: failed to get active user');
+ $scope.stop();
+ } else {
+ $scope.userList = [];
+ // defend against huge list that should never happen.
+ var maxItems = 25;
+ if (res.length < maxItems)
+ maxItems = res.length;
+ for (var i=0; i < maxItems; i++) {
+ var data= {
+ userId:res[i],
+ linkQ:userProfileService.getSortedUserIdCombination(userProfileService.userProfile.orgUserId , res[i])
+ }
+ $scope.userList.push(data);
+ }
+ if (debug)
+ $log.debug('UserbarCtrl::updateActiveUsers: user list ' + JSON.stringify($scope.userList));
+ }
+ }).catch(err=> {
+ $log.error('UserbarCtrl::updateActiveUsers: caught exception: ' + err);
+ // Do not show stale result
+ $scope.userList = [];
+ $scope.stop();
+ }).finally(() => {
+ var footerOff = $('#online-userbar').offset().top;
+ var headOff = $('#footer').offset().top;
+ var userbarHeight= parseInt($(".online-user-container").css('height'),10);
+ var defaultOffSet = 45;
+ $(".online-user-container").css({
+ "height" : headOff - footerOff-defaultOffSet
+ });
+ });
+ }
+
+ $scope.start = function(rate) {
+ // stops any running interval to avoid two intervals running at the same time
+ $scope.stop();
+ // store the interval promise
+ intervalPromise = $interval(updateActiveUsers, rate);
+ };
+
+ $scope.stop = function() {
+ if (intervalPromise != null) {
+ $interval.cancel(intervalPromise);
+ intervalPromise = null;
+ }
+ };
+
+ $scope.$watch('myservice.getRefreshCount()', function (newVal,oldVal) {
+ //$log.info("refresh "+$scope.myservice.refreshCount + " more time(s)");
+ if (newVal<=0) {
+ // $log.info("UserbarCtrl: stopped refreshing online users");
+ $scope.stop();
+ } else if (newVal== $scope.myservice.maxCount){
+ // $log.info("UserbarCtrl: start refreshing online users at every "+($scope.updateRate/1000)+" sec");
+ // initial update of activate users
+ updateActiveUsers();
+ $scope.start($scope.updateRate);
+ }
+ });
+
+ // Load the active users.
+ //updateActiveUsers();
+ dashboardService.getOnlineUserUpdateRate().then(res=> {
+ // $log.info('getting Online User Update Rate init***********************', res);
+ if (res == null || res.response == null) {
+ $log.error('UserbarCtlr: failed to get online user update rate or duration, check system.properties file.');
+ } else {
+ // $log.debug('UserbarCtlr: got online User Update Rate ' + res.response);
+ var rate = parseInt(res.response.onlineUserUpdateRate);
+ // var updateRate = parseInt(res.response.onlineUserUpdateRate);
+ var duration = parseInt(res.response.onlineUserUpdateDuration);
+ userbarUpdateService.setMaxRefreshCount(parseInt(duration/rate)+1);
+ userbarUpdateService.setRefreshCount(userbarUpdateService.maxCount);
+ // $scope.refreshCount = userbarUpdateService.getRefreshCount();
+
+ if (rate != NaN && duration != NaN) {
+ // $log.debug('UserbarCtlr: scheduling function at interval ' + millis);
+ $scope.updateRate=rate;
+ $scope.start($scope.updateRate);
+ }
+ }
+ }).catch(err=> {
+ $log.error('UserbarCtlr: getOnlineUserUpdateRate() failed: ' + err);
+ });
+ }
+ }
+ UserbarCtrl.$inject = ['userProfileService', 'userbarUpdateService', '$log', '$rootScope','$interval','$scope','$timeout','dashboardService'];
+ angular.module('ecompApp').controller('UserbarCtrl', UserbarCtrl);
+})();
diff --git a/ecomp-portal-FE-os/client/src/views/userbar/userbar.tpl.html b/ecomp-portal-FE-os/client/src/views/userbar/userbar.tpl.html
new file mode 100644
index 00000000..b40a1748
--- /dev/null
+++ b/ecomp-portal-FE-os/client/src/views/userbar/userbar.tpl.html
@@ -0,0 +1,20 @@
+<!--
+ ================================================================================
+ ECOMP Portal
+ ================================================================================
+ Copyright (C) 2017 AT&T Intellectual Property
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ================================================================================
+ -->
+<right-menu id="userbar.userList" user-list="userList" class="w-ecomp-sidebar"></right-menu>