diff options
Diffstat (limited to 'ecomp-portal-FE-os/client/src')
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> 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> 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>
+
+
+
+ <div class="usr-app-roles" ng-show=displyUserAppCatalogRoles>
+ <div class="userApp-Catalog-Roles-label">{{title}}</div>
+
+ <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>
+
+ <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"> </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>
|