summaryrefslogtreecommitdiffstats
path: root/ecomp-portal-FE/client/app/views
diff options
context:
space:
mode:
Diffstat (limited to 'ecomp-portal-FE/client/app/views')
-rw-r--r--ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js214
-rw-r--r--ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js130
-rw-r--r--ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html66
-rw-r--r--ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less99
-rw-r--r--ecomp-portal-FE/client/app/views/admins/admins.controller.js151
-rw-r--r--ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/admins/admins.less47
-rw-r--r--ecomp-portal-FE/client/app/views/admins/admins.tpl.html82
-rw-r--r--ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js244
-rw-r--r--ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html183
-rw-r--r--ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less122
-rw-r--r--ecomp-portal-FE/client/app/views/applications/applications.controller.js111
-rw-r--r--ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/applications/applications.less45
-rw-r--r--ecomp-portal-FE/client/app/views/applications/applications.tpl.html100
-rw-r--r--ecomp-portal-FE/client/app/views/catalog/catalog.controller.js166
-rw-r--r--ecomp-portal-FE/client/app/views/catalog/catalog.less439
-rw-r--r--ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html91
-rw-r--r--ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js62
-rw-r--r--ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html42
-rw-r--r--ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html31
-rw-r--r--ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js41
-rw-r--r--ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less53
-rw-r--r--ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html28
-rw-r--r--ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html31
-rw-r--r--ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html27
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html152
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js422
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less101
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js312
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js78
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard.less766
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html346
-rw-r--r--ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js47
-rw-r--r--ecomp-portal-FE/client/app/views/errors/error.404.tpl.html31
-rw-r--r--ecomp-portal-FE/client/app/views/errors/error.controller.js36
-rw-r--r--ecomp-portal-FE/client/app/views/errors/error.tpl.html26
-rw-r--r--ecomp-portal-FE/client/app/views/footer/footer.controller.js49
-rw-r--r--ecomp-portal-FE/client/app/views/footer/footer.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/footer/footer.less53
-rw-r--r--ecomp-portal-FE/client/app/views/footer/footer.tpl.html36
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js384
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html35
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html106
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less103
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js331
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less70
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html48
-rw-r--r--ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js192
-rw-r--r--ecomp-portal-FE/client/app/views/header/header.controller.js419
-rw-r--r--ecomp-portal-FE/client/app/views/header/header.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/header/header.less380
-rw-r--r--ecomp-portal-FE/client/app/views/header/header.tpl.html277
-rw-r--r--ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js134
-rw-r--r--ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less61
-rw-r--r--ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html69
-rw-r--r--ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js244
-rw-r--r--ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js77
-rw-r--r--ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less164
-rw-r--r--ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html59
-rw-r--r--ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js284
-rw-r--r--ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js1
-rw-r--r--ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less137
-rw-r--r--ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html123
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js85
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html32
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less99
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js127
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html73
-rw-r--r--ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less56
-rw-r--r--ecomp-portal-FE/client/app/views/search/search.controller.js184
-rw-r--r--ecomp-portal-FE/client/app/views/search/search.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/search/search.less55
-rw-r--r--ecomp-portal-FE/client/app/views/search/search.tpl.html82
-rw-r--r--ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js143
-rw-r--r--ecomp-portal-FE/client/app/views/sidebar/sidebar.less37
-rw-r--r--ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html20
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js194
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less159
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html136
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js166
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less104
-rw-r--r--ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html125
-rw-r--r--ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js70
-rw-r--r--ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/support/get-access/get-access.less67
-rw-r--r--ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html88
-rw-r--r--ecomp-portal-FE/client/app/views/tabs/tabframe.html22
-rw-r--r--ecomp-portal-FE/client/app/views/tabs/tabs.controller.js231
-rw-r--r--ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js80
-rw-r--r--ecomp-portal-FE/client/app/views/tabs/tabs.less658
-rw-r--r--ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html46
-rw-r--r--ecomp-portal-FE/client/app/views/userbar/userbar.controller.js262
-rw-r--r--ecomp-portal-FE/client/app/views/userbar/userbar.less41
-rw-r--r--ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html20
-rw-r--r--ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js211
-rw-r--r--ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js222
-rw-r--r--ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html70
-rw-r--r--ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less126
-rw-r--r--ecomp-portal-FE/client/app/views/users/users.controller.js187
-rw-r--r--ecomp-portal-FE/client/app/views/users/users.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/users/users.less59
-rw-r--r--ecomp-portal-FE/client/app/views/users/users.tpl.html105
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js202
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html132
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less94
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widgets.controller.js151
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js19
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widgets.less60
-rw-r--r--ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html77
114 files changed, 14144 insertions, 0 deletions
diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.js
new file mode 100644
index 00000000..4d77a97c
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.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.
+ * ================================================================================
+ */
+
+'use strict';
+(function () {
+ class NewAdminModalCtrl {
+ constructor($log, adminsService, $scope, confirmBoxService, utilsService, $location) {
+
+ let init = () => {
+ this.isSaving = false;
+ this.originalApps = [];
+ /* istanbul ignore if */
+ 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.getAdminAppsRoles();
+ }
+ } else {
+ this.isShowBack = true;
+ this.selectedUser = null;
+ this.dialogState = 1;
+ }
+
+ $log.info('NewAdminModalCtrl::initiated');
+ this.appsOrder = [];
+ };
+
+ let orderList = (apps) => {
+ this.appsOrder = [];
+ for (var i = 0; i < apps.length; i++) {
+ if (apps[i].isAdmin) {
+ this.appsOrder.push(apps[i].id);
+ }
+ }
+ };
+
+ this.orderFilter = app => {
+ if (!app || !app.id || !this.appsOrder.length) {
+ return;
+ }
+ return this.appsOrder.indexOf(app.id);
+ };
+
+ this.getAdminAppsRoles = () => {
+ if (!this.selectedUser || !this.selectedUser.orgUserId) {
+ $log.error('No user is selected / searchUsers is InProgress');
+ this.dialogState = 1;
+ return;
+ }
+ adminsService.getAdminAppsRoles(this.selectedUser.orgUserId).then(roles => {
+ $log.debug('apps roles res: ', JSON.stringify(roles));
+ if (!roles.appsRoles) {
+ return;
+ }
+
+ this.adminAppsRoles = [];
+ for (var i = 0; i < roles.appsRoles.length; i++) {
+ if (!roles.appsRoles[i].restrictedApp) {
+ $log.debug('pushing: {id: ', roles.appsRoles[i].id,
+ 'name: ', roles.appsRoles[i].appName,
+ 'restrictedApp: ', roles.appsRoles[i].restrictedApp,
+ 'isAdmin: ', roles.appsRoles[i].isAdmin, '}');
+ this.adminAppsRoles.push({
+ id: roles.appsRoles[i].id,
+ appName: roles.appsRoles[i].appName,
+ isAdmin: roles.appsRoles[i].isAdmin,
+ restrictedApp: roles.appsRoles[i].restrictedApp
+ });
+ }
+ }
+ this.dialogState = 2;
+ this.adminAppsRoles = this.adminAppsRoles.sort(getSortOrder("appName"));
+
+ orderList(roles.appsRoles);
+ if (this.appsOrder != null) {
+ for (var j = 0; j < this.appsOrder.length; j++) {
+ this.originalApps.push(this.appsOrder[j]);
+ }
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ };
+
+ let getSortOrder = (prop) => {
+ return function (a, b) {
+ if (a[prop].toLowerCase() > b[prop].toLowerCase()) {
+ return 1;
+ } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ this.setSelectedUser = (user) => {
+ $log.debug('selected user: ', user);
+ this.selectedUser = user;
+ };
+
+ this.unadminApp = (app) => {
+ confirmBoxService.deleteItem(app.appName).then(confirmed => {
+ if (confirmed === true) {
+ app.isAdmin = false;
+ this.appsOrder.splice(this.appsOrder.indexOf(app.id), 1);
+ }
+ }).catch(err => {
+ $log(err);
+ });
+ };
+
+ this.updateAdminAppsRoles = () => {
+ if (!this.selectedUser || !this.selectedUser.orgUserId || !this.adminAppsRoles) {
+ return;
+ }
+ this.isSaving = true;
+ $log.debug('going to update user: ' + this.selectedUser.orgUserId + ' with app roles: ' + JSON.stringify(this.adminAppsRoles));
+ confirmBoxService.makeAdminChanges('Are you sure you want to make these admin changes?')
+ .then(confirmed => {
+ adminsService.updateAdminAppsRoles({
+ orgUserId: this.selectedUser.orgUserId,
+ appsRoles: this.adminAppsRoles
+ })
+ .then(res => {
+ $log.debug('Admin apps roles updated successfully!', res);
+ this.remindToAddUserIfNecessary();
+ $scope.closeThisDialog(true);
+ }).catch(err => {
+ $log.error('NewAdminModalCtrl.updateAdminAppsRoles:: Failed - ' + err);
+ }).finally(()=> {
+ this.isSaving = false;
+ })
+ });
+ };
+
+ this.navigateBack = () => {
+ if (this.dialogState === 1) {
+ }
+ if (this.dialogState === 2) {
+ this.dialogState = 1;
+ }
+ };
+
+ init();
+
+ $scope.$watch('newAdmin.selectedNewApp', (newVal) => {
+ if (!newVal || newVal.isAdmin === undefined) {
+ return;
+ }
+ let app = _.find(this.adminAppsRoles, {id: newVal.id});
+ if (app) {
+ app.isAdmin = true;
+ this.appsOrder.push(app.id);
+ }
+ this.selectedNewApp = null;
+ });
+
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+
+ this.remindToAddUserIfNecessary = () => {
+ var adminAddedToNewApp = false;
+ if ((this.originalApps != null) && (this.originalApps.length > 0)) {
+ for (var i = 0; i < this.appsOrder.length; i++) {
+ var foundApp = false;
+ for (var j = 0; j < this.originalApps.length; j++) {
+ if (this.originalApps[j] == this.appsOrder[i]) {
+ foundApp = true;
+ }
+ }
+ if (foundApp == false) {
+ adminAddedToNewApp = true;
+ break;
+ }
+ }
+ } else {
+ adminAddedToNewApp = true;
+ }
+
+ if (adminAddedToNewApp == true) {
+ confirmBoxService.confirm('Add this person as an application user? This allows them to access the application from OpenECOMP Portal. Press OK to go to the Add Users page.')
+ .then(confirmed => {
+ if (confirmed == true) {
+ $location.path('/users');
+ }
+ });
+ }
+ }
+
+ }
+ }
+ NewAdminModalCtrl.$inject = ['$log', 'adminsService', '$scope', 'confirmBoxService', 'utilsService', '$location'];
+ angular.module('ecompApp').controller('NewAdminModalCtrl', NewAdminModalCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js
new file mode 100644
index 00000000..c0f73078
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.controller.spec.js
@@ -0,0 +1,130 @@
+/*-
+ * ================================================================================
+ * 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';
+
+describe('Controller: NewAdminCtrl ', () => {
+ beforeEach(module('ecompApp'));
+
+ beforeEach(inject((_CacheFactory_)=> {
+ _CacheFactory_.destroyAll();
+ }));
+
+
+ let newCtrl, $controller, $q, $rootScope, $log;
+
+ beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> {
+ [$controller, $q, $rootScope, $log] = [_$controller_, _$q_, _$rootScope_, _$log_];
+ }));
+
+ let deferredAdminAppsRoles, deferredUpdateRolesRes;
+ let adminsServiceMock;
+ beforeEach(()=> {
+ [deferredAdminAppsRoles, deferredUpdateRolesRes] = [$q.defer(), $q.defer()];
+
+ adminsServiceMock = jasmine.createSpyObj('adminsServiceMock', ['getAdminAppsRoles', 'updateAdminAppsRoles']);
+
+ adminsServiceMock.getAdminAppsRoles.and.returnValue(deferredAdminAppsRoles.promise);
+ adminsServiceMock.updateAdminAppsRoles.and.returnValue(deferredUpdateRolesRes.promise);
+
+ newCtrl = $controller('NewAdminModalCtrl', {
+ $log: $log,
+ adminsService: adminsServiceMock,
+ $scope: $rootScope
+ });
+ });
+
+ it('should init default values when loading the controller', ()=> {
+ expect(newCtrl.dialogState).toBe(1);
+ expect(newCtrl.selectedUser).toBe(null);
+ });
+
+ it('should populate admin apps roles and move to the next screen when adminsService.getAdminAppsRoles succeeded', ()=> {
+ let userApps = {appsRoles: [{id: 1, isAdmin: false}, {id: 2, isAdmin: true}]};
+ deferredAdminAppsRoles.resolve(userApps);
+
+ newCtrl.selectedUser = {orgUserId: 'orgUserId'};
+
+ newCtrl.getAdminAppsRoles();
+ $rootScope.$apply();
+
+ expect(adminsServiceMock.getAdminAppsRoles).toHaveBeenCalledWith(newCtrl.selectedUser.userId);
+ expect(newCtrl.adminAppsRoles).toEqual(userApps.appsRoles);
+ expect(newCtrl.dialogState).toBe(2);
+ });
+
+ it('should log the error when adminsService.getAdminAppsRoles fails', ()=> {
+ spyOn($log, 'error');
+ deferredAdminAppsRoles.reject('some error');
+
+ newCtrl.searchUsersInProgress = false;
+ newCtrl.selectedUser = {orgUserId: 'orgUserId'};
+
+ newCtrl.getAdminAppsRoles();
+ $rootScope.$apply();
+
+ expect($log.error).toHaveBeenCalled();
+ });
+ it('should log the error when trying to getAdminAppsRoles without selecting user ', ()=> {
+ spyOn($log, 'error');
+
+ newCtrl.searchUsersInProgress = false;
+ newCtrl.selectedUser = null;
+
+ newCtrl.getAdminAppsRoles();
+ $rootScope.$apply();
+
+ expect($log.error).toHaveBeenCalled();
+ });
+
+ it('should set isAdmin as true when adding app via the dropdown menu', ()=> {
+ newCtrl.adminAppsRoles = [{id: 1, isAdmin: false},{id: 2, isAdmin: true}];
+ $rootScope.$apply('newAdmin.selectedNewApp = null');
+ $rootScope.$apply('newAdmin.selectedNewApp = {id: 1, isAdmin: true}');
+
+ expect(newCtrl.adminAppsRoles[0].isAdmin).toBe(true);
+ expect(newCtrl.selectedNewApp).toBe(null);
+ });
+
+ it('should close the modal when updating apps roles succeeded', ()=> {
+ $rootScope.closeThisDialog = () => {};
+ spyOn($rootScope,'closeThisDialog');
+
+ newCtrl.selectedUser = {orgUserId: 'orgUserId'};
+ newCtrl.adminAppsRoles = [{id: 1}];
+
+ deferredUpdateRolesRes.resolve();
+ newCtrl.updateAdminAppsRoles();
+ $rootScope.$apply();
+
+ expect(adminsServiceMock.updateAdminAppsRoles).toHaveBeenCalledWith({userId: newCtrl.selectedUser.userId, appsRoles: newCtrl.adminAppsRoles});
+ expect($rootScope.closeThisDialog).toHaveBeenCalled();
+ });
+ it('should log the error when updating apps roles fails', ()=> {
+ newCtrl.selectedUser = {orgUserId: 'orgUserId'};
+ newCtrl.adminAppsRoles = [{id: 1}];
+
+ spyOn($log,'error');
+ deferredUpdateRolesRes.reject();
+ newCtrl.updateAdminAppsRoles();
+ $rootScope.$apply();
+ expect($log.error).toHaveBeenCalled();
+ });
+});
diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html
new file mode 100644
index 00000000..0a8fc727
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.html
@@ -0,0 +1,66 @@
+<!--
+ ================================================================================
+ 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-admin-modal">
+ <div class="search-users" ng-show="newAdmin.dialogState===1">
+ <search-users search-title="New Admin"
+ selected-user="newAdmin.selectedUser"></search-users>
+ <div class="dialog-control">
+ <div id="search-users-button-next" class="next-button" ng-click="newAdmin.selectedUser && newAdmin.getAdminAppsRoles()"
+ ng-class="{disabled: !newAdmin.selectedUser}">Next
+ </div>
+ <div id="search-users-button-cancel" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+ </div>
+ <div id="div-admin-app-roles" class="admin-app-roles" ng-show="newAdmin.dialogState===2">
+ <div class="title" id="title"
+ ng-bind="newAdmin.selectedUser.firstName + ' ' + newAdmin.selectedUser.lastName + ' (' + newAdmin.selectedUser.orgUserId + ')'"></div>
+ <div class="app-roles-main">
+ <div id="div-app-roles-main-title" class="app-roles-main-title">
+ <span class="left">Administrates:</span>
+ </div>
+ <div class="dropdown-container">
+ <div class ="right_arrow_down"></div>
+ <div class="select-input custom-select-wrap">
+ <select class="new-administrated-app" id="dropdown-select-app"
+ ui-select2 ng-model="newAdmin.selectedNewApp"
+ data-placeholder="Select application"
+ ng-options="app as app.appName for app in (filteredApps = (newAdmin.adminAppsRoles | filter:{isAdmin:'false'})) track by app.id "
+ ng-disabled="!filteredApps.length">
+ <option id="option-select-app" value="" disabled style="display: none;">Select application</option>
+ </select>
+ </div>
+ </div>
+ <div class="admin-roles-list">
+ <div ng-repeat="app in (newAdmin.adminAppsRoles | orderBy:newAdmin.orderFilter) track by app.id" ng-show="app.isAdmin">
+ <div id="select-app-{{app.appName.split(' ').join('-')}}" class="administrated-application" ng-bind="app.appName | elipsis: 57"></div>
+ <i id="i-delete-application" class="ion-trash-b" ng-click="newAdmin.unadminApp(app)"></i>
+ </div>
+ </div>
+ <div class="dialog-control">
+ <span class="ecomp-save-spinner" ng-show="newAdmin.isSaving"></span>
+ <div id="button-back" ng-show="newAdmin.isShowBack" class="back-button" ng-click="newAdmin.navigateBack()">Back</div>
+ <div id="div-updateAdminAppsRoles" class="next-button" ng-click="newAdmin.updateAdminAppsRoles()"
+ ng-class="{disabled: false}">Save
+ </div>
+ <div id="div-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less
new file mode 100644
index 00000000..b992c928
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/add-admin-dialogs/new-admin.modal.less
@@ -0,0 +1,99 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .new-admin-modal {
+ display:block;
+ overflow:auto;
+ min-height: 450px;
+
+ .search-users {
+ }
+
+ .admin-app-roles {
+ .title {
+ .n18r;
+ border-bottom: @a 3px solid;
+
+ }
+
+ .app-roles-main {
+ margin-top: 16px;
+ .app-roles-main-title {
+ .n14r;
+ margin-bottom: 8px;
+ .left {
+ display: inline-block;
+ }
+ .right {
+ display: inline-block;
+ color: @a;
+ float: right;
+ cursor: pointer;
+ }
+ }
+
+ .select-input{
+ width: 460px;
+ }
+
+ .new-administrated-app {
+ height: 30px;
+ line-height: 30px;
+
+ border: 1px solid @p;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding-left: 6px;
+ padding-top: 0;
+ .o14r;
+ }
+
+ .admin-roles-list {
+ height: 240px;
+ overflow-y: auto;
+ }
+
+ .administrated-application {
+ width: 460px;
+ height: 30px;
+ border: 1px solid @p;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding: 6px;
+ .o14r;
+ display: inline-block;
+
+ }
+
+ .delete-application {
+ .ico_trash_default;
+ display: inline-block;
+ vertical-align: 4px;
+ cursor: pointer;
+ position: relative;
+ top: 6px;
+ color: transparent;
+ margin-left: 8px;
+ }
+
+ }
+
+ }
+}
+
diff --git a/ecomp-portal-FE/client/app/views/admins/admins.controller.js b/ecomp-portal-FE/client/app/views/admins/admins.controller.js
new file mode 100644
index 00000000..402ff9d6
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/admins.controller.js
@@ -0,0 +1,151 @@
+/*-
+ * ================================================================================
+ * 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 AdminsCtrl {
+ constructor($log, adminsService, applicationsService, ngDialog) {
+
+ let allPortalsFilterObject = {index: 0, title: 'All applications', value: ''};
+
+ let updateTableData = () => {
+ this.isLoadingTable = true;
+ adminsService.getAccountAdmins().then(res=> {
+ if (!res || !res.length) {
+ $log.info('AdminsCtrl::updateTableData: no admins err handling');
+ this.adminsTableData = [];
+ return;
+ }
+ this.adminsTableData = res;
+ }).catch(err=> {
+ $log.error('AdminsCtrl::updateTableData error: ', err);
+ }).finally(() => {
+ this.isLoadingTable = false;
+ });
+ };
+
+ let init = () => {
+ $log.info('AdminsCtrl:: ::initializing...');
+ this.isLoadingTable = false;
+ this.availableApps = [allPortalsFilterObject];
+ this.filterByApp = this.availableApps[0];
+
+ /* Table general configuration params*/
+ this.searchString= '';
+ /*Table data*/
+ this.adminsTableHeaders = ['First Name', 'Last Name', 'User ID', 'Applications'];
+ this.adminsTableData = [];
+ updateTableData();
+ };
+
+ applicationsService.getAvailableApps().then(res=> {
+ this.availableApps = [allPortalsFilterObject];
+ var res1 = res.sort(getSortOrder("title"));
+ var realAppIndex = 1;
+ for(let i=1; i<=res1.length; i++){
+ if (!res1[i-1].restrictedApp) {
+ $log.debug('AdminsCtrl:getAvailableApps:: pushing: {index: ', realAppIndex, 'title: ', res1[i - 1].title,
+ '| value: ', res1[i -1].value, '}');
+ this.availableApps.push({
+ index: realAppIndex,
+ title: res1[i - 1].title,
+ value: res1[i - 1].value
+ });
+ realAppIndex = realAppIndex + 1;
+ } else {
+ $log.debug('AdminsCtrl:getAvailableApps:: Restricted/URL only App will not be used = ' + res1[i - 1].title);
+ }
+ }
+ }).catch(err=> {
+ $log.error(err);
+ this.availableApps = [allPortalsFilterObject];
+ });
+
+ let getSortOrder = (prop) => {
+ return function(a, b) {
+ if (a[prop].toLowerCase() > b[prop].toLowerCase()) {
+ return 1;
+ } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ init();
+
+ this.portalsRowFilter = (input) => {
+ if (this.filterByApp.value === '') {
+ return true;
+ }
+ return _.find(input.apps, {appName: this.filterByApp.value}) !== undefined;
+ };
+
+ this.openAddNewAdminModal = (user) => {
+ let data = null;
+ if(user){
+ data = {
+ dialogState: 2,
+ selectedUser:{
+ orgUserId: user.orgUserId,
+ firstName: user.firstName,
+ lastName: user.lastName
+ }
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/admins/add-admin-dialogs/new-admin.modal.html',
+ controller: 'NewAdminModalCtrl',
+ controllerAs: 'newAdmin',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ $log.debug('AdminsCtrl:openAddNewAdminModal:: updating table data...');
+ updateTableData();
+ }
+ });
+ };
+
+ this.openEditUserModal = (loginId) => {
+ var data = {
+ loginId : loginId,
+ updateRemoteApp : false,
+ appId : this.selectedApp!=null?this.selectedApp.id:''
+ }
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/header/user-edit/edit-user.tpl.html',
+ controller: 'editUserController',
+ data: data,
+ resolve: {
+ message: function message() {
+ var message = {
+ type: 'Contact',
+ };
+ return message;
+ }
+ }
+ }).closePromise.then(needUpdate => {
+ updateTableData();
+ });
+ }
+ }
+ }
+ AdminsCtrl.$inject = ['$log', 'adminsService', 'applicationsService', 'ngDialog'];
+ angular.module('ecompApp').controller('AdminsCtrl', AdminsCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js b/ecomp-portal-FE/client/app/views/admins/admins.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/admins.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/client/app/views/admins/admins.less b/ecomp-portal-FE/client/app/views/admins/admins.less
new file mode 100644
index 00000000..cdf0c0f5
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/admins.less
@@ -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.
+ * ================================================================================
+ */
+ .admins-page-main {
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .admins-table {
+ width: @table-width;
+ margin: @table-margin;
+
+ .table-control {
+ .table-dropdown-filter{
+ width: @table-dropdown-filter-width;
+ display: @table-dropdown-filter-display;
+ }
+ }
+
+ .table-body {
+ cursor: pointer;
+ }
+ }
+}
+
diff --git a/ecomp-portal-FE/client/app/views/admins/admins.tpl.html b/ecomp-portal-FE/client/app/views/admins/admins.tpl.html
new file mode 100644
index 00000000..543b9ead
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/admins/admins.tpl.html
@@ -0,0 +1,82 @@
+<!--
+ ================================================================================
+ 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="admins-page-main" id="contentId">
+ <div id="title" class="w-ecomp-main-view-title">Admins</div>
+ <div class="admins-table">
+ <div class="table-control">
+ <div class="table-dropdown-filter">
+ <div class="c-ecomp-att-abs-select default">
+ <div class="form-field" id="dropdown-apps"
+ att-select="admins.availableApps"
+ ng-model="admins.filterByApp"></div>
+ </div>
+ </div>
+ <input id="input-table-search" class="table-search" type="text"
+ placeholder="Search in entire table"
+ ng-model="admins.searchString"/>
+ <div id="Add-Admin-button" class="add-button" ng-click="admins.openAddNewAdminModal()">Add Admin</div>
+ </div>
+ <span class="ecomp-spinner" ng-show="admins.isLoadingTable"></span>
+ <div class="c-ecomp-att-abs-table default" ng-hide="admins.isLoadingTable">
+ <table att-table
+ table-data="admins.adminsTableData"
+ search-string="admins.searchString"
+ view-per-page="admins.viewPerPageIgnored"
+ current-page="admins.currentPageIgnored"
+ total-page="admins.totalPageIgnored">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-first-name" att-table-header key="firstName" default-sort="a">{{admins.adminsTableHeaders[0]}}</th>
+ <th id="th-last-name" att-table-header key="lastName" sortable="true">{{admins.adminsTableHeaders[1]}}</th>
+ <th id="th-userId" att-table-header key="userId" sortable="true">{{admins.adminsTableHeaders[2]}}</th>
+ <th id="th-apps" att-table-header key="apps" sortable="false">{{admins.adminsTableHeaders[3]}}</th>
+ </tr>
+ </thead>
+ <tbody att-table-row type="body"
+ class="table-body"
+ style="overflow-y:scroll"
+ row-repeat="rowData in admins.adminsTableData | filter: admins.portalsRowFilter">
+ <tr ng-click="admins.openAddNewAdminModal(rowData)" >
+ <td att-table-body class="td-first">
+ <div id="div-{{rowData.userId}}-{{rowData.firstName}}" ng-bind="rowData.firstName"></div>
+ </td>
+ <td att-table-body>
+ <div id="div-{{rowData.userId}}-{{rowData.lastName}}" ng-bind="rowData.lastName"></div>
+ </td>
+ <td att-table-body>
+ <div id="div-{{rowData.userId}}" style="float: left;" ng-bind="rowData.orgUserId"></div>
+ <div>
+ <span style="float: left; margin-left:15px" class="ion-person" ng-click="admins.openEditUserModal(rowData.orgUserId);$event.stopPropagation()"></span>
+ </div>
+ </td>
+ <td att-table-body>
+ <div id="apps-{{rowData.userId}}-{{app.appName}}" ng-repeat="app in rowData.apps" ng-bind="app.appName"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</div>
diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js
new file mode 100644
index 00000000..8c6ffe48
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.js
@@ -0,0 +1,244 @@
+/*-
+ * ================================================================================
+ * 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 = 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.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){
+ err.data = [err.data]
+ }
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ $scope.appForm[field.name].$setValidity('conflict', false);
+ 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=> {
+ $scope.userId = profile.orgUserId;
+ $log.debug('AppDetailsModalCtrl:emptyCookies for the following userId: ' + profile.orgUserId);
+ if ($cookies.getObject($scope.userId + '_apps') != undefined && $cookies.getObject($scope.userId + '_apps') != null) {
+ $cookies.remove($scope.userId + '_apps');
+ $log.debug('AppDetailsModalCtrl:emptyCookies removed: ' + $scope.userId + '_apps');
+ }
+ if ($cookies.getObject($scope.userId + '_widget') != undefined && $cookies.getObject($scope.userId + '_widget') != null) {
+ $cookies.remove($scope.userId + '_widget');
+ $log.debug('AppDetailsModalCtrl:emptyCookies removed: ' + $scope.userId + '_widget');
+ }
+ }).catch(err => {
+ $log.error('AppDetailsModalCtrl:emptyCookies error:: '+ JSON.stringify(err));
+ });
+ };
+
+
+ let watchOnce = {
+ name: () => {
+ let unregisterName = $scope.$watch('appDetails.app.name', (newVal, oldVal) => {
+ 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($scope.appForm.$invalid){
+ return;
+ }
+ this.isSaving = true;
+
+ 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 => {
+ if(err.status === 409){
+ handleConflictErrors(err);
+ }
+ if(err.status === 500){
+ confirmBoxService.showInformation('There was a problem updating the application changes. ' +
+ 'Please try again later.').then(isConfirmed => {});
+ }
+ if(err.status === 403){
+ confirmBoxService.showInformation('There was a problem updating the application changes. ' +
+ 'Please try again. If the problem persists, then try again later.').then(isConfirmed => {});
+ }
+ $log.error('applicationsService:updateOnboardingApp error status:: '+ err.status);
+ $log.error('applicationsService:updateOnboardingApp error:: '+ JSON.stringify(err));
+ }).finally(()=>{
+ this.isSaving = false;
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('AppDetailsModalCtrl:updateOnboardingApp:: Browser is IE, forcing Refresh');
+ $window.location.reload();
+ }
+ });
+ }else{
+ applicationsService.addOnboardingApp(this.app)
+ .then(() => {
+ $log.debug('App creation succeeded!');
+ $scope.closeThisDialog(true);
+ emptyCookies();
+ }).catch(err => {
+ if(err.status === 409){
+ handleConflictErrors(err);
+ }
+ if(err.status === 500){
+ confirmBoxService.showInformation('There was a problem adding the application information. ' +
+ 'Please try again later.').then(isConfirmed => {});
+ }
+ $log.error('applicationsService:addOnboardingApp error status:: '+ err.status);
+ $log.error('applicationsService:addOnboardingApp error:: '+ JSON.stringify(err));
+ }).finally(()=>{
+ this.isSaving = false;
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('applicationsService:addOnboardingApp:: Browser is IE, forcing Refresh');
+ $window.location.reload();
+ }
+ });
+ }
+ };
+
+
+ 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 => {
+ e.preventDefault();
+ });
+ }
+ }
+ AppDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'errorMessageByCode',
+ 'ECOMP_URL_REGEX','userProfileService','$cookies', 'confirmBoxService'];
+ angular.module('ecompApp').controller('AppDetailsModalCtrl', AppDetailsModalCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/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/client/app/views/applications/application-details-dialog/application-details.modal.html b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html
new file mode 100644
index 00000000..57f4e61d
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.html
@@ -0,0 +1,183 @@
+<!--
+ ================================================================================
+ 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 class="title">Application Details</div>
+ <div class="app-properties-main" scroll-top="appDetails.scrollApi">
+ <form name="appForm" novalidate autocomplete="off">
+ <script type="text/javascript">
+ document.getElementById("appForm").reset();
+ </script>
+ <div class="left-container">
+ <div class="property">
+ <input id="checkbox-app-is-restricted" type="checkbox" class="checkbox-field" 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" class="input-field" 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 required">
+ <div id="url-property-label" class="property-label">URL</div>
+ <input class="input-field" 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 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 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-show="appForm.url.$error.url">Application URL must be a valid URL</small>
+ </div>
+ </div>
+ </div>
+
+ <div class="property" ng-show="!appDetails.app.restrictedApp">
+ <div class="property-label">Rest API URL</div>
+ <input class="input-field" 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 class="error-container" ng-show="appForm.restUrl.$dirty || appDetails.isEditMode">
+ <div ng-messages="appForm.restUrl.$error" class="error-container">
+ <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 class="input-field" type="text"
+ ng-model="appDetails.app.username"
+ name="username"
+ maxlength="256"
+ ng-required="!appDetails.app.restrictedApp"/>
+ <div 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">Rest Api 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 class="input-field" type="password" id="input-mylogins-password"
+ ng-model="appDetails.app.appPassword" autocomplete="new-password"
+ name="appPassword"
+ maxlength="256"
+ ng-required="!appDetails.app.restrictedApp"/>
+ <div 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">Rest Api Password is required</small>
+ </div>
+ </div>
+ </div>
+
+
+ <div class="property" ng-show="!appDetails.app.restrictedApp">
+ <div class="property-label">Communication Inbox</div>
+ <input class="input-field" type="text" id="input-UEB-topicname"
+ ng-model="appDetails.app.uebTopicName"
+ name="uebTopicName" readonly="readonly"/>
+ </div>
+ <div class="property" ng-show="!appDetails.app.restrictedApp">
+ <div class="property-label">Communication Key</div>
+ <input class="input-field" type="text" id="input-UEB-communication-key"
+ ng-model="appDetails.app.uebKey"
+ name="uebKey" readonly="readonly" />
+ </div>
+ <div class="property" ng-show="!appDetails.app.restrictedApp">
+ <div class="property-label">Communication Secret</div>
+ <input class="input-field" type="text" id="input-UEB-communication-secret"
+ ng-model="appDetails.app.uebSecret"
+ name="uebSecret" readonly="readonly" />
+ </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"/>
+
+ <div 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.imageUrl || appDetails.app.thumbnail || appDetails.emptyImgForPreview}}"/>
+
+ <div class="property">
+ <input id="checkbox-app-is-open" type="checkbox" class="checkbox-field" ng-model="appDetails.app.isOpen" ng-checked="appDetails.app.isOpen || appDetails.app.restrictedApp" ng-disabled="appDetails.app.restrictedApp" />
+ <div class="property-label checkbox-label">Allow guest access</div>
+ </div>
+ <div class="property">
+ <input id="checkbox-app-is-enabled" type="checkbox" class="checkbox-field" ng-model="appDetails.app.isEnabled"/>
+ <div 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>
+ <div id="button-save-app" class="next-button" ng-class="{disabled: appForm.$invalid}" ng-click="appDetails.saveChanges()">Save</div>
+ <div id="button-app-cancel" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+</div>
+
+<script type="application/javascript">
+ $(document).ready(function(){
+ $(".ngdialog-content").css("top","-150px")
+ });
+</script>
diff --git a/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less
new file mode 100644
index 00000000..6db4845a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/applications/application-details-dialog/application-details.modal.less
@@ -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.
+ * ================================================================================
+ */
+ .application-details-modal {
+ height: 700px;
+
+ .title {
+ .n18r;
+ border-bottom: @a 3px solid;
+
+ }
+
+ .app-properties-main{
+ padding-left: 16px;
+ padding-top: 16px;
+ padding-bottom: 16px;
+ height: 630px;
+ overflow-y: auto;
+
+
+ .left-container{
+ display: inline-block;
+ width: 48%;
+
+ }
+ .right-container{
+ display: inline-block;
+ width: 48%;
+ float: right;
+
+
+ }
+
+ .property{
+ position: relative;
+ margin-bottom: 18px;
+ .property-label{
+ .o14r;
+ }
+ .checkbox-label{
+ display: inline-block;
+ padding-left: 3px;
+ }
+ .checkbox-field{
+ padding: 0;
+ margin: 0;
+ vertical-align: middle;
+ position: relative;
+ top: -1px;
+ }
+ .preview{
+ width: 220px;
+ margin-top: 22px;
+ display: block;
+
+ .left-label{
+ display:inline-block;
+ float: left;
+ }
+ .remove{
+ cursor: pointer;
+ display: inline-block;
+ float: right;
+ .a14r;
+ }
+ }
+
+ .input-field{
+ .custom-input-field;
+ width: 220px;
+ }
+
+ .input-file-field{
+ width: 220px;
+ }
+ .select-field {
+ .custom-select-field;
+ }
+
+ .image-preview{
+ background: #f0f0f0;
+ background-size: cover;
+ width: 220px;
+ height: 184px;
+ margin-top: 10px;
+ border: 2px solid #e8e8e8;
+ border-radius: 4px;
+ }
+
+ .error-container{
+ position: absolute;
+ width: 220px;
+ display: block;
+ height: 12px;
+ line-height: 12px;
+
+ .err-message{
+ color: red;
+ font-size: 10px;
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE/client/app/views/applications/applications.controller.js b/ecomp-portal-FE/client/app/views/applications/applications.controller.js
new file mode 100644
index 00000000..7214fd2d
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/applications/applications.controller.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.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class ApplicationsCtrl {
+ constructor($log, applicationsService, confirmBoxService, ngDialog,userProfileService,$cookies) {
+ $log.info('ApplicationsCtrl::init: Starting up');
+
+ this.emptyImgForPreview = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
+ let getOnboardingApps = () => {
+ this.isLoadingTable = true;
+ applicationsService.getOnboardingApps()
+ .then(appsList => {
+ this.appsList = appsList;
+ }).catch(err => {
+ $log.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 => {
+ $log.error(err);
+ });
+ }
+ }).catch(err => {
+ $log.error('ApplicationsCtrl:deleteApp error:: ', err);
+ });
+
+ };
+
+ }
+ }
+ ApplicationsCtrl.$inject = ['$log', 'applicationsService', 'confirmBoxService', 'ngDialog','userProfileService','$cookies'];
+ angular.module('ecompApp').controller('ApplicationsCtrl', ApplicationsCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js b/ecomp-portal-FE/client/app/views/applications/applications.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/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/client/app/views/applications/applications.less b/ecomp-portal-FE/client/app/views/applications/applications.less
new file mode 100644
index 00000000..eed77ac2
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/applications/applications.less
@@ -0,0 +1,45 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .applications-page-main{
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .apps-table {
+ width: @table-width;
+ margin: @table-margin;
+
+ .delete-app{
+ .ico_trash_default;
+ }
+ .small-thumbnail{
+ width: 72px;
+ height: 60px;
+ border: 1px solid #d8d8d8;
+ border-radius: 2px;
+ }
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/applications/applications.tpl.html b/ecomp-portal-FE/client/app/views/applications/applications.tpl.html
new file mode 100644
index 00000000..a27821b1
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/applications/applications.tpl.html
@@ -0,0 +1,100 @@
+<!--
+ ================================================================================
+ 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="contentId">
+ <div id='app-title' class="w-ecomp-main-view-title">Application Onboarding</div>
+ <div class="apps-table">
+ <div class="table-control">
+ <input class="table-search"
+ id="app-input-table-search"
+ type="text"
+ placeholder="Search by Application Name"
+ ng-model="searchAppName"/>
+ <div id="button-openAddNewApp" class="add-button" ng-click="apps.openAddNewAppModal()">Add App</div>
+ </div>
+ <span class="ecomp-spinner" ng-show="apps.isLoadingTable"></span>
+ <div class="c-ecomp-att-abs-table default" ng-hide="apps.isLoadingTable" id="div-app-table">
+ <table att-table id="app-onboarding-table"
+ table-data="apps.appsList"
+ search-string="apps.searchString"
+ view-per-page="apps.viewPerPageIgnored"
+ current-page="apps.currentPageIgnored"
+ total-page="apps.totalPageIgnored">
+ <thead att-table-row type="header" id="att-table-row">
+ <tr>
+ <th id="app-header-delete" att-table-header sortable="false">Delete</th>
+ <th id="app-header-Thumbnail" att-table-header sortable="false">Thumbnail</th>
+ <th id="app-header-AppName" att-table-header sortable="true">Application Name</th>
+ <th id="app-header-Active" att-table-header sortable="false">Active?</th>
+ <th id="app-header-IntType" att-table-header sortable="false">Integration Type</th>
+ <th id="app-header-Guest" att-table-header sortable="false">Guest Access</th>
+ <th id="app-header-URL" att-table-header sortable="false">URL</th>
+ <th id="app-header-RESTURL" att-table-header sortable="false">REST URL</th>
+ <th id="app-header-Topic" att-table-header sortable="false">Communication Topic</th>
+ <th id="app-header-CommKey" att-table-header sortable="false">Communication Key</th>
+ <th id="app-header-Secret" att-table-header sortable="false">Communication Secret</th>
+ </tr>
+ </thead>
+ <tbody att-table-row type="body"
+ class="table-body"
+ row-repeat="rowData in apps.appsList | filter:{name:searchAppName} | orderBy: 'name'">
+ <tr>
+ <td ng-click="apps.deleteApp(rowData)">
+ <div id="{{$index}}-app-delete" class="ion-trash-b"></div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <img id="{{$index}}-app-imageUrl" class="small-thumbnail" ng-src="{{rowData.imageUrl || apps.emptyImgForPreview}}">
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-name">{{rowData.name}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-isEnabled">{{(rowData.isEnabled) ? 'yes' : 'no'}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-restrictedApp">{{(rowData.restrictedApp) ? 'link' : 'standard'}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-isOpen">{{(rowData.isOpen) ? 'yes' : 'no'}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-url">{{rowData.url | elipsis: 27}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-restUrl">{{rowData.restUrl | elipsis: 27}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-uebTopicName">{{rowData.uebTopicName}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-uebKey">{{rowData.uebKey}}</div>
+ </td>
+ <td ng-click="apps.openAddNewAppModal(rowData)">
+ <div id="{{$index}}-uebSecret">{{rowData.uebSecret}}</div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/catalog/catalog.controller.js b/ecomp-portal-FE/client/app/views/catalog/catalog.controller.js
new file mode 100644
index 00000000..652c0886
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/catalog/catalog.controller.js
@@ -0,0 +1,166 @@
+/*-
+ * ================================================================================
+ * 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 _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+}
+
+(function() {
+ var HTTP_PROTOCOL_RGX = /https?:\/\//;
+
+ var CatalogCtrl = function CatalogCtrl(catalogService, confirmBoxService,
+ $log, $window, userProfileService, $scope, $timeout, $interval,
+ $modal, ngDialog) {
+
+ var _this = this;
+
+ _classCallCheck(this, CatalogCtrl);
+
+ // activate spinner
+ this.isLoading = true;
+ $scope.getAppCatalogIsDone = false;
+ $scope.radioValue = 'All';
+ $scope.$watch('radioValue', function(newValue, oldValue) {
+ var appCatalog = $scope.appCatalog;
+ $scope.appCatalog = [];
+ $scope.appCatalog = appCatalog;
+
+
+ });
+
+ this.catalogService = catalogService;
+ this.userProfileService = userProfileService;
+ $scope.demoNum = 1;
+
+ this.getAccess = function(item) {
+ if(!item.access)
+ confirmBoxService.showDynamicInformation(item,
+ 'app/views/catalog/information-box.tpl.html','CatalogConfirmationBoxCtrl'
+ ).then(isConfirmed => {});
+ };
+
+ // Run this function when user clicks on checkbox.
+ this.storeSelection = function(item) {
+ // $log.debug('CatalogCtrl:storeSelection: item.id is ' + item.id + ', select is ' + item.select);
+ var pendingFlag = false;
+
+ if(item.access)
+ pendingFlag = false;
+ else
+ pendingFlag = item.pending;
+
+
+
+
+ var appData = {
+ appId : item.id,
+ select : item.select,
+ pending : pendingFlag // TODO
+ };
+ catalogService.updateAppCatalog(appData).then(
+ function(result) {
+ // $log.debug('CatalogCtrl:storeSelection result is ', result);
+ })['catch'](function(err) {
+ $log.error('CatalogCtrl:storeSelection: exception: ', err);
+ });
+ };
+
+ userProfileService
+ .getUserProfile()
+ .then(
+ function(profile) {
+ $scope.attuid = profile.attuid;
+ $scope.appCatalog = [];
+
+ // applicationsService.getUserApps()
+ catalogService
+ .getAppCatalog()
+ .then(
+ function(res) {
+ // $log.info(res);
+ _this.apps = res;
+ var rowNo = 0;
+ for (var i = 0; i < _this.apps.length; i++) {
+ $scope.appCatalog[i] = {
+ sizeX : 2,
+ sizeY : 2,
+ id : _this.apps[i].id,
+ headerText : _this.apps[i].name,
+ imageLink : _this.apps[i].thumbnail
+ || _this.apps[i].imageUrl,
+ url : _this.apps[i].url,
+ restricted : _this.apps[i].restricted,
+ select : (_this.apps[i].select || (_this.apps[i].access && _this.apps[i].pending)),
+ access : _this.apps[i].access,
+ pending: _this.apps[i].pending
+ };
+ }
+ //$log.debug('CatalogCtrl: getAppCatalog count : '
+ // + $scope.appCatalog.length);
+ _this.isLoading = false;
+ $scope.getAppCatalogIsDone = true;
+ })['catch'](function(err) {
+ $log.error('CatalogCtrl: failed to get app list: ', err);
+ _this.isLoading = false;
+ $scope.getAppCatalogIsDone = true;
+ });
+ });
+
+ this.gridsterOpts = {
+ columns : 12,
+ colWidth : 95,
+ rowHeight : 95,
+ margins : [ 20, 20 ],
+ outerMargin : true,
+ pushing : true,
+ floating : true,
+ swapping : true,
+ };
+
+ if (getParameterByName('noUserError') != null) {
+ if (getParameterByName('noUserError') == "Show") {
+ $("#errorInfo").show();
+ }
+
+ }
+ };
+
+ CatalogCtrl.$inject = [ 'catalogService', 'confirmBoxService', '$log',
+ '$window', 'userProfileService', '$scope', '$timeout', '$interval',
+ '$modal', 'ngDialog' ];
+ angular.module('ecompApp').controller('CatalogCtrl', CatalogCtrl);
+})();
+
+function getParameterByName(name, url) {
+ if (!url)
+ url = window.location.href;
+ name = name.replace(/[\[\]]/g, "\\$&");
+ var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex
+ .exec(url);
+ if (!results)
+ return '';
+ if (!results[2])
+ return '';
+ return results[2].replace(/\+/g, " ");
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/catalog/catalog.less b/ecomp-portal-FE/client/app/views/catalog/catalog.less
new file mode 100644
index 00000000..9385c8c5
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/catalog/catalog.less
@@ -0,0 +1,439 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+.w-ecomp-app-catalog {
+ //.bg_u;//white for 1702
+ .bg_w; // gray for 1610
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .gridster-item-container .gridster-item-body {
+ overflow-y:auto;
+ overflow-x:hidden;
+ }
+
+ .app-catalog-container {
+ .content_justify;
+ position: relative;
+ padding: 0 0 32px 0;
+ width: 100%;
+
+ .app-catalog-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+ .app-catalog-list {
+ min-height: 90vh;
+ //display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: 1170px;
+
+ margin: auto;
+ margin-bottom: 63px;
+
+ .app-gridster-header {
+ background-color: @u;
+ font-size:12px;
+ overflow:hidden
+ }
+
+ .app-gridster-footer {
+ background-color: @u;
+ }
+
+ }
+ }
+}
+
+.build-number {
+ .o12i;
+}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.slide.ng-hide-add,
+.slide.ng-hide-remove,
+.slide.ng-enter,
+.slide.ng-leave {
+ transition: all 0.5s ease;
+}
+.slide.ng-hide,
+.slide.ng-enter {
+ transform: translate(-100%, 0);
+}
+.slide.ng-enter-active {
+ transform: translate(0, 0);
+}
+.slide.ng-leave {
+ transform: translate(0, 0);
+}
+.slide.ng-leave-active {
+ transform: translate(+100%, 0);
+}
+.appCatalogue-boarder{
+ // border: 2px solid #ccc!important;
+ // border-radius: 16px!important;
+ height: 800px;
+ overflow: auto;
+}
+.app-visibility.false {
+ opacity: 0.5;
+}
+
+.dock ul{
+display: inline-block;
+
+width: auto;
+margin: 0px;
+padding: 0px;
+list-style: none;
+
+}
+.dock ul li {
+width: auto;
+height: auto;
+display: inline-block;
+bottom: 0;
+vertical-align: bottom;
+margin-top: -43px;
+}
+.dock ul li a {
+display: block;
+height: 150px;
+width: 150px;
+position: relative;
+-webkit-transition-property: width, height,margin-top;
+-webkit-transition-duration: 0.5s;
+-o-transition-property: width, height,margin-top;
+-o-transition-duration: 0.5s;
+-moz-transition-property: width, height,margin-top;
+-moz-transition-duration: 0.5s;
+}
+.dock ul li a:hover {
+width: 200px;
+height: 200px;
+margin-top: -50px;
+}
+.dock ul li a img {
+width: 100%;
+position: absolute;
+bottom: 0;
+left: 0;
+border: none;
+padding: 0px 0px 0px 30px;
+}
+.dock_left{
+width: 31px;
+-webkit-transform: rotate(33deg);
+-moz-transform: rotate(33deg);
+-o-transform: rotate(33deg);
+position: relative;
+background: #EEE;
+overflow: hidden;
+float: left;
+height: 100px;
+z-index: 2;
+margin: -18px;
+}
+.dock_right{
+width: 36px;
+-webkit-transform: rotate(-33deg);
+-moz-transform: rotate(-33deg);
+-o-transform: rotate(-33deg);
+position: relative;
+background: #EEE;
+overflow: hidden;
+float: left;
+height: 100px;
+z-index: 2;
+margin: -18px;
+}
+
+
+
+
+/*Time for the CSS*/
+* {margin: 0; padding: 0;}
+body {background: #ccc;}
+
+.slider{
+ width: 640px; /*Same as width of the large image*/
+ position: relative;
+ /*Instead of height we will use padding*/
+ padding-top: 320px; /*That helps bring the labels down*/
+
+ margin: 50px auto;
+
+ /*Lets add a shadow*/
+ box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.75);
+}
+
+
+/*Last thing remaining is to add transitions*/
+.slider>img{
+ position: absolute;
+ left: 0; top: 0;
+ transition: all 0.5s;
+}
+
+.slider input[name='slide_switch'] {
+ display: none;
+}
+
+.slider label {
+ /*Lets add some spacing for the thumbnails*/
+ margin: 18px 0 0 18px;
+ border: 3px solid #999;
+
+ float: left;
+ cursor: pointer;
+ transition: all 0.5s;
+
+ /*Default style = low opacity*/
+ opacity: 0.6;
+}
+
+.slider label img{
+ display: block;
+}
+
+/*Time to add the click effects*/
+.slider input[name='slide_switch']:checked+label {
+ border-color: #666;
+ opacity: 1;
+}
+/*Clicking any thumbnail now should change its opacity(style)*/
+/*Time to work on the main images*/
+.slider input[name='slide_switch'] ~ img {
+ opacity: 0;
+ transform: scale(1.1);
+}
+/*That hides all main images at a 110% size
+On click the images will be displayed at normal size to complete the effect
+*/
+.slider input[name='slide_switch']:checked+label+img {
+ opacity: 1;
+ transform: scale(1);
+}
+/*Clicking on any thumbnail now should activate the image related to it*/
+
+.caption {
+ background: rgba(0, 0, 0, 0.5);
+ position: absolute;
+ bottom: 0;
+ width: 640px
+}
+
+.caption a {
+ display: block;
+ color: #fff;
+ text-decoration: none;
+ font: normal 16px 'Lato', Helvetica, Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ padding: 15px;
+}
+
+
+/*widgets*/
+.singleBtnBorder {
+ border-radius: 6px 6px 6px 6px;
+}
+
+.icon-anchor {
+ color: #888;
+}
+
+.widgetHeaderBtn:hover, .widgetHeaderBtn:focus {
+ background: rgba(0, 0, 0, 0);
+ color: #3a7999;
+ box-shadow: inset 0 0 0 2px #3a7999;
+}
+
+.box-one {
+ -webkit-transition:all linear 0.4s;
+ transition:all linear 0.4s;
+ height:100px; width:200px; background:white; border: 2px solid #ccc!important;
+ border-radius: 16px!important;
+}
+.box-one.ng-hide {
+display: block!important;
+ opacity:0;
+}
+
+.gridsterContent{
+ height:120px;
+}
+.gridsterAppContent{
+ height:120px;
+
+}
+
+ .gridsterImage{
+ height:84px;
+ width:168px;
+ }
+ .grider-content-ecomp{
+ transition: transform 0.5s ease-out;
+ }
+ .grider-content-ecomp:hover{
+ transform: scale(1.1);
+}
+
+/*application empty div*/
+.app-error-block {
+ padding-top: 10px;
+}
+
+.text-center { text-align: center; }
+
+.text-right { text-align: right; }
+
+.text-justify { text-align: justify; }
+
+/*widget header*/
+.optionsMenu{
+ position: absolute;
+ list-style: none;
+ top: 25px;
+ right: 10px;
+ border: 1px solid #067ab4;
+ display: none;
+ z-index: 2;
+ border-radius: 6px 0px 6px 6px;
+ background: #fff;
+ width: 130px;
+}
+
+.optionsMenuLink {
+ position: relative;
+ padding-left: 8px;
+ padding-right: 2px;
+ font-size: 12px;
+ line-height: 30px;
+ color: #444444;
+}
+.optionsMenu > li:hover a {
+ color: #ffffff !important;
+}
+.optionsMenu > li {
+ width: 100%;
+ text-align: left;
+}
+.optionsMenu > li:hover {
+ background-color: #0faaf7;
+ border-color: none !important;
+ cursor: pointer;
+}
+
+.simulateCatGridHeader{
+ position: relative;
+ height: 70px !important;
+ border: 1px solid #d3d3d3;
+ border-bottom: 0;
+ background-color: #E5E5E5;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ z-index: 1;
+}
+
+.simulateCatGridHeaderTitle{
+ line-height: 20px;
+ margin-top: 10px;
+ margin-left: 26px;
+ font-family: arial;
+ font-size: 18px;
+ color: #444444;
+ float: left;
+}
+
+.simulateCatGridHeaderRadio{
+ line-height: 20px;
+ margin-top: 10px;
+ margin-left: 10px;
+ font-family: arial;
+ font-size: 12px;
+ color: #444444;
+ float: left;
+}
+
+.simulateCatGridHeaderDetails{
+ line-height: 20px;
+ margin-left: 10px;
+ font-family: arial;
+ font-size: 12px;
+ color: #444444;
+ float: left;
+}
+
+.simulateGridHeaderHandle{
+ cursor: move;
+ margin: 12px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ vertical-align: middle;
+ -ms-interpolation-mode: bicubic;
+ display: block;
+}
+
+.att-checkbox--on .att-checkbox__indicator, .att-radio--on .att-radio__indicator {
+ opacity: 1;
+}
+
+.animate-enter,
+.animate-leave
+{
+ -webkit-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
+ -moz-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
+ -ms-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
+ -o-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
+ transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
+ position: relative;
+ display: block;
+}
+
+.animate-enter.animate-enter-active,
+.animate-leave {
+ opacity: 1;
+ top: 0;
+ height: 30px;
+}
+
+.animate-leave.animate-leave-active,
+.animate-enter {
+ opacity: 0;
+ top: -50px;
+ height: 0px;
+}
diff --git a/ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html b/ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html
new file mode 100644
index 00000000..e169afa9
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/catalog/catalog.tpl.html
@@ -0,0 +1,91 @@
+<!--
+ ================================================================================
+ 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-app-catalog" id="contentId">
+ <div class="app-catalog-container">
+ <div id="app-catalog-title" class="app-catalog-title">Application Catalog</div>
+ <div class="app-catalog-list">
+ <div class="simulateCatGridHeader">
+ <span class="simulateCatGridHeaderDetails"> Click the check
+ boxes below to choose which applications are shown on the <a
+ href="applicationsHome">home page</a>.
+ </span> <br>
+ <br> <label class="simulateCatGridHeaderRadio"> Filter:
+ <input type="radio" ng-model="radioValue" value="All"> All
+ </label> <label class="simulateCatGridHeaderRadio"> <input
+ type="radio" ng-model="radioValue" value="Accessible">
+ Accessible
+ </label> <label class="simulateCatGridHeaderRadio"> <input
+ type="radio" ng-model="radioValue" value="Selected"> On
+ Home Page
+ </label>
+
+ </div>
+ <div class="appCatalogue-boarder">
+ <div class="gridster-container override_background">
+ <div att-gridster att-gridster-options="catalog.gridsterOpts">
+ <div class="style_prevu_kit" att-gridster-item='item'
+ ng-repeat="item in appCatalog" ng-animate="'animate'"
+ ng-if="(item.select && radioValue=='Selected') || (item.access && radioValue=='Accessible') || (radioValue == 'All')"
+ id="app-{{item.headerText.split(' ').join('-')}}">
+
+ <div class="grider-content-ecomp app-visibility {{item.access}}"
+ ng-show="(item.select && radioValue=='Selected') || (item.access && radioValue=='Accessible') || (radioValue == 'All')">
+ <div id="grips-{{item.headerText.split(' ').join('-')}}"
+ ng-hide="users.isLoadingTable && !users.getAppCatalogIsDone"
+ att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{item.headerText | elipsis: 15}}"
+ class="app-gridster-header">
+ <div ng-show="item.access || isAdminPortalAdmin">
+ <input type="checkbox" ng-model="item.select"
+ ng-change="catalog.storeSelection(item)">
+ </div>
+ <div ng-show="item.pending && !item.access">
+ <i style="font-size: 8px">Pending Access</i>
+ </div>
+ </div>
+ <div id="app-image-{{item.headerText.split(' ').join('-')}}"
+ att-gridster-item-body
+ ng-style="{'cursor':'pointer',
+ 'background-image': 'url('+(item.imageLink)+')',
+ 'order': item.order,
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px'}"
+ ng-click="catalog.getAccess(item)"
+ ng-hide="users.isLoadingTable && !users.getAppCatalogIsDone"
+ class="gridsterContent"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <br>
+
+ <div class="w-ecomp-main-disclaimer">
+ To request access to an application, please visit the <a
+ ng-href="getAccess">Get Access</a> page.
+ </div>
+
+ <br>
+ </div>
+
+ </div>
+</div> \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js b/ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js
new file mode 100644
index 00000000..83438e9c
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/catalog/catalogconfirmation.controller.js
@@ -0,0 +1,62 @@
+/*-
+ * ================================================================================
+ * 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 CatalogConfirmationBoxCtrl {
+ constructor($scope,$state,catalogService) {
+
+ let init = () => {
+ let item = ($scope.ngDialogData && $scope.ngDialogData.item) || 'this';
+ this.message = $scope.ngDialogData.message ? $scope.ngDialogData.message : `Are you sure you want to delete "${item}"?`;
+ this.title = $scope.ngDialogData.title ? $scope.ngDialogData.title : '';
+ };
+
+ this.closeBox = isConfirmed => {
+ $scope.closeThisDialog(isConfirmed);
+ };
+
+ this.goTo = (state, params) => {
+ $scope.closeThisDialog(false);
+ $state.go(state,params);
+
+ };
+
+ this.storeSelection = function(item) {
+ // $log.debug('CatalogCtrl:storeSelection: item.id is ' + item.id + ', select is ' + item.select);
+ var appData = {
+ appId : item.id,
+ select : item.select,
+ pending : item.pending
+ };
+ catalogService.updateAppCatalog(appData).then(
+ function(result) {
+ // $log.debug('CatalogCtrl:storeSelection result is ', result);
+ })['catch'](function(err) {
+ $log.error('CatalogCtrl:storeSelection: exception: ', err);
+ });
+ };
+
+ init();
+ }
+ }
+ CatalogConfirmationBoxCtrl.$inject = ['$scope','$state','catalogService'];
+ angular.module('ecompApp').controller('CatalogConfirmationBoxCtrl', CatalogConfirmationBoxCtrl);
+})(); \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html b/ecomp-portal-FE/client/app/views/catalog/information-box.tpl.html
new file mode 100644
index 00000000..ccc0a9f5
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/catalog/information-box.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 class="confirmation-box-container">
+ <div class="confirmation-message-wrap">
+ <div class="confirmation-message" >
+ <div>
+ You do not have access to this application.
+ See the
+ <a href="" ng-click="confirmBox.goTo('root.getAccess', {appName: confirmBox.message.headerText})">
+ Get Access page</a> and request access.
+ <br><br>
+ You may check this box if access is pending:
+ <input type="checkbox"
+
+ ng-model="confirmBox.message.pending"
+ ng-change="confirmBox.storeSelection(confirmBox.message)"
+ >
+ </div>
+
+ </div>
+ </div>
+ <div class="dialog-control">
+ <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Close</div>
+ </div>
+</div> \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html
new file mode 100644
index 00000000..61a812bc
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/confirmation-box/admin-confirmation-box.tpl.html
@@ -0,0 +1,31 @@
+<!--
+ ================================================================================
+ 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="confirmation-box-container">
+ <div class="confirmation-title-wrap">
+ <div class="confirmation-title" ng-bind="confirmBox.title"></div>
+ </div>
+ <div class="confirmation-message-wrap">
+ <div class="confirmation-message" ng-bind="confirmBox.message"></div>
+ </div>
+ <div class="dialog-control">
+ <div class="next-button" ng-click="confirmBox.closeBox(true)">Confirm</div>
+ <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js
new file mode 100644
index 00000000..4f5e14b3
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.controller.js
@@ -0,0 +1,41 @@
+/*-
+ * ================================================================================
+ * 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 ConfirmationBoxCtrl {
+ constructor($scope) {
+
+ let init = () => {
+ let item = ($scope.ngDialogData && $scope.ngDialogData.item) || 'this';
+ this.message = $scope.ngDialogData.message ? $scope.ngDialogData.message : `Are you sure you want to delete "${item}"?`;
+ this.title = $scope.ngDialogData.title ? $scope.ngDialogData.title : '';
+ };
+
+ this.closeBox = isConfirmed => {
+ $scope.closeThisDialog(isConfirmed);
+ };
+
+ init();
+ }
+ }
+ ConfirmationBoxCtrl.$inject = ['$scope'];
+ angular.module('ecompApp').controller('ConfirmationBoxCtrl', ConfirmationBoxCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less
new file mode 100644
index 00000000..410d3967
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.less
@@ -0,0 +1,53 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .confirmation-box-container {
+ .confirmation-message-wrap {
+ display: table;
+ height: 60px;
+ width: 100%;
+
+ .confirmation-message {
+ .n14r;
+ display: table-cell;
+ vertical-align: middle;
+ max-width: 100px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .confirmation-title-wrap {
+ display: table;
+ height: 20px;
+ width: 100%;
+
+ .confirmation-title {
+ .n14r;
+ display: table-cell;
+ vertical-align: middle;
+ text-align: center;
+ font-weight: bold;
+ max-width: 100px;
+ overflow: hidden;
+ padding-bottom: 2px;
+ text-overflow: ellipsis;
+ }
+ }
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html
new file mode 100644
index 00000000..759bf137
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/confirmation-box/confirmation-box.tpl.html
@@ -0,0 +1,28 @@
+<!--
+ ================================================================================
+ 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="confirmation-box-container">
+ <div class="confirmation-message-wrap">
+ <div id="confirmation-message" class="confirmation-message" ng-bind="confirmBox.message"></div>
+ </div>
+ <div class="dialog-control">
+ <div id="confirmation-button-next" class="next-button" ng-click="confirmBox.closeBox(true)">OK</div>
+ <div id="confirmation-button-cancel" class="cancel-button" ng-click="confirmBox.closeBox(false)">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html
new file mode 100644
index 00000000..61a812bc
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/confirmation-box/dragdrop-confirmation-box.tpl.html
@@ -0,0 +1,31 @@
+<!--
+ ================================================================================
+ 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="confirmation-box-container">
+ <div class="confirmation-title-wrap">
+ <div class="confirmation-title" ng-bind="confirmBox.title"></div>
+ </div>
+ <div class="confirmation-message-wrap">
+ <div class="confirmation-message" ng-bind="confirmBox.message"></div>
+ </div>
+ <div class="dialog-control">
+ <div class="next-button" ng-click="confirmBox.closeBox(true)">Confirm</div>
+ <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html b/ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html
new file mode 100644
index 00000000..1e17e50a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/confirmation-box/information-box.tpl.html
@@ -0,0 +1,27 @@
+<!--
+ ================================================================================
+ 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="confirmation-box-container">
+ <div class="confirmation-message-wrap">
+ <div class="confirmation-message" ng-bind="confirmBox.message"></div>
+ </div>
+ <div class="dialog-control">
+ <div class="cancel-button" ng-click="confirmBox.closeBox(false)">Close</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html
new file mode 100644
index 00000000..84ac2a44
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget-manage.html
@@ -0,0 +1,152 @@
+<!--
+ ================================================================================
+ 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 style="margin-top: 10px; margin-bottom: 10psx;">
+ <h1 class="heading1"></h1>
+</div>
+<br />
+
+<div style="margin-top: 25px;">
+ <div id="title" class="manage-contactUs-home-manageWidgets-title">Manage {{widgetTypeDisplay}} Widgets</div>
+ <div class="widgets-popup-margin">
+ <div class="get-access-table">
+ <div class="table-control" style="margin:30px;">
+ <div class="c-ecomp-att-abs-table" style="height:300px"">
+ <table att-table id="table-main"
+ table-data="ignoredTableData"
+ current-page="ignoredCurrentPage">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-users-1" att-table-header key="title" sortable="true" >Title</th>
+ <th id="th-users-2" att-table-header key="href" sortable="true" >URL</th>
+ <th ng-show="{{widgetType=='EVENTS'}}" id="th-users-3" att-table-header key="event_date" sortable="true" >Date</th>
+ <th ng-show="{{widgetType=='EVENTS'}}" id="th-users-4" att-table-header key="content" sortable="true" >Content</th>
+ <th id="th-users-5" att-table-header key="sortOrder" sortable="true" >Order</th>
+ <th id="th-users-6" att-table-header key="edit" sortable="false" >Edit</th>
+ <th id="th-users-7" att-table-header key="delete" sortable="false" >Delete</th>
+ </tr>
+ </thead>
+ <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data -->
+ <tbody att-table-row type="body" class="table-body" track-by="$index" row-repeat="rowData in widgetData">
+ <tr id="tr-rowData" ng-click="">
+ <td att-table-body>
+ <div id="widget-td-title" ng-hide="rowData.showEdit" ng-bind="rowData.title"></div>
+ <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].title"/>
+ </td>
+ <td att-table-body>
+ <div id="widget-td-href" ng-hide="rowData.showEdit" ng-bind="rowData.href"></div>
+ <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].href"/>
+ </td>
+ <td ng-show="{{widgetType=='EVENTS'}}" att-table-body>
+ <div id="widget-td-date" ng-hide="rowData.showEdit" ng-bind="rowData.eventDate"></div>
+ <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].eventDate"/>
+ </td>
+ <td ng-show="{{widgetType=='EVENTS'}}" att-table-body>
+ <div id="widget-td-content" ng-hide="rowData.showEdit">{{rowData.content | cut:true:20:' ...'}}</div>
+ <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].content"/>
+ </td>
+ <td att-table-body>
+ <div id="widget-td-order" ng-hide="rowData.showEdit" ng-bind="rowData.sortOrder"></div>
+ <input type="text" ng-show="rowData.showEdit" ng-model="widgetData[$index].sortOrder"/>
+ </td>
+ <td att-table-body>
+ <div class="delete-contact-us" ng-hide="rowData.showEdit" ng-click="setEdit($index);">
+ <span class="ion-edit"></span>
+ </div>
+ <span ng-show="rowData.showEdit">
+ <a att-button btn-type="primary" ng-click="modify($index);" class="button button--primary" size="small" >Save</a>
+ </span>
+ </td>
+ <td att-table-body>
+ <div class="delete-contact-us" ng-click="remove($index);">
+ <span class="ion-trash-b"></span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div att-divider-lines></div>
+ <div style="margin-top: 15px; margin-left: -80px;" >
+ <form name = "widgetForm" novalidate>
+ <div id="addWidgetHeader" class="contact-us-margin">
+ <h1 style="font-size:18px;">Add</h1>
+ <div class="errMsg">{{errMsg}}</div>
+ <div class="sucessMsg" ng-bind="successMsg"></div>
+ </div>
+ <div id="addWidget" class="contact-us-margin">
+ <div>
+ <div>
+ <div class="add-widget-field">
+ <div id="mots-property-label" class="property-label">Title</div>
+ <input class="input-text-area" type="text" ng-model="newWidgetObject.title" name="title" ng-required="true"/>
+ </div>
+ <div class="add-widget-field">
+ <div class="property-label">URL</div>
+ <input class="input-text-area" type="text" ng-model="newWidgetObject.href" type="url" size="3" name = "url" ng-required = "true"/>
+ </div>
+ <div class="add-widget-field" >
+ <div class="property-label">Sort Order</div>
+ <input class="input-text-area" type="text" ng-model="newWidgetObject.sortOrder"/>
+ </div>
+ </div>
+ <div class="add-widget-field" ng-controller="DatePickerController" ng-show="{{widgetType=='EVENTS'}}">
+ <h4>Event Date</h4>
+ <div>
+ <div>
+ <p class="input-group">
+ <input type="text" name = "eventDate" datetime="yyyy-MM-dd" class="input-text-area" uib-datepicker-popup="{{format}}"
+ ng-model="newWidgetObject.eventDate" is-open="popup1.opened" datepicker-options="dateOptions" close-text="Close" alt-input-formats="altInputFormats"
+ ng-focus="open1()"
+ ng-required = "widgetType =='EVENTS'" />
+ </p>
+ </div>
+ </div>
+ </div>
+
+ <div class="add-contact-us-field-des" ng-show="{{widgetType=='EVENTS'}}">
+ <div class="property-label">Content</div>
+ <textarea style="margin-top: 0px; margin-bottom: 0px; height: 150px" ng-model="newWidgetObject.content" name="content" ng-required = "widgetType =='EVENTS'">
+ </textarea>
+ </div>
+ </div>
+ <div style="height:50px;">
+ <a style="float:right; margin-top:20px;" att-button btn-type="primary" class="button button--primary" size="small" ng-click="saveNew()">Add New</a>
+
+ </div>
+
+ <div att-divider-lines></div>
+
+ </div>
+ </form>
+ </div>
+ <div style="height:50px;">
+ <a style="float:right;" att-button btn-type="primary" class="button button--primary" size="small" ng-click="closeDialog()">Close</a>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script type="application/javascript">
+ $(document).ready(function(){
+ $(".ngdialog-content").css("width","85%")
+ });
+</script>
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js
new file mode 100644
index 00000000..d489ac42
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.js
@@ -0,0 +1,422 @@
+/*-
+ * ================================================================================
+ * 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 CommonWidgetController {
+ constructor(dashboardService, $scope, message, $q, $http, conf, $filter,confirmBoxService,$log) {
+ $scope.widgetType = message.type;
+ $scope.widgetTypeDisplay = message.type;
+ $scope.modflag = false;
+
+ switch (message.type) {
+ case 'EVENTS':
+ $scope.widgetTypeDisplay = 'Events'
+ break;
+ case 'NEWS':
+ $scope.widgetTypeDisplay = 'News'
+ break;
+ case 'IMPORTANTRESOURCES':
+ $scope.widgetTypeDisplay = 'Resources'
+ break;
+ }
+
+ $scope.widgetData = [];
+ dashboardService.getCommonWidgetData(message.type)
+ .then(function (res) {
+ // console.log('CommonWidgetController: result is ' + res);
+ $scope.widgetData = res.response.items;
+ });
+
+
+
+ $scope.setEdit = function(index) {
+
+ //for(var i=0; i<$scope.widgetData )
+
+ if($scope.modflag === false){
+ $scope.widgetData[index].showEdit = true;
+ $scope.modflag = true;
+ }
+ }
+
+ $scope.modify = function(index) {
+
+ $scope.widgetObject = {};
+ $scope.widgetObject.id = $scope.widgetData[parseInt(index)].id;
+ $scope.widgetObject.category = $scope.widgetData[parseInt(index)].category;
+ $scope.widgetObject.title = $scope.widgetData[parseInt(index)].title;
+ $scope.widgetObject.href = $scope.widgetData[parseInt(index)].href;
+ $scope.widgetObject.eventDate = $scope.widgetData[parseInt(index)].eventDate;
+ $scope.widgetObject.content = $scope.widgetData[parseInt(index)].content;
+ $scope.widgetObject.sortOrder = $scope.widgetData[parseInt(index)].sortOrder;
+
+ var validateMsg = $scope.validateWidgetObject($scope.widgetObject);
+ if (validateMsg) {
+ confirmBoxService.showInformation(validateMsg).then(isConfirmed => {return;});
+ return;
+ }
+
+ dashboardService.saveCommonWidgetData($scope.widgetObject)
+ .then(function(res){
+ if (res.status == 'OK') {
+ dashboardService.getCommonWidgetData(message.type)
+ .then(function(res){
+ $scope.widgetData = res.response.items;
+ $scope.modflag = false;
+ $scope.cancelEdit(index);
+ });
+ }
+ else {
+ // Save failed
+ confirmBoxService.showInformation("Save failed").then(isConfirmed => {return;});
+ return;
+ }
+ });
+
+ };
+
+ $scope.newWidgetObject = {};
+
+ // Answers string error if validation fails;
+ // answers null if all is well.
+ $scope.validateWidgetObject = function(wo) {
+ if (wo.title == null || wo.title == '')
+ return "Please enter a title.";
+ if (wo.href == null || wo.href == '' || !validateUrl(wo.href))
+ return "Please enter a valid URL that starts with http.";
+ if (wo.sortOrder == null || wo.sortOrder == '' || isNaN(parseInt(wo.sortOrder)))
+ return "Please enter a number for the sort order.";
+ if (wo.category=='EVENTS') {
+ if (wo.eventDate == null || wo.eventDate == '')
+ return "Please enter a date for the event.";
+ // Parses and normalizes the date with rollover.
+ var filteredDate = $filter('date')(wo.eventDate, "yyyy-MM-dd");
+ // $log.debug('dashboard-widget-controller: date filter yields ' + filteredDate);
+ // The date picker shows mm/dd/yy.
+ if (filteredDate == null || filteredDate.length != 10)
+ return "Please enter a valid date as YYYY-MM-DD";
+ if (wo.content==null || wo.content=='')
+ return "Please enter content for the event.";
+ }
+ return null;
+ };
+
+ $scope.saveNew = function() {
+ $scope.newWidgetObject.category = message.type;
+ // $log.info($scope.newWidgetObject);
+ var validateMsg = $scope.validateWidgetObject($scope.newWidgetObject);
+ if (validateMsg) {
+ confirmBoxService.showInformation(validateMsg).then(isConfirmed => {return;});
+ return;
+ }
+ // Transform date into expected storage format
+ $scope.newWidgetObject.eventDate = $filter('date')($scope.newWidgetObject.eventDate, "yyyy-MM-dd");
+
+ dashboardService.saveCommonWidgetData($scope.newWidgetObject)
+ .then(function(res){
+ if (res.status == 'OK') {
+ $scope.widgetForm.$setPristine();
+ confirmBoxService.showInformation('You have added a new item').then(isConfirmed => {
+ });
+ dashboardService.getCommonWidgetData(message.type)
+ .then(function(res){
+ $scope.widgetData = res.response.items;
+ $scope.newWidgetObject = {};
+ });
+ }
+ else {
+ confirmBoxService.showInformation("Save failed").then(isConfirmed => {return;});
+ return;
+ }
+ });
+ };
+
+ $scope.remove = function(index) {
+ var confirmMsg = 'Are you sure you want to delete this item from the list?' + ' Press OK to delete.';
+ confirmBoxService.confirm(confirmMsg).then(function (confirmed) {
+ if (confirmed == true) {
+ $scope.widgetObject = {};
+ $scope.widgetObject.id = $scope.widgetData[parseInt(index)].id;
+ $scope.widgetObject.category = $scope.widgetData[parseInt(index)].category;
+ $scope.widgetObject.title = $scope.widgetData[parseInt(index)].title;
+ $scope.widgetObject.href = $scope.widgetData[parseInt(index)].href;
+ $scope.widgetObject.sortOrder = $scope.widgetData[parseInt(index)].sortOrder;
+
+
+ dashboardService.removeCommonWidgetData($scope.widgetObject)
+ .then(function(res){
+ dashboardService.getCommonWidgetData(message.type)
+ .then(function(res){
+ $scope.widgetData = res.response.items;
+ $scope.cancelEdit(index);
+ });
+ });
+ }
+ });
+
+ };
+ $scope.closeDialog = function(){
+ $scope.closeThisDialog( $scope.widgetData);
+ }
+ $scope.deleteSpeedDial = function(widget){
+
+ }
+
+ $scope.manage = function(index) {
+ $scope.modflag = true;
+
+ var thisdata = $scope.widgetData[index];
+ $scope.editMode=true;
+ var category = "#category"+index;
+ var categoryEdit = "#categoryEdit"+index;
+
+ var title = "#title"+index;
+ var titleEdit = "#titleEdit"+index;
+
+ var url = "#href"+index;
+ var urlEdit = "#hrefEdit"+index;
+
+ var order = "#order"+index;
+ var orderEdit = "#orderEdit"+index;
+
+ var eventDate = "#eventDate"+index;
+ var eventDateEdit = "#eventDateEdit"+index;
+
+
+ var dsavebutton ="#savebutton"+index;
+ var dremovebutton="#removebutton"+index;
+ var dmanagebutton="#managebutton"+index;
+ var deditRbutton="#editRbutton"+index;
+
+ $(title).css('display', 'none');
+ $(titleEdit).css('display', 'inherit');
+ $(url).css('display', 'none');
+ $(urlEdit).css('display', 'inherit');
+ $(order).css('display', 'none');
+ $(orderEdit).css('display', 'inherit');
+ $(eventDate).css('display', 'none');
+ $(eventDateEdit).css('display', 'inherit');
+
+ $(dsavebutton).css('display', 'inherit');
+ $(dremovebutton).css('display', 'inherit');
+ $(dmanagebutton).css('display', 'none');
+ $(deditRbutton).css('display', 'inherit');
+
+ };
+
+ $scope.cancelEdit = function(index) {
+
+ $scope.editMode=false;
+ var category = "#category"+index;
+ var categoryEdit = "#categoryEdit"+index;
+
+ var title = "#title"+index;
+ var titleEdit = "#titleEdit"+index;
+
+ var url = "#href"+index;
+ var urlEdit = "#hrefEdit"+index;
+
+ var order = "#order"+index;
+ var orderEdit = "#orderEdit"+index;
+
+ var eventDate = "#eventDate"+index;
+ var eventDateEdit = "#eventDateEdit"+index;
+
+ var dsavebutton ="#savebutton"+index;
+ var dremovebutton ="#removebutton"+index;
+ var dmanagebutton="#managebutton"+index;
+ var deditRbutton="#editRbutton"+index;
+
+ $(title).css('display', 'inherit');
+ $(titleEdit).css('display', 'none');
+
+ $(url).css('display', 'inherit');
+ $(urlEdit).css('display', 'none');
+
+ $(order).css('display', 'inherit');
+ $(orderEdit).css('display', 'none');
+
+ $(eventDate).css('display', 'inherit');
+ $(eventDateEdit).css('display', 'none');
+
+ $(dsavebutton).css('display', 'none');
+ $(dremovebutton).css('display', 'none');
+ $(dmanagebutton).css('display', 'inherit');
+ $(deditRbutton).css('display', 'none');
+
+ };
+
+ $scope.popupConfirmWin = function(title, msgBody, callback){
+ modalService.popupConfirmWin.apply(null, arguments);
+ };
+ }
+ }
+ CommonWidgetController.$inject = ['dashboardService', '$scope', 'message', '$q', '$http', 'conf', '$filter','confirmBoxService','$log'];
+ angular.module('ecompApp').controller('CommonWidgetController', CommonWidgetController);
+})();
+
+angular.module('ecompApp').filter('cut', function () {
+ return function (value, wordwise, max, tail) {
+ if (!value) return '';
+
+ max = parseInt(max, 10);
+ if (!max) return value;
+ if (value.length <= max) return value;
+
+ value = value.substr(0, max);
+ if (wordwise) {
+ var lastspace = value.lastIndexOf(' ');
+ if (lastspace != -1) {
+ //Also remove . and , so its gives a cleaner result.
+ if (value.charAt(lastspace-1) == '.' || value.charAt(lastspace-1) == ',') {
+ lastspace = lastspace - 1;
+ }
+ value = value.substr(0, lastspace);
+ }
+ }
+
+ return value + (tail || ' …');
+ };
+});
+
+angular.module('ecompApp').controller('DatePickerController', function ($scope, uibDateParser) {
+ $scope.today = function() {
+ $scope.dt = new Date();
+ };
+ $scope.today();
+
+ $scope.clear = function() {
+ $scope.dt = null;
+ };
+
+ $scope.inlineOptions = {
+ customClass: getDayClass,
+ minDate: new Date(),
+ showWeeks: true
+ };
+
+ $scope.dateOptions = {
+ dateDisabled: disabled,
+ formatYear: 'yy',
+ minDate: new Date(),
+ startingDay: 1
+ };
+
+ // Disable weekend selection
+ function disabled(data) {
+ var date = data.date,
+ mode = data.mode;
+ return mode === 'day' && (date.getDay() === 0 || date.getDay() === 6);
+ }
+
+ $scope.toggleMin = function() {
+ $scope.inlineOptions.minDate = $scope.inlineOptions.minDate ? null : new Date();
+ $scope.dateOptions.minDate = $scope.inlineOptions.minDate;
+ };
+
+ $scope.toggleMin();
+
+ $scope.open1 = function() {
+ // console.log('In open1');
+ $scope.popup1.opened = true;
+ };
+
+ $scope.open2 = function() {
+ // console.log('In open2');
+ $scope.popup2.opened = true;
+ };
+
+
+ $scope.setDate = function(year, month, day) {
+ $scope.dt = new Date(year, month, day);
+ };
+
+ $scope.formats = ['dd-MMMM-yyyy', 'yyyy/MM/dd', 'dd.MM.yyyy', 'shortDate'];
+ $scope.format = $scope.formats[3];
+ $scope.altInputFormats = ['M!/d!/yyyy'];
+
+ $scope.popup1 = {
+ opened: false
+ };
+
+ $scope.popup2 = {
+ opened: false
+ };
+
+ var tomorrow = new Date();
+ tomorrow.setDate(tomorrow.getDate() + 1);
+ var afterTomorrow = new Date();
+ afterTomorrow.setDate(tomorrow.getDate() + 1);
+ $scope.events = [
+ {
+ date: tomorrow,
+ status: 'full'
+ },
+ {
+ date: afterTomorrow,
+ status: 'partially'
+ }
+ ];
+
+ function getDayClass(data) {
+ var date = data.date,
+ mode = data.mode;
+ if (mode === 'day') {
+ var dayToCheck = new Date(date).setHours(0,0,0,0);
+
+ for (var i = 0; i < $scope.events.length; i++) {
+ var currentDay = new Date($scope.events[i].date).setHours(0,0,0,0);
+
+ if (dayToCheck === currentDay) {
+ return $scope.events[i].status;
+ }
+ }
+ }
+
+ return '';
+ }
+});
+
+
+
+function toggleCollapsible(id, clickedIconId, subtitutingIconId){
+ $("#"+id).toggle();
+ $("#"+clickedIconId).toggle();
+ $("#"+subtitutingIconId).toggle();
+ setTimeout(function(){focusFirstEle(id);}, 1000);
+}
+
+function focusFirstEle(id){
+ var focusItems = $("#"+id).find(":focusable");
+ for(var i=0; i<focusItems.length; i++){
+ if(!isHidden(focusItems[i])){
+ var targetClassName = focusItems[i].className;
+ if(targetClassName!='collapsibleArrow'){
+ focusItems[i].focus();
+ return;
+ }
+ }
+ }
+}
+
+function validateUrl(value){
+ return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
+ }
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less
new file mode 100644
index 00000000..dda8e300
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard-widget.controller.less
@@ -0,0 +1,101 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .searchLiHeader {
+font-weight: bold;
+color: #0574ac;
+font-size: 16px;
+padding-bottom: 10px;
+line-height: 1.5;
+font-family: arial;
+
+}
+
+.searchLiItems{
+cursor: pointer;
+font-weight: normal;
+font-size: 12px;
+color: #444444;
+font-family: arial;
+}
+
+.searchUl {
+list-style: none;
+border-bottom: 1px solid #bbb;
+padding-bottom: 20px;
+}
+
+#editWidgetsContent {
+ height: 300px;
+ width: 1770px;
+}
+
+#editWidgetsContent .scroll-viewport {
+ height: 300px;
+ width: 1770px;
+}
+
+.full button span {
+ background-color: limegreen;
+ border-radius: 32px;
+ color: black;
+}
+
+.partially button span {
+ background-color: orange;
+ border-radius: 32px;
+ color: black;
+ }
+
+ .add-widget-field{
+ width:250px;
+ display: inline-table;
+ margin:10px;
+
+ }
+ .uib-datepicker-popup {
+ th {
+ vertical-align: middle !important;
+ }
+ }
+
+ .input-text-area {
+ font-style: italic;
+ padding: 7px 10px;
+ width: 250px !important;
+ display: inline-block;
+ position: relative;
+ margin-bottom: 10px;
+ border-radius: 6px;
+ border: 1px solid #d8d8d8;
+ height: 32px;
+ border-color: slategrey !important;
+ }
+ .input-inline-edit-text {
+ font-style: italic;
+ padding: 7px 10px;
+ display: inline-block;
+ position: relative;
+ margin-bottom: 10px;
+ border-radius: 6px;
+ border: 1px solid #d8d8d8;
+ height: 32px;
+ border-color: slategrey !important;
+ width:100%;
+ } \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js
new file mode 100644
index 00000000..3228f636
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.js
@@ -0,0 +1,312 @@
+/*-
+ * ================================================================================
+ * 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 _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError('Cannot call a class as a function');
+ }
+}
+
+(function() {
+ var HTTP_PROTOCOL_RGX = /https?:\/\//;
+
+ var DashboardCtrl = function DashboardCtrl(applicationsService, $log,
+ $window, userProfileService, $scope, $cookies, $timeout, $interval,
+ $modal, $state, dashboardService,ngDialog) {
+
+ var _this = this;
+
+ _classCallCheck(this, DashboardCtrl);
+
+ // activate spinner
+ this.isLoading = true;
+ $scope.getUserAppsIsDone = false;
+ this.userProfileService = userProfileService;
+ $scope.demoNum = 1;
+ $scope.event_content_show = false;
+ $scope.widgetData = [];
+
+ $scope.editWidgetModalPopup = function(availableData, resourceType) {
+ $scope.editData = JSON.stringify(availableData);
+ $scope.availableDataTemp = $scope.availableData;
+ ngDialog.open({
+ templateUrl : 'app/views/dashboard/dashboard-widget-manage.html',
+ controller : 'CommonWidgetController',
+ resolve : {
+ message : function message() {
+ var message = {
+ type : resourceType,
+ availableData : $scope.editData
+ };
+ return message;
+ }
+ }
+ }).closePromise.then(needUpdate => {
+ if(resourceType=='NEWS'){
+ $scope.updateNews();
+ }else if(resourceType=='EVENTS'){
+ $scope.updateEvents();
+ }else if(resourceType=='IMPORTANTRESOURCES'){
+ $scope.updateImportRes();
+ }
+ });
+ };
+
+ userProfileService.getUserProfile()
+ .then(
+ function(profile) {
+ // $log.info(profile);
+ $scope.attuid = profile.attuid;
+ // $log.info('user has the following attuid: ' +
+ // profile.attuid);
+ $scope.appsView = [];
+
+ applicationsService.getPersUserApps()
+ .then(
+ function(res) {
+ // $log.info(res);
+ _this.apps = res;
+
+ for (var i = 0; i < _this.apps.length; i++) {
+ $scope.appsView[i] = {
+ sizeX : 2,
+ sizeY : 2,
+ headerText : _this.apps[i].name,
+ subHeaderText : _this.apps[i].notes,
+ imageLink : _this.apps[i].thumbnail || _this.apps[i].imageUrl,
+ order : _this.apps[i].order,
+ restrictedApp : _this.apps[i].restrictedApp,
+ url : _this.apps[i].url
+ };
+ }
+
+ // Append the show add/remove applications tile
+ $scope.appsView[_this.apps.length] = {
+ addRemoveApps : true,
+ sizeX : 2,
+ sizeY : 2,
+ headerText : 'Add/Remove Applications',
+ subHeaderText : '',
+ imageLink : 'assets/images/cloud.png',
+ order : '',
+ restrictedApp : false,
+ url : ''
+ };
+
+ // $log.info('getUserApps apps count : ' +
+ // $scope.appsView.length);
+ // Show 2 rows in the gridster if needed
+ if ($scope.appsView.length > 6) {
+ $(".dashboard-boarder").css({
+ "height" : "400px"
+ });
+ }else{
+ $(".dashboard-boarder").css({
+ "height" : "210px"
+ });
+ }
+ _this.isLoading = false;
+ $scope.getUserAppsIsDone = true;
+ })['catch']
+ (function(err) {
+ $log.error('DashboardCtlr: failed to get applications list', err);
+ _this.isLoading = false;
+ $scope.getUserAppsIsDone = true;
+ });
+ });
+
+
+ /* Widget Gridster Section */
+ $scope.newsGridsterItem = {
+ sizeX : 4,
+ sizeY : 4,
+ headerText : 'News',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : ''
+ };
+
+ $scope.eventsGridsterItem = {
+ sizeX : 4,
+ sizeY : 4,
+ headerText : 'Calendar Events',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : ''
+ };
+
+ $scope.impoResGridsterItem = {
+ sizeX : 4,
+ sizeY : 4,
+ headerText : 'Resources',
+ subHeaderText : '',
+ imageLink : '',
+ order : '',
+ url : ''
+ };
+ this.gridsterOpts = {
+ columns : 12,
+ colWidth : 95,
+ rowHeight : 95,
+ margins : [ 20, 20 ],
+ outerMargin : true,
+ pushing : true,
+ floating : true,
+ swapping : true,
+ draggable : {
+ handle: 'img'
+ }
+ };
+
+ this.emptyGridsterOpts = {
+ columns : 24,
+ colWidth : 190,
+ rowHeight : 190,
+ margins : [ 20, 20 ],
+ outerMargin : true,
+ pushing : true,
+ floating : true,
+ swapping : true,
+ draggable : {
+ handle: 'img'
+ }
+ };
+
+ this.goToCatalog = function(item) {
+ $state.go('root.appCatalog');
+ }
+
+ // navigate to application url in new tab
+ this.goToPortal = function(item) {
+ if (!item.url) {
+ $log.error('No URL found for this application, doing nothing!');
+ return;
+ }
+ if (item.restrictedApp) {
+ // Link-based apps open in their own browser tab
+ $window.open(item.url, '_blank');
+ } else {
+ // cache control so browsers load app page every time
+ var ccParam = '?cc=' + new Date().getTime();
+ var appUrl = null;
+ var urlParts = item.url.split('#');
+ if (urlParts.length < 2)
+ appUrl = item.url + ccParam;
+ else
+ appUrl = urlParts[0] + ccParam + "#" + urlParts[1];
+ // $log.debug('DashboardCtrlr::goToPortal: opening tab with URL
+ // ' + appUrl);
+ var tabContent = {
+ id : new Date(),
+ title : item.headerText,
+ url : appUrl
+ };
+ $cookies.putObject('addTab', tabContent);
+ }
+ };
+
+ /* News data */
+ $scope.newsData = [];
+ $scope.updateNews = function() {
+ $scope.newsData.length=0;
+ dashboardService.getCommonWidgetData('NEWS').then(function(res) {
+ // $log.info(res.message);
+ var newsDataJsonArray = res.response.items;
+ for (var i = 0; i < newsDataJsonArray.length; i++) {
+ $scope.newsData.push(newsDataJsonArray[i]);
+ }
+ })['catch'](function(err) {
+ $log.error('dashboard controller: failed to get news list', err);
+ _this.isLoading = false;
+ });
+ }
+ $scope.updateNews();
+
+ /* Events data */
+ $scope.eventData = [];
+ $scope.updateEvents = function() {
+ $scope.eventData.length=0;
+ dashboardService.getCommonWidgetData('EVENTS').then(function(res) {
+ var eventDataJsonArray = res.response.items;
+ for (var i = 0; i < eventDataJsonArray.length; i++) {
+ if(eventDataJsonArray[i].eventDate !=null) {
+ // yyyy-mm-dd
+ eventDataJsonArray[i].year = eventDataJsonArray[i].eventDate.substring(2,4);
+ eventDataJsonArray[i].mon = eventDataJsonArray[i].eventDate.substring(5,7);
+ eventDataJsonArray[i].day = eventDataJsonArray[i].eventDate.substring(8,10);
+ }
+ $scope.eventData.push(eventDataJsonArray[i]);
+ }
+ })['catch'](function(err) {
+ $log.error('dashboard controller: failed to get Events list', err);
+ _this.isLoading = false;
+ });
+ }
+ $scope.updateEvents();
+
+ /* Important Resources data */
+ $scope.importResData = [];
+ $scope.updateImportRes = function() {
+ $scope.importResData.length=0;
+ dashboardService.getCommonWidgetData('IMPORTANTRESOURCES').then(
+ function(res) {
+ // $log.error('couldnt get important list...',
+ // res.response.dataBlob);
+ // $log.info(res);
+ var importResDataJSONArray = res.response.items;
+ for (var i = 0; i < importResDataJSONArray.length; i++) {
+ $scope.importResData.push(importResDataJSONArray[i]);
+ }
+ })['catch'](function(err) {
+ $log.error('dashboard controller: failed to get resources list...', err);
+ _this.isLoading = false;
+ });
+ }
+ $scope.updateImportRes();
+
+ if (getParameterByName('noUserError') != null) {
+ if (getParameterByName('noUserError') == "Show") {
+ $("#errorInfo").show();
+ }
+ }
+
+ };
+
+ DashboardCtrl.$inject = [ 'applicationsService', '$log', '$window',
+ 'userProfileService', '$scope', '$cookies', '$timeout',
+ '$interval', '$modal', '$state', 'dashboardService','ngDialog'];
+ angular.module('ecompApp').controller('DashboardCtrl', DashboardCtrl);
+})();
+
+function getParameterByName(name, url) {
+ if (!url)
+ url = window.location.href;
+ name = name.replace(/[\[\]]/g, "\\$&");
+ var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), results = regex
+ .exec(url);
+ if (!results)
+ return '';
+ if (!results[2])
+ return '';
+ return results[2].replace(/\+/g, " ");
+}
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js
new file mode 100644
index 00000000..0a2ddd3a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.controller.spec.js
@@ -0,0 +1,78 @@
+/*-
+ * ================================================================================
+ * 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';
+
+describe('Controller: DashboardCtrl ', function() {
+ beforeEach(module('ecompApp'));
+
+ let DashboardCtrl, $controller, $q, rootScope, $log, $window, $cookies, scope;
+ let deferredApps, deferredUserProfile, applicationsServiceMock, userProfileServiceMock;
+
+
+ beforeEach(inject( (_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_, _CacheFactory_)=>{
+ rootScope = _$rootScope_;
+ scope = rootScope.$new();
+ $q = _$q_;
+ $controller = _$controller_;
+ $log = _$log_;
+ $window = _$window_;
+ $cookies = _$cookies_;
+
+ _CacheFactory_.destroyAll();
+
+ deferredApps = $q.defer();
+ deferredUserProfile = $q.defer();
+ applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']);
+ applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise);
+
+ userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']);
+ userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise);
+
+ DashboardCtrl = $controller('DashboardCtrl', {
+ applicationsService: applicationsServiceMock,
+ $log: $log,
+ $window: $window,
+ userProfileService: userProfileServiceMock,
+ $scope: scope,
+ $cookies: $cookies
+ });
+ scope.$digest();
+ }));
+
+ it('should populate this.apps with data from portals service getUserApps', inject(function ( _$q_) {
+ $q = _$q_;
+
+ let profile = {roles: 'superAdmin', userId: 'userid'};
+
+ deferredUserProfile.resolve(profile)
+ deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]);
+ scope.$digest();
+ expect(scope.appsViewData.length).toBe(3);
+ }));
+
+ it('should call $log error when getAllPortals fail', inject(function ( _$q_) {
+ $q = _$q_;
+ spyOn($log, 'error');
+ deferredUserProfile.reject('something happened!');
+ scope.$digest();
+ expect($log.error).toHaveBeenCalled();
+ }));
+
+});
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.less b/ecomp-portal-FE/client/app/views/dashboard/dashboard.less
new file mode 100644
index 00000000..4dc1a6c8
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.less
@@ -0,0 +1,766 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-applications-home {
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .gridster-item-container .gridster-item-body {
+ overflow-y:auto;
+ overflow-x:hidden;
+ }
+
+ .go-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ border-radius: 0px;
+ }
+
+ .applications-home-container {
+ .content_justify;
+ position: relative;
+ padding: 15px 0 32px 0;
+ width: 100%;
+
+ .applications-home-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+ .portals-list {
+ min-height: 70vh;
+ //display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: 1170px;
+
+ margin: auto;
+ margin-bottom: 63px;
+
+ .app-gridster-header {
+ background-color: @u;
+ font-size:12px;
+ overflow:hidden
+ }
+
+ .app-gridster-footer {
+ background-color: @u;
+ }
+
+ .portals-list-item {
+ background-color: @u;
+ border-radius: 2px;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ display: inline-block;
+ width: 360px;
+ height: 300px;
+ background-size: cover;
+ cursor: pointer;
+ margin: 15px;
+ overflow: hidden;
+
+ .portals-item-info {
+ background-color: @u;
+ height: 120px;
+ top: 180px;
+ position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+
+ .info-title {
+ .a24r;
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .c16r;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+ .info-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ bottom: 16px;
+ left: 16px;
+ }
+
+ &:hover {
+ opacity: .93;
+ z-index: 3;
+ }
+ }
+ }
+ }
+ }
+}
+.w-ecomp-main-error{
+ .o14r;
+ position: absolute;
+ width: 100%;
+ line-height: 1.5em;
+}
+.w-ecomp-main-disclaimer {
+ text-align: center;
+ .o14r;
+ //position: absolute;
+ bottom: -75px;
+ line-height: 1.5em;
+ margin: 0 auto;
+ width:1170px;
+ position: relative;
+
+}
+.build-number {
+ .o12i;
+}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.slide.ng-hide-add,
+.slide.ng-hide-remove,
+.slide.ng-enter,
+.slide.ng-leave {
+ transition: all 0.5s ease;
+}
+.slide.ng-hide,
+.slide.ng-enter {
+ transform: translate(-100%, 0);
+}
+.slide.ng-enter-active {
+ transform: translate(0, 0);
+}
+.slide.ng-leave {
+ transform: translate(0, 0);
+}
+.slide.ng-leave-active {
+ transform: translate(+100%, 0);
+}
+.dashboard-boarder{
+ height: 210px;
+ overflow: auto;
+}
+.dashboard-information-boarder{
+ border: 2px solid #ccc!important;
+ border-radius: 16px!important;
+}
+
+#information-section {
+ margin-top:50px;
+}
+
+.information-section-title{
+ color: #0574ac;
+ font-family: arial;
+ font-size: 25px;
+ margin: 20px;
+ width: 1170px;
+}
+#left {
+ float: left;
+ width: 33%;
+ height:300px;
+ background-color:white;
+}
+
+#center {
+ float: right;
+ width: 33%;
+ height:300px;
+ background: white;
+}
+#right {
+ margin-left:34%;
+ margin-right:34%;
+ height:300px;
+ background:white;
+}
+
+
+.dock ul{
+display: inline-block;
+
+width: auto;
+margin: 0px;
+padding: 0px;
+list-style: none;
+
+}
+.dock ul li {
+width: auto;
+height: auto;
+display: inline-block;
+bottom: 0;
+vertical-align: bottom;
+margin-top: -43px;
+}
+.dock ul li a {
+display: block;
+height: 150px;
+width: 150px;
+position: relative;
+-webkit-transition-property: width, height,margin-top;
+-webkit-transition-duration: 0.5s;
+-o-transition-property: width, height,margin-top;
+-o-transition-duration: 0.5s;
+-moz-transition-property: width, height,margin-top;
+-moz-transition-duration: 0.5s;
+}
+.dock ul li a:hover {
+width: 200px;
+height: 200px;
+margin-top: -50px;
+}
+.dock ul li a img {
+width: 100%;
+position: absolute;
+bottom: 0;
+left: 0;
+border: none;
+padding: 0px 0px 0px 30px;
+}
+.dock_left{
+width: 31px;
+-webkit-transform: rotate(33deg);
+-moz-transform: rotate(33deg);
+-o-transform: rotate(33deg);
+position: relative;
+background: #EEE;
+overflow: hidden;
+float: left;
+height: 100px;
+z-index: 2;
+margin: -18px;
+}
+.dock_right{
+width: 36px;
+-webkit-transform: rotate(-33deg);
+-moz-transform: rotate(-33deg);
+-o-transform: rotate(-33deg);
+position: relative;
+background: #EEE;
+overflow: hidden;
+float: left;
+height: 100px;
+z-index: 2;
+margin: -18px;
+}
+
+
+
+
+* {margin: 0; padding: 0;}
+body {background: #ccc;}
+
+.slider{
+ width: 640px;
+ position: relative;
+ padding-top: 320px;
+
+ margin: 50px auto;
+
+ box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.75);
+}
+
+.slider>img{
+ position: absolute;
+ left: 0; top: 0;
+ transition: all 0.5s;
+}
+
+.slider input[name='slide_switch'] {
+ display: none;
+}
+
+.slider label {
+ margin: 18px 0 0 18px;
+ border: 3px solid #999;
+
+ float: left;
+ cursor: pointer;
+ transition: all 0.5s;
+
+ opacity: 0.6;
+}
+
+.slider label img{
+ display: block;
+}
+
+.slider input[name='slide_switch']:checked+label {
+ border-color: #666;
+ opacity: 1;
+}
+
+.slider input[name='slide_switch'] ~ img {
+ opacity: 0;
+ transform: scale(1.1);
+}
+/*That hides all main images at a 110% size
+On click the images will be displayed at normal size to complete the effect
+*/
+.slider input[name='slide_switch']:checked+label+img {
+ opacity: 1;
+ transform: scale(1);
+}
+
+.accordion {
+ width: 895px; height: 320px;
+ overflow: hidden;
+ box-shadow: 0 10px 6px -6px #111;
+ margin: 20px auto
+}
+.accordion ul { width: 200% }
+
+.accordion li {
+ position: relative;
+ display: block;
+ width: 160px;
+ float: left;
+ box-shadow: 0 0 30px 8px #222;
+ transition: all 0.4s ease .300ms;
+}
+
+.accordion ul:hover li {width: 40px }
+.accordion ul li:hover {width: 640px }
+
+.caption {
+ background: rgba(0, 0, 0, 0.5);
+ position: absolute;
+ bottom: 0;
+ width: 640px
+}
+
+.caption a {
+ display: block;
+ color: #fff;
+ text-decoration: none;
+ font: normal 16px 'Lato', Helvetica, Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ padding: 15px;
+}
+
+
+
+/*events*/
+.events-date{
+ margin-left: 10px;
+ float:left;
+ white-space: normal;
+ display: inline-block;
+ vertical-align: middle;
+ width: 55px;
+}
+.event-title-div{
+ float: left;
+ width: 235px;
+ line-height: 20px;
+ padding: 5px;
+ font-size: 14px;
+ margin-left: 4px;
+}
+.events-date{
+ height: 20px;
+ margin-left: 4px;
+ font-size: 14px;
+}
+.events-content{
+ font-size: 14px;
+ color: #444;
+ margin-right: 10px;
+}
+.events-content-body{
+ margin-top:5px;
+ color:#444;
+ margin-left:12px;
+ line-height:1.5;
+}
+.events {
+ border-radius: 4px;
+ padding: 3px;
+ -webkit-user-select: none;
+}
+.events ul {
+ float: left;
+ width:100%;
+ -webkit-user-select: none
+}
+.events ul li {
+ line-height: 30px;
+ list-style: none;
+ border-bottom: 2px solid grey;
+ height: 100%;
+ min-height: 42px;
+}
+.events ul li:hover {
+ background: #ddd;
+ cursor: pointer;
+}
+.events ul li a {
+ color: black;
+ text-decoration: none;
+ font: 14px Helvetica, Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -webkit-user-select: none;
+ font-family: arial;
+}
+.events-link{
+ color: #067ab4 !important;
+}
+
+
+
+/*widgets*/
+.singleBtnBorder {
+ border-radius: 6px 6px 6px 6px;
+}
+
+.widgetHeaderBtn{
+
+ height: 30px;
+ background-color: #FFFFFF;
+ position: relative;
+ display: inline-block;
+ -moz-background-clip: padding-box;
+ -webkit-background-clip: padding-box;
+ background-clip: padding-box;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 8px 20px;
+ font-size: 14px;
+ line-height: 14px;
+ min-width: 60px;
+ border: none;
+ border-radius: 6px;
+ background-color: #ffffff;
+ background-image: none;
+ color: #ffffff;
+ vertical-align: middle;
+ text-align: center;
+ text-decoration: none;
+ text-transform: capitalize;
+ text-shadow: none;
+ white-space: nowrap;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ -webkit-transition: background-color 0.3s ease-out;
+ -moz-transition: background-color 0.3s ease-out;
+ transition: background-color 0.3s ease-out;
+ }
+
+.widgetHeaderBtnPosition {
+ width: 30px;
+ min-width: 0px;
+ border: 1px solid #AAAAAA;
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+
+.icon-anchor {
+ color: #888;
+}
+
+.widgetHeaderBtn:hover, .widgetHeaderBtn:focus {
+ background: rgba(0, 0, 0, 0);
+ color: #3a7999;
+ box-shadow: inset 0 0 0 2px #3a7999;
+}
+
+@keyframes ticker {
+ 0% {margin-top: 0}
+ 25% {margin-top: -55px}
+ 50% {margin-top: -110px}
+ 75% {margin-top: -165px}
+ 100% {margin-top: 0}
+}
+
+body { background: #333; width: 100%; height: 100% }
+
+.news {
+
+ width: 350px;
+ height: 250px;
+ margin: 0px auto;
+ border-radius: 4px;
+ padding: 3px;
+ -webkit-user-select: none;
+
+}
+
+
+
+.news ul {
+ float: left;
+ width:100%;
+ -webkit-user-select: none
+}
+
+.news ul li {line-height: 30px; list-style: none;
+ border-bottom: 2px solid grey;
+ min-height: 42px;
+ }
+
+.news ul li:hover {
+ background: #ddd;
+ cursor: pointer;
+}
+
+.news ul li a {
+ color: black;
+ text-decoration: none;
+ font-size: 14px;
+ line-height: 1.5;
+ display: inline-block;
+ width:100%;
+ min-height:40px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ -webkit-font-smoothing: antialiased;
+ -webkit-user-select: none;
+
+ /* font-family: arial;*/
+}
+
+.news ul:hover { animation-play-state: paused }
+.news span:hover+ul { animation-play-state: paused }
+.resources {
+
+ width: 350px;
+ height: 200px;
+ margin-left: 5px;
+ border-radius: 4px;
+ padding: 3px;
+ -webkit-user-select: none;
+
+}
+
+
+
+.resources ul {
+ float: left;
+ width:100%;
+
+ -webkit-user-select: none
+}
+
+.resources ul li {line-height: 30px; list-style: none;
+ border-bottom: 2px solid grey;
+ min-height: 42px;
+ }
+
+.resources ul li:hover {
+ background: #ddd;
+ cursor: pointer;
+}
+
+.resources ul li a {
+ color: black;
+ text-decoration: none;
+ font-size: 14px;
+ line-height: 1.5;
+ width:100%;
+ min-height:40px;
+ display: inline-block;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ -webkit-font-smoothing: antialiased;
+ -webkit-user-select: none;
+}
+
+
+.blue { background: #347fd0 }
+.blue span { background: #2c66be }
+.red { background: #d23435 }
+.red span { background: #c22b2c }
+.green { background: #699B67 }
+.green span { background: #547d52 }
+.magenta { background: #b63ace }
+.magenta span { background: #842696 }
+
+
+.box-one {
+ -webkit-transition:all linear 0.4s;
+ transition:all linear 0.4s;
+ height:100px; width:200px; background:white; border: 2px solid #ccc!important;
+ border-radius: 16px!important;
+}
+.box-one.ng-hide {
+display: block!important;
+ opacity:0;
+}
+
+.gridsterContent{
+ height:120px;
+}
+.gridsterAppContent{
+ height:120px;
+
+}
+
+ .gridsterImage{
+ height:84px;
+ width:168px;
+ }
+ .grider-content-ecomp{
+ transition: transform 0.5s ease-out;
+ }
+ .grider-content-ecomp:hover{
+ transform: scale(1.1);
+}
+
+
+.information-section{
+ /*margin-top:25px;*/
+}
+.information-section-gridsterContent{
+ height:300px;
+}
+.information-sections-gridster-header{
+ color: #0574ac;
+ font-family: arial;
+ font-size: 25px;
+
+}
+/*application empty div*/
+.app-error-block {
+ padding-top: 10px;
+
+}
+.activity-error-block {
+ padding-top: 60px;
+
+}
+
+.activity-error-msg1{
+ text-align: center;
+ margin-top: 20px;
+ font-family: arial;
+ color: #444;
+ font-size: 20px;
+
+}
+
+.newstape {
+ background: white;
+ color: black;
+ height: 400px;
+ overflow: hidden;
+}
+
+.newstape-content {
+ position: relative;
+ padding: 15px;
+}
+
+.newstape-drag { cursor: ns-resize; }
+
+.text-center { text-align: center; }
+
+.text-right { text-align: right; }
+
+.text-justify { text-align: justify; }
+
+.optionsMenu{
+ position: absolute;
+ list-style: none;
+ top: 25px;
+ right: 10px;
+ border: 1px solid #067ab4;
+ display: none;
+ z-index: 2;
+ border-radius: 6px 0px 6px 6px;
+ background: #fff;
+ width: 130px;
+}
+
+.optionsMenuLink {
+ position: relative;
+ padding-left: 8px;
+ padding-right: 2px;
+ font-size: 12px;
+ line-height: 30px;
+ color: #444444;
+}
+.optionsMenu > li:hover a {
+ color: #ffffff !important;
+}
+.optionsMenu > li {
+ width: 100%;
+ text-align: left;
+}
+.optionsMenu > li:hover {
+ background-color: #0faaf7;
+ border-color: none !important;
+ cursor: pointer;
+}
+
+.simulateGridHeader{
+ position: relative;
+ height: 50px !important;
+ border: 1px solid #d3d3d3;
+ border-bottom: 0;
+ background-color: #E5E5E5;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ z-index: 1;
+}
+
+.simulateGridHeaderTitle{
+ line-height: 44px;
+ margin-left: 26px;
+ font-family: arial;
+ font-size: 18px;
+ color: #444444;
+ float: left;
+}
+
+.simulateGridHeaderHandle{
+ cursor: move;
+ margin: 12px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ border: 0;
+ vertical-align: middle;
+ -ms-interpolation-mode: bicubic;
+ display: block;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html b/ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html
new file mode 100644
index 00000000..0d96a585
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/dashboard.tpl.html
@@ -0,0 +1,346 @@
+<!--
+ ================================================================================
+ 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-applications-home" id="contentId">
+ <div class="applications-home-container">
+ <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>
+ </div>
+ <div class="dashboard-boarder">
+ <div class="gridster-container override_background">
+
+ <div att-gridster att-gridster-options="dashboard.gridsterOpts">
+
+ <div ng-if="appsView.length==1">
+ <i class="ion-information-circled full-linear-ion-information-circled"
+ style="margin-left: 50%; font-size: 90px; color: black"></i> <br>
+ <div class="activity-error-msg1">
+ <span>Click on the tile at the left to personalize your applications.</span>
+ </div>
+ </div>
+
+ <div class="style_prevu_kit" att-gridster-item='item'
+ ng-repeat="item in appsView"
+ id="app-{{item.headerText.split(' ').join('-')}}">
+ <!-- A regular application tile -->
+ <div class="grider-content-ecomp" ng-if="item.addRemoveApps == null">
+ <div id="grips-{{item.headerText.split(' ').join('-')}}"
+ ng-hide="users.isLoadingTable && !users.getUserAppsIsDone"
+ att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{item.headerText | elipsis: 33}}"
+ class="app-gridster-header"></div>
+ <div id="app-image-{{item.headerText.split(' ').join('-')}}"
+ att-gridster-item-body
+ ng-style="{'cursor':'pointer',
+ 'background-image': 'url('+(item.imageLink)+')',
+ 'order': item.order,
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px'}"
+ ng-click="dashboard.goToPortal(item)"
+ ng-hide="users.isLoadingTable && !users.getUserAppsIsDone"
+ class="gridsterContent"></div>
+ </div>
+ <!-- Format the special tile differently -->
+ <div class="grider-content-ecomp" style="width: 170px" ng-if="item.addRemoveApps">
+ <div id="grips-add-remove-applications-tile" att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text='Select applications..'
+ ng-click="dashboard.goToCatalog()"
+ ng-style="{'cursor':'pointer'}"
+ class="app-gridster-header">
+ </div>
+ <div id="app-image-empty" att-gridster-item-body
+ ng-style="{'cursor':'pointer',
+ 'text-align':'center',
+ 'order': item.order,
+ 'background-color':'white',
+ 'background-repeat': 'no-repeat',
+ 'background-size': '170px 130px',
+ 'width': '170px',
+ 'height': '120px',
+ 'opacity': '1'}"
+ ng-click="dashboard.goToCatalog()" class="gridsterContent">
+ <label style="font-size:12px">Click here to personalize <br> this applications page</label>
+ <i class="ion-plus-circled" style="font-size:70px;color: #067ab4"></i>
+ </div>
+ </div>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+
+ <br>
+ <br>
+
+ <div class="information-section">
+ <div id="news-gridster-container"
+ class="gridster-container override_background">
+ <div att-gridster att-gridster-options="dashboard.gridsterOpts">
+
+ <div class="style_prevu_kit">
+ <!-- News -->
+ <div att-gridster-item='newsGridsterItem'>
+ <div att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{newsGridsterItem.headerText | elipsis: 33}}"
+ header-icon="full-linear-icon-webwindow"
+ sub-header-text="{{newsGridsterItem.subHeaderText | elipsis: 56}}"
+ class="information-sections-gridster-header"
+ style="font-size: 18px">
+
+ <div ng-if="isAdminPortalAdmin == true"
+ style="font-size: 20px;">
+
+ <a id="widgetHeaderBtns"
+ class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder"
+ ng-mousedown="showNewsOption=!showNewsOption"
+ ng-mouseleave="showNewsOption=false"
+ ng-class="singleBtnBorder" role="button" alt="Settings"
+ title="Settings" att-accessibility-click="13,32" tabindex="0">
+ <span class="ion-more" style="color:#888"></span>
+ </a>
+ <ul class="optionsMenu"
+ style="max-width: 130px; height: 32px; display: block"
+ ng-show="showNewsOption" ng-mouseenter="showNewsOption=true"
+ ng-mouseleave="showNewsOption=false">
+ <!-- ngIf: widget.duplicateAllowed -->
+ <li ng-click="editWidgetModalPopup(newsData, 'NEWS');"
+ ddh-accessibility-click="13,32"><a
+ class="optionsMenuLink" href="javascript:void(0)">Edit</a></li>
+ <!-- end ngIf: widget.duplicateAllowed -->
+
+ </ul>
+ </div>
+
+ </div>
+ <div id="newsContainer" att-gridster-item-body
+ class="information-section-gridsterContent">
+ <div class="news">
+ <ul id="newsContent" ng-show="newsData.length!=0">
+ <li ng-repeat="news in newsData"><a href="{{news.href}}"
+ target="_blank" ng-bind="news.title"></a></li>
+ </ul>
+ <div ng-hide="newsData.length!=0">
+ <div class="activity-error-container"
+ style="background: rgb(255, 255, 255); overflow: auto !important; width: 100%;">
+ <div class="activity-error-block">
+ <i class="ion-information-circled full-linear-ion-information-circled"
+ style="margin-left: 125px; font-size: 90px"></i> <br>
+ <div class="activity-error-msg1">There's currently no
+ news available.</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <!-- Events -->
+ <div att-gridster-item='eventsGridsterItem'>
+ <div att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{eventsGridsterItem.headerText | elipsis: 33}}"
+ header-icon="full-linear-icon-calendar"
+ sub-header-text="{{eventsGridsterItem.subHeaderText | elipsis: 56}}"
+ class="information-sections-gridster-header"
+ style="font-size: 18px">
+ <div style="font-size: 20px;"
+ ng-if="isAdminPortalAdmin == true" class="ng-scope"
+ role="button" tabindex="0">
+ <a id="widgetHeaderBtns"
+ class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder"
+ ng-mousedown="showEventsOption=!showEventsOption"
+ ng-mouseleave="showEventsOption=false"
+ ng-class="singleBtnBorder" role="button" alt="Settings"
+ title="Settings" att-accessibility-click="13,32" tabindex="0">
+ <span class="ion-more" style="color:#888"></span>
+ </a>
+ <ul class="optionsMenu"
+ style="max-width: 130px; height: 32px; display: block"
+ ng-show="showEventsOption"
+ ng-mouseenter="showEventsOption=true"
+ ng-mouseleave="showEventsOption=false">
+ <!-- ngIf: widget.duplicateAllowed -->
+ <li ng-click="editWidgetModalPopup(eventData, 'EVENTS');">
+ <a class="optionsMenuLink" href="javascript:void(0)"
+ ddh-accessibility-click="13,32">Edit</a>
+ </li>
+ <!-- end ngIf: widget.duplicateAllowed -->
+ </ul>
+ </div>
+ </div>
+ <div att-gridster-item-body
+ class="information-section-gridsterContent">
+ <div class="events">
+ <ul ng-show="eventData.length!=0">
+ <li ng-repeat="event in eventData">
+ <div ng-click="event_content_show=!event_content_show">
+ <div class="events-date">{{event.mon}}/{{event.day}}/{{event.year}}
+ </div>
+ <div>
+ <div class="event-title-div">
+ <p ng-bind="event.title"></p>
+ </div>
+ <div>
+ <span class="ion-ios-arrow-up" ng-if="event_content_show"
+ style="color: #888; font-size: 22px;"></span> <span
+ class="ion-ios-arrow-down" ng-if="!event_content_show"
+ style="color: #888; font-size: 22px;"></span>
+
+ </div>
+ <div style="clear: both;"></div>
+ </div>
+ </div>
+ <div class="events-content" ng-show="event_content_show">
+ <div class="events-content-body">
+ <a class="events-link" ng-href="{{event.href}}"
+ target="_blank"> <span ng-bind="event.content"></span>
+ </a>
+ </div>
+ <div></div>
+ </div>
+ </li>
+
+
+ </ul>
+ <div ng-hide="eventData.length!=0">
+ <div class="activity-error-container"
+ style="background: rgb(255, 255, 255); overflow: hidden !important; width: 100%;">
+ <div class="activity-error-block">
+ <i class="ion-information-circled full-linear-ion-information-circled"
+ style="margin-left: 125px; font-size: 90px"></i> <br>
+ <div class="activity-error-msg1">There's currently no
+ event available.</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Important Resources -->
+ <div att-gridster-item='impoResGridsterItem'>
+ <div att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{impoResGridsterItem.headerText | elipsis: 33}}"
+ header-icon="full-linear-icon-cloud"
+ sub-header-text="{{impoResGridsterItem.subHeaderText | elipsis: 56}}"
+ class="information-sections-gridster-header"
+ style="font-size: 18px">
+ <!-- <span class="full-linear-icon-cloud" style="font-size: 33px; font-weight: bold; margin-right: 10px;"></span>
+ -->
+ <div style="font-size: 20px;"
+ ng-if="isAdminPortalAdmin == true" class="ng-scope"
+ role="button" tabindex="0">
+ <a id="widgetHeaderBtns"
+ class="widgetHeaderBtn widgetHeaderBtnPosition singleBtnBorder"
+ ng-class="singleBtnBorder" role="button" alt="Settings"
+ ng-mousedown="showImpResOption=!showImpResOption"
+ ng-mouseleave="showImpResOption=false" title="Settings"
+ att-accessibility-click="13,32" tabindex="0"> <span
+ class="ion-more" style="color:#888"></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, 'IMPORTANTRESOURCES');">
+ <a class="optionsMenuLink" href="javascript:void(0)"
+ ddh-accessibility-click="13,32">Edit</a>
+ </li>
+ <!-- end ngIf: widget.duplicateAllowed -->
+
+ </ul>
+ </div>
+ </div>
+ <div att-gridster-item-body
+ class="information-section-gridsterContent">
+ <div class="resources">
+ <ul ng-show="importResData.length!=0">
+
+ <li ng-repeat="item in importResData"><a
+ href="{{item.href}}" target="_blanl">{{item.title}}</a></li>
+ </ul>
+ <div ng-hide="importResData.length!=0">
+ <div class="activity-error-container"
+ style="background: rgb(255, 255, 255); overflow: hidden !important; width: 100%;">
+ <div class="activity-error-block">
+ <i class="ion-information-circled full-linear-ion-information-circled"
+ style="margin-left: 125px; font-size: 90px"></i> <br>
+ <div class="activity-error-msg1">There's currently no
+ links available.</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="style_prevu_kit" att-gridster-item='item'
+ ng-repeat="item in widgetData">
+ <div>
+ <div att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{item.headerText | elipsis: 33}}"
+ sub-header-text="{{item.subHeaderText | elipsis: 56}}"
+ class="app-gridster-header"></div>
+ <div att-gridster-item-body></div>
+ </div>
+
+ </div>
+
+ <div class="style_prevu_kit" att-gridster-item='item'
+ ng-repeat="item in widgetData">
+ <div>
+ <div att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text="{{item.headerText | elipsis: 33}}"
+ sub-header-text="{{item.subHeaderText | elipsis: 56}}"
+ class="app-gridster-header"></div>
+ <div att-gridster-item-body></div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div></div>
+
+ </div>
+
+ </div>
+</div> \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js b/ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js
new file mode 100644
index 00000000..88e6942e
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/dashboard/newsticker.controller.js
@@ -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.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class NewsTickerController {
+ constructor($scope) {
+ $scope.newstape = function(){
+ jQuery('.newstape').newstape({
+
+ period: 30,
+
+ offset: 1,
+
+ mousewheel: true,
+
+ mousewheelRate: 30,
+
+ dragable: true,
+
+ heightSpy: true
+
+ });
+ };
+ $scope.newstape();
+
+ }
+ }
+ NewsTickerController.$inject = ['$scope'];
+ angular.module('ecompApp').controller('NewsTickerController', NewsTickerController);
+})();
diff --git a/ecomp-portal-FE/client/app/views/errors/error.404.tpl.html b/ecomp-portal-FE/client/app/views/errors/error.404.tpl.html
new file mode 100644
index 00000000..255bfe36
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/errors/error.404.tpl.html
@@ -0,0 +1,31 @@
+<!--
+ ================================================================================
+ 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="admins-page-main">
+ <div id="404-title" class="w-ecomp-main-view-title">Page Not Found</div>
+ <div class="admins-table">
+ <span id="404-error-text" class="errors-text ">The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable. Please select from the
+ menu on the left to navigate to a valid page.
+ </span>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/errors/error.controller.js b/ecomp-portal-FE/client/app/views/errors/error.controller.js
new file mode 100644
index 00000000..7bf86c24
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/errors/error.controller.js
@@ -0,0 +1,36 @@
+/*-
+ * ================================================================================
+ * 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 ErrorCtrl {
+ constructor($scope, $window) {
+
+ $scope.errorMsg = '';
+
+ if(window.location.href.indexOf('noUserError') !== -1){
+ $scope.errorMsg = 'You do not have a user account for this application. Please contact your system administrator';
+ } else if(window.location.href.indexOf('unKnownError') !== -1){
+ $scope.errorMsg = 'An unknown error has occurred. Please contact your system administrator';
+ }
+ }
+ }
+ ErrorCtrl.$inject = ['$scope','$window'];
+ angular.module('ecompApp').controller('ErrorCtrl', ErrorCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/errors/error.tpl.html b/ecomp-portal-FE/client/app/views/errors/error.tpl.html
new file mode 100644
index 00000000..9de8084a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/errors/error.tpl.html
@@ -0,0 +1,26 @@
+<!--
+ ================================================================================
+ 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 >
+ <div class="admins-table" align="center" id="errorInfo" style="font-size:12px;margin-left:5px"><span class="errors-text">{{errorMsg}}</span></div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/footer/footer.controller.js b/ecomp-portal-FE/client/app/views/footer/footer.controller.js
new file mode 100644
index 00000000..adcd470a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/footer/footer.controller.js
@@ -0,0 +1,49 @@
+/*-
+ * ================================================================================
+ * 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();
+
+ $scope.portalName = "OpenECOMP Portal Version: ";
+
+ 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/client/app/views/footer/footer.controller.spec.js b/ecomp-portal-FE/client/app/views/footer/footer.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/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/client/app/views/footer/footer.less b/ecomp-portal-FE/client/app/views/footer/footer.less
new file mode 100644
index 00000000..f8eee07d
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/footer/footer.less
@@ -0,0 +1,53 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-footer {
+ z-index: 101;
+ background-color: rgb(6, 122, 180);
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: @footer-height;
+ .o12r;
+ color: white;
+
+ a {
+ color: white;
+ }
+
+ .footer-image {
+ margin: auto;
+
+ }
+
+ .build-number {
+ float: right;
+ padding: 5px;
+ color: white;
+ }
+ .footer-content {
+ align-items: center;
+ text-align: center;
+ vertical-align: middle;
+ display: flex;
+ justify-content: center;
+ //width: @table-width;
+ margin: auto;
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/footer/footer.tpl.html b/ecomp-portal-FE/client/app/views/footer/footer.tpl.html
new file mode 100644
index 00000000..a1788a3a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/footer/footer.tpl.html
@@ -0,0 +1,36 @@
+<!--
+ ================================================================================
+ 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 >
+ <img id="logo-image" class="footer-image">
+ </div>
+ <div >
+ <p>
+
+ </p>
+ <p>
+
+ </p>
+ <p id="build-number" class="build-number" >OpenECOMP Portal Version: 1.0.0</p>
+ <!-- <p id="build-number" class="build-number" >ECOMP Portal Version: {{buildInfo.manifest['Implementation-Version']}}.{{buildInfo.manifest['Build-Number']}}</p> -->
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js
new file mode 100644
index 00000000..0d27d868
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.controller.js
@@ -0,0 +1,384 @@
+/*-
+ * ================================================================================
+ * 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 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()){
+ this.preSelectedRoles = {roles:[]};
+ }else if(!angular.isUndefined(this.menuItem.menuDetails) &&
+ $scope.ngDialogData.source=='edit' && this.isLeafMenuItem() &&
+ this.menuItem.menuDetails.appid!=appid) {
+ 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;
+ 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) {
+ $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;
+ }
+ getAvailableApps();
+ $log.debug("MenuDetailsModalCtrl::init: Menu details: " + JSON.stringify(this.menuItem.menuDetails));
+ };
+
+
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+
+ this.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){
+ err.data = [err.data]
+ }
+ $log.debug('MenuDetailsModalCtrl::handleConflictErrors: err.data = ' + JSON.stringify(err.data));
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ $log.debug('MenuDetailsModalCtrl::handleConflictErrors: fieldName = ' + field.name);
+ $scope.functionalMenuForm[field.name].$setValidity('conflict', false);
+ 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) => {
+ 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 = () => {
+
+ 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') {
+ $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 (!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){
+ handleConflictErrors(err);
+ }
+ $log.error('MenuDetailsModalCtrl::saveChanges: error - ',err);
+ }).finally(()=>{
+ this.isSaving = false;
+ });
+
+ $log.debug("MenuDetailsModalCtrl::saveChanges: Edit Menu output will be: " + JSON.stringify(activeMenuItem));
+ } else {
+ $log.debug('MenuDetailsModalCtrl::saveChanges: Will be saving a New menu item');
+ var newMenuItem = {
+ menuId:null,
+ column:this.menuItem.menu.column,
+ text:this.menutitle,
+ 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);
+ }
+ $log.error('MenuDetailsModalCtrl::saveChanges error: ', err);
+ }).finally(()=>{
+ this.isSaving = false;
+ });
+
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ 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/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html
new file mode 100644
index 00000000..7157886b
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.delete.modal.html
@@ -0,0 +1,35 @@
+<!--
+ ================================================================================
+ 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">Delete Menu Item</div>
+ <div 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 class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html
new file mode 100644
index 00000000..58588ce5
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/menu-details.modal.html
@@ -0,0 +1,106 @@
+<!--
+ ================================================================================
+ 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>
+ </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/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less
new file mode 100644
index 00000000..c322f453
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu-dialog/modal-details.modal.less
@@ -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.
+ * ================================================================================
+ */
+ .functionalMenu-details-modal {
+ height: 430px;
+
+ .title {
+ .n18r;
+ border-bottom: @a 3px solid;
+
+ }
+
+ .btn-medium{
+ width:216px;
+ }
+ .dropdown-menu-medium{
+ width:236px;
+ max-height: 200px;
+ overflow-y: auto;
+ }
+
+ .functionalMenu-properties-main {
+ padding: 16px;
+ height: 306px;
+ overflow-y: visible;
+
+ .item{
+ position: relative;
+ margin-bottom: 18px;
+
+ .input-field{
+ .custom-input-field;
+ width: 100%;
+ &.url{
+ width: 78%;
+ display: inline-block;
+ }
+ }
+
+ .select-field {
+ .custom-select-field;
+ }
+
+ .item-label{
+ .o14r;
+ }
+
+ .right-item{
+ position: relative;
+ display: inline-block;
+ width: 48%;
+ float: right;
+ }
+ .left-item{
+ display: inline-block;
+ width: 48%;
+ }
+
+ .url-validation-button{
+ .btn-blue;
+ width: 20%;
+ display: inline-block;
+ float: right;
+ }
+
+ .error-container{
+ position: absolute;
+ width: 220px;
+ display: block;
+ height: 12px;
+ line-height: 12px;
+
+ .err-message{
+ color: red;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: green;
+ font-size: 9px;
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js
new file mode 100644
index 00000000..d17c4ab8
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.controller.js
@@ -0,0 +1,331 @@
+/*-
+ * ================================================================================
+ * 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=[];
+
+ for(let i = 0; i < res.length; i++){
+ res[i].children=[];
+ res[i].label=res[i].text;
+ res[i].id=res[i].text;
+
+ }
+
+ 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]);
+
+ }
+ }
+ }
+
+ res.sort(function(a, b) {
+ return a.column-b.column;
+ })
+
+ for(let i = 0; i < res.length; i++){
+ res[i].children.sort(function(a, b){
+ return a.column-b.column;
+ })
+ }
+
+ 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();
+ }
+ });
+ }
+ });
+ };
+
+ $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(() => {
+ $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 != "")) {
+ 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){
+ 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
+ };
+
+ 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);
+ });
+ });
+
+
+ $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/client/app/views/functionalMenu/functionalMenu.less b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less
new file mode 100644
index 00000000..24a91287
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.less
@@ -0,0 +1,70 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .functional-admin-page-main{
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+ .u16r;
+
+ .functional-admin-container {
+ .content_justify;
+ position: relative;
+ padding: 0 0 32px;
+ width: 1170px;
+
+ .functional-admin-home-title {
+ .a24r;
+ padding-bottom: 20px;
+ .content_justify;
+ }
+ .tree{
+ }
+ .functional-admin-button-container {
+ padding-top: 10px;
+ .btn-blue{
+ height: 35px;
+ width: 140px;
+ }
+ }
+ }
+ .error-text {
+ width: 1170px;
+ margin: auto;
+ padding: 20px;
+ left: 20px;
+ font-weight: bold;
+ font-size: 16px;
+ text-align: left;
+ color: @err;
+ background-color: @u;
+
+ .error-help {
+ color: @o;
+ font-weight: normal;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html
new file mode 100644
index 00000000..a04cfe4c
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/functionalMenu.tpl.html
@@ -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.
+ ================================================================================
+ -->
+<div class="w-ecomp-main">
+ <div class="w-ecomp-main-container">
+ <div class="functional-admin-page-main" id="contentId">
+ <div class="functional-admin-container">
+ <div id="functional-admin-home-title" class="functional-admin-home-title">Edit Functional Menu</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="ion-eye"></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="ion-plus-round"></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="ion-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="ion-trash-b"></i> Delete</a></li>
+ </ul>
+ </script>
+ <div class="functional-admin-button-container">
+ <button class="btn-blue" ng-click="functionalMenu.regenerateFunctionalMenuAncestors()">Regenerate Menu</button>
+ <span class="n16r">Click when you are done with your changes</span>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js b/ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js
new file mode 100644
index 00000000..21e8454d
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/functionalMenu/jqTreeContextMenu.js
@@ -0,0 +1,192 @@
+/*-
+ * ================================================================================
+ * 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) {
+
+ var self = this;
+ var $el = this;
+
+ var $menuEl = menuElement;
+
+ var nodeToDisabledMenuItems = {};
+
+ $menuEl.hide();
+
+ $el.bind("contextmenu", function (e) {
+ e.preventDefault();
+ return false;
+ });
+
+ $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) {
+ y = y - menuHeight;
+ }
+ if (menuWidth + x + xPadding > windowWidth) {
+ x = x - menuWidth;
+ }
+
+ 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');
+ }
+ }
+
+ $menuEl.show();
+
+ $menuEl.offset({ left: x, top: y });
+
+ var dismissContextMenu = function () {
+ $(document).unbind('click.jqtreecontextmenu');
+ $el.unbind('tree.click.jqtreecontextmenu');
+ $menuEl.hide();
+ }
+
+ $(document).bind('click.jqtreecontextmenu', function () {
+ dismissContextMenu();
+ });
+
+ $el.bind('tree.click.jqtreecontextmenu', function (e) {
+ dismissContextMenu();
+ });
+
+ var selectedNode = $el.tree('getSelectedNode');
+ if (selectedNode !== event.node) {
+ $el.tree('selectNode', event.node);
+ }
+
+ 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) {
+ $menuEl.find('li:not(.disabled)').addClass('disabled');
+ $menuEl.find('li a').unbind('click');
+ nodeToDisabledMenuItems = {};
+ } else if (arguments.length === 1) {
+ 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) {
+ var nodeName = arguments[0];
+ var items = arguments[1];
+ nodeToDisabledMenuItems[nodeName] = items;
+ }
+ };
+
+ this.enable = function () {
+ if (arguments.length === 0) {
+ $menuEl.find('li.disabled').removeClass('disabled');
+ nodeToDisabledMenuItems = {};
+ } else if (arguments.length === 1) {
+ 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) {
+ 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/client/app/views/header/header.controller.js b/ecomp-portal-FE/client/app/views/header/header.controller.js
new file mode 100644
index 00000000..8d3cab29
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/header.controller.js
@@ -0,0 +1,419 @@
+/*-
+ * ================================================================================
+ * 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 HeaderCtrl {
+ constructor($log, $window, userProfileService, menusService, $scope, ECOMP_URL_REGEX, $cookies, $state) {
+ this.firstName = '';
+ this.lastName = '';
+ this.$log = $log;
+ this.menusService = menusService;
+ this.$scope = $scope;
+ this.favoritesMenuItems = '';
+ $scope.favoriteItemsCount = 0;
+ $scope.favoritesMenuItems = '';
+ $scope.showFavorites = false;
+ $scope.emptyFavorites = false;
+ $scope.favoritesWindow = false;
+
+ $scope.showNotification = true;
+
+ $scope.hideMenus = false;
+
+ $scope.menuItems = [];
+ $scope.activeClickSubMenu = {
+ x: ''
+ };
+ $scope.activeClickMenu = {
+ x: ''
+ };
+ $scope.megaMenuDataObject =[];
+
+ this.isLoading = true;
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+
+ var menuStructureConvert = function(menuItems) {
+ $scope.megaMenuDataObjectTemp = [
+ {
+ text: "Manage/Functions",
+ children: [],
+ },
+ {
+ text: "Help",
+ children: [{
+ restrictedApp: true,
+ text:"Contact Us",
+ url:""
+ },
+ {
+ text:"Get Access",
+ url:"root.access"
+ }]
+ }
+
+ ];
+ return $scope.megaMenuDataObjectTemp;
+ };
+
+ var unflatten = function( array, parent, tree ){
+
+ tree = typeof tree !== 'undefined' ? tree : [];
+ parent = typeof parent !== 'undefined' ? parent : { menuId: null };
+ var children = _.filter( array, function(child){ return child.parentMenuId == parent.menuId; });
+
+ if( !_.isEmpty( children ) ){
+ if( parent.menuId === null ){
+ tree = children;
+ }else{
+ parent['children'] = children
+ }
+ _.each( children, function( child ){ unflatten( array, child ) } );
+ }
+
+ return tree;
+ }
+
+ var self = this;
+ $scope.$on('handleUpdateUserInfo', function () {
+ userProfileService.resetFunctionalMenuStaticInfo()
+ .then(reset_res=> {
+ userProfileService.getFunctionalMenuStaticInfo()
+ .then(res=> {
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info init');
+ if(res==null || res.firstName==null || res.firstName=='' || res.lastName==null || res.lastName=='' ){
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: failed getting userinfo from shared context.. ');
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session ');
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session success');
+ self.firstName = profile.firstName;
+ self.lastName = profile.lastName;
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: user has the following roles: ' + profile.roles);
+ }).catch(err=> {
+ $log.error('Header Controller:: getUserProfile() failed: ' + err);
+ });
+ }else{
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info successfully',res);
+ self.firstName = res.firstName;
+ self.lastName = res.lastName;
+ $scope.contactUsURL = res.contact_us_link;
+ }
+
+ menusService.GetFunctionalMenuForUser()
+ .then(jsonHeaderMenu=> {
+ $scope.menuItems = unflatten( jsonHeaderMenu );
+ $scope.megaMenuDataObject = $scope.menuItems;
+ }).catch(err=> {
+ $scope.megaMenuDataObject = menuStructureConvert('');
+ $log.error('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err);
+ });
+
+ userProfileService.refreshUserBusinessCard();
+
+ }).catch(err=> {
+ $log.error('HeaderCtrl::getFunctionalMenuStaticInfo failed: ' + err);
+ });
+ }).catch(err=> {
+ $log.error('HeaderCtrl::resetFunctionalMenuStaticInfo failed: ' + err);
+ })
+
+
+ });
+
+ userProfileService.getFunctionalMenuStaticInfo()
+ .then(res=> {
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info init');
+ if(res==null || res.firstName==null || res.firstName=='' || res.lastName==null || res.lastName=='' ){
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: failed getting userinfo from shared context.. ');
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session ');
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting userinfo from session success');
+ this.firstName = profile.firstName;
+ this.lastName = profile.lastName;
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: user has the following roles: ' + profile.roles);
+ }).catch(err=> {
+ $log.error('Header Controller:: getUserProfile() failed: ' + err);
+ });
+ }else{
+ $log.info('HeaderCtrl::getFunctionalMenuStaticInfo: getting Functional Menu Static Info successfully',res);
+ this.firstName = res.firstName;
+ this.lastName = res.lastName;
+ $scope.contactUsURL = res.contact_us_link;
+ }
+
+ menusService.GetFunctionalMenuForUser()
+ .then(jsonHeaderMenu=> {
+ $scope.menuItems = unflatten( jsonHeaderMenu );
+ $scope.megaMenuDataObject = $scope.menuItems;
+ }).catch(err=> {
+ $scope.megaMenuDataObject = menuStructureConvert('');
+ $log.error('HeaderCtrl::GetFunctionalMenuForUser: HeaderCtrl json returned: ' + err);
+ });
+
+ }).catch(err=> {
+ $log.error('HeaderCtrl::getFunctionalMenuStaticInfo failed: ' + err);
+ });
+
+
+ $scope.loadFavorites = function () {
+ $scope.hideMenus = false;
+ $log.debug('HeaderCtrl::loadFavorites: loadFavorites has happened.');
+ if ($scope.favoritesMenuItems == '') {
+ generateFavoriteItems();
+ $log.debug('HeaderCtrl::loadFavorites: loadFavorites is calling generateFavoriteItems()');
+ } else {
+ $log.debug('HeaderCtrl::loadFavorites: loadFavorites is NOT calling generateFavoriteItems()');
+ }
+ }
+
+ $scope.goToUrl = (item) => {
+ $log.info('HeaderCtrl::goToUrl has started');
+ let url = item.url;
+ let restrictedApp = item.restrictedApp;
+ if (!url) {
+ $log.info('HeaderCtrl::goToUrl: No url found for this application, doing nothing..');
+ return;
+ }
+ if (restrictedApp) {
+ $window.open(url, '_blank');
+ } else {
+ if(item.url=="getAccess" || item.url=="contactUs"){
+ // if (url = window.location.href)
+ $state.go("root."+url);
+ var tabContent = { id: new Date(), title: 'Home', url: item.url };
+ $cookies.putObject('addTab', tabContent );
+ } else {
+ var tabContent = { id: new Date(), title: item.text, url: item.url };
+ $cookies.putObject('addTab', tabContent );
+ }
+ $log.debug('HeaderCtrl::goToUrl: url = ', url);
+ }
+ $scope.hideMenus = true;
+ }
+
+
+
+ $scope.submenuLevelAction = function(index, column) {
+ if ($scope.favoritesMenuItems == '') {
+ generateFavoriteItems();
+ $log.debug('HeaderCtrl::submenuLevelAction: submenuLevelAction is calling generateFavoriteItems()');
+ }
+ if (index=='Favorites' && $scope.favoriteItemsCount != 0) {
+ $log.debug('HeaderCtrl::submenuLevelAction: Showing Favorites window');
+ $scope.favoritesWindow = true;
+ $scope.showFavorites = true;
+ $scope.emptyFavorites = false;
+ }
+ if (index=='Favorites' && $scope.favoriteItemsCount == 0) {
+ $log.debug('HeaderCtrl::submenuLevelAction: Hiding Favorites window in favor of No Favorites Window');
+ $scope.favoritesWindow = true;
+ $scope.showFavorites = false;
+ $scope.emptyFavorites = true;
+ }
+ if (index!='Favorites' ) {
+ $scope.favoritesWindow = false;
+ $scope.showFavorites = false;
+ $scope.emptyFavorites = false;
+ }
+
+ };
+
+ $scope.hideFavoritesWindow = function() {
+ $scope.showFavorites = false;
+ $scope.emptyFavorites = false;
+ }
+
+ $scope.isUrlFavorite = function (menuId) {
+ var jsonMenu = JSON.stringify($scope.favoritesMenuItems);
+ var isMenuFavorite = jsonMenu.indexOf('menuId\":' + menuId);
+ if (isMenuFavorite==-1) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ let generateFavoriteItems = () => {
+ menusService.getFavoriteItems()
+ .then(favorites=> {
+ $scope.favoritesMenuItems = favorites;
+ $scope.favoriteItemsCount = Object.keys(favorites).length;
+ $log.info('HeaderCtrl.getFavoriteItems:: number of favorite menus: ' + $scope.favoriteItemsCount);
+ }).catch(err=> {
+ $log.error('HeaderCtrl.getFavoriteItems:: Error retrieving Favorites menus: ' + err);
+ });
+ }
+
+ $scope.setAsFavoriteItem = function(event, menuId){
+ var jsonMenuID = angular.toJson({'menuId': + menuId });
+ $log.debug('HeaderCtrl::setFavoriteItems: ' + jsonMenuID + " - " + event.target.id);
+
+ menusService.setFavoriteItem(jsonMenuID)
+ .then(() => {
+ angular.element('#' + event.target.id).css('color', '#fbb313');
+ generateFavoriteItems();
+ }).catch(err=> {
+ $log.error('HeaderCtrl::setFavoriteItems:: API setFavoriteItem error: ' + err);
+ });
+ };
+
+ $scope.removeAsFavoriteItem = function(event, menuId){
+ $log.debug('-----------------------------removeAsFavoriteItem: ' + menuId + " - " + event.target.id);
+ menusService.removeFavoriteItem(menuId)
+ .then(() => {
+ angular.element('#' + event.target.id).css('color', '#666666');
+ generateFavoriteItems();
+ }).catch(err=> {
+ $log.error('HeaderCtrl::removeAsFavoriteItem: API removeFavoriteItem error: ' + err);
+ });
+ };
+
+ $scope.goToPortal = (headerText, url) => {
+ if (!url) {
+ $log.info('HeaderCtrl::goToPortal: No url found for this application, doing nothing..');
+ return;
+ }
+ if (!ECOMP_URL_REGEX.test(url)) {
+ url = 'http://' + url;
+ }
+
+ if(headerText.startsWith("vUSP")) {
+ window.open(url, '_blank');
+ }
+ else {
+ var tabContent = { id: new Date(), title: headerText, url: url };
+ $cookies.putObject('addTab', tabContent );
+ }
+ };
+
+ }
+ }
+ class LoginSnippetCtrl {
+ constructor($log, $scope, $cookies, $timeout, userProfileService, sessionService,ngDialog) {
+ $scope.firstName="";
+ $scope.lastName="";
+
+ $scope.allAppsLogout = function(){
+
+ var cookieTabs = $cookies.getObject('visInVisCookieTabs');
+ if(cookieTabs!=null){
+ for(var t in cookieTabs){
+
+ var url = cookieTabs[t].content;
+ if(url != "") {
+ sessionService.logout(url);
+ }
+ }
+ }
+ // wait for individular applications to logoutout before the portal logout
+ $timeout(function() {
+ window.location = "logout.htm";
+ }, 2000);
+ }
+
+ $scope.openEditUserModal = function(){
+
+ var data = {
+ loginId : $scope.loginSnippetUserId,
+ updateRemoteApp : false,
+ appId : $scope.selectedApp!=null?$scope.selectedApp.id:''
+ }
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/header/user-edit/edit-user.tpl.html',
+ controller: 'editUserController',
+ data: data,
+ resolve: {
+ message: function message() {
+ var message = {
+ type: 'Contact',
+ };
+ return message;
+ }
+ }
+ }).closePromise.then(needUpdate => {
+ //updateContactUsTable();
+ });
+
+ }
+
+ try {
+ userProfileService.getFunctionalMenuStaticInfo()
+ .then(res=> {
+ $log.info('HeaderCtrl::LoginSnippetCtrl: Login information: ' + JSON.stringify(res));
+ $scope.firstName = res.firstName;
+ $scope.lastName = res.lastName;
+ $scope.loginSnippetEmail = res.email;
+ $scope.loginSnippetUserId = res.userId;
+ $scope.lastLogin = res.last_login;
+ }).catch(err=> {
+ $log.error('HeaderCtrl::LoginSnippetCtrl: User Profile error: ' + err);
+ });
+ } catch (err) {
+ $log.error('HeaderCtrl::LoginSnippetCtrl:getFunctionalMenuStaticInfo failed: ' + err);
+ }
+
+ $scope.$on('refreshUserBusinessCard', function () {
+ try {
+ userProfileService.getFunctionalMenuStaticInfo()
+ .then(res=> {
+ $log.info('HeaderCtrl::LoginSnippetCtrl: Login information: ' + JSON.stringify(res));
+ $scope.firstName = res.firstName;
+ $scope.lastName = res.lastName;
+ $scope.loginSnippetEmail = res.email;
+ $scope.loginSnippetUserId = res.userId;
+ $scope.lastLogin = res.last_login;
+ }).catch(err=> {
+ $log.error('HeaderCtrl::LoginSnippetCtrl: User Profile error: ' + err);
+ });
+ } catch (err) {
+ $log.error('HeaderCtrl::LoginSnippetCtrl:getFunctionalMenuStaticInfo failed: ' + err);
+ }
+ });
+
+
+ }
+ }
+ class NotificationCtrl{
+ constructor($log, $scope, $cookies, $timeout, sessionService) {
+ $scope.notifications=[];
+ for(var i=0;i<6;i++){
+ var data ={
+ content:'Notification '+i,
+ time:'10:0'+i+'AM'
+ };
+ $scope.notifications.push(data);
+ }
+
+ $scope.deleteNotification = function(index){
+
+ $scope.notifications.splice(index,1);
+ }
+ }
+ }
+ NotificationCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService'];
+ LoginSnippetCtrl.$inject = ['$log', '$scope', '$cookies', '$timeout','userProfileService', 'sessionService','ngDialog'];
+ HeaderCtrl.$inject = ['$log', '$window', 'userProfileService', 'menusService', '$scope', 'ECOMP_URL_REGEX','$cookies','$state'];
+ angular.module('ecompApp').controller('HeaderCtrl', HeaderCtrl);
+ angular.module('ecompApp').controller('loginSnippetCtrl', LoginSnippetCtrl);
+ angular.module('ecompApp').controller('notificationCtrl', NotificationCtrl);
+
+})();
diff --git a/ecomp-portal-FE/client/app/views/header/header.controller.spec.js b/ecomp-portal-FE/client/app/views/header/header.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/header.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/client/app/views/header/header.less b/ecomp-portal-FE/client/app/views/header/header.less
new file mode 100644
index 00000000..19ea467a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/header.less
@@ -0,0 +1,380 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .header-section {
+ position: relative;
+ z-index: 999;
+}
+
+.logo-image {
+ .ecomp-logo;
+ display: inline-block;
+ vertical-align: middle;
+ margin-top: -3px;
+ }
+
+.portal-title {
+ font-weight: 400;
+ font-family: arial !important;
+ font-size: 18px;
+}
+
+
+.megamenu-tabs .megamenu__item {
+ padding: 0 0;
+}
+.megamenu-tabs .megamenu__item span {
+ font-size: 18px;
+}
+
+.submenu-tabs {
+ line-height:50px;
+}
+.submenu-tabs .sub__menu{
+ top:54px;
+ padding-top: 10px;
+
+ padding-left: 20px;
+
+ .third-level-title{
+ padding-left: 10px;
+ margin-bottom: 10px;
+ }
+}
+
+.submenu-tabs .sub__menu .tabsbid__item{
+ width:100%
+}
+
+.top-megamenu .megamenu-tabs ul{
+ width:98%;
+ list-style: none;
+}
+
+.megaMenuContainer {
+ margin-top: 0;
+ min-height: 50px;
+ overflow: visible;
+}
+
+.megaMenuDataObject {
+ float: left;
+ width: 100%;
+}
+
+.parentmenu-tabs {
+ height: 55px;
+}
+
+.menu-section {
+ float: left;
+}
+
+.controlCls{
+ font-size: .975rem;
+ color: #666;
+ display: inline-block;
+ font-family: arial;
+ cursor: pointer;
+ height: 37px;
+ line-height: 37px;
+ padding-bottom: 10px;
+ vertical-align: middle;
+ width: 100%;
+}
+
+
+.controlCls:hover{
+ color:#199DDF !important;
+}
+
+.login-section {
+ float: right;
+ min-width:150px;
+}
+
+.login-snippet-text {
+ display: inline-block;
+ font-size: 12px;
+ font-weight: bold;
+ margin-left: 5px;
+ overflow: hidden;
+ max-height: 60px;
+ max-width: 120px;
+ padding-top: 20px;
+ margin-top: 0;
+ white-space: nowrap;
+}
+
+.megamenu__item {
+ width: 100px;
+}
+
+.megamenu-item-top {
+ line-height:55px;
+}
+
+
+.newrow {
+ clear: left;
+}
+
+.header-columns{
+ -webkit-column-count:4;
+ -moz-column-rule: 1px outset #D3D3D3;
+ -moz-column-count:4;
+ column-count: 4;
+ line-height: 12px;
+ max-height: 500px;
+ overflow-x: hidden;
+ overflow-y:hidden;
+ column-gap: 13px;
+ column-rule: 1px outset #D3D3D3;
+
+
+}
+
+.header-columns li{
+ -webkit-column-break-inside: avoid;
+ break-inside: avoid;
+ //-webkit-page-break-inside: avoid;
+ page-break-inside: avoid;
+
+ margin-top: 0px !important;
+
+}
+
+.header-columns-div{
+ width:100%;
+ margin-left: 12px;
+ margin-top: 12px;
+ &:hover{
+ .level3-favorites-icon-inactive {
+ opacity: 1;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+ filter: alpha(opacity=100);
+ -moz-opacity: 1;
+ -khtml-opacity: 1;
+ }
+ }
+}
+
+
+.header-level4-div{
+ width:100%;
+ margin-left: 12px;
+ margin-top: 12px;
+ &:hover{
+ .level4-favorites-icon-inactive {
+ opacity: 1;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+ filter: alpha(opacity=100);
+ -moz-opacity: 1;
+ -khtml-opacity: 1;
+ }
+ }
+}
+
+.favorites-icon-active {
+ position: relative;
+ margin-top: 5px;
+ margin-left: 5px;
+ top: 3px;
+ color: @i;
+}
+
+.level3-favorites-icon-inactive {
+ .favorites-icon-inactive;
+ opacity: 0;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ filter: alpha(opacity=0);
+ -moz-opacity: 0;
+ -khtml-opacity: 0;
+}
+
+.level4-favorites-icon-inactive {
+ .favorites-icon-inactive;
+ opacity: 0;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ filter: alpha(opacity=0);
+ -moz-opacity: 0;
+ -khtml-opacity: 0;
+}
+
+.favorites-icon-inactive {
+ position: relative;
+ margin-top: 5px;
+ margin-left: 5px;
+ top: 3px;
+ color: @o;
+}
+
+.favorites-window-empty {
+ width: 100%;
+ height: auto;
+ display:flex;
+ justify-content:center;
+ align-items:center;
+ margin: auto;
+ text-align: center;
+
+ .largeText {
+ font-weight: 400;
+ font-family: arial !important;
+ font-size: 18px;
+ text-align: center;
+ color: @o;
+ }
+
+ .normal {
+ color: @o;
+ font-size: 14px;
+ text-align: center;
+ }
+
+
+}
+.favorites-window {
+ width: 100%;
+ height: auto;
+ //margin: auto;
+ font-size: 14px !important;
+ display: flex;
+ font-family: arial;
+ margin-top: 25px;
+ margin-left: 25px;
+ z-index: 1000;
+
+ .fav-links {
+ margin-right: 25px;
+ }
+
+ .largeText {
+ font-weight: 400;
+ font-family: arial !important;
+ font-size: 18px;
+ text-align: center;
+ color: @o;
+ }
+
+ a:link, a:active, a:hover {
+ margin-left: 3px;
+ margin-right: 20px;
+ text-decoration: none;
+ }
+
+ a:hover {
+ color: @z;
+ }
+}
+
+
+
+ .notifications-count
+ {
+ .border-radius(50%);
+ background:#db3434;
+ color: @colorWhite;
+ font: normal .85em 'Lato';
+ height: 16px;
+
+ position: absolute;
+ right: -7px;
+ text-align: center;
+ top: -8px;
+ width: 16px;
+ }
+
+ .notification-header{
+ border-bottom: 1px solid #b4b4b4;
+ padding: 10px 40px 0px 40px;
+ }
+ .notification-heading{
+ font-family: arial;
+ font-size: 24px;
+ padding-top: 15px;
+ margin-bottom: 1rem;
+ }
+ .notificationBox{
+ border-bottom: 1px solid #b4b4b4;
+
+ }
+
+ .notification-info-icon{
+ padding-top: 41px;
+ font-size: 47px;
+ }
+ .notification-text {
+ line-height: 15px;
+ margin: 0;
+ padding: 0 0 24px 0;
+ text-align: center;
+ font-family: arial;
+ font-size: 16px;
+ }
+
+#icon-user-small{
+ display: inline-block;
+ vertical-align: middle;
+ width: 20px;
+ padding-top: 20px;
+}
+
+.notificationBox .icon-circle-action-close {
+ cursor: pointer;
+ font-size: 16px;
+ }
+
+ .notification-close {
+ padding: 2px 2px 0px 0px;
+ float: right;
+ }
+
+ #fav-icon{
+ color: #ffb81c;
+ font-size: 80px;
+ }
+
+ .edit-user-button{
+ margin-top:5px;
+ display: inline;
+ margin-left:2px;
+ width: 20px;
+ }
+
+ .log-out-button{
+ margin-top:5px;
+ display: inline;
+ margin-left:10px;
+ width: 20px;
+ }
+
+ .new-button {
+ margin-left:5px;
+ float: left;
+ display: block;
+ width: 80px;
+ height: 25px;
+ background: #0574ac!important;
+ padding: 6px;
+ text-align: center;
+ border-radius: 5px;
+ color: white;
+ font-size:13px;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/header/header.tpl.html b/ecomp-portal-FE/client/app/views/header/header.tpl.html
new file mode 100644
index 00000000..1928cb64
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/header.tpl.html
@@ -0,0 +1,277 @@
+<!--
+ ================================================================================
+ 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.
+ ================================================================================
+ -->
+<style type="text/css">
+
+ .att-popover .pop-over-caret.pop-over-caret--below{
+ left: 5%;
+ color:#fff;
+ }
+ .att-popover .pop-over-caret.pop-over-caret-border--below{
+ left: 5%;
+ }
+
+ .popover-demo.att-popover{
+ float: left;
+ top: 44px;
+ }
+ .reg-profileDetails{
+ outline:none !important;
+ }
+ #reg-header-snippet .reg-profileDetails {
+ padding: 0px;
+ min-height: 150px;
+ }
+ #reg-header-snippet .reg-profileDetails .reg-userEmail-label .reg-userEmail-label-spn {
+ color: #0574ac; font-size: 13px; font-weight: bold;
+ }
+ #reg-header-snippet .reg-profileDetails .reg-userRole-label .reg-userRole-label-spn {
+ color: #0574ac;font-size: 13px; font-weight: bold;
+ }
+
+</style>
+<div class="header-section">
+ <div class="headerContainer" id="headerContainer">
+ <div id="megaMenuContainer" class="megaMenuContainer">
+ <div>
+<!--for mega Menu-->
+<!-- Mega Menu parent-tab directive with three models menu-items, active-sub-menu, active-menu -->
+ <div id="topMenu" class="top-megamenu" ng-mouseleave="activeClickSubMenu.x.active=false; activeClickMenu.x.active=false">
+ <div class="megaMenuDataObject" parent-tab menu-items="megaMenuDataObject"
+ active-sub-menu='activeClickSubMenu.x' active-menu='activeClickMenu.x'>
+ <div parentmenu-tabs mega-menu="true" menu-items="megaMenuDataObject" class="parentmenu-tabs">
+ <div class="menu-section">
+<!-- TITLE -->
+ <li class="megamenu__item" style="line-height:55px;" onclick="window.location = 'applicationsHome'">
+ <span id="logo-image" class="logo-image"></span>
+ <span id="portal-title" class="portal-title" >OpenECOMP Portal</span>
+ </li>
+<!-- MENUS --> <!-- MENUS -->
+ <div id="menu-tabs-mega-menu"
+ menu-tabs mega-menu="true"
+ tab-name="item.text"
+ menu-item="item"
+ ng-mousedown="loadFavorites()"
+ active-menu="activeClickMenu.x"
+ ng-repeat="item in megaMenuDataObject" style="font-size: 18px;">
+ <div id="parentmenu-tabs" parentmenu-tabs sub-menu="true"
+ ng-show="activeClickMenu.x.active && item.active"
+ menu-items="activeClickMenu.x.children">
+
+<!-- Second level menu --> <!-- Second level menu -->
+ <div id="second-level-menus" menu-tabs sub-menu="true"
+ menu-item="subItem" tab-name="subItem.text" tab-url="subItem.url"
+ ng-repeat="subItem in activeClickMenu.x.children | orderBy : 'column'"
+ active-menu="activeClickSubMenu.x"
+ sub-item-active="{{subItem.active}}" style="float:left;"
+ aria-label="{{subItem.text}} | orderBy : 'column'"
+ ng-mousemove="submenuLevelAction(subItem.text,subItem.column)"
+ ng-if="item.text!='Help' && item.text!='Support'"
+ >
+ <i ng-if="subItem.text=='Favorites'" id="favorite-star" data-size="large"
+ class="ion-star favorites-icon-active">
+ </i>
+ </div>
+ <div id="second-level-menus-help" menu-tabs sub-menu="true"
+ menu-item="subItem"
+ ng-repeat="subItem in activeClickMenu.x.children | orderBy : 'column'"
+ ng-if="item.text=='Help' || item.text=='Support'"
+ >
+ <span ng-click="goToUrl(subItem)">{{subItem.text}}</span>
+ </div>
+
+
+ <div class="sub__menu" >
+ <ul ng-show="activeClickSubMenu.x.active" role="menubar" class="header-columns">
+<!-- Third level menu --> <!-- Third level menu -->
+ <div id="third-level-menus" class="header-columns-div"
+ ng-hide='favoritesWindow || hideMenus'
+ menu-tabs menu-item="subItem"
+ ng-repeat="subItem in activeClickSubMenu.x.children | orderBy : 'column'"
+ ng-show="activeClickSubMenu.x.active" >
+ <i id="level3-star-inactive-{{subItem.menuId}}" ng-cloak
+ class="ion-star level3-favorites-icon-inactive" data-size="large"
+ data-ng-click="setAsFavoriteItem($event, subItem.menuId)"
+ ng-if="subItem.url.length > 1 && isUrlFavorite(subItem.menuId)==false">
+ </i>
+ <i id="level3-star-active-{{subItem.menuId}}" ng-cloak
+ ng-if="subItem.url.length > 1 && isUrlFavorite(subItem.menuId)"
+ class="ion-star favorites-icon-active ng-cloak" data-size="large"
+ data-ng-click="removeAsFavoriteItem($event, subItem.menuId)"
+ >
+ </i>
+ <span class="title"
+ aria-label="{{subItem.text | elipsis: 50}}"
+ ng-click="goToUrl(subItem)">{{subItem.text}}</span>
+<!-- Fourth level menus - <!-- Fourth level menus -->
+ <div class ="header-level4-div" att-links-list="" ng-repeat="tabValue in subItem.children" >
+ <i id="level4-star-inactive-{{tabValue.menuId}}" ng-cloak
+ class="ion-star level4-favorites-icon-inactive"
+ data-ng-click="setAsFavoriteItem($event, tabValue.menuId)"
+ ng-if="tabValue.url.length > 1 && isUrlFavorite(tabValue.menuId)==false">
+ </i>
+ <i id="level4-star-active-{{tabValue.menuId}}" ng-cloak
+ class="ion-star favorites-icon-active"
+ data-ng-click="removeAsFavoriteItem($event, tabValue.menuId)"
+ ng-if="tabValue.url.length > 1 && isUrlFavorite(tabValue.menuId)">
+
+ </i>
+
+ <span ng-click="goToUrl(tabValue)"
+ role="menuitem" att-links-list-item=""
+ style="display: inline; padding-left: 8px"
+ att-accessibility-click="13,32"
+ title="{{tabValue.text}}"
+ ng-class="{'disabled': tabValue.disabled}">{{tabValue.text | elipsis: 50}}</span>
+ </div>
+ <div att-divider-lines ng-show="!$last"></div>
+ </div>
+ </ul>
+<!-- Favorites level menu --> <!-- Favorites level menu -->
+ <div class="header-columns-div" ng-show='favoritesWindow' ng-mouseleave="hideFavoritesWindow()">
+ <div id="favorites-menu-items" att-links-list=""
+ ng-show="showFavorites"
+ ng-hide="hideMenus"
+ ng-repeat="subItem in favoritesMenuItems"
+ att-links-list="" >
+
+ <div class="fav-links">
+ <i id="favorite-selector-favorites-list"
+ class="ion-star favorites-icon-active"
+ style="font-size:18px"
+ data-ng-click="removeAsFavoriteItem($event, subItem.menuId)"
+ ng-mousedown="removeAsFavoriteItem($event, subItem.menuId)">
+ </i>
+ <a id="favorites-list" aria-label="{{subItem.text}}" style="color: #000"
+ ng-click="goToUrl(subItem)">{{subItem.text}}
+ </a>
+ </div>
+ </div>
+ <!-- Favorites when empty -->
+ <div id="favorites-empty"
+ ng-show="emptyFavorites"
+ class="favorites-window-empty" >
+ <div>
+ <span id="fav-icon" class="ion-ios-star" ></span>
+ <p id="p-no-favs" class="largeText">No Favorites</p>
+ <p id="p-no-favs-desc" class="normal">Add your favorite items for quick access.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </div >
+ </div>
+
+ <div class="login-section">
+ <li id="bcLoginSnippet" class="megamenu__item" style="width: 140px;" >
+ <div popover="loginSnippet.html" aria-label="Login Snippet" referby="loginSnippet" att-accessibility-click="13,32" popover-style="" popover-trigger="click" popover-placement="below" style="width: 150px; height: 55px; display: inline-flex">
+ <div id="icon-user-small" class="ion-android-person login-snippet-icon"></div>
+ <div id="login-snippet-text" class="login-snippet-text">{{header.firstName}}</div>
+ </div>
+ </li>
+ </div>
+ </div>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<script type="text/ng-template" id="loginSnippet.html">
+ <div ng-controller="loginSnippetCtrl" >
+ <div id="reg-header-snippet">
+ <div tabindex="0" class="reg-profileDetails" id="reg-profiledetails-id">
+ <ul class="reg-Details-table" style="list-style: none; border-bottom: 1px solid #bbb; padding-bottom: 20px;" >
+ <li>
+ <div class="reg-userName-table">
+ <div id="reg-userName-table-row">
+ <div id="reg-userName-table-cell">
+ <h3 class="att-global-fonts" style="font-size:18px; line-height:1.6 !important;" id="reg-userName" >
+ {{firstName}} {{lastName}}&nbsp;</h3>
+ <span>&nbsp;</span>
+ </div>
+ </div>
+ </div>
+ </li>
+ <li><div class="reg-userEmail-label"><span class="reg-userEmail-label-spn">Email<span class="visuallyhidden">:
+ </span></span></div></li>
+ <li><div class="reg-userEmail-value"><span class="reg-userEmail-value-spn">
+ {{loginSnippetEmail}}</span></div></li>
+ <li>&nbsp;</li>
+ <li><div class="reg-userRole-label"><span class="reg-userRole-label-spn">
+ User ID<span class="visuallyhidden">:</span></span></div></li>
+ <li><div class="reg-userRole-value"><span class="reg-userRole-value-spn">
+ {{loginSnippetUserId}}<span class="visuallyhidden"></span></span></div></li>
+
+ </ul>
+ <div id="reg-logout-div" style="padding-top: 8px;">
+ <p>Last login: <span ng-bind="lastLogin"></span></p>
+ <div style="margin-top: 10px">
+ <a href="javascript:void(0)" id="allLogout" ng-click="openEditUserModal()" style="" class="new-button">
+ Profile
+ </a>
+ <a href="javascript:void(0)" id="allLogout" ng-click="allAppsLogout()" class="new-button">
+ Log out
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+</script>
+<script type="text/ng-template" id="notificationCtrl.html">
+ <div style="line-height: normal; right: 167px; min-height: 122px; height: auto; width: auto; " ng-controller="notificationCtrl" >
+ <div class="ng-scope">
+ <div id="notification" class="notificationBox ">
+ <div class="notification-header">
+ <div style="float:left;">
+ <p class="notification-heading">Notifications</p>
+ </div>
+ <!-- <div class="notification-close ng-isolate-scope" tabindex="0" ddh-accessibility-click="13,32" b2b-close-flyout="">
+ <span class="icon-circle-action-close" aria-label="close button"></span>
+ </div>-->
+ <div style="clear:both;"></div>
+ </div>
+ <div class="notification-main">
+ <div style="height:113px;">
+ <div align="center" class="icon-information notification-info-icon"></div>
+ </div>
+ <div>
+ <p class="notification-text">No New Notifications.</p>
+ </div>
+ </div>
+ <div class="notification-main ng-hide" ng-show="showList">
+ <!-- ngRepeat: notification in notifications track by $index -->
+ </div>
+ <div class="notification-footer">
+ <div class="notification-links">
+ <div style="clear:both;"></div>
+ </div>
+ </div>
+ <!--/div -->
+
+
+ </div>
+ </div>
+ </div>
+</script>
diff --git a/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js
new file mode 100644
index 00000000..507b3a0e
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.controller.js
@@ -0,0 +1,134 @@
+/*-
+ * ================================================================================
+ * 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 EditUserController {
+ constructor($scope,$log, message, $q, $http, conf,contactUsService,confirmBoxService,userProfileService,adminsService) {
+
+ $scope.newUser ={
+ firstName:'',
+ lastName:'',
+ emailAddress:'',
+ middleName:'',
+ loginId:'',
+ loginPwd:'',
+ };
+
+ $scope.userId = $scope.ngDialogData.loginId;
+ $scope.updateRemoteApp = $scope.ngDialogData.updateRemoteApp;
+ try {
+ userProfileService.getCurrentUserProfile($scope.userId)
+ .then(res=> {
+ $scope.newUser.firstName = res.firstName;
+ $scope.newUser.lastName = res.lastName;
+ $scope.newUser.emailAddress = res.email;
+ $scope.newUser.middleName = res.middleInitial;
+ $scope.newUser.loginId = res.loginId;
+ $scope.newUser.loginPwd = res.loginPwd;
+ $scope.newUser.loginPwdCheck = res.loginPwd;
+
+ }).catch(err=> {
+ $log.error('HeaderCtrl::LoginSnippetCtrl:get Current User Profile error: ' + err);
+ });
+ } catch (err) {
+ $log.error('HeaderCtrl::LoginSnippetCtrl:getFunctionalMenuStaticInfo failed: ' + err);
+ }
+
+ $scope.changePwdText = function(){
+ $scope.newUser.loginPwdCheck = '';
+ }
+
+ $scope.closeDialog = function(){
+ $scope.closeThisDialog( $scope.widgetData);
+ }
+
+ $scope.updateUserFun = function(){
+
+ if ($scope.newUser.firstName==''||$scope.newUser.lastName==''||$scope.newUser.loginPwd=='') {
+ var warningMsg = "Please enter missing values";
+ confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;});
+ return;
+ } else if ($scope.newUser.loginPwd != $scope.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($scope.newUser.loginPwd);
+ if (warningMsg != null) {
+ confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;});
+ return;
+ }
+
+ adminsService.addNewUser($scope.newUser,'No').then(res=> {
+
+ $scope.closeThisDialog();
+
+ confirmBoxService.showInformation('Update User Info successfully');
+ userProfileService.broadCastUpdatedUserInfo();
+
+
+ if($scope.updateRemoteApp){
+ //need update remote app's database
+ var remoteAppId = $scope.ngDialogData.appId;
+ if(remoteAppId!=null){
+
+ userProfileService.updateRemoteUserProfile($scope.userId,remoteAppId).then(res=> {
+
+
+ }).catch(err=> {
+ $log.error('userProfileService: update user profile in remote app error:: ', err);
+ confirmBoxService.showInformation('Update User in remote app failed: ' + err);
+
+ }).finally(() => {
+
+ });
+
+
+ }
+
+ }
+
+ }).catch(err=> {
+ $log.error('adminsService: addNewUser error:: ', err);
+ // $scope.errMsg=err;
+ confirmBoxService.showInformation('Add New User failed: ' + err);
+
+ }).finally(() => {
+ //this.isLoadingTable = false;
+
+ });
+
+ }
+ }
+
+
+ }
+ }
+ EditUserController.$inject = ['$scope','$log', 'message', '$q', '$http', 'conf','contactUsService','confirmBoxService','userProfileService','adminsService'];
+ angular.module('ecompApp').controller('editUserController', EditUserController);
+
+
+})();
+function validateUrl(value){
+ return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
+ } \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less
new file mode 100644
index 00000000..853e48db
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.less
@@ -0,0 +1,61 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+
+ .main{
+ padding:10px;
+ margin-left: 40px;
+
+ }
+ .manage-user-home-title{
+
+ margin-top:15px;
+ color: #067ab4;
+ font-family: arial;
+ font-size: 24px;
+ margin-left: 0px;
+ width: 250px;
+
+ }
+
+ .input-text-edit-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-edit-user-div{
+ float: left;
+ width: 300px;
+ }
+
+ .edit-user-section{
+
+ margin-top:5px;
+ margin-left:10px;
+
+ }
+ \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html
new file mode 100644
index 00000000..719f8bde
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/header/user-edit/edit-user.tpl.html
@@ -0,0 +1,69 @@
+<!--
+ ================================================================================
+ 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="main" style="margin-top: 5px; height: 350px;">
+ <div id="title" class="manage-user-home-title">Profile</div>
+
+ <div class="edit-user-section" >
+ <div class="input-edit-user-div" >
+ <div class="">*First Name</div>
+ <input class="input-text-edit-user" type="text" ng-model="newUser.firstName"/>
+ </div>
+ <div class="input-edit-user-div" >
+ <div class="">Middle Name</div>
+ <input class="input-text-edit-user" type="text" ng-model="newUser.middleName"/>
+ </div>
+ <div class="input-edit-user-div" >
+ <div class="">*Last Name</div>
+ <input class="input-text-edit-user" type="text" ng-model="newUser.lastName"/>
+ </div>
+ <div class="input-edit-user-div" >
+ <div class="">*Email Address ID</div>
+ <input class="input-text-edit-user" type="text" disabled ng-model="newUser.emailAddress"/>
+ </div>
+ <div class="input-edit-user-div" >
+ <div class="">*Login ID</div>
+ <input class="input-text-edit-user" type="text" disabled ng-model="newUser.loginId"/>
+ </div>
+ <div class="input-edit-user-div" >
+ <div class="">*Login Password</div>
+ <input class="input-text-edit-user" type="password" ng-model="newUser.loginPwd" ng-change="changePwdText()"/>
+ </div>
+ <div class="input-edit-user-div" >
+ <div class="">*Confirm Login Password</div>
+ <input class="input-text-edit-user" type="password" ng-model="newUser.loginPwdCheck"/>
+ </div>
+ </div>
+ <div style="bottom: 2px; height:50px;width:180px;margin-top:60px">
+ <div class="dialog-control">
+ <div id="div-updateAdminAppsRoles" class="next-button" ng-click="updateUserFun()"
+ ng-class="{disabled: false}">Save
+ </div>
+ <div id="div-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+ </div>
+
+
+</div>
+
+<script type="application/javascript">
+ $(document).ready(function(){
+ $(".ngdialog-content").css("width","85%")
+ });
+</script> \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js
new file mode 100644
index 00000000..2071f4cc
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.js
@@ -0,0 +1,244 @@
+/*-
+ * ================================================================================
+ * 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 () {
+ const HTTP_PROTOCOL_RGX = /https?:\/\//;
+ class ApplicationsHomeCtrl {
+ constructor(applicationsService, $log, $window, userProfileService, $scope,$cookies, utilsService) {
+ //activate spinner
+ this.isLoading = true;
+ $scope.getUserAppsIsDone = false;
+ this.userProfileService = userProfileService;
+ //get all user's applications on init
+ $scope.buildNumber = "OpenECOMP Portal Version: 1.0.0"
+
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('ApplicationsHomeCtrl::getUserProfile', profile);
+ $scope.userId = profile.orgUserId;
+ //$scope.appsViewData = ['notempty']; // initialize with length != 0
+ $scope.appsViewData = [];
+ $scope.appsView = [];
+
+ //redirect to login.htm, if the EPService cookie is missing and logged in user is not a guest.
+ if (!$cookies.get('EPService') && !profile.guestSession) {
+ this.isLoading = false;
+ var myHostName;
+ myHostName = location.host;
+ $log.info("EPService cookie is missing, so attempting to redirecting to login page.");
+ if (utilsService.isRunningInLocalDevEnv()) {
+ $log.info("Portal is running in local development and redirecting to 'http://localhost:8080/ecompportal/login.htm'.");
+ $window.location.href = "http://localhost:8080/ecompportal/login.htm";
+ } else {
+ $log.info("Redirecting to 'login.htm'.");
+ $window.location.href = "login.htm";
+ }
+ }
+
+ applicationsService.getUserApps()
+ .then(res => {
+ $log.info('ApplicationsHomeCtrl::getUserApps: ', res);
+ this.apps = res;
+ let rowNo = 0;
+ for (let i = 0; i < this.apps.length; i++) {
+ $scope.appsView[i] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: '',
+ subHeaderText: '',
+ imageLink: '',
+ order: '',
+ url: ''
+ };
+ $scope.appsView[i].headerText = this.apps[i].name;
+ $scope.appsView[i].subHeaderText = this.apps[i].notes;
+ $scope.appsView[i].imageLink = this.apps[i].thumbnail || this.apps[i].imageUrl;
+ $scope.appsView[i].order = this.apps[i].order;
+ $scope.appsView[i].url = this.apps[i].url;
+ $scope.appsView[i].restrictedApp = this.apps[i].restrictedApp;
+ }
+ $log.info('ApplicationsHomeCtrl::getUserApps: apps count : ' + $scope.appsView.length);
+
+ if ($cookies.getObject($scope.userId+'_apps') == undefined || $cookies.getObject($scope.userId+'_apps') == null || $cookies.getObject($scope.userId+'_apps').length == 0) {
+ if (($scope.appsView != undefined) && ($scope.appsView != null) && ($scope.appsView.length > 0)){
+ $scope.appsViewData = $scope.appsView;
+ $cookies.putObject($scope.userId + '_apps', $scope.appsView);
+ }
+ }
+ else{
+ this.listChanged = false;
+ this.appsListFromCookie = $cookies.getObject($scope.userId+'_apps');
+ this.finalAppsList = [];
+ //
+ // If an app is still valid for this user from backend(appsView) and
+ // it was in the cookie already, put it in the list in the same order
+ // it was in within the cookie.
+ //
+ let finalCount = 0;
+ for (let i = 0; i < this.appsListFromCookie.length; i++) {
+ this.foundAppInListFromBackend = false;
+ for (let j = 0; j < $scope.appsView.length; j++) {
+ if (this.appsListFromCookie[i].url == $scope.appsView[j].url) {
+ this.finalAppsList[finalCount] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: '',
+ subHeaderText: '',
+ imageLink: '',
+ order: '',
+ url: ''
+ };
+ this.finalAppsList[finalCount].headerText = this.appsListFromCookie[i].headerText;
+ this.finalAppsList[finalCount].subHeaderText = this.appsListFromCookie[i].subHeaderText;
+ this.finalAppsList[finalCount].imageLink = this.appsListFromCookie[i].imageLink;
+ this.finalAppsList[finalCount].order = this.appsListFromCookie[i].order;
+ this.finalAppsList[finalCount].url = this.appsListFromCookie[i].url;
+ this.finalAppsList[finalCount].restrictedApp = this.appsListFromCookie[i].restrictedApp;
+ finalCount++;
+ this.foundAppInListFromBackend = true;
+ break;
+ }
+ }
+ if (this.foundAppInListFromBackend == false) {
+ this.listChanged = true;
+ }
+ }
+
+ //
+ // Fill in the rest of the list with the Apps retrieved from the backend that we did not already add. There could have been
+ // new Apps configured for the user that are not in the cookie.
+ //
+ for (let i = 0; i < $scope.appsView.length; i++) {
+ this.found = false;
+ for (let j = 0; j < this.finalAppsList.length; j++) {
+ if ($scope.appsView[i].url == this.finalAppsList[j].url) {
+ // already present
+ this.found = true;
+ break;
+ }
+ }
+ if (this.found == false) {
+ this.finalAppsList[finalCount] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: '',
+ subHeaderText: '',
+ imageLink: '',
+ order: '',
+ url: ''
+ };
+ this.finalAppsList[finalCount].headerText = $scope.appsView[i].headerText;
+ this.finalAppsList[finalCount].subHeaderText = $scope.appsView[i].subHeaderText;
+ this.finalAppsList[finalCount].imageLink = $scope.appsView[i].imageLink;
+ this.finalAppsList[finalCount].order = $scope.appsView[i].order;
+ this.finalAppsList[finalCount].url = $scope.appsView[i].url;
+ this.finalAppsList[finalCount].restrictedApp = $scope.appsView[i].restrictedApp;
+ finalCount++;
+ this.listChanged = true;
+ }
+ }
+
+ if ((this.finalAppsList != undefined) && (this.finalAppsList != null) && (this.finalAppsList.length > 0)) {
+ if (this.listChanged) {
+ $scope.appsViewData = this.finalAppsList;
+ $cookies.putObject($scope.userId + '_apps', this.finalAppsList);
+ } else {
+ $scope.appsViewData = $cookies.getObject($scope.userId+'_apps');
+ }
+ }
+ this.isLoading = false;
+ $scope.getUserAppsIsDone = true;
+ }
+ }).catch(err => {
+ $log.error('oh no! couldnt get applications list...', err);
+ this.isLoading = false;
+ $scope.getUserAppsIsDone = true;
+ });
+ });
+
+
+ this.gridsterOpts = {
+ columns: 6,
+ colWidth: 190,
+ rowHeight: 190,
+ margins: [20, 20],
+ outerMargin: true,
+ pushing: true,
+ floating: true,
+ swapping: true,
+ draggable : {
+ stop: function () {
+ $cookies.putObject($scope.userId + '_apps', $scope.appsViewData);
+ }
+ }
+ };
+
+ //navigate to application url in new tab
+ this.goToPortal = (item) => {
+ $log.info("goToPortal called with item: " + item);
+ let url = item.url;
+ let restrictedApp = item.restrictedApp;
+ if (!url) {
+ $log.info('No url found for this application, doing nothing..');
+ return;
+ }
+ if (!HTTP_PROTOCOL_RGX.test(url)) {
+ url = 'http://' + url;
+ }
+ if (restrictedApp) {
+ $window.open(url, '_blank');
+ } else {
+ var tabContent = { id: new Date(), title: item.headerText, url:item.url.split('#')[0] + '?' + (new Date).getTime() + "#" + item.url.split('#')[1]};
+ $cookies.putObject('addTab', tabContent );
+ }
+
+ };
+ // try {
+ // userProfileService.getUserProfile()
+ // .then(profile=> {
+ // if (profile.roles.indexOf('superAdmin') > -1) {
+ // $scope.buildNumber = "ECOMP Portal Version: 1610.0.2058"
+ // }
+ // }).catch(err=> {
+ // $log.error('Applications Home:: getUserProfile() failed: ' + err);
+ // });
+ // } catch (err) {
+ // $log.error('Applications Home:: getUserProfile() failed: ' + err);
+ // }
+ if(getParameterByName('noUserError')!=null){
+ if(getParameterByName('noUserError')=="Show"){
+ $("#errorInfo").show();
+ }
+ }
+ }
+ }
+ ApplicationsHomeCtrl.$inject = ['applicationsService', '$log', '$window', 'userProfileService', '$scope','$cookies', 'utilsService'];
+ angular.module('ecompApp').controller('ApplicationsHomeCtrl', ApplicationsHomeCtrl);
+})();
+
+function getParameterByName(name, url) {
+ if (!url) url = window.location.href;
+ name = name.replace(/[\[\]]/g, "\\$&");
+ var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
+ results = regex.exec(url);
+ if (!results) return '';
+ if (!results[2]) return '';
+ return (results[2].replace(/\+/g, " "));
+}
diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js
new file mode 100644
index 00000000..e3782ab2
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.controller.spec.js
@@ -0,0 +1,77 @@
+/*-
+ * ================================================================================
+ * 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';
+describe('Controller: ApplicationsHomeCtrl ', function() {
+ beforeEach(module('ecompApp'));
+
+ let ApplicationsHomeCtrl, $controller, $q, rootScope, $log, $window, $cookies, scope;
+ let deferredApps, deferredUserProfile, applicationsServiceMock, userProfileServiceMock;
+
+
+ beforeEach(inject( (_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_, _CacheFactory_)=>{
+ rootScope = _$rootScope_;
+ scope = rootScope.$new();
+ $q = _$q_;
+ $controller = _$controller_;
+ $log = _$log_;
+ $window = _$window_;
+ $cookies = _$cookies_;
+
+ _CacheFactory_.destroyAll();
+
+ deferredApps = $q.defer();
+ deferredUserProfile = $q.defer();
+ applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']);
+ applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise);
+
+ userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']);
+ userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise);
+
+ ApplicationsHomeCtrl = $controller('ApplicationsHomeCtrl', {
+ applicationsService: applicationsServiceMock,
+ $log: $log,
+ $window: $window,
+ userProfileService: userProfileServiceMock,
+ $scope: scope,
+ $cookies: $cookies
+ });
+ scope.$digest();
+ }));
+
+ it('should populate this.apps with data from portals service getUserApps', inject(function ( _$q_) {
+ $q = _$q_;
+
+ let profile = {roles: 'superAdmin', userId: 'userid'};
+
+ deferredUserProfile.resolve(profile)
+ deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]);
+ scope.$digest();
+ expect(scope.appsViewData.length).toBe(3);
+ }));
+
+ it('should call $log error when getAllPortals fail', inject(function ( _$q_) {
+ $q = _$q_;
+ spyOn($log, 'error');
+ deferredUserProfile.reject('something happened!');
+ scope.$digest();
+ expect($log.error).toHaveBeenCalled();
+ }));
+
+});
diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less
new file mode 100644
index 00000000..d91acc17
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.less
@@ -0,0 +1,164 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-applications-home {
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .go-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ border-radius: 0px;
+ }
+
+ .applications-home-container {
+ .content_justify;
+ position: relative;
+ padding: 15px 0 32px 0;
+ width: 100%;
+
+ .applications-home-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+ .portals-list {
+ min-height: 70vh;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: 1170px;
+
+ margin: auto;
+ margin-bottom: 63px;
+
+ .app-gridster-header {
+ background-color: @u;
+ }
+
+ .app-gridster-footer {
+ background-color: @u;
+ }
+
+ .portals-list-item {
+ background-color: @u;
+ border-radius: 2px;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ display: inline-block;
+ width: 360px;
+ height: 300px;
+ background-size: cover;
+ cursor: pointer;
+ margin: 15px;
+ overflow: hidden;
+
+ .portals-item-info {
+ background-color: @u;
+ height: 120px;
+ top: 180px;
+ position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+
+ .info-title {
+ .a24r;
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .c16r;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+ .info-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ bottom: 16px;
+ left: 16px;
+ }
+
+ &:hover {
+ opacity: .93;
+ z-index: 3;
+ }
+ }
+ }
+ }
+ }
+}
+.w-ecomp-main-error{
+ .o14r;
+ position: absolute;
+ width: 100%;
+ line-height: 1.5em;
+}
+.w-ecomp-main-disclaimer {
+ text-align: center;
+ .o14r;
+ //position: absolute;
+ bottom: -75px;
+ line-height: 1.5em;
+ margin: 0 auto;
+ width:1170px;
+ position: relative;
+
+}
+.build-number {
+ .o12i;
+}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+.slide.ng-hide-add,
+.slide.ng-hide-remove,
+.slide.ng-enter,
+.slide.ng-leave {
+ transition: all 0.5s ease;
+}
+.slide.ng-hide,
+.slide.ng-enter {
+ transform: translate(-100%, 0);
+}
+.slide.ng-enter-active {
+ transform: translate(0, 0);
+}
+.slide.ng-leave {
+ transform: translate(0, 0);
+}
+.slide.ng-leave-active {
+ transform: translate(+100%, 0);
+}
diff --git a/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html
new file mode 100644
index 00000000..645807f6
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/applications-home/applications-home.tpl.html
@@ -0,0 +1,59 @@
+<!--
+ ================================================================================
+ 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-applications-home" ng-style="{bottom: tabBottom}">
+ <div class="applications-home-container">
+ <div id="app-home-title" class="applications-home-title" >Applications</div>
+ <div align="center" id="errorInfo" style="display:none;font-size:12px;margin-left:5px">
+ <span style="color:red">You don't have user account in that application.Please ask system administrator </span></div>
+ <div class="portals-list">
+ <div class="gridster-container override_background">
+ <span class="ecomp-spinner" ng-show="applicationsHome.isLoading" ></span>
+ <div att-gridster att-gridster-options="applicationsHome.gridsterOpts" >
+ <div att-gridster-item='item' ng-repeat="item in appsViewData" id="app-{{item.headerText.split(' ').join('-')}}" >
+ <div id="grips-{{item.headerText.split(' ').join('-')}}" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone"
+ att-gridster-item-header grips-img-path="assets/images/grips.png"
+ header-text="{{item.headerText | elipsis: 33}}" sub-header-text="{{item.subHeaderText | elipsis: 56}}" class="app-gridster-header">
+ </div>
+ <div id="app-image-{{item.headerText.split(' ').join('-')}}"att-gridster-item-body
+ ng-style="{'cursor':'pointer',
+ 'background-image': 'url('+(item.imageLink)+')',
+ 'order': item.order,
+ 'background-repeat': 'no-repeat'}"
+ ng-click="applicationsHome.goToPortal(item)" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone">
+ </div>
+ <div att-gridster-item-footer class="app-gridster-footer" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone">
+ <span id="go-{{item.headerText.split(' ').join('-')}}" class="go-button" ng-click="applicationsHome.goToPortal(item)" >GO</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div>
+ <div id="app-home-disclaimer" class="w-ecomp-main-error" ng-if="appsViewData.length==0" ng-hide="users.isLoadingTable && !users.getUserAppsIsDone">
+ You do not have access to any application/functionality within OpenECOMP Portal. Please contact your administrator to get access to ECOMP Portal</a><br>
+ </div>
+ </div>
+ </div>
+ <div id="app-home-disclaimer-mylogins" class="w-ecomp-main-disclaimer">
+
+ <p id="build-number" class="build-number" ng-bind-html="buildNumber"></p>
+ </div>
+
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js
new file mode 100644
index 00000000..3f624117
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.js
@@ -0,0 +1,284 @@
+'use strict';
+(function () {
+
+ class WidgetsHomeCtrl {
+ constructor(widgetsService, $log, $cookies, $scope, userProfileService,dashboardService) {
+ this.userProfileService = userProfileService;
+ this.isLoading = true;
+
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('WidgetsHomeCtrl::getUserProfile: ', profile);
+ $scope.attuid = profile.attuid;
+ $log.info('WidgetsHomeCtrl::getUserProfile: user has the following attuid: ' + profile.attuid);
+ $scope.widgetsViewData = [];
+ $scope.widgetsView = [];
+
+ widgetsService.getUserWidgets()
+ .then(widgets => {
+ $log.info('WidgetsHomeCtrl::getUserWidgets', widgets);
+ this.widgets = widgets;
+ this.enlargeURL = "";
+ this.isEnlarged = false;
+ let rowNo = 0;
+ for (let i = 0; i < this.widgets.length; i++) {
+ $scope.widgetsView[i] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: '',
+ width: '',
+ height: '',
+ url: '',
+ selected:false
+ };
+ $scope.widgetsView[i].headerText = this.widgets[i].name;
+ $scope.widgetsView[i].url = this.widgets[i].url;
+ //$scope.widgetsView[i].width = this.widgets[i].width;
+ //$scope.widgetsView[i].height = this.widgets[i].height;
+ //$scope.widgetsView[i].sizeX = this.widgets[i].width/180;
+ //$scope.widgetsView[i].sizeY = this.widgets[i].height/150;
+ }
+ /*News Events Resources*/
+ $scope.widgetsView[this.widgets.length] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: 'News',
+ width: '',
+ height: '',
+ url: '',
+ selected:true
+ };
+ $scope.widgetsView[this.widgets.length+1] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: 'Events',
+ width: '',
+ height: '',
+ url: '',
+ selected:true
+ };
+ $scope.widgetsView[this.widgets.length+2] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: 'Resources',
+ width: '',
+ height: '',
+ url: '',
+ selected:true
+ };
+
+ /*Setting News data*/
+ $scope.newsData = [];
+ $scope.updateNews = function() {
+ $scope.newsData.length=0;
+ dashboardService.getCommonWidgetData('NEWS').then(function(res) {
+ // $log.info(res.message);
+ var newsDataJsonArray = res.response.items;
+ for (var i = 0; i < newsDataJsonArray.length; i++) {
+ $scope.newsData.push(newsDataJsonArray[i]);
+ }
+ })['catch'](function(err) {
+ $log.error('dashboard controller: failed to get news list', err);
+ _this.isLoading = false;
+ });
+ }
+ $scope.updateNews();
+ /*Setting Events data*/
+
+ $scope.eventData = [];
+
+ $scope.updateEvents = function() {
+ $scope.eventData.length=0;
+ dashboardService.getCommonWidgetData('EVENTS').then(function(res) {
+ var eventDataJsonArray = res.response.items;
+ for (var i = 0; i < eventDataJsonArray.length; i++) {
+ if(eventDataJsonArray[i].eventDate !=null) {
+ // yyyy-mm-dd
+ eventDataJsonArray[i].year = eventDataJsonArray[i].eventDate.substring(2,4);
+ eventDataJsonArray[i].mon = eventDataJsonArray[i].eventDate.substring(5,7);
+ eventDataJsonArray[i].day = eventDataJsonArray[i].eventDate.substring(8,10);
+ }
+ $scope.eventData.push(eventDataJsonArray[i]);
+ }
+ })['catch'](function(err) {
+ $log.error('dashboard controller: failed to get Events list', err);
+ _this.isLoading = false;
+ });
+ }
+ $scope.updateEvents();
+ /*Setting Important Resources data*/
+
+ $scope.importResData = [];
+ $scope.updateImportRes = function() {
+ $scope.importResData.length=0;
+ dashboardService.getCommonWidgetData('IMPORTANTRESOURCES').then(
+ function(res) {
+ // $log.error('couldnt get important list...', res.response.dataBlob);
+ $log.info(res);
+ var importResDataJSONArray = res.response.items;
+ for (var i = 0; i < importResDataJSONArray.length; i++) {
+ $scope.importResData.push(importResDataJSONArray[i]);
+ }
+ })['catch'](function(err) {
+ $log.error('dashboard controller: failed to get resources list...', err);
+ _this.isLoading = false;
+ });
+ }
+ $scope.updateImportRes();
+
+ /********End hardcoded news events and resources****************/
+
+
+ $log.info('WidgetsHomeCtrl::getUserWidgets count : ' + $scope.widgetsView.length);
+
+ if ($cookies.getObject($scope.attuid + '_widget') == undefined || $cookies.getObject($scope.attuid + '_widget') == null || $cookies.getObject($scope.attuid + '_widget').length == 0) {
+ if (($scope.widgetsView != undefined) && ($scope.widgetsView != null) && ($scope.widgetsView.length > 0)) {
+ $scope.widgetsViewData = $scope.widgetsView;
+ //$cookies.putObject($scope.attuid + '_widget', $scope.widgetsView);
+ }
+ }
+ else {
+ this.listChanged = false;
+ this.listFromCookie = $cookies.getObject($scope.attuid + '_widget');
+ this.finalList = [];
+ //
+ // If a widget is still valid for this user from backend and
+ // it was in the cookie already, put it in the list in the same order
+ // it was in within the cookie.
+ //
+ let finalCount = 0;
+ for (let i = 0; i < this.listFromCookie.length; i++) {
+ this.foundInListFromBackend = false;
+ for (let j = 0; j < $scope.widgetsView.length; j++) {
+ if ((this.listFromCookie[i].url == $scope.widgetsView[j].url) &&
+ (this.listFromCookie[i].headerText == $scope.widgetsView[j].headerText)) {
+ this.finalList[finalCount] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: '',
+ width: '',
+ height: '',
+ url: '',
+ selected:false
+ };
+ this.finalList[finalCount].headerText = this.listFromCookie[i].headerText;
+ //this.finalList[finalCount].width = this.listFromCookie[i].width;
+ //this.finalList[finalCount].height = this.listFromCookie[i].height;
+ this.finalList[finalCount].url = this.listFromCookie[i].url;
+ //this.finalList[finalCount].sizeX = this.listFromCookie[i].width/180;
+ //this.finalList[finalCount].sizeY = this.listFromCookie[i].height/150;
+ finalCount++;
+ this.foundInListFromBackend = true;
+ break;
+ }
+ }
+ if (this.foundInListFromBackend == false) {
+ this.listChanged = true;
+ }
+ }
+
+ //
+ // Fill in the rest of the list with the widgets retrieved from the backend that we did
+ // not already add. There could have been
+ // new widgets configured for the user that are not in the cookie.
+ //
+ for (let i = 0; i < $scope.widgetsView.length; i++) {
+ this.found = false;
+ for (let j = 0; j < this.finalList.length; j++) {
+ if (($scope.widgetsView[i].url == this.finalList[j].url) &&
+ ($scope.widgetsView[i].headerText == this.finalList[j].headerText)){
+ // already present
+ this.found = true;
+ break;
+ }
+ }
+ if (this.found == false) {
+ this.finalList[finalCount] = {
+ sizeX: 2,
+ sizeY: 2,
+ headerText: '',
+ width: '',
+ height: '',
+ url: '',
+ selected:false
+ };
+ this.finalList[finalCount].headerText = $scope.widgetsView[i].headerText;
+ //this.finalList[finalCount].width = $scope.widgetsView[i].width;
+ //this.finalList[finalCount].height = $scope.widgetsView[i].height;
+ this.finalList[finalCount].url = $scope.widgetsView[i].url;
+ //this.finalList[finalCount].sizeX = $scope.widgetsView[i].width/180;
+ //this.finalList[finalCount].sizeY = $scope.widgetsView[i].height/150;
+ finalCount++;
+ this.listChanged = true;
+ }
+ }
+
+ if ((this.finalList != undefined) && (this.finalList != null) && (this.finalList.length > 0)) {
+ if (this.listChanged) {
+ $scope.widgetsViewData = this.finalList;
+ $cookies.putObject($scope.attuid + '_widget', this.finalList);
+ } else {
+ $scope.widgetsViewData = $cookies.getObject($scope.attuid + '_widget');
+ }
+ }
+ this.isLoading = false;
+ }
+ }).catch(err => {
+ $log.error('WidgetsHomeCtrl::getUserWidgets: oh no! couldnt get widgets list...', err);
+ this.isLoading = false;
+ });
+ });
+
+
+ this.gridsterOpts = {
+ columns: 6,
+ colWidth: 190,
+ rowHeight: 190,
+ margins: [20, 20],
+ outerMargin: true,
+ pushing: true,
+ floating: true,
+ swapping: true,
+ draggable: {
+ handle: 'img',
+ stop: function () {
+ $cookies.putObject($scope.attuid + '_widget', $scope.widgetsViewData);
+ }
+ }
+ };
+ }
+ }
+
+ WidgetsHomeCtrl.$inject = ['widgetsService', '$log', '$cookies', '$scope', 'userProfileService','dashboardService'];
+ angular.module('ecompApp').controller('WidgetsHomeCtrl', WidgetsHomeCtrl);
+
+ angular.module('ecompApp').constant('refreshInterval', '30000');
+
+ angular.module('ecompApp').directive('refreshIframe', ['$interval', 'refreshInterval', function ($interval, refreshInterval) {
+
+ function link(scope, element, attrs) {
+ var timeoutId;
+
+ function updateIframe() {
+ if(attrs.isEnlarged == "false")
+ {
+ element.attr('src', element.attr('src'));
+ }
+ }
+
+ element.on('$destroy', function () {
+ $interval.cancel(timeoutId);
+ });
+
+ // start the UI update process; save the timeoutId for cancelling
+ /*timeoutId = $interval(function () {
+ updateIframe(); // update DOM
+ }, refreshInterval);*/
+ }
+
+ return {
+ link: link
+ };
+ } ]);
+
+})();
diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.controller.spec.js
@@ -0,0 +1 @@
+
diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less
new file mode 100644
index 00000000..46e5b1fd
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.less
@@ -0,0 +1,137 @@
+.w-ecomp-widgets-home{
+ //.bg_u;//white for 1702
+ .bg_w; // gray for 1610
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .widget-gridster-header {
+ background-color:#ffffff;
+ }
+
+ .widget-gridster-footer {
+ background-color:#ffffff
+ }
+
+ .widget-iframe {
+ width: 100%;
+ height: 90%;
+ }
+
+ .widget-image {
+ cursor: pointer;
+ width: 20px;
+ height: 25px;
+ }
+
+ .enlarge-content{
+ top: 120px;
+ left: 400px;
+ width:1170px;
+ height:600px;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background: none repeat scroll 0 0 #f2f2f2;
+ }
+
+ .enlarge-close-button{
+ .btn-blue;
+ position: relative;
+ top : -20px;
+ left : 1145px;
+ width : 25px;
+ }
+
+ .widgets-home-container {
+ .content_justify;
+ position: relative;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: 32px;
+ width: 100%;
+
+ .widgets-home-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+ .portals-list {
+ min-height: 70vh;
+ //display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: 1170px;
+
+ margin: auto;
+ margin-bottom: 63px;
+
+ .portals-list-item {
+ background-color: @u;
+ border-radius: 2px;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ display: inline-block;
+ width: 360px;
+ height: 300px;
+ background-size: cover;
+ cursor: pointer;
+ margin: 15px;
+ overflow: hidden;
+
+ .portals-item-info {
+ background-color: @u;
+ height: 50px;
+ top: 180px;
+ // position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+
+ .info-title {
+ .a24r;
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .c16r;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+ .info-button {
+ .btn-green;
+ width: 96px;
+ // position: absolute;
+ bottom: 16px;
+ left: 16px;
+ }
+
+ &:hover {
+ opacity: .93;
+ z-index: 3;
+ }
+ }
+ }
+ }
+ }
+}
+
+.gridster-item-container .gridster-item-body{
+ bottom:0px;
+ overflow:hidden;
+}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html
new file mode 100644
index 00000000..8e385c1e
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/home/widgets-home/widgets-home.tpl.html
@@ -0,0 +1,123 @@
+<div class="w-ecomp-widgets-home" id="contentId">
+ <div class="widgets-home-container">
+ <div id="widgets-home-title" class="widgets-home-title">Widgets</div>
+
+
+ <div class="portals-list">
+ <div class="gridster-container override_background">
+ <span class="ecomp-spinner" ng-show="widgetsHome.isLoading"></span>
+ <div ng-hide="widgetsHome.isEnlarged" att-gridster
+ att-gridster-options="widgetsHome.gridsterOpts" >
+ <div att-gridster-item='item' ng-repeat="item in widgetsViewData">
+ <div id="widget-grips-{{item.headerText.split(' ').join('-')}}"
+ att-gridster-item-header
+ grips-img-path="assets/images/grips.png"
+ header-text={{item.headerText}}
+ class="widget-gridster-header">
+ </div>
+ <div att-gridster-item-body ng-if="item.headerText=='News'">
+ <div id="newsContainer" class="information-section-gridsterContent">
+ <div class="news">
+ <ul id="newsContent" ng-show="newsData.length!=0">
+ <li ng-repeat="news in newsData"><a href="{{news.href}}" target="_blank" ng-bind="news.title"></a></li>
+ </ul>
+ <div ng-hide="newsData.length!=0">
+ <div class="activity-error-container" style="background: rgb(255, 255, 255); overflow: auto!important; width: 100%;">
+ <div class="activity-error-block">
+ <i class="ion-information-circled full-linear-ion-information-circled" style="margin-left:125px; font-size: 90px"></i> <br>
+ <div class="activity-error-msg1">There's currently no news available.</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div att-gridster-item-body ng-if="item.headerText=='Events'">
+ <div class="information-section-gridsterContent">
+ <div class="events">
+ <ul ng-show="eventData.length!=0">
+ <li ng-repeat="event in eventData">
+ <div ng-click="event_content_show=!event_content_show">
+ <div class="events-date">{{event.mon}}/{{event.day}}/{{event.year}} </div>
+ <div>
+ <div class="event-title-div">
+ <p ng-bind="event.title"></p>
+ </div>
+ <div>
+ <span class="ion-ios-arrow-up" ng-if="event_content_show" style="color: #888; font-size: 22px;"></span>
+ <span class="ion-ios-arrow-down" ng-if="!event_content_show" style="color: #888; font-size: 22px;"></span>
+
+ </div>
+ <div style="clear: both;"></div>
+ </div>
+ </div>
+ <div class="events-content" ng-show="event_content_show">
+ <div class="events-content-body">
+ <a class="events-link" ng-href="{{event.href}}" target="_blank">
+ <span ng-bind="event.content"></span>
+ </a>
+ </div>
+ <div></div>
+ </div>
+ </li>
+
+
+ </ul>
+ <div ng-hide="eventData.length!=0">
+ <div class="activity-error-container" style="background: rgb(255, 255, 255); overflow: hidden!important; width: 100%;">
+ <div class="activity-error-block">
+ <i class="ion-information-circled full-linear-ion-information-circled" style="margin-left:125px; font-size: 90px"></i> <br>
+ <div class="activity-error-msg1">There's currently no event available.</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div att-gridster-item-body ng-if="item.headerText=='Resources'">
+ <div class="">
+ <div class="resources">
+ <ul ng-show="importResData.length!=0">
+ <li ng-repeat="item in importResData"><a href="{{item.href}}" target="_blanl">{{item.title}}</a></li>
+ </ul>
+ <div ng-hide="importResData.length!=0">
+ <div class="activity-error-container" style="background: rgb(255, 255, 255); overflow: hidden!important; width: 100%;">
+ <div class="activity-error-block">
+ <i class="ion-information-circled full-linear-ion-information-circled" style="margin-left:125px; font-size: 90px"></i> <br>
+ <div class="activity-error-msg1">There's currently no links available.</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div att-gridster-item-body ng-if="item.headerText!='News' && item.headerText!='Events' && item.headerText!='Resources'">
+ <iframe id="widget-iframe-{{item.headerText.split(' ').join('-')}}"
+ class="widget-iframe"
+ ng-src="{{item.url | trusted}}"
+ refresh-iframe
+ is-enlarged="{{widgetsHome.isEnlarged}}">
+ </iframe>
+ </div>
+ <div id="widget-footer-{{item.headerText.split(' ').join('-')}}"
+ att-gridster-item-footer class="widget-gridster-footer">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div ng-show="widgetsHome.isEnlarged" >
+ <div id="widgets-button-enlarge-close" class="enlarge-close-button" ng-click="widgetsHome.isEnlarged=false">X</div>
+ <iframe id="iframe-widget-enlarge-close" ng-src="{{widgetsHome.enlargeURL | trusted}}" class="enlarge-content" >
+ </iframe>
+ </div>
+
+ </div>
+
+ <div id="widgets-disclaimer" class="w-ecomp-main-disclaimer">
+ To request access to an application, please visit the <a
+ ng-href="getAccess">Get Access</a> page.
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js
new file mode 100644
index 00000000..b5216101
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.js
@@ -0,0 +1,85 @@
+/*-
+ * ================================================================================
+ * 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 NewPortalAdminModalCtrl {
+ constructor($log, portalAdminsService, $scope, confirmBoxService) {
+
+ let init = () => {
+ this.isSaving = false;
+ if($scope.ngDialogData && $scope.ngDialogData.selectedUser && $scope.ngDialogData.dialogState){
+ this.selectedUser = $scope.ngDialogData.selectedUser;
+ this.dialogState = $scope.ngDialogData.dialogState;
+ }else{
+ this.selectedUser = null;
+ this.dialogState = 1;
+ }
+ $log.info('NewPortalAdminModalCtrl:: initiated');
+ };
+
+ this.addNewPortalAdmin = () => {
+ portalAdminsService.getPortalAdmins().then(result=> {
+ var dupNameCheck = JSON.stringify(result).search(this.selectedUser.orgUserId);
+ if (dupNameCheck != -1) {
+ $log.error("NewPortalAdminModalCtrl::addNewPortalAdmin: userId already exists as a portal admin! dupNameCheck=",dupNameCheck);
+ confirmBoxService.showInformation('This user already exists as a portal admin!').then(function (isConfirmed) {
+ $scope.closeThisDialog(true);
+ });
+ } else {
+ confirmBoxService.makeAdminChanges('Are you sure you want to add "' + this.selectedUser.firstName + ' ' + this.selectedUser.lastName + '" as a Portal Admin?')
+ .then(isConfirmed => {
+ if(isConfirmed) {
+ if (!this.selectedUser || !this.selectedUser.orgUserId) {
+ $log.error('NewPortalAdminModalCtrl::makeAdminChanges: No portal admin or ID... cannot add');
+ return;
+ }
+ portalAdminsService.addPortalAdmin(this.selectedUser.orgUserId)
+ .then(() => {
+ $log.debug("NewPortalAdminModalCtrl::addNewPortalAdmin: portal admin added successfully");
+ $scope.closeThisDialog(true);
+ }).catch(err => {
+ $log.error('NewPortalAdminModalCtrl::addNewPortalAdmin error status: ' + err.status);
+ confirmBoxService.showInformation('There was a unknown problem adding the portal admin. ' + 'Please try again later. Error Status: '+ err.status).then(function (isConfirmed) {});
+ });
+ }
+ }).catch(err => {
+ $log.error('portalAdminsService.addPortalAdmin error status: '+ err.status);
+ });
+ }
+ }).catch(err=> {
+ $log.error('NewPortalAdminModalCtrl::addNewPortalAdmin error getting portal admins list:',err);
+ });
+ };
+
+ this.setSelectedUser = (user) => {
+ $log.debug('NewPortalAdminModalCtrl::setSelectedUser: selected user: ', user);
+ this.selectedUser = user;
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+ }
+ }
+ NewPortalAdminModalCtrl.$inject = ['$log', 'portalAdminsService', '$scope', 'confirmBoxService'];
+ angular.module('ecompApp').controller('NewPortalAdminModalCtrl', NewPortalAdminModalCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.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/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html
new file mode 100644
index 00000000..f235b391
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html
@@ -0,0 +1,32 @@
+<!--
+ ================================================================================
+ 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-admin-modal">
+ <div class="search-users">
+ <search-users search-title="Add New Portal Admin"
+ selected-user="newPortalAdmin.selectedUser"></search-users>
+ <div class="dialog-control">
+ <div id="pa-search-users-button-save" class="save-button"
+ ng-click="newPortalAdmin.selectedUser && newPortalAdmin.addNewPortalAdmin()"
+ ng-class="{disabled: !newPortalAdmin.selectedUser}">Save
+ </div>
+ <div id="pa-search-users-button-cancel" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less
new file mode 100644
index 00000000..dcc4fc52
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/new-portal-admin/new-portal-admin.modal.less
@@ -0,0 +1,99 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .new-portal-admin-modal {
+ display:block;
+ overflow:auto;
+ min-height: 450px;
+
+ .search-users {
+ }
+
+ .admin-app-roles {
+ .title {
+ .n18r;
+ border-bottom: @a 3px solid;
+
+ }
+
+ .app-roles-main {
+ margin-top: 16px;
+ .app-roles-main-title {
+ .n14r;
+ margin-bottom: 8px;
+ .left {
+ display: inline-block;
+ }
+ .right {
+ display: inline-block;
+ color: @a;
+ float: right;
+ cursor: pointer;
+ }
+ }
+
+ .select-input{
+ width: 460px;
+ }
+
+ .new-administrated-app {
+ height: 30px;
+ line-height: 30px;
+
+ border: 1px solid @p;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding-left: 6px;
+ padding-top: 0;
+ .o14r;
+ }
+
+ .admin-roles-list {
+ height: 240px;
+ overflow-y: auto;
+ }
+
+ .administrated-application {
+ width: 460px;
+ height: 30px;
+ border: 1px solid @p;
+ margin-bottom: 8px;
+ border-radius: 2px;
+ padding: 6px;
+ .o14r;
+ display: inline-block;
+
+ }
+
+ .delete-application {
+ .ico_trash_default;
+ display: inline-block;
+ vertical-align: 4px;
+ cursor: pointer;
+ position: relative;
+ top: 6px;
+ color: transparent;
+ margin-left: 8px;
+ }
+
+ }
+
+ }
+}
+
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js
new file mode 100644
index 00000000..10043005
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin-controller.js
@@ -0,0 +1,127 @@
+/*-
+ * ================================================================================
+ * 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 PortalAdminsCtrl {
+ constructor($log, portalAdminsService, ngDialog, confirmBoxService) {
+
+ let updateTableData = () => {
+ this.isLoadingTable = true;
+ portalAdminsService.getPortalAdmins().then(result=> {
+ $log.debug('PortalAdminsCtrl::updateTableData: result: ' + JSON.stringify(result));
+ if (!result || !result.length) {
+ $log.info('PortalAdminsCtrl::updateTableData: no Portal Admins err handling');
+ this.portalAdminsTableData = [];
+ return;
+ }
+ this.portalAdminsTableData = result;
+ }).catch(err=> {
+ $log.error('PortalAdminsCtrl::updateTableData error :',err);
+ }).finally(() => {
+ this.isLoadingTable = false;
+ });
+ };
+
+ let init = () => {
+ $log.info('portalAdminsService.getPortalAdmins::initializing...');
+ this.isLoadingTable = false;
+
+ this.searchString= '';
+ this.portalAdminsTableHeaders = ['First Name', 'Last Name', 'User ID', 'Delete'];
+ this.portalAdminsTableData = [];
+ updateTableData();
+ };
+
+ init();
+
+ this.removePortalAdmin = pAdmin => {
+ $log.debug('pAdmin = ' + JSON.stringify(pAdmin));
+ confirmBoxService.deleteItem(pAdmin.firstName + ' ' + pAdmin.lastName )
+ .then(isConfirmed => {
+ if(isConfirmed){
+ if(!pAdmin || !pAdmin.userId){
+ $log.error('PortalAdminsCtrl::removePortalAdmin No portal admin or ID... cannot delete');
+ return;
+ }
+ portalAdminsService.removePortalAdmin(pAdmin.userId).then(() => {
+ $log.info("PortalAdminsCtrl::removePortalAdmin removed admin");
+ init();
+ }).catch(err => {
+ $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err);
+ });
+ }
+ }).catch(err => {
+ $log.error('PortalAdminsCtrl::removePortalAdmin.deleteItem error: '+ err);
+ });
+ };
+
+ this.openAddNewPortalAdminModal = (user) => {
+ let data = null;
+ if(user){
+ data = {
+ dialogState: 2,
+ selectedUser:{
+ userId: user.orgUserId,
+ firstName: user.firstName,
+ lastName: user.lastName
+ }
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/portal-admin/new-portal-admin/new-portal-admin.modal.html',
+ controller: 'NewPortalAdminModalCtrl',
+ controllerAs: 'newPortalAdmin',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ $log.debug('PortalAdminsCtrl::openAddNewPortalAdminModal: updating Portal Admin table data...');
+ updateTableData();
+ }
+ });
+ };
+
+ this.openEditUserModal = (loginId) => {
+ var data = {
+ loginId : loginId,
+ updateRemoteApp : false,
+ appId : this.selectedApp!=null?this.selectedApp.id:''
+ }
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/header/user-edit/edit-user.tpl.html',
+ controller: 'editUserController',
+ data: data,
+ resolve: {
+ message: function message() {
+ var message = {
+ type: 'Contact',
+ };
+ return message;
+ }
+ }
+ }).closePromise.then(needUpdate => {
+ updateTableData();
+ });
+ }
+
+ }
+ }
+ PortalAdminsCtrl.$inject = ['$log', 'portalAdminsService', 'ngDialog', 'confirmBoxService'];
+ angular.module('ecompApp').controller('PortalAdminsCtrl', PortalAdminsCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html
new file mode 100644
index 00000000..05216f34
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admin.tpl.html
@@ -0,0 +1,73 @@
+<!--
+ ================================================================================
+ 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="portal-admins-page-main" id="contentId">
+ <div id="title" class="w-ecomp-main-view-title">Portal Admins</div>
+ <div class="portal-admins-table">
+ <div class="table-control">
+ <input id="input-table-search" class="table-search" type="text"
+ placeholder="Search in entire table"
+ ng-model="portalAdmin.searchString"/>
+ <div id="add-portal-admin-button" class="add-button portal-add-button" ng-click="portalAdmin.openAddNewPortalAdminModal()">Add Portal Admin</div>
+ </div>
+ <span class="ecomp-spinner" ng-show="portalAdmin.isLoadingTable"></span>
+ <div class="c-ecomp-att-abs-table default" ng-hide="portalAdmin.isLoadingTable">
+ <table att-table
+ table-data="portalAdmin.portalAdminsTableData"
+ search-string="portalAdmin.searchString"
+ view-per-page="portalAdmin.viewPerPageIgnored"
+ current-page="portalAdmin.currentPageIgnored"
+ total-page="portalAdmin.totalPageIgnored">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-first-name" att-table-header key="firstName" default-sort="a">{{portalAdmin.portalAdminsTableHeaders[0]}}</th>
+ <th id="th-last-name" att-table-header key="lastName" sortable="true">{{portalAdmin.portalAdminsTableHeaders[1]}}</th>
+ <th id="th-userId" att-table-header key="userId" sortable="true">{{portalAdmin.portalAdminsTableHeaders[2]}}</th>
+ <th id="portal-admin-th-header-delete" att-table-header sortable="{{false}}">{{portalAdmin.portalAdminsTableHeaders[3]}}</th>
+ </tr>
+ </thead>
+ <tbody att-table-row type="body"
+ class="table-body"
+ row-repeat="rowData in portalAdmin.portalAdminsTableData | filter: portalAdmin.portalsRowFilter">
+ <tr>
+ <td att-table-body>
+ <div id="div-{{rowData.userId}}-{{rowData.firstName}}" ng-bind="rowData.firstName"></div>
+ </td>
+ <td att-table-body>
+ <div id="div-{{rowData.userId}}-{{rowData.lastName}}" ng-bind="rowData.lastName"></div>
+ </td>
+ <td att-table-body>
+ <div id="div-{{rowData.userId}}" style="float: left;" ng-bind="rowData.loginId"></div>
+ <div>
+ <span style="float: left; margin-left:15px" class="ion-person" ng-click="portalAdmin.openEditUserModal(rowData.loginId);$event.stopPropagation()"></span>
+ </div>
+ </td>
+ <td att-table-body>
+ <div id="portal-admin-delete-{{$index}}" class="ion-trash-b" ng-click="portalAdmin.removePortalAdmin(rowData)"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less b/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less
new file mode 100644
index 00000000..29fd9e32
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/portal-admin/portal-admins.less
@@ -0,0 +1,56 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .portal-admins-page-main {
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+
+ .portal-admins-table {
+ width: @table-width;
+ margin: @table-margin;
+
+ .table-control {
+ .table-dropdown-filter{
+ width: @table-dropdown-filter-width;
+ display: @table-dropdown-filter-display;
+ }
+ }
+
+ .table-body {
+ cursor: pointer;
+ }
+ }
+
+ .delete-user{
+ .ico_trash_default;
+ }
+
+ .portal-add-button {
+ width: 160px;
+ }
+}
+
diff --git a/ecomp-portal-FE/client/app/views/search/search.controller.js b/ecomp-portal-FE/client/app/views/search/search.controller.js
new file mode 100644
index 00000000..60e27593
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/search/search.controller.js
@@ -0,0 +1,184 @@
+/*-
+ * ================================================================================
+ * 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;
+
+
+
+ function goToUrl (item, type) {
+ $log.info("goToUrl called")
+ $log.info(item + "/" + type);
+
+
+ if(type == 'intra') {
+
+ var intraSearcLink = "http://to_do_link.com";
+ var intraSpecSearcLink = intraSearcLink + encodeURIComponent(dashboardService.searchString);
+ $window.open(intraSpecSearcLink, '_blank');
+
+ } else if (type == 'extra') {
+ var extraSearcLink = "https://to_do_link.com";
+ 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/client/app/views/search/search.controller.spec.js b/ecomp-portal-FE/client/app/views/search/search.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/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/client/app/views/search/search.less b/ecomp-portal-FE/client/app/views/search/search.less
new file mode 100644
index 00000000..a21559ec
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/search/search.less
@@ -0,0 +1,55 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .searchLiHeader {
+font-weight: bold;
+color: #0574ac;
+font-size: 16px;
+padding-bottom: 10px;
+line-height: 1.5;
+font-family: arial;
+
+}
+
+.searchLiItems{
+cursor: pointer;
+font-weight: normal;
+font-size: 12px;
+color: #444444;
+font-family: arial;
+}
+
+.searchUl {
+list-style: none;
+border-bottom: 1px solid #bbb;
+padding-bottom: 20px;
+}
+
+#contentVertical {
+ height: 300px;
+ width: 250px;
+ }
+
+#contentVertical .scroll-viewport {
+ height: 300px;
+ width: 250px;
+ }
+#mainSearchText::-ms-clear{
+ display:none;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/search/search.tpl.html b/ecomp-portal-FE/client/app/views/search/search.tpl.html
new file mode 100644
index 00000000..bd37f772
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/search/search.tpl.html
@@ -0,0 +1,82 @@
+<!--
+ ================================================================================
+ 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="col-md-12 font-clearview">
+ <div class="form-field form-field__small" >
+ <input id="mainSearchText" type="text" placeholder="What are you looking for?" class="form-field form-field__small" style="height:30px;border-radius: 10px;border: 2px solid rgb(250,250,250);" />
+ <i class="ion-ios-search" id="search-icon" style="position:relative;line-height:10px;top:-30px;right:-250px">&nbsp;</i>
+
+ <div id="mainSearchSnippet" popover="searchSnippet.html"
+ aria-label="search Snippet" referby="searchSnippet" att-accessibility-click="13,32"
+ popover-style="" popover-trigger="click" popover-placement="below" >
+ </div>
+ </div>
+
+</div>
+
+
+
+
+<script type="text/ng-template" id="searchSnippet.html">
+ <div style="line-height: normal; right: 167px; min-height: 122px; height: auto; width: auto;" ng-controller="searchSnippetCtrl" >
+ <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="ion-android-apps"></a>
+ <a id="searchItem" ng-click="goToUrl(item)" class="searchLiHeader">Applications:</a>
+ </li>
+ <li ng-repeat='item in items.application'>
+ <a id="searchItem" ng-click="goToUrl(item)" class="searchLiItems">{{item.name}}</a>
+ </li>
+ </ul>
+
+ <ul class="searchUl">
+ <li>
+ <a class="ion-android-apps"></a>
+ <a id="searchItem" ng-click="goToUrl(item)" class="searchLiHeader">Functional Menus:</a>
+ </li>
+ <li ng-repeat='item in items.menu'>
+ <a id="searchItem" ng-click="goToUrl(item)" class="searchLiItems">{{item.name}}</a>
+ </li>
+ </ul>
+
+
+ <ul class="searchUl">
+ <li>
+ <a class="ion-ios-person-outline" style="font-size:16px;"></a>
+ <a id="searchItem" ng-click="goToUrl(item)" class="searchLiHeader">Users:</a>
+ </li>
+ <li ng-repeat='item in items.user'>
+ <a id="searchItem" ng-href="mailto:{{item.target}}" class="searchLiItems">{{item.name}}</a>
+
+ </li>
+ </ul>
+
+
+ </div>
+ </div>
+ </div>
+ </div>
+
+
+</script>
diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js b/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js
new file mode 100644
index 00000000..76d5301c
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.controller.js
@@ -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.
+ * ================================================================================
+ */
+'use strict';
+(function () {
+ class SidebarCtrl {
+ constructor(userProfileService, $log, $rootScope) {
+ this.$log = $log;
+ this.userProfileService = userProfileService;
+ this.$rootScope = $rootScope;
+ $rootScope.isAdminPortalAdmin = false;
+
+ //if (bowser.msie || bowser.msedge)
+ // $log.debug('SidebarCtrl::init: Browser is: Internet Explorer or Edge');
+ // else
+ // $log.debug('SidebarCtrl::init: Browser is: ' + bowser.name + ': ' + bowser.version);
+
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(superAdmin) = ' + profile.roles.indexOf('superAdmin'));
+ // $log.debug('SidebarCtrl::getUserProfile: profile.roles.indexOf(admin) = ' + profile.roles.indexOf('admin'));
+ if (profile.roles.indexOf('superAdmin') > -1) {
+ $rootScope.isAdminPortalAdmin = true;
+ // this.$log.debug('SidebarCtrl::getUserProfile: user has the superAdmin role');
+ this.sidebarModel = {
+ label: 'ECOMP portal',
+ navItems: [
+ {
+ name: 'Home',
+ state: 'root.applicationsHome',
+ imageSrc : 'ion-home'
+ }, {
+ name: 'Application Catalog',
+ state: 'root.appCatalog',
+ imageSrc : 'ion-grid'
+ }, {
+ name: 'Widgets',
+ state: 'root.widgetsHome',
+ imageSrc : 'ion-android-apps'
+ }, {
+ name: 'Admins',
+ state: 'root.admins',
+ imageSrc : 'ion-android-star'
+ }, {
+ name: 'Users',
+ state: 'root.users',
+ imageSrc : 'ion-person'
+ },{
+ name: 'Portal Admins',
+ state: 'root.portalAdmins',
+ imageSrc : 'ion-gear-a'
+ }, {
+ name: 'Application Onboarding',
+ state: 'root.applications',
+ imageSrc : 'ion-ios-upload-outline'
+ }, {
+ name: 'Widget Onboarding',
+ state: 'root.widgets',
+ imageSrc : 'ion-ios-upload-outline'
+ },{
+ name: 'Edit Functional Menu',
+ state: 'root.functionalMenu',
+ imageSrc : 'ion-compose'
+ }
+ ]
+ };
+ }
+ else if (profile.roles.indexOf('admin') > -1) {
+ // this.$log.debug('SidebarCtrl::getUserProfile: user has the admin role');
+ this.sidebarModel = {
+ label: 'ECOMP portal',
+ navItems: [
+ {
+ name: 'Home',
+ state: 'root.applicationsHome',
+ imageSrc : 'ion-home'
+ }, {
+ name: 'Application Catalog',
+ state: 'root.appCatalog',
+ imageSrc : 'ion-grid'
+ }, {
+ name: 'Widgets',
+ state: 'root.widgetsHome',
+ imageSrc : 'ion-grid'
+ }, {
+ name: 'Users',
+ state: 'root.users',
+ imageSrc : 'ion-person'
+ }, {
+ name: 'Widget Onboarding',
+ state: 'root.widgets',
+ imageSrc : 'ion-grid'
+ }
+ ]
+ };
+ }
+ else {
+ $rootScope.isAdminPortalAdmin = false;
+ // this.$log.debug('SidebarCtrl::getUserProfile: user is not superAdmin nor admin');
+ this.sidebarModel = {
+ label: 'ECOMP portal',
+ navItems: [
+ {
+ name: 'Home',
+ state: 'root.applicationsHome',
+ imageSrc : 'ion-home'
+ }, {
+ name: 'Application Catalog',
+ state: 'root.appCatalog',
+ imageSrc : 'ion-grid'
+ }, {
+ name: 'Widgets',
+ state: 'root.widgetsHome',
+ imageSrc : 'ion-grid'
+ }
+ ]
+ };
+ }
+ }).catch(err=> {
+ $log.error('SidebarCtrl: failed to get user profile: ' + err);
+ });
+
+ }
+ }
+ SidebarCtrl.$inject = ['userProfileService', '$log', '$rootScope'];
+ angular.module('ecompApp').controller('SidebarCtrl', SidebarCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.less b/ecomp-portal-FE/client/app/views/sidebar/sidebar.less
new file mode 100644
index 00000000..97650fed
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.less
@@ -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.
+ * ================================================================================
+ */
+ .w-ecomp-sidebar {
+ position: relative;
+ left: 0;
+ right: 0;
+ top: 5px;
+ padding-left: 0;
+}
+
+ @media screen and (-webkit-min-device-pixel-ratio:0)
+{
+ .w-ecomp-sidebar {
+ position: relative;
+ left: 0;
+ right: 0;
+ top: -5px;
+ padding-left: 0;
+ }
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html b/ecomp-portal-FE/client/app/views/sidebar/sidebar.tpl.html
new file mode 100644
index 00000000..a5cbf8ff
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/sidebar/sidebar.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.
+ ================================================================================
+ -->
+<left-menu id="sidebar.sidebarModel" sidebar-model="sidebar.sidebarModel" class="w-ecomp-sidebar"></left-menu>
diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js
new file mode 100644
index 00000000..368f2fe3
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.js
@@ -0,0 +1,194 @@
+/*-
+ * ================================================================================
+ * 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 ContactUsManageController {
+ constructor($scope,$log, message, $q, $http, conf,contactUsService,confirmBoxService) {
+ $scope.contactUsList=[];
+ $scope.contactUsAllAppList=[];
+ $scope.errMsg='';
+ $scope.newContactUs ={
+ app:'',
+ name:'',
+ email:'',
+ url:'',
+ desc:''
+ };
+ /*Get the existing contact us first, then based on the existing, filter from all apps*/
+ $scope.getContactUsList = function(){
+ contactUsService.getContactUs().then(res=> {
+ // $log.info('getting getContactUs',res.response);
+ if(res!=null && res.response!=null){
+ for(var i=0; i<res.response.length;i++){
+ if(res.response[i].appId!=1)
+ $scope.contactUsList.push(res.response[i]);
+ }
+ }
+ /*get all the apps*/
+ contactUsService.getListOfApp().then(res=> {
+ var tableData=[];
+ $scope.contactUsAllAppListTemp=[];
+ // $log.info('contactUsService::getListOfApp: getting res');
+ var result = (typeof(res.data) != "undefined" && res.data!=null)?res.data:null;
+ // $log.info('contactUsService::getListOfApp: result',result);
+ var res1 = result;
+ var realAppIndex = 0;
+ $scope.contactUsAllAppList.length=0;
+ for (var i = 1; i <= res1.length; i++) {
+ if (!res1[i - 1].restrictedApp) {
+ var okToAdd = true;
+ for(var j =0; j<$scope.contactUsList.length;j++){
+ if(res1[i - 1].title==$scope.contactUsList[j].appName)
+ okToAdd=false;
+ }
+ // not allowed to add(duplicate) another entry if the app is already available in the table
+ if(okToAdd){
+ if(res1[i - 1].title){
+ $scope.contactUsAllAppList.push({
+ index: realAppIndex,
+ title: res1[i - 1].title,
+ value: res1[i - 1].index
+ });
+ }
+ realAppIndex = realAppIndex + 1;
+ }
+ } else {
+ // $log.debug('contactUsService:getAvailableApps:: Restricted/URL only App will not be used = ' + res1[i - 1].title);
+ }
+ }
+ }).catch(err=> {
+ $log.error('contactUsService:error:: ', err);
+
+
+ }).finally(() => {
+ //this.isLoadingTable = false;
+ });
+ });
+ }
+
+ $scope.getContactUsList();
+
+ $scope.closeDialog = function(){
+ $scope.closeThisDialog( $scope.widgetData);
+ }
+
+ /*Add new Contact Us*/
+ $scope.newContactUsFun = function(){
+ if($scope.newContactUs.app.title==null || $scope.newContactUs.app.title=='' ){
+ confirmBoxService.showInformation('Please select an App to add').then(isConfirmed => {
+ return;
+ });
+ }
+
+ if($scope.newContactUs.url !=null && $scope.newContactUs.url != '' && !validateUrl($scope.newContactUs.url)){
+ var warningMsg = "Please enter a valid URL";
+ confirmBoxService.showInformation(warningMsg).then(isConfirmed => {return;});
+ return;
+ }
+
+ contactUsService.addContactUs($scope.newContactUs).then(res=> {
+ // $log.info('contactUsService: add ContactUs successfully');
+ $scope.contactUsList.length=0;
+ // $log.info('contactUsService: refreshing the Contact US table');
+ $scope.getContactUsList();
+ $scope.errMsg='';
+ /* confirmBoxService.showInformation('You have added a new Contact Us item').then(isConfirmed => { });*/
+ var defaultSel={
+ index: 0,
+ title: '',
+ value: ''
+ }
+ $scope.newContactUs ={
+ app:defaultSel,
+ name:'',
+ email:'',
+ url:'',
+ desc:''
+ };
+
+ }).catch(err=> {
+ $log.error('contactUsService: addContactUs error:: ', err);
+ // $scope.errMsg=err;
+ confirmBoxService.showInformation('Add Contact Us list failed: ' + err);
+
+ }).finally(() => {
+ //this.isLoadingTable = false;
+ });
+ }
+ /* Edit Contact Us*/
+ $scope.editContactUsFun = function(contactObj){
+ // $log.info('contactUsService: edit ContactUs save successfully', contactObj);
+ var contactUsObj={
+ appId:contactObj.appId,
+ appName:contactObj.appName,
+ description:contactObj.description,
+ contactName:contactObj.contactName,
+ contactEmail:contactObj.contactEmail,
+ url:contactObj.url,
+ };
+
+ contactUsService.modifyContactUs(contactUsObj).then(res=> {
+ // $log.info('contactUsService: edit ContactUs successfully');
+ // confirmBoxService.showInformation('You have saved the changes').then(isConfirmed => {});
+ $scope.errMsg='';
+
+ }).catch(err=> {
+ $log.error('contactUsService: editContactUs error:: ', err);
+ confirmBoxService.showInformation('Edit Contact Us list failed: ' + err);
+ // $scope.errMsg=err;
+ }).finally(() => {
+ //this.isLoadingTable = false;
+ });
+
+ }
+ /*del Contact Us*/
+ $scope.delContactUsFun = function(appObj){
+ var confirmMsg = 'Are you sure you want to delete '+appObj.appName +' from the list?' + ' Press OK to delete.';
+ confirmBoxService.confirm(confirmMsg).then(function (confirmed) {
+ if (confirmed == true) {
+ contactUsService.removeContactUs(appObj.appId).then(res=> {
+ // $log.info('delContactUsFun: delete ContactUs successfully',res);
+ $scope.errMsg='';
+ $scope.contactUsList.length=0;
+ $scope.getContactUsList();
+ confirmBoxService.showInformation('Item has been deleted').then(isConfirmed => {});
+ }).catch(err=> {
+ $log.error('contactUsService: addContactUs error:: ', err);
+ confirmBoxService.showInformation('Deletion failed: ' + err);
+ // $scope.errMsg=err;
+ }).finally(() => {
+ //this.isLoadingTable = false;
+ });
+ }
+ });
+
+ }
+
+ }
+ }
+ ContactUsManageController.$inject = ['$scope','$log', 'message', '$q', '$http', 'conf','contactUsService','confirmBoxService'];
+ angular.module('ecompApp').controller('ContactUsManageController', ContactUsManageController);
+
+
+})();
+function validateUrl(value){
+ return /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
+ }
diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.less
new file mode 100644
index 00000000..ef8c88df
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.controller.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.
+ * ================================================================================
+ */
+ .searchLiHeader {
+font-weight: bold;
+color: #0574ac;
+font-size: 16px;
+padding-bottom: 10px;
+line-height: 1.5;
+font-family: arial;
+
+}
+
+.searchLiItems{
+cursor: pointer;
+font-weight: normal;
+font-size: 12px;
+color: #444444;
+font-family: arial;
+}
+
+.searchUl {
+list-style: none;
+border-bottom: 1px solid #bbb;
+padding-bottom: 20px;
+}
+
+.manage-contactUs-home-title{
+ color: #067ab4;
+ font-family: arial;
+ font-size: 24px;
+ margin-left: 78px;
+ width: 1170px;
+ }
+
+.manage-contactUs-home-manageWidgets-title{
+ color: #067ab4;
+ font-family: arial;
+ font-size: 24px;
+ margin-left: 30px;
+ width: 1170px;
+ }
+
+#editWidgetsContent {
+ height: 300px;
+ width: 1250px;
+ }
+
+#editWidgetsContent .scroll-viewport {
+ height: 300px;
+ width: 1250px;
+ }
+
+ .add-contact-us-field{
+ width:250px;
+ display: inline-block;
+ margin:10px;
+ }
+ .add-contact-us-field-des{
+ width:800px;
+ margin:10px;
+ }
+
+.errMsg{
+ color:red;
+}
+.sucessMsg{
+ color:green;
+}
+.contact-us-margin{
+ margin-left: 80px;
+ margin-right: 120px;
+}
+.contact-us-manage-table {
+
+
+ .delete-contact-us{
+ font-size:20px;
+ }
+
+ .portal-add-button {
+ width: 160px;
+ }
+}
+
+.select2-container .select2-chosen, .select2-container .select2-container input{
+ line-height:39px;
+}
+.select2-container .select2-choice{
+ -webkit-transition: border-color .2s ease-out;
+ -moz-transition: border-color .2s ease-out;
+ transition: border-color .2s ease-out;
+ background: #fff;
+ border-radius: 6px;
+ display: block;
+ border: 1px solid #888;
+ box-shadow: none;
+ filter: none;
+ height: 32px !important;
+ padding: 0;
+ position: relative;
+ z-index: 5;
+ cursor: pointer;
+}
+
+.c-ecomp-att-abs-table .contactUs{
+
+ height:300px;
+
+}
+
+.ngdialog-overlay {
+ pointer-events: none !important;
+}
+
+.input-text-area {
+ font-style: italic;
+ padding: 7px 10px;
+ width: 250px;
+ display: inline-block;
+ position: relative;
+ margin-bottom: 10px;
+ border-radius: 6px;
+ border: 1px solid #d8d8d8;
+ height: 32px !important;
+ border-color: slategrey !important;
+}
+.input-inline-edit-text {
+ font-style: italic;
+ padding: 7px 10px;
+ display: inline-block;
+ position: relative;
+ margin-bottom: 10px;
+ border-radius: 6px;
+ border: 1px solid #d8d8d8;
+ height: 32px;
+ border-color: slategrey !important;
+ width:100%;
+ }
+.glyphicon {
+ position: relative !important;
+ line-height: 0 !important;
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html
new file mode 100644
index 00000000..257226a0
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us-manage/contact-us-manage.html
@@ -0,0 +1,136 @@
+<!--
+ ================================================================================
+ 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 style="margin-top: 10px; margin-bottom: 10px;">
+
+</div>
+<br/>
+
+<div style="margin-top: 25px;">
+ <div id="title" class="manage-contactUs-home-title">Manage Contact Us</div>
+ <div class="contact-us-margin">
+ <div class="get-access-table">
+ <div class="table-control">
+ <div class="c-ecomp-att-abs-table" style="height:300px"">
+ <table att-table id="table-main" table-data="contactUsList" current-page="ignoredCurrentPage">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-users-0" att-table-header key="ecomp_function" default-sort="a" >App Name</th>
+ <th id="th-users-1" att-table-header key="app_name" sortable="true" >Contact Name</th>
+ <th id="th-users-2" att-table-header key="app_name" sortable="true" >Contact Email</th>
+ <th id="th-users-3" att-table-header key="role_name" sortable="true">Contact URL</th>
+ <th id="th-users-4" att-table-header key="role_name" sortable="true" >Description</th>
+ <th id="th-users-5" att-table-header key="role_name" sortable="true" >Edit</th>
+ <th id="th-users-6" att-table-header key="role_name" sortable="true" >Delete</th>
+ </tr>
+ </thead>
+ <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data -->
+ <tbody att-table-row type="body" class="table-body" track-by="$index" row-repeat="rowData in contactUsList">
+ <tr id="tr-rowData" ng-click="">
+ <td att-table-body>
+ <div id="users-page-td-appName" ng-bind="rowData.appName"></div>
+ </td>
+ <td att-table-body>
+ <div id="users-page-td-name" ng-hide="rowData.showEdit" ng-bind="rowData.contactName"></div>
+ <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].contactName"/>
+ </td>
+ <td att-table-body>
+ <div id="users-page-td-email" ng-hide="rowData.showEdit" ng-bind="rowData.contactEmail"></div>
+ <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].contactEmail"/>
+ </td>
+ <td att-table-body>
+ <div id="users-page-td-url" ng-hide="rowData.showEdit" ng-bind="rowData.url"></div>
+ <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].url"/>
+ </td>
+ <td att-table-body>
+ <div id="users-page-td-descr" ng-hide="rowData.showEdit" ng-bind=" rowData.description"></div>
+ <input class="input-inline-edit-text" type="text" ng-show="rowData.showEdit" ng-model="contactUsList[$index].description"/>
+ </td>
+ <td att-table-body>
+ <div class="delete-contact-us" ng-hide="rowData.showEdit" ng-click="rowData.showEdit=true">
+ <span class="ion-edit"></span>
+ </div>
+ <span ng-show="rowData.showEdit" >
+ <a att-button btn-type="primary" ng-click="editContactUsFun(rowData); rowData.showEdit=false" class="button button--primary" size="small" >Save</a>
+ </span>
+ </td>
+ <td att-table-body>
+ <div class="delete-contact-us" ng-click="delContactUsFun(rowData)">
+ <span class="ion-trash-b"></span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div att-divider-lines></div>
+ <div style="margin-top: 15px; margin-left: -78px;">
+ <div id="addWidgetHeader" class="contact-us-margin">
+ <h1 style="font-size:18px;">Add Application Contact Information</h1>
+ <!-- <div class="errMsg">{{errMsg}}</div> -->
+ </div>
+ <div id="addWidget" class="contact-us-margin">
+ <div>
+ <div class="add-contact-us-field">
+ <div id="mots-property-label" class="property-label">App Name</div>
+ <div class="form-field" att-select="contactUsAllAppList" ng-model="newContactUs.app" placeholder="Select an App"></div>
+ </div>
+ <br>
+ <div class="add-contact-us-field" >
+ <div class="property-label">Contact Name</div>
+ <input class="input-text-area" type="text" ng-model="newContactUs.name"/>
+ </div>
+ <div class="add-contact-us-field" >
+ <div class="property-label">Contact Email</div>
+ <input class="input-text-area" type="text" ng-model="newContactUs.email"/>
+ </div>
+ <div class="add-contact-us-field" >
+ <div class="property-label">Contact URL</div>
+ <input class="input-text-area" type="text" ng-model="newContactUs.url"/>
+ </div>
+ <div class="add-contact-us-field-des">
+ <div class="property-label">Description</div>
+ <textarea style="margin-top: 0px; margin-bottom: 0px; height: 100px" ng-model="newContactUs.desc"></textarea>
+ </div>
+ </div>
+ <div style="height:50px;">
+ <a style="float:right; margin-top:20px" att-button btn-type="primary" class="button button--primary" size="small" ng-click="newContactUsFun()">Add New</a>
+ </div>
+
+ <div att-divider-lines></div>
+
+ <div style="height:50px;">
+ <a style="float:right; margin-right: -162px;" att-button btn-type="primary" class="button button--primary" size="small" ng-click="closeDialog()">Close</a>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+ </div>
+ </div>
+</div>
+
+ <script type="application/javascript">
+ $(document).ready(function(){
+ $(".ngdialog-content").css("width","85%")
+ });
+</script> \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js
new file mode 100644
index 00000000..63e38a48
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.js
@@ -0,0 +1,166 @@
+/*-
+ * ================================================================================
+ * 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 ContactUsCtrl {
+ constructor($log, contactUsService, applicationsService, $modal, ngDialog, $state) {
+
+ contactUsService.getContactUSPortalDetails().then(res => {
+ // $log.info('ContactUsCtrl:: contactUsService getContactUSPortalDetails res',res);
+ // $log.info('getting res',res);
+ var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null;
+ // $log.info('result',result);
+ // $log.info('done');
+ var source = JSON.parse(result);
+ // $log.info(source);
+ this.ush_TicketInfoUrl = source.ush_ticket_url;
+ this.portalInfo_Address = source.feedback_email_address;
+ this.feedback_Url = source.portal_info_url;
+ }).catch(err=> {
+ $log.error('ContactUsCtrl:error:: ', err);
+ }).finally(() => {
+ });
+
+ let init = () => {
+ // $log.info('ecomp app::contact-us-controller::initializing...');
+ this.appTable=[];
+ this.functionalTableData=[];
+ };
+ init();
+
+ let updateContactUsTable = () => {
+ contactUsService.getAppsAndContacts().then(res=> {
+ // $log.info('ContactUsCtrl:: contactUsService getAppsAndContacts res',res);
+ var tableData=[];
+ // $log.info('getting res',res);
+ var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null;
+ // $log.info('result',result);
+ // $log.info('done');
+ var source = result;
+ // $log.info(source);
+ // Drop Portal app, empty name entries
+ for(var i=0;i<source.length; i++) {
+ var dataArr = source[i];
+ if ( !dataArr.appName || dataArr.appId == 1) {
+ continue;
+ }
+ var dataTemp={
+ app_name: dataArr.appName,
+ contact_name: dataArr.contactName,
+ contact_email: dataArr.contactEmail,
+ desc: dataArr.description,
+ url_Info: dataArr.url,
+ app_Id: dataArr.appId,
+ }
+ tableData.push(dataTemp);
+ }
+ this.appTable=tableData;
+ }).catch(err=> {
+ $log.error('ContactUsCtrl.updateContactUsTable:error:: ', err);
+ })
+ };
+
+ contactUsService.getAppCategoryFunctions().then(res=> {
+ // $log.info('ContactUsCtrl:: contactUsService getAppCategoryFunctionsthen res',res);
+ var tablefunctionalData=[];
+ // $log.info('getting res',res);
+ var result = (typeof(res.response) != "undefined" && res.response!=null)?res.response:null;
+ // $log.info('result',result);
+ // $log.info('done');
+ var source = result;
+ // $log.info(source);
+ for(var i=0;i<source.length; i++) {
+ var datafunctionalArr = source[i];
+ var datafuntionalTemp={
+ category: datafunctionalArr.category,
+ app_Name: datafunctionalArr.application,
+ functions: datafunctionalArr.functions,
+ app_Id: datafunctionalArr.appId,
+ }
+ tablefunctionalData.push(datafuntionalTemp);
+ }
+ this.functionalTableData=tablefunctionalData;
+ }).catch(err=> {
+ $log.error('ContactUsCtrl:error:: ', err);
+ })
+
+ updateContactUsTable();
+ this.editContactUsModalPopup = () => {
+ // $log.debug('ContactUsCtrl::editContactUsModalPopup updating table data...');
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/support/contact-us/contact-us-manage/contact-us-manage.html',
+ controller: 'ContactUsManageController',
+ resolve: {
+ message: function message() {
+ var message = {
+ type: 'Contact',
+ };
+ return message;
+ }
+ }
+ }).closePromise.then(needUpdate => {
+ updateContactUsTable();
+ });
+ };
+
+ this.goToSection = (id) => {
+ var targetDiv = document.getElementById(id);
+ var offSetHeight = 0;
+ for(var i=0;i<this.appTable.length;i++){
+ if(this.appTable[i].app_Id==id)
+ break;
+ if(this.appTable[i].showFlag==true){
+ offSetHeight+=document.getElementById('collapse'+i).clientHeight;
+ }
+ }
+ this.appTable.forEach(d => d.showFlag = false);
+ // let index = this.appTable.findIndex(a => a.app_Id == id);
+ var index =-1;
+ for(var i=0; i<this.appTable.length;i++){
+ if(this.appTable[i].app_Id==id){
+ index = i;
+ break;
+ }
+ }
+ console.log(index);
+ if (index > -1) {
+ // setting the showFlag to true based on index comparing with the app_Id
+ this.appTable[index].showFlag = true;
+ $('#contentId').animate({
+ scrollTop: targetDiv.offsetTop-offSetHeight + 'px'
+ }, 'fast');
+ }
+
+ };
+
+ // Take the user to the application on the get access page.
+ this.goGetAccess = (appName) => {
+ // $log.debug('ContactUsCtrl::goGetAccess received name ' + appName);
+ applicationsService.goGetAccessAppName = appName;
+ $state.go('root.getAccess');
+ };
+
+ }
+ }
+ ContactUsCtrl.$inject = ['$log','contactUsService', 'applicationsService', '$modal', 'ngDialog', '$state'];
+ angular.module('ecompApp').controller('ContactUsCtrl', ContactUsCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.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/client/app/views/support/contact-us/contact-us.less b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less
new file mode 100644
index 00000000..b0c0af4f
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.less
@@ -0,0 +1,104 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-contactUs-home {
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ padding-left: @padding-left-side;
+
+ .contactUs-home-container {
+ .content_justify;
+ position: relative;
+ //padding-top: 15px;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: 32px;
+ width: 100%;
+
+ .contactUs-home-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+
+ .contactUs-general-div {
+ margin: auto;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ .content_justify;
+ }
+ }
+
+.collapsibleArrow {
+ width: 30px;
+ height: 30px;
+ margin-left: 10px;
+ }
+
+.contactUs-collapsible-panel {
+ margin: auto;
+ width: 1170px;
+
+ }
+
+ .contactUs-collapsible-table {
+ margin: auto;
+ width: 1170px;
+ }
+
+ .contactUs-txt {
+ font-weight: normal;
+ color: #666666;
+ font-size: 15px;
+ font-family: arial;
+ margin:10px;
+ }
+
+.contactUs-panel-header {
+ font-weight: normal;
+ color: #666666;
+ font-size: 18px;
+ font-family: sans-serif;
+ border-top: 1px solid #ccc;
+ outline : none;
+ height: 35px;
+ width: 1050px;
+ }
+
+ .contactUs-panel-header:hover{
+ color: #428bca;
+ background: #eae8e8;
+ cursor: pointer; cursor: hand;
+ }
+
+.contactUs-panel-labels {
+ font-weight: normal;
+ color: #666666;
+ font-size: 15px;
+ font-family: sans-serif;
+ padding-top: 5px;
+ padding-bottom: 5px;
+}
+
+}
diff --git a/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html
new file mode 100644
index 00000000..0b403ece
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/contact-us/contact-us.tpl.html
@@ -0,0 +1,125 @@
+<!--
+ ================================================================================
+ 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-contactUs-home">
+ <div class="contactUs-home-container">
+ <div class="admins-page-main" id="contentId">
+ <div id="title" class="contactUs-home-title">Contact Us
+ <span ng-if="isAdminPortalAdmin == true" class="ion-edit" style="float: right;" ng-click="contact.editContactUsModalPopup()"></span>
+ </div>
+
+ <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>
+
+ <br/>
+ <!-- Function Categories -->
+ <div id="appFunctionInfo" style="font-size: 20px;" class="contactUs-home-title"> Application Functions
+ <div class="contactUs-general-div">
+ <div style="width: 1050px; height:auto;" class="c-ecomp-att-abs-table default">
+ <table att-table id="table-main"
+ table-data="contact.functionalTableData"
+ current-page="ignoredCurrentPage">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-functionalItem-0" style="width:20%;cursor: default" att-table-header sortable="false">Category</th>
+ <th id="th-functionalItem-1" style="width:60%;cursor: default" att-table-header sortable="false">ECOMP Functions</th>
+ <th id="th-functionalItem-2" style="cursor: default" att-table-header sortable="false">ECOMP Application</th>
+ <!-- <th id="th-functionalItem-3" att-table-header >Contact</th> -->
+ </tr>
+ </thead>
+ <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data -->
+ <tbody att-table-row
+ type="body"
+ class="table-body"
+ track-by="$index"
+ row-repeat="rowData in contact.functionalTableData">
+ <tr id="tr-rowData" ng-click="">
+ <td att-table-body>
+ <div id="functional-item-td-category"
+ ng-show="$index == 0 || contact.functionalTableData[$index-1].category != rowData.category"
+ ng-bind="rowData.category">
+ </div>
+ </td>
+ <td att-table-body>
+ <div id="functional-item-td-functions" ng-bind="rowData.functions"></div>
+ </td>
+ <td att-table-body>
+ <a ng-click="contact.goToSection(rowData.app_Id, $index);">{{rowData.app_Name}}</a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <br/>
+ <!-- Display applicationInfo panels -->
+ <div id="allAppInfo">
+ <div style="font-size: 20px;" class="contactUs-home-title"> All Applications
+ </div>
+ <div ng-repeat="rowData in contact.appTable"
+ class="contactUs-collapsible-panel">
+ <div id="{{rowData.app_Id}}" style="height:60px; " class="contactUs-panel-header" ng-click="rowData.showFlag = !rowData.showFlag" >
+ <span ng-bind="rowData.app_name" style="position:relative; top:20px"></span>
+
+ <span style="float: right; margin-right: 30px; margin-top:20px;">
+ <span ng-hide="rowData.showFlag" class="ion-ios-arrow-down" alt="Expand app contact section"> </span>
+ <span ng-show="rowData.showFlag" class="ion-ios-arrow-up" alt="App contact section"> </span>
+ </span>
+ </div>
+
+ <div id="collapse{{$index}}" ng-show="rowData.showFlag"
+ class="contactUs-collapsible-panel">
+ <div class="contactUs-txt" ng-hide="rowData.contact_name || rowData.contact_email || rowData.url_Info || rowData.desc">
+ <span style="margin-bottom: 20px;"> No application information is available. Please use the links above to contact the OpenECOMP Portal team. </span>
+ </div>
+ <div class="contactUs-txt" ng-show="rowData.contact_name || rowData.contact_email || rowData.url_Info || rowData.desc">
+ <table style="border-spacing: 10px 5px; max-width:950px; margin:10px;">
+ <tr>
+ <td class="contactUs-panel-labels" style="width: 120px;">Contact:</td>
+ <td class="contactUs-panel-labels" style="width: 830px;">{{rowData.contact_name}}</td>
+ </tr>
+ <tr>
+ <td class="contactUs-panel-labels">Email:</td>
+ <td class="contactUs-panel-labels"><a ng-href="mailto:{{rowData.contact_email}}" target="_top">{{rowData.contact_email}}</a></td>
+ </tr>
+ <tr>
+ <td class="contactUs-panel-labels">Info URL:</td>
+ <td class="contactUs-panel-labels"><a ng-href="{{rowData.url_Info}}" target="_blank">{{rowData.url_Info}}</a></td>
+ </tr>
+ <tr>
+ <td class="contactUs-panel-labels">Description:</td>
+ <td class="contactUs-panel-labels">{{rowData.desc}}</td>
+ </tr>
+ <tr>
+ <td class="contactUs-panel-labels">Get access:</td>
+ <td class="contactUs-panel-labels"><a href="" ng-click="contact.goGetAccess(rowData.app_name);">Click for application and role information</a></td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ </div>
+ </div>
+
+ </div>
+</div>
+</div> \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js
new file mode 100644
index 00000000..b9b254ac
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.js
@@ -0,0 +1,70 @@
+/*-
+ * ================================================================================
+ * 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 GetAccessCtrl {
+ constructor($log, $stateParams, getAccessService,applicationsService, ngDialog) {
+ // $log.debug('GetAccessCtrl: appService param is: ' + applicationsService.goGetAccessAppName);
+
+ this.updateAppsList = () => {
+ getAccessService.getListOfApp().then(res=> {
+ var tableData=[];
+ // $log.info('GetAccessCtrl::updateAppsList: getting res');
+ var result = (typeof(res.data) != "undefined" && res.data!=null)?res.data:null;
+ // $log.info('GetAccessCtrl::updateAppsList: result',result);
+ // $log.info('GetAccessCtrl::updateAppsList: done');
+ var source = result;
+ // $log.info('GetAccessCtrl::updateAppsList source: ', source);
+ for(var i=0;i<source.length; i++){
+ var dataArr = source[i];
+ var dataTemp={
+ ecomp_function: dataArr.ecompFunction,
+ app_name:dataArr.appName,
+ role_name:dataArr.roleName
+ }
+ tableData.push(dataTemp);
+ }
+ this.appTable=tableData;
+
+ if( $stateParams.appName != null)
+ this.searchString = $stateParams.appName;
+ else
+ this.searchString = applicationsService.goGetAccessAppName;
+ // the parameter has been used; clear the value.
+ applicationsService.goGetAccessAppName = '';
+ }).catch(err=> {
+ $log.error('GetAccessCtrl:error:: ', err);
+ }).finally(() => {
+ this.isLoadingTable = false;
+ });
+ };
+ let init = () => {
+ // $log.info('GetAccessCtrl:: initializing...');
+ this.searchString = '';
+ this.getAccessTableHeaders = ['ECOMP Function', 'Application Name', 'Role Name'];
+ this.appTable=[];
+ this.updateAppsList();
+ };
+ init();
+ }
+ }
+ GetAccessCtrl.$inject = ['$log', '$stateParams', 'getAccessService', 'applicationsService', 'ngDialog'];
+ angular.module('ecompApp').controller('GetAccessCtrl', GetAccessCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js b/ecomp-portal-FE/client/app/views/support/get-access/get-access.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.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/client/app/views/support/get-access/get-access.less b/ecomp-portal-FE/client/app/views/support/get-access/get-access.less
new file mode 100644
index 00000000..20b406e9
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.less
@@ -0,0 +1,67 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-get-access-home {
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ padding-left: @padding-left-side;
+
+ .get-access-home-container {
+ .content_justify;
+ position: relative;
+ padding-right: 0;
+ padding-left: 0;
+ padding-bottom: 32px;
+ width: 100%;
+
+ .get-access-home-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+
+ .get-access-general-div {
+ margin: auto;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ .content_justify;
+ }
+ }
+
+ .get-access-table {
+ width: @table-width;
+ margin: @table-margin;
+
+ .table-control {
+ .table-dropdown-filter {
+ width: @table-dropdown-filter-width;
+ display: @table-dropdown-filter-display;
+ }
+ }
+
+ .table-body {
+ cursor: pointer;
+ }
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html b/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html
new file mode 100644
index 00000000..3ed92f14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/support/get-access/get-access.tpl.html
@@ -0,0 +1,88 @@
+<!--
+ ================================================================================
+ 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-get-access-home">
+ <div class="get-access-home-container">
+ <div class="admins-page-main" id="contentId">
+ <div id="title" class="get-access-home-title">Get Access</div>
+ <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>
+ <div class="get-access-table">
+ <div class="table-control">
+ <input id="input-table-search"
+ class="table-search"
+ type="text"
+ placeholder="Search"
+ ng-model="access.searchString"/>
+ <span class="ecomp-spinner" ng-show="access.isLoadingTable"></span>
+ <div style="height: auto;" class="c-ecomp-att-abs-table default" ng-hide="access.isLoadingTable">
+ <!-- expects data to arrive sorted by first column: ecomp_function -->
+ <table att-table id="table-main"
+ table-data="access.appTable"
+ search-string="access.searchString"
+ view-per-page="access.viewPerPageIgnored"
+ current-page="access.currentPageIgnored"
+ total-page="access.totalPageIgnored">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-access-0" att-table-header key="ecomp_function" sortable="false">{{access.getAccessTableHeaders[0]}}</th>
+ <th id="th-access-1" att-table-header key="app_name" sortable="false">{{access.getAccessTableHeaders[1]}}</th>
+ <th id="th-access-2" att-table-header key="role_name" sortable="false">{{access.getAccessTableHeaders[2]}}</th>
+ </tr>
+ </thead>
+ <!-- Use track-by="UNIQUE KEY HERE" or leave out if no unique keys in data -->
+ <tbody att-table-row type="body"
+ class="table-body"
+ track-by="$index"
+ row-repeat="rowData in access.appTable">
+ <tr id="tr-rowData" ng-click="">
+ <td att-table-body>
+ <div id="access-page-function"
+ ng-show="$index == 0 || access.appTable[$index-1].ecomp_function != rowData.ecomp_function"
+ ng-bind="rowData.ecomp_function">
+ </div>
+ </td>
+ <td att-table-body>
+ <div id="access-page-appName"
+ ng-show="$index == 0 || access.appTable[$index-1].app_name != rowData.app_name"
+ ng-bind="rowData.app_name"></div>
+ </td>
+ <td att-table-body>
+ <div id="access-page-roleName" ng-bind="rowData.role_name"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</div>
+<style>
+.tablesorter-default{
+ cursor: default;
+}
+.admins-page-main .admins-table .table-body {
+ cursor: default;
+}
+</style>
diff --git a/ecomp-portal-FE/client/app/views/tabs/tabframe.html b/ecomp-portal-FE/client/app/views/tabs/tabframe.html
new file mode 100644
index 00000000..bc428832
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/tabs/tabframe.html
@@ -0,0 +1,22 @@
+<!--
+ ================================================================================
+ 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.
+ ================================================================================
+ -->
+<iframe id= "tabframe-{{tab.title.split(' ').join('-')}}" scrolling='yes' frameBorder='0' width='100%' style='height: 90vh;'
+src='{{tab.content | trusted}}'
+></iframe>
diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js
new file mode 100644
index 00000000..0c970f0e
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.js
@@ -0,0 +1,231 @@
+/*-
+ * ================================================================================
+ * 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 () {
+ const HTTP_PROTOCOL_RGX = /https?:\/\//;
+ class TabsCtrl {
+ constructor(applicationsService, $log, $window, conf, userProfileService, $scope,$cookies,$rootScope,confirmBoxService) {
+
+ var counter = 1;
+ var tabLimit = 6;
+ this.conf = conf;
+ var cookieDomain = this.conf.cookieDomain;
+ $scope.tabs = [];
+ $scope.notificationShow=true;
+ $rootScope.showFooter = "";
+ console.log("****************** get cookie domain from config json file " + this.conf.cookieDomain);
+ $cookies.putObject('show_app_header', false,{domain: cookieDomain, path : '/'});
+
+
+ let noRefresh = function () {
+ window.onbeforeunload = function(e) {
+
+ var isQtoHref = false;
+ try{
+ isQtoHref = e.srcElement.activeElement.href.includes("mailto");
+ } catch(err) {
+
+ }
+
+ if ($scope.tabs.length > 1 && isQtoHref == false) {
+ return "Changes you made may not be saved. Are you sure you want to refresh?";
+ } else {
+ return null;
+ }
+ }
+ }
+ var addTab = function (title, content) {
+ if($scope.tabs.length==tabLimit){
+ confirmBoxService.showInformation('You have reached your maximum limit of tabs allowed.').then(isConfirmed => {});
+ } else {
+ if(title!=='Home' && content.indexOf('https') == -1){
+ console.log('App URL: '+content+'. The application URL you are trying to open is not HTTPS. We recommend to use secured HTTPS URL while on-boarding the application.');
+ }
+
+ $scope.tabs.push({ title: title, content: content });
+ counter++;
+ $scope.selectedIndex = $scope.tabs.length - 1;
+ if ($scope.tabs.length > 1) {
+ noRefresh();
+ }
+ $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'});
+ $cookies.putObject('visInVisCookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'});
+ }
+ };
+
+ var adjustTitle = function (title) {
+ var index = 15;
+ var nonEmptyCharPattern = /(\s|\w)/;
+ var adjustedTitle = title.substring(0,index);
+ var ext = title.charAt(index).replace(nonEmptyCharPattern,'...');
+ return adjustedTitle.concat(ext);
+
+
+ };
+
+ var removeTab = function (event, index) {
+ event.preventDefault();
+ event.stopPropagation();
+ $scope.tabs.splice(index, 1);
+ $cookies.putObject('cookieTabs', $scope.tabs,{domain: cookieDomain, path : '/'});
+ };
+
+ var selectTab = function (title) {
+ if(title=='Home') {
+ $rootScope.ContentModel.IsVisible=true;
+ $rootScope.showFooter = true;
+ $rootScope.tabBottom = 75;
+ }
+ else {
+ $rootScope.ContentModel.IsVisible=false;
+ $rootScope.showFooter = false;
+ $rootScope.tabBottom = 0;
+ }
+ };
+
+ $scope.addTab = addTab;
+ $scope.removeTab = removeTab;
+ $scope.selectTab = selectTab;
+ $scope.adjustTitle = adjustTitle;
+
+
+ $rootScope.ContentModel = {
+ IsVisible : false,
+ ViewUrl : null,
+ };
+
+
+ var sessionActive = applicationsService.ping()
+ .then(sessionActive => {
+ $log.debug('TabsCtrl::addTab: applicationsService.ping() = ' + JSON.stringify(sessionActive));
+
+ var cookieTabs = $cookies.getObject('cookieTabs');
+ if(cookieTabs!=null){
+ for(var t in cookieTabs){
+ console.log('TabsCtrl::addTab: cookieTabs title: '+cookieTabs[t].title);
+ if(cookieTabs[t].title!=null && cookieTabs[t].title==='Home'){
+ cookieTabs[t].content = "";
+ $rootScope.ContentModel.IsVisible=true;
+ }
+
+ addTab( cookieTabs[t].title, cookieTabs[t].content) ;
+ }
+ } else {
+ for (var i = 0; i < 1; i++) {
+ var content="";
+ var title="";
+ if(i==0){
+ title="Home";
+ $rootScope.ContentModel.IsVisible=true;
+ }
+ addTab(title, content);
+ }
+ }
+
+ $scope.selectedIndex = 0;
+ });
+
+ $scope.$watchCollection(function() { return $cookies.getObject('addTab'); }, function(newValue) {
+ var tabContent = $cookies.getObject('addTab');
+ if(tabContent!=null && tabContent.url!=null){
+ var tabExists = false;
+ for(var x in $scope.tabs){
+ console.log($scope.tabs[x].content);
+ if($scope.tabs[x].title==tabContent.title){
+ tabExists = true;
+ $scope.selectedIndex = x;
+ }
+ }
+ if(!tabExists){
+ addTab( tabContent.title, tabContent.url) ;
+ }
+ $cookies.remove('addTab');
+ }
+ });
+
+
+ }
+
+
+ }
+
+ TabsCtrl.$inject = ['applicationsService', '$log', '$window', 'conf','userProfileService', '$scope','$cookies','$rootScope','confirmBoxService'];
+ angular.module('ecompApp').controller('TabsCtrl', TabsCtrl);
+
+ angular.module('ecompApp').directive('mainArea', function() {
+ return {
+ restrict: "E",
+ templateUrl: 'app/views/tabs/tabframe.html',
+ link: function(scope, element) {
+
+ }
+ }
+ });
+
+
+
+ angular.module('ecompApp').directive('tabHighlight', [function () {
+ return {
+ restrict: 'A',
+ link: function (scope, element) {
+ var x, y, initial_background = '#c3d5e6';
+
+ element
+ .removeAttr('style')
+ .mousemove(function (e) {
+ if(!element.hasClass('md-active'))
+ {
+ x = e.pageX - this.offsetLeft;
+ y = e.pageY - this.offsetTop;
+
+ element
+ .css({ background: '-moz-radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background })
+ .css({ background: '-webkit-radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background })
+ .css({ background: 'radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(255,255,255,0.4) 0px, rgba(255,255,255,0.0) 45px), ' + initial_background });
+ }
+ })
+ .mouseout(function () {
+ element.removeAttr('style');
+ });
+ }
+ };
+ }]);
+
+
+
+})();
+
+function getParameterByName(name, url) {
+ if (!url) url = window.location.href;
+ name = name.replace(/[\[\]]/g, "\\$&");
+ var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
+ results = regex.exec(url);
+ if (!results) return '';
+ if (!results[2]) return '';
+ return (results[2].replace(/\+/g, " "));
+}
+
+function isCascadeFrame(ref) {
+ if (self != top) {
+ var e = document.body;
+ e.parentNode.removeChild(e);
+ window.location = "unKnownError";
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js
new file mode 100644
index 00000000..44aea239
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/tabs/tabs.controller.spec.js
@@ -0,0 +1,80 @@
+/*-
+ * ================================================================================
+ * 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';
+
+describe('Controller: TabsCtrl ',() => {
+ beforeEach(module('ecompApp'));
+
+ beforeEach(inject((_CacheFactory_)=>{
+ _CacheFactory_.destroyAll();
+ }));
+
+ let TabsCtrl, $controller, $q, $rootScope, $log, $window, $cookies,$scope;
+
+ beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_)=>{
+ [$controller, $q, $rootScope, $log, $window, $cookies] = [_$controller_, _$q_, _$rootScope_, _$log_, _$window_, _$cookies_];
+ }));
+
+ var deferredApps, deferredUserProfile;
+ beforeEach(()=>{
+ deferredApps = $q.defer();
+ deferredUserProfile = $q.defer();
+ let applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getUserApps']);
+ applicationsServiceMock.getUserApps.and.returnValue(deferredApps.promise);
+
+ let userProfileServiceMock = jasmine.createSpyObj('userProfileServiceMock',['getUserProfile']);
+ userProfileServiceMock.getUserProfile.and.returnValue(deferredUserProfile.promise);
+
+ $scope = $rootScope.$new();
+ TabsCtrl = $controller('TabsCtrl', {
+ applicationsService: applicationsServiceMock,
+ $log: $log,
+ $window: $window,
+ userProfileService: userProfileServiceMock,
+ $scope: $scope,
+ $cookies: $cookies,
+ $rootScope: $rootScope
+ });
+ });
+
+ it('should populate this.apps with data from portals service getUserApps', ()=>{
+ var profile = {roles:'superAdmin',userId:'userid'};
+ deferredUserProfile.resolve(profile);
+ deferredApps.resolve([{name: 'portal1'},{name: 'portal2'},{name: 'portal3'}]);
+ $rootScope.$apply();
+ expect($scope.appsViewData.length).toBe(3);
+ });
+
+ it('should call $log error when getAllPortals fail', ()=>{
+ spyOn($log, 'error');
+ deferredUserProfile.reject('something happened!');
+ $rootScope.$apply();
+ expect($log.error).toHaveBeenCalled();
+ });
+
+ it('should open link in a new window when clicking app thumbnail', () => {
+ spyOn($window, 'open');
+ let someUrl = 'http://some/url/';
+ TabsCtrl.goToPortal(someUrl);
+ expect($window.open).toHaveBeenCalledWith(someUrl, '_self');
+ });
+
+
+});
diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.less b/ecomp-portal-FE/client/app/views/tabs/tabs.less
new file mode 100644
index 00000000..0247e3d0
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/tabs/tabs.less
@@ -0,0 +1,658 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-tabs {
+ .bg_s;
+
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: @footer-height;
+ overflow-y: hidden;
+ top: @header-height;
+ padding-left: 0;
+
+ .go-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ border-radius: 0px;
+ }
+
+ .tabs-container {
+ .content_justify;
+ position: relative;
+ padding: 15px 0 32px 0;
+ width: 100%;
+
+ .tabs-title {
+ .a24r;
+ margin: auto;
+ .content_justify;
+ }
+ .portals-list {
+ min-height: 70vh;
+ justify-content: center;
+ flex-flow: row wrap;
+ width: 1170px;
+
+ margin: auto;
+ margin-bottom: 63px;
+
+ .app-gridster-header {
+ background-color: @u;
+ }
+
+ .app-gridster-footer {
+ background-color: @u;
+ }
+
+ .portals-list-item {
+ background-color: @u;
+ border-radius: 2px;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ display: inline-block;
+ width: 360px;
+ height: 300px;
+ background-size: cover;
+ cursor: pointer;
+ margin: 15px;
+ overflow: hidden;
+
+ .portals-item-info {
+ background-color: @u;
+ height: 120px;
+ top: 180px;
+ position: relative;
+ box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.1);
+ padding: 16px;
+
+ .info-title {
+ .a24r;
+ margin-bottom: 4px;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .info-description {
+ .c16r;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ }
+ .info-button {
+ .btn-green;
+ width: 96px;
+ position: absolute;
+ bottom: 16px;
+ left: 16px;
+ }
+
+ &:hover {
+ opacity: .93;
+ z-index: 3;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+.build-number {
+ .o12i;
+}
+
+@keyframes fadein {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+
+
+.tab-container {
+ background: #8dc8fb;
+ margin: 0;
+ padding: 0;
+ max-height: 40px;
+}
+.tab-container ul.nav-tabs {
+ margin: 0;
+ list-style-type: none;
+ line-height: 40px;
+ max-height: 40px;
+ overflow: hidden;
+ display: inline-block;
+ display: -webkit-flex;
+ display: flex;
+ padding-right: 20px;
+ border-bottom: 5px solid #f7f7f7;
+}
+.tab-container ul.nav-tabs > li {
+ margin: 5px -14px 0;
+ -moz-border-radius-topleft: 28px 145px;
+ -webkit-border-top-left-radius: 28px 145px;
+ border-top-left-radius: 28px 145px;
+ -moz-border-radius-topright: 28px 145px;
+ -webkit-border-top-right-radius: 28px 145px;
+ border-top-right-radius: 28px 145px;
+ padding: 0 30px 0 25px;
+ height: 170px;
+ background: #c3d5e6;
+ position: relative;
+ -moz-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
+ -webkit-box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
+ width: 200px;
+ max-width: 200px;
+ min-width: 20px;
+ border: 1px solid #aaa;
+}
+.tab-container ul.nav-tabs > li:first-child {
+ margin-left: 0;
+}
+.tab-container ul.nav-tabs > li:last-of-type {
+ margin-right: 0;
+}
+.tab-container ul.nav-tabs > li > a {
+ display: block;
+ max-width: 100%;
+ text-decoration: none;
+ color: #222;
+ padding: 3px 7px;
+}
+.tab-container ul.nav-tabs > li > a span {
+ overflow: hidden;
+ white-space: nowrap;
+ display: block;
+}
+.tab-container ul.nav-tabs > li > a:focus, .tab-container ul.nav-tabs > li > a:hover {
+ background-color: transparent;
+ border-color: transparent;
+}
+.tab-container ul.nav-tabs > li > a .glyphicon-remove {
+ color: #777;
+ display: inline-block;
+ padding: 3px;
+ font-size: 10px;
+ position: absolute;
+ z-index: 10;
+ top: 7px;
+ right: -10px;
+ -moz-border-radius: 50%;
+ -webkit-border-radius: 50%;
+ border-radius: 50%;
+}
+.tab-container ul.nav-tabs > li > a .glyphicon-remove:hover {
+ background: #d39ea3;
+ color: white;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25);
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.25);
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
+}
+.tab-container ul.nav-tabs > li.active {
+ z-index: 4;
+ background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Y3ZjdmNyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
+ background-size: 100%;
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 30, color-stop(0%, #ffffff), color-stop(100%, #f7f7f7));
+ background-image: -moz-linear-gradient(#ffffff, #f7f7f7 30px);
+ background-image: -webkit-linear-gradient(#ffffff, #f7f7f7 30px);
+ background-image: linear-gradient(#ffffff, #f7f7f7 30px);
+}
+.tab-container ul.nav-tabs > li.active > a {
+ background-color: transparent;
+ border-color: transparent;
+ border-bottom-color: transparent;
+}
+.tab-container ul.nav-tabs > li.active > a:focus, .tab-container ul.nav-tabs > li.active > a:hover {
+ background-color: transparent;
+ border-color: transparent;
+ border-bottom-color: transparent;
+}
+.tab-container ul.nav-tabs .btn {
+ float: left;
+ height: 20px;
+ width: 35px;
+ min-width: 35px;
+ max-width: 35px;
+ margin: 10px 0 0 0;
+ border-color: #71a0c9;
+ outline: none;
+ -moz-transform: skew(30deg);
+ -ms-transform: skew(30deg);
+ -webkit-transform: skew(30deg);
+ transform: skew(30deg);
+}
+.tab-container ul.nav-tabs .btn.btn-default {
+ background: #c3d5e6;
+}
+.tab-container ul.nav-tabs .btn.btn-default:hover {
+ background: #d2deeb;
+}
+.tab-container ul.nav-tabs .btn.btn-default:active {
+ background: #9cb5cc;
+}
+.tab-container .tab-pane {
+ padding: 0px 0px;
+ text-align: center;
+}
+.tab-container .tab-pane.active {
+ border-top: 1px solid #ddd;
+}
+
+.tab-container md-content {
+ background-color: transparent !important; }
+ .tab-container md-content md-tabs {
+ border: 1px solid #e1e1e1; }
+ .tab-container md-content md-tabs md-tab-content {
+ background: #f6f6f6; }
+ .tab-container md-content md-tabs md-tabs-canvas {
+ background: white; }
+ .tab-container md-content h1:first-child {
+ margin-top: 0; }
+.tab-container md-input-container {
+ padding-bottom: 0; }
+.tab-container .remove-tab {
+ margin-bottom: 40px; }
+.tab-container .demo-tab > div > div {
+ padding: 25px;
+ box-sizing: border-box; }
+.tab-container .edit-form input {
+ width: 100%; }
+.tab-container md-tabs {
+ border-bottom: 1px solid rgba(0, 0, 0, 0.12); }
+.tab-container md-tab[disabled] {
+ opacity: 0.5; }
+.tab-container label {
+ text-align: left; }
+.tab-container .long > input {
+ width: 264px; }
+.tab-container .md-button.add-tab {
+ transform: translateY(5px); }
+
+.md-tab{
+ font-size: 13px;
+ line-height: 30px;
+ margin: 5px -3px 0;
+ border-top-left-radius: 88px 205px;
+ border-top-right-radius: 88px 205px;
+ padding: 0 30px 0 25px;
+ height: 40px;
+ background: #c3d5e6;
+ position: relative;
+ box-shadow: 0 4px 6px rgba(0,0,0,.5);
+ width: 180px;
+ max-width: 200px;
+ min-width: 20px;
+ border: 1px solid #aaa;
+ text-transform: capitalize;
+ text-align: left;
+}
+
+md-tabs .md-tab {
+ color: #222;
+}
+
+md-tabs-canvas {
+ border-bottom: 5px solid #f7f7f7;
+ height: 40px;
+}
+.md-tab.md-active {
+ z-index: 4;
+ background-color: #f5f5f5 !important;
+}
+.md-tab:first-child{
+ margin-left: 10px;
+}
+md-ink-bar{
+ z-index: 5 !important;
+}
+
+.glyphicon {
+ position: fixed;
+ line-height: 4;
+}
+.close_button {
+ font-size: x-small;width: 10px;margin-left: 130px;
+}
+@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
+ .close_button {
+ font-size: x-small;width: 10px;margin-left: 130px;margin-top: 55px;
+ }
+}
+
+*,
+*:after,
+*:before
+{
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+
+.button-default
+{
+ .transition(@transitionDefault color);
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ margin: 0;
+ outline: none;
+ position: relative;
+}
+
+.show-notifications
+{
+ position: relative;
+
+ &:hover,
+ &:focus,
+ &.active
+ {
+ #icon-bell
+ {
+ fill: @colorWetAsphalt;
+ }
+ }
+
+ #icon-bell
+ {
+ fill: @colorAsbestos;
+ }
+
+
+
+ &.active ~ .notifications
+ {
+ opacity: 1;
+ top: 35px;
+ }
+}
+.notifications_li{
+ border-top: 1px solid #bdc3c7;
+ color: #7f8c8d;
+ cursor: default;
+ display: block;
+ padding: 10px;
+ position: relative;
+ white-space: nowrap;
+ width: 350px;
+}
+.notifications_li:hover{
+ background:#eee;
+}
+.notifications_detail{
+ margin-left: 10px;
+ white-space: normal;
+ width: 280px;
+ display: inline-block;
+ vertical-align: middle;
+}
+
+.notifications_empty{
+ display: none;
+ text-align: center;
+}
+.notifications_title{
+ display: block;
+}
+
+.notifications_date{
+ color: #95a5a6;
+ font-size: .85em;
+ margin-top: 3px;
+}
+.notifications
+{
+ .border-radius(@borderRadius);
+ .transition(@transitionDefault opacity);
+ background: @colorClouds;
+ border: 1px solid @colorSilver;
+ left: 10px;
+ opacity: 0;
+
+ &:after
+ {
+ border: 10px solid transparent;
+ border-bottom-color: @colorPeterRiver;
+ content: '';
+ display: block;
+ height: 0;
+ left: 10px;
+ position: absolute;
+ top: -20px;
+ width: 0;
+ }
+
+ h3,
+ .show-all
+ {
+ background: @colorPeterRiver;
+ color: @colorWhite;
+ margin: 0;
+ padding: 10px;
+ width: 350px;
+ }
+
+ h3
+ {
+ cursor: default;
+ font-size: 1.05em;
+ font-weight: normal;
+ }
+
+ .show-all
+ {
+ display: block;
+ text-align: center;
+ text-decoration: none;
+
+ &:hover,
+ &:focus
+ {
+ text-decoration: underline;
+ }
+ }
+
+ .notifications-list
+ {
+ list-style: none;
+ margin: 0;
+ overflow: hidden;
+ padding: 0;
+
+ .item
+ {
+ .transition-transform(@transitionDefault);
+ border-top: 1px solid @colorSilver;
+ color: @colorAsbestos;
+ cursor: default;
+ display: block;
+ padding: 10px;
+ position: relative;
+ white-space: nowrap;
+ width: 350px;
+
+ &:before,
+ .details,
+ .button-dismiss
+ {
+ display: inline-block;
+ vertical-align: middle;
+ }
+
+ &:before
+ {
+ .border-radius(50%);
+ background: @colorPeterRiver;
+ content: '';
+ height: 8px;
+ width: 8px;
+ }
+
+ .details
+ {
+ margin-left: 10px;
+ white-space: normal;
+ width: 280px;
+
+ .title,
+ .date
+ {
+ display: block;
+ }
+
+ .date
+ {
+ color: @colorConcrete;
+ font-size: .85em;
+ margin-top: 3px;
+ }
+ }
+
+ .button-dismiss
+ {
+ color: @colorSilver;
+ font-size: 2.25em;
+
+ &:hover,
+ &:focus
+ {
+ color: @colorConcrete;
+ }
+ }
+
+ &.no-data
+ {
+ display: none;
+ text-align: center;
+
+ &:before
+ {
+ display: none;
+ }
+ }
+
+ &.expired
+ {
+ color: @colorSilver;
+
+ &:before
+ {
+ background: @colorSilver;
+ }
+
+ .details
+ {
+ .date
+ {
+ color: @colorSilver;
+ }
+ }
+ }
+
+ &.dismissed
+ {
+ .transform(translateX(100%));
+ }
+ }
+ }
+
+ &.empty
+ {
+ .notifications-list
+ {
+ .no-data
+ {
+ display: block;
+ padding: 10px;
+ }
+ }
+ }
+}
+
+/* variables */
+@colorClouds: #ecf0f1;
+@colorSilver: #bdc3c7;
+@colorWhite: #fefefe;
+@colorPeterRiver: #3498db;
+@colorConcrete: #95a5a6;
+@colorAsbestos: #7f8c8d;
+@colorWetAsphalt: #34495e;
+
+@borderRadius: 2px;
+
+@transitionDefault: 0.25s ease-out 0.10s;
+
+/* mixins */
+.background-clip(@value: border-box)
+{
+ -moz-background-clip: @value;
+ -webkit-background-clip: @value;
+ background-clip: @value;
+}
+
+.border-radius(@value: 5px)
+{
+ -moz-border-radius: @value;
+ -webkit-border-radius: @value;
+ border-radius: @value;
+ .background-clip(padding-box);
+}
+
+.transform(@value)
+{
+ -webkit-transform: @value;
+ -moz-transform: @value;
+ -ms-transform: @value;
+ -o-transform: @value;
+ transform: @value;
+}
+
+.transition(@value: all 0.25s ease-out)
+{
+ -webkit-transition: @value;
+ -moz-transition: @value;
+ -o-transition: @value;
+ transition: @value;
+}
+
+.transition-transform(@transition: 0.25s ease-out)
+{
+ -webkit-transition: -webkit-transform @transition;
+ -moz-transition: -moz-transform @transition;
+ -o-transition: -o-transform @transition;
+ transition: transform @transition;
+}
+
+.tab-cross-sign{
+ position:relative;
+ margin-left:130px;
+ font-size:9px;
+ top:-30px;
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+ top:-70px;
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html b/ecomp-portal-FE/client/app/views/tabs/tabs.tpl.html
new file mode 100644
index 00000000..62af73e9
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/tabs/tabs.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="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)">
+ <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">
+ <i class="ion-close-round" ng-click="removeTab($event, $index)" ng-show="'{{tab.title}}'!='Home'"></i>
+ </span>
+ </md-tab-label>
+ <md-tab-body>
+ <main-area id="tab-home" ng-show="'{{tab.title}}'!='Home'"></main-area>
+ </md-tab-body>
+ </md-tab>
+ </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/client/app/views/userbar/userbar.controller.js b/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js
new file mode 100644
index 00000000..5dae2f86
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/userbar/userbar.controller.js
@@ -0,0 +1,262 @@
+/*-
+ * ================================================================================
+ * 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;
+ 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 callAtInterval() {
+ userProfileService.getActiveUser()
+ .then(res=> {
+ $log.info('sasasasasa getting userinfo from shared context.. ');
+ if(res==null){
+ $log.info('failed getting userinfo from shared context.. ');
+ $log.info('getting userinfo from session ');
+ }else{
+ $log.info('getting ActiveUser successfully',res);
+ $scope.userList = [];
+ for(var i=0;i<res.length;i++){
+ var data= {
+ userId:res[i][0],
+ linkQ:userProfileService.getSortedUserIdCombination(userProfileService.userProfile.userId , res[i][0])
+ }
+ $scope.userList.push(data);
+ }
+ $log.info(' ActiveUser =',$scope.userList);
+ }
+ }).catch(err=> {
+ $log.error('Header Controller:: getActiveUser() failed: ' + err);
+ }).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
+ });
+ });
+ }
+
+
+ var intervalPromise;
+ 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!');
+ } 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('Header Controller:: getOnlineUserUpdateRate() failed: ' + err);
+ });
+
+ $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() {
+ $interval.cancel(intervalPromise);
+ };
+
+ $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
+ userProfileService.getUserProfile().then(res=> {
+ if (res == null || res.orgUserId == null) {
+ $log.error('UserbarCtlr: failed to get profile of the user!');
+ }
+ else {
+ updateActiveUsers();
+ $scope.start($scope.updateRate);
+ socketSetup(null, res.orgUserId, null, "socketSend");
+ }
+ });
+
+
+ }
+ });
+ function updateActiveUsers() {
+ userProfileService.getActiveUser()
+ .then(res=> {
+ $log.info('getting Active User init***********************');
+ // decrement the counter every time updateActiveUser is called;
+ userbarUpdateService.decrementRefreshCount();
+
+ if(res==null ||res.status!='OK'){
+ $log.error('failed updateActiveUsers res is null.. ');
+
+ }else{
+ $log.info('getting ActiveUser successfully',res);
+ $scope.userList = [];
+ var maxItems = 10;
+ var users = res.response;
+ if (users.length < maxItems)
+ maxItems = users.length;
+ for(var i=0;i<maxItems;i++){
+ var data= {
+ userId:users[i][0],
+ linkQ:userProfileService.getSortedUserIdCombination(userProfileService.userProfile.orgUserId , users[i][0])
+ }
+ $scope.userList.push(data);
+ }
+
+ $log.info(' ActiveUser =',$scope.userList);
+ }
+ }).catch(err=> {
+ $log.error('Header Controller:: getActiveUser() failed: ' + err);
+ }).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
+ });
+ });
+ }
+ }
+ }
+ UserbarCtrl.$inject = ['userProfileService', 'userbarUpdateService', '$log', '$rootScope','$interval','$scope','$timeout','dashboardService'];
+ angular.module('ecompApp').controller('UserbarCtrl', UserbarCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.less b/ecomp-portal-FE/client/app/views/userbar/userbar.less
new file mode 100644
index 00000000..489185f7
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/userbar/userbar.less
@@ -0,0 +1,41 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .w-ecomp-sidebar {
+
+ position: relative;
+ left: 0;
+ right: 0;
+ top: 5px;
+ padding-left: 0;
+
+ }
+
+ @media screen and (-webkit-min-device-pixel-ratio:0)
+{
+ .w-ecomp-sidebar {
+
+ position: relative;
+ left: 0;
+ right: 0;
+ top: -5px;
+ padding-left: 0;
+
+ }
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html b/ecomp-portal-FE/client/app/views/userbar/userbar.tpl.html
new file mode 100644
index 00000000..dab62910
--- /dev/null
+++ b/ecomp-portal-FE/client/app/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>
diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js
new file mode 100644
index 00000000..182ffe8f
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.js
@@ -0,0 +1,211 @@
+/*-
+ * ================================================================================
+ * 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 NewUserModalCtrl {
+ constructor($scope, $log, usersService, applicationsService, confirmBoxService) {
+ let init = () => {
+ $log.info('NewUserModalCtrl::init');
+ this.isSaving = false;
+ this.anyChanges = false;
+ 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 === 3){
+ this.getUserAppsRoles();
+ }
+ }else{
+ this.isShowBack = true;
+ this.selectedUser = null;
+ this.dialogState = 1;
+ }
+ };
+
+ 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){
+ app.isChanged = true;
+ this.anyChanges = true;
+ app.isDeleted = true;
+ app.appRoles.forEach(function(role){
+ role.isApplied = false;
+ });
+ }
+ }).catch(err => {
+ $log.error('NewUserModalCtrl::deleteApp error: ',err);
+ });
+ };
+
+ this.getUserAppsRoles = () => {
+ if (!this.selectedUser || !this.selectedUser.orgUserId) {
+ $log.error('NewUserModalCtrl::getUserAppsRoles error: No user is selected');
+ this.dialogState = 1;
+ return;
+ }
+ $log.debug('NewUserModalCtrl::getUserAppsRoles: about to call getAdminAppsSimpler');
+ this.isGettingAdminApps = true;
+ applicationsService.getAdminAppsSimpler().then((apps) => {
+ $log.debug('NewUserModalCtrl::getUserAppsRoles: beginning of then for getAdminAppsSimpler');
+ this.isGettingAdminApps = false;
+ if (!apps || !apps.length) {
+ $log.error('NewUserModalCtrl::getUserAppsRoles error: no apps found');
+ return null;
+ }
+ $log.debug('NewUserModalCtrl::getUserAppsRoles: then for getAdminAppsSimpler: step 2');
+ $log.debug('NewUserModalCtrl::getUserAppsRoles: admin apps: ', apps);
+ this.adminApps = apps;
+ this.dialogState = 3;
+ this.userAppRoles = {};
+ this.numberAppsProcessed = 0;
+ this.isLoading = true;
+ apps.forEach(app => {
+ $log.debug('NewUserModalCtrl::getUserAppsRoles: app: id: ', app.id, 'name: ',app.name);
+ app.isChanged = false;
+ 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.orgUserId).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);
+ })
+
+ }
+
+ this.getAdminApps = () => {
+ if (!this.selectedUser || !this.selectedUserorgUserId) {
+ $log.error('NewUserModalCtrl::getAdminApps: No user is selected');
+ this.dialogState = 1;
+ return;
+ }
+ applicationsService.getAdminApps().promise().then(apps => {
+ if (!apps || !apps.length) {
+ $log.error('NewUserModalCtrl::getAdminApps: no apps found');
+ return null;
+ }
+ $log.debug('NewUserModalCtrl::getAdminApps: admin apps: ', apps);
+ this.adminApps = apps;
+ this.dialogState = 3;
+ return;
+ }).catch(err => {
+ $log.error('NewUserModalCtrl::getAdminApps: ', err);
+ })
+
+ }
+
+ this.updateUserAppsRoles = () => {
+ $log.debug('NewUserModalCtrl::updateUserAppsRoles: entering updateUserAppsRoles');
+ if(!this.selectedUser || !this.selectedUser.orgUserId || !this.adminApps){
+ $log.debug('NewUserModalCtrl::updateUserAppsRoles: returning early');
+ return;
+ }
+ this.isSaving = true;
+ $log.debug('NewUserModalCtrl::updateUserAppsRoles: going to update user: ' + this.selectedUser.orgUserId);
+ 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;
+ usersService.updateUserAppRoles({orgUserId: this.selectedUser.orgUserId, appId: app.id, appRoles: app.appRoles})
+ .then(res => {
+ $log.debug('NewUserModalCtrl::updateUserAppsRoles: User app roles updated successfully on app: ',app.id);
+ app.isUpdating = false;
+ app.isDoneUpdating = true;
+ this.numberAppsSucceeded++;
+ }).catch(err => {
+ $log.error(err);
+ app.isErrorUpdating = true;
+ }).finally(()=>{
+ this.numberAppsProcessed++;
+ if (this.numberAppsProcessed == this.adminApps.length) {
+ this.isSaving = false;
+ }
+ if (this.numberAppsSucceeded == this.adminApps.length) {
+ $scope.closeThisDialog(true);
+ }
+ })
+ } else {
+ $log.debug('NewUserModalCtrl::updateUserAppsRoles: app roles have NOT changed; NOT going to update: id: ', app.id, '; name: ', app.name);
+ app.noChanges = true;
+ app.isError = false;
+ this.numberAppsProcessed++;
+ this.numberAppsSucceeded++;
+ if (this.numberAppsProcessed == this.adminApps.length) {
+ this.isSaving = false;
+ }
+ if (this.numberAppsSucceeded == this.adminApps.length) {
+ $scope.closeThisDialog(true);
+ }
+ }
+ });
+ };
+
+ this.navigateBack = () => {
+ if (this.dialogState === 1) {
+ }
+ if (this.dialogState === 3) {
+ this.dialogState = 1;
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+ }
+ }
+ NewUserModalCtrl.$inject = ['$scope', '$log', 'usersService', 'applicationsService', 'confirmBoxService'];
+ angular.module('ecompApp').controller('NewUserModalCtrl', NewUserModalCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js
new file mode 100644
index 00000000..54c564b7
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.controller.spec.js
@@ -0,0 +1,222 @@
+/*-
+ * ================================================================================
+ * 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';
+
+describe('Controller: NewUserModalCtrl ', () => {
+ beforeEach(module('testUtils'));
+ beforeEach(module('ecompApp'));
+
+ let promisesTestUtils;
+ beforeEach(inject((_CacheFactory_, _promisesTestUtils_)=> {
+ _CacheFactory_.destroyAll();
+ promisesTestUtils = _promisesTestUtils_;
+ }));
+
+ let newUser, $controller, $q, $rootScope, $log, $scope;
+
+ let applicationsServiceMock, usersServiceMock, confirmBoxServiceMock;
+ let deferredAdminApps, deferredUsersAccounts, deferredUsersAppRoles, deferredUsersAppRoleUpdate;
+
+ beforeEach(inject((_$controller_, _$q_, _$rootScope_, _$log_)=> {
+ $rootScope = _$rootScope_;
+ $q = _$q_;
+ $controller = _$controller_;
+ $log = _$log_;
+ }));
+
+ beforeEach(()=> {
+ [deferredAdminApps, deferredUsersAccounts, deferredUsersAppRoles, deferredUsersAppRoleUpdate] = [$q.defer(),$q.defer(), $q.defer(), $q.defer()];
+
+ /*applicationsServiceMock = {
+ getAdminApps: () => {
+ var promise = () => {return deferredAdminApps.promise};
+ var cancel = jasmine.createSpy();
+ return {
+ promise: promise,
+ cancel: cancel
+ }
+ }
+ };*/
+
+ confirmBoxServiceMock = {
+ deleteItem: () => {
+ var promise = () => {return deferredAdminApps.promise};
+ var cancel = jasmine.createSpy();
+ return {
+ promise: promise,
+ cancel: cancel
+ }
+ }
+ };
+
+ applicationsServiceMock = jasmine.createSpyObj('applicationsServiceMock', ['getAdminAppsSimpler']);
+ applicationsServiceMock.getAdminAppsSimpler.and.returnValue(deferredAdminApps.promise);
+
+ usersServiceMock = jasmine.createSpyObj('usersServiceMock', ['getAccountUsers','getUserAppRoles','updateUserAppsRoles']);
+
+ usersServiceMock.getAccountUsers.and.returnValue(deferredUsersAccounts.promise);
+ usersServiceMock.getUserAppRoles.and.returnValue(deferredUsersAppRoles.promise);
+ usersServiceMock.updateUserAppsRoles.and.returnValue(deferredUsersAppRoleUpdate.promise);
+
+ $scope = $rootScope.$new();
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+ });
+
+ it('should open modal window without user when no user is selected', ()=> {
+ expect(newUser.selectedUser).toBe(null);
+ });
+
+ it('should open modal window with selectedUser apps roles when user is selected', ()=> {
+ let roles = {apps: [{id: 1, appRoles: [{id: 3, isApplied: true}]}]};
+ let someUser = {userId: 'asdfjl'};
+
+ deferredUsersAppRoles.resolve(roles);
+ deferredAdminApps.resolve(roles.apps);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ newUser.getUserAppsRoles();
+ $scope.$apply();
+
+ expect(newUser.selectedUser).toBe(someUser);
+ expect(newUser.adminApps).toEqual(roles.apps);
+ });
+
+ it('should push to apps order list only apps that has applied roles when initializing', () => {
+ let roles = {apps: [{appId: 13, appRoles: [{id: 3, isApplied: true}]},{appId: 20, appRoles: [{id: 3, isApplied: false}]}]};
+ let someUser = {userId: 'asdfjl'};
+
+ deferredUsersAppRoles.resolve(roles);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ $scope.$apply();
+
+ });
+
+ it('should push app to apps order list when applying at least one role to user from app', () => {
+ let roles = {apps: [{appId: 13, appRoles: [{id: 3, isApplied: true}]},{appId: 20, appRoles: [{id: 3, isApplied: false}]}]};
+ let someUser = {userId: 'asdfjl'};
+
+ deferredUsersAppRoles.resolve(roles);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ $scope.$apply();
+
+ });
+
+
+ it('should remove app from list when removing all user roles in it', () => {
+ let roles = {apps: [{appName: 'aaa', appId: 13, appRoles: [{id: 3, isApplied: true}]},{appName: 'vvv', appId: 20, appRoles: [{id: 3, isApplied: true}]}]};
+ let someUser = {userId: 'asdfjl'};
+
+ promisesTestUtils.resolvePromise(confirmBoxServiceMock, 'deleteItem', true);
+
+ deferredUsersAppRoles.resolve(roles);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+
+ $scope.$apply();
+ newUser.deleteApp(roles.apps[0]);
+ $scope.$apply();
+
+ });
+
+ it('should close the modal when update changes succeeded', () => {
+ let roles = {apps: [{appName: 'aaa', appId: 13, appRoles: [{id: 3, isApplied: true}]},{appName: 'vvv', appId: 20, appRoles: [{id: 3, isApplied: true}]}]};
+ let someUser = {userId: 'asdfjl'};
+ deferredUsersAppRoles.resolve(roles);
+ deferredUsersAppRoleUpdate.resolve();
+ deferredAdminApps.resolve(roles.apps);
+
+ $scope.ngDialogData = {
+ selectedUser: someUser,
+ dialogState: 2
+ };
+
+ newUser = $controller('NewUserModalCtrl', {
+ $scope: $scope,
+ $log: $log,
+ usersService: usersServiceMock,
+ applicationsService: applicationsServiceMock,
+ confirmBoxService: confirmBoxServiceMock
+ });
+ $scope.closeThisDialog = function(){};
+ spyOn($scope, 'closeThisDialog');
+
+ newUser.getUserAppsRoles();
+ $scope.$apply();
+ newUser.updateUserAppsRoles();
+ $scope.$apply();
+ expect($scope.closeThisDialog).toHaveBeenCalledWith(true);
+ });
+ });
diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html
new file mode 100644
index 00000000..e50c9d4a
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.html
@@ -0,0 +1,70 @@
+<!--
+ ================================================================================
+ 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-user-modal">
+ <div class="search-users" ng-show="newUser.dialogState===1">
+ <search-users search-title="New User"
+ selected-user="newUser.selectedUser"></search-users>
+ <div class="dialog-control">
+ <div id="search-user-next-button" class="next-button" ng-click="newUser.selectedUser && newUser.getUserAppsRoles()"
+ ng-class="{disabled: !newUser.selectedUser}">Next
+ </div>
+ <div id="search-user-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+ </div>
+ <div class="user-apps-roles" ng-show="newUser.dialogState===3">
+ <div class="title"
+ ng-bind="newUser.selectedUser.firstName + ' ' + newUser.selectedUser.lastName + ' (' + newUser.selectedUser.orgUserId + ')'"></div>
+ <div class="app-roles-main">
+ <div class="app-roles-main-title">
+ <span class="left">Access and roles:</span>
+ </div>
+ <div class="app-roles-list">
+ <div class="app-item" ng-repeat="app in (newUser.adminApps) track by app.id" ng-show="!app.isDeleted">
+ <div class="app-item-left" id="div-app-name-{{app.name.split(' ').join('-')}}">{{app.name | elipsis: 27}}</div>
+ <div class="app-item-right" ng-show="!app.isError && !app.isLoading && !app.noChanges && !app.isUpdating && !app.isDoneUpdating && !app.isErrorUpdating">
+ <multiple-select id="app-roles"
+ unique-data="{{$index}}"
+ placeholder="Select roles"
+ ng-model="app.appRoles"
+ on-change="newUser.appChanged($index)"
+ name-attr="roleName"
+ value-attr="isApplied"></multiple-select>
+ </div>
+ <div id="app-item-no-contact" class="app-item-right-error" ng-show="app.isError">{{app.errorMessage}}</div>
+ <div id="app-item-contacting" class="app-item-right-contacting" ng-show="app.isLoading">Contacting application...</div>
+ <div id="app-item-no-changes" class="app-item-right-contacting" ng-show="app.noChanges">No changes</div>
+ <div id="app-item-no-updating" class="app-item-right-contacting" ng-show="app.isUpdating">Updating application...</div>
+ <div id="app-item-done-updating" class="app-item-right-contacting" ng-show="app.isDoneUpdating">Finished updating application</div>
+ <div id="app-item-cannot-update" class="app-item-right-error" ng-show="app.isErrorUpdating">Could not update application...</div>
+ <i id="app-item-delete" class="ion-trash-b" ng-click="newUser.deleteApp(app)" ng-show="!app.isLoading && !app.isError"></i>
+ <div id='ecomp-small-spinner' class="ecomp-small-spinner" ng-show="app.isLoading"></div>
+ </div>
+ </div>
+ <div class="dialog-control">
+ <span id="ecomp-save-spinner" class="ecomp-save-spinner" ng-show="newUser.isSaving || newUser.isGettingAdminApps"></span>
+ <div id="new-user-back-button" ng-show="newUser.isShowBack" class="back-button" ng-click="newUser.navigateBack()">Back</div>
+ <div id="new-user-next-button" class="next-button" ng-click="newUser.updateUserAppsRoles()"
+ ng-class="{disabled: !newUser.anyChanges}">Save
+ </div>
+ <div id="new-user-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less
new file mode 100644
index 00000000..9f86b022
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/new-user-dialogs/new-user.modal.less
@@ -0,0 +1,126 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .new-user-modal {
+ display:block;
+ overflow:auto;
+ min-height: 450px;
+
+ .user-apps-roles{
+ .title{
+ .n18r;
+ border-bottom: @a 3px solid;
+ }
+
+ .app-roles-list{
+ height: 286px;
+ overflow-y: auto;
+
+ .app-item{
+ border: 1px solid #d8d8d8;
+ border-radius: 2px;
+ background-color: #f8f8f8;
+
+ padding: 10px;
+ margin-top: 8px;
+
+ .app-item-left{
+ padding-top: 0;
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid #d8d8d8;
+ margin-right: 10px;
+ padding-left: 4px;
+ background: #fff;
+ white-space: nowrap;
+
+ }
+ .app-item-right{
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid #d8d8d8;
+ background: #fff;
+ vertical-align: middle;
+ }
+
+ .app-item-right-error{
+ .k;
+ padding: 7px 7px 7px 7px;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid #d8d8d8;
+ background: #fff;
+ vertical-align: middle;
+ }
+
+ .app-item-right-contacting{
+ .e;
+ padding: 7px 7px 7px 7px;
+ display:inline-block;
+ width: 45%;
+ border-radius: 2px;
+ border: 1px solid #d8d8d8;
+ background: #fff;
+ vertical-align: middle;
+ }
+
+ .app-select-left{
+ width: 45%;
+ margin-right: 10px;
+ vertical-align: middle;
+
+
+ .select-field{
+ padding-top: 0;
+ line-height: 30px;
+ height: 30px;
+ vertical-align: middle;
+ border-radius: 2px;
+ border: 1px solid #d8d8d8;
+ margin-right: 10px;
+ padding-left: 4px;
+ background: #fff;
+ display:inline-block;
+ }
+ }
+
+
+ .app-item-delete{
+ .ico_trash_default;
+ display: inline-block;
+ vertical-align: 2px;
+ cursor: pointer;
+ position: relative;
+ top: 6px;
+ color: transparent;
+ margin-left: 8px;
+
+ }
+
+ }
+ }
+
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/users/users.controller.js b/ecomp-portal-FE/client/app/views/users/users.controller.js
new file mode 100644
index 00000000..433b24e4
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/users.controller.js
@@ -0,0 +1,187 @@
+/*-
+ * ================================================================================
+ * 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 UsersCtrl {
+ constructor($log, applicationsService, usersService, $scope, ngDialog,$timeout) {
+ this.$log = $log;
+ $scope.adminAppsIsNull = false;
+ $scope.appsIsDown = false;
+
+ $log.info('UsersCtrl:: initializing...');
+ let activeRequests = [];
+ let clearReq = (req) => {
+ activeRequests.splice(activeRequests.indexOf(req), 1);
+ };
+
+ let getAdminApps = () => {
+ $log.debug('UsersCtrl::getAdminApps: - Starting getAdminApps');
+ try {
+ this.isLoadingTable = true;
+ var adminAppsReq = applicationsService.getAdminApps();
+
+ activeRequests.push(adminAppsReq);
+ adminAppsReq.promise().then(apps => {
+ $log.debug('UsersCtrl::getAdminApps: Apps for this user are: ' + JSON.stringify(apps));
+ $log.debug('UsersCtrl::getAdminApps: Apps length: ' + apps.length);
+ var res1 = apps.sort(getSortOrder("name"));
+ if (!res1 || !res1.length) {
+ $log.error('UsersCtrl::getAdminApps: - no apps found');
+ return null;
+ }
+ for (let i = 0; i < apps.length; i++) {
+ res1[i].index = i;
+ res1[i].value = apps[i].name;
+ res1[i].title = apps[i].name;
+ }
+
+ this.adminApps = apps;
+ this.selectedApp = apps[0];
+ clearReq(adminAppsReq);
+ $scope.adminAppsIsNull = false;
+ }).catch(e => {
+ $scope.adminAppsIsNull = true;
+ $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed = '+ e.message);
+ clearReq(adminAppsReq);
+ }).finally(() => {
+ this.isLoadingTable = false;
+ });
+ } catch (e) {
+ $scope.adminAppsIsNull = true;
+ $log.error('UsersCtrl::getAdminApps: - getAdminApps() failed!');
+ this.isLoadingTable = false;
+ }
+ };
+
+ let getSortOrder = (prop) => {
+ return function(a, b) {
+ if (a[prop] > b[prop]) {
+ return 1;
+ } else if (a[prop] < b[prop]) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ this.updateUsersList = () => {
+ $scope.appsIsDown = false;
+ $log.debug('UsersCtrl::updateUsersList: Starting updateUsersList');
+ this.searchString = '';
+ this.isAppSelectDisabled = true;
+ this.isLoadingTable = true;
+ usersService.getAccountUsers(this.selectedApp.id)
+ .then(accountUsers => {
+ $log.debug('UsersCtrl::updateUsersList length: '+ Object.keys(accountUsers).length);
+ this.isAppSelectDisabled = false;
+ this.accountUsers = accountUsers;
+ }).catch(err => {
+ this.isAppSelectDisabled = false;
+ $log.error('UsersCtrl::updateUsersList: ' + err);
+ $scope.appsIsDown = true;
+ }).finally(() => {
+ this.isLoadingTable = false;
+ });
+ };
+
+
+ let init = () => {
+ this.isLoadingTable = false;
+ this.selectedApp = null;
+ this.isAppSelectDisabled = false;
+ getAdminApps();
+
+ this.searchString = '';
+ this.usersTableHeaders = ['First Name', 'Last Name', 'User ID', 'Roles'];
+ this.accountUsers = [];
+ };
+
+ this.openAddNewUserModal = (user) => {
+ let data = null;
+ if (user) {
+ data = {
+ dialogState: 3,
+ selectedUser: {
+ orgUserId: user.orgUserId,
+ firstName: user.firstName,
+ lastName: user.lastName
+ }
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/users/new-user-dialogs/new-user.modal.html',
+ controller: 'NewUserModalCtrl',
+ controllerAs: 'newUser',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if (needUpdate.value === true) {
+ $log.debug('UsersCtrl::openAddNewUserModal updating table data...');
+ this.updateUsersList();
+ }
+ });
+ };
+
+ this.openEditUserModal = (loginId) => {
+ var data = {
+ loginId : loginId,
+ updateRemoteApp : true,
+ appId : this.selectedApp!=null?this.selectedApp.id:''
+ }
+ var modalInstance = ngDialog.open({
+ templateUrl: 'app/views/header/user-edit/edit-user.tpl.html',
+ controller: 'editUserController',
+ data: data,
+ resolve: {
+ message: function message() {
+ var message = {
+ type: 'Contact',
+ };
+ return message;
+ }
+ }
+ }).closePromise.then(needUpdate => {
+ //update selected app's database for this user.
+ console.log("'''''''''''''''''' now updating user list after update remote server");
+ $timeout(this.updateUsersList, 1500);
+ });
+ }
+
+
+ $scope.$watch('users.selectedApp', (newVal, oldVal) => {
+ if (!newVal || _.isEqual(newVal, oldVal)) {
+ return;
+ }
+ $log.debug('UsersCtrl::openAddNewUserModal:$watch selectedApp -> Fire with: ', newVal);
+ this.accountUsers = [];
+ this.updateUsersList();
+ });
+
+ $scope.$on('$destroy', () => {
+ activeRequests.forEach(req => {
+ req.cancel();
+ });
+ });
+
+ init();
+ }
+ }
+ UsersCtrl.$inject = ['$log', 'applicationsService', 'usersService', '$scope', 'ngDialog','$timeout'];
+ angular.module('ecompApp').controller('UsersCtrl', UsersCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/users/users.controller.spec.js b/ecomp-portal-FE/client/app/views/users/users.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/users.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/client/app/views/users/users.less b/ecomp-portal-FE/client/app/views/users/users.less
new file mode 100644
index 00000000..ff2d815f
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/users.less
@@ -0,0 +1,59 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .users-page-main{
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .users-table {
+ width: @table-width;
+ margin: @table-margin;
+
+ }
+
+ .error-text {
+ width: 1170px;
+ margin: auto;
+ padding: 20px;
+ left: 20px;
+ font-weight: bold;
+ font-size: 16px;
+ text-align: left;
+ color: @err;
+ background-color: @u;
+
+ .error-help {
+ color: @o;
+ font-weight: normal;
+ }
+
+ .error-help-bold {
+ color: @o;
+ font-weight: bold;
+ }
+
+ }
+}
diff --git a/ecomp-portal-FE/client/app/views/users/users.tpl.html b/ecomp-portal-FE/client/app/views/users/users.tpl.html
new file mode 100644
index 00000000..88a3b62d
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/users/users.tpl.html
@@ -0,0 +1,105 @@
+<!--
+ ================================================================================
+ 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="users-page-main" id="contentId">
+ <div id="users-page-title" class="w-ecomp-main-view-title">Users</div>
+ <div class="users-table">
+
+
+ <div class="table-control">
+ <div class="c-ecomp-att-abs-select default">
+ <div class="form-field" id="form-field"
+ att-select="users.adminApps"
+ ng-model="users.selectedApp"
+ ng-disabled="users.isAppSelectDisabled"
+ ng-class="{disabled: users.isAppSelectDisabled}"></div>
+ </div>
+ <input class="table-search" type="text" id="input-search"
+ placeholder="Search in entire table"
+ ng-model="users.searchString"/>
+
+ <div id="users-page-button-add" class="add-button" ng-click="users.openAddNewUserModal()">Add User</div>
+ </div>
+ <div ng-hide="users.isLoadingTable">
+ <div class="error-text"
+ id="div-error-app-down"
+ ng-show="appsIsDown===true">
+ <h1 class="error-help-bold">Attention:</h1>
+ <p>&nbsp;</p>
+ <p class="error-help">Select "Add User" in order to add User and Roles to the '{{users.selectedApp.name}}' Application.</p>
+ </div>
+ </div>
+ <span class="ecomp-spinner" ng-show="users.isLoadingTable"></span>
+ <div class="c-ecomp-att-abs-table default" ng-hide="users.isLoadingTable">
+ <table att-table id="table-main"
+ table-data="users.accountUsers"
+ search-string="users.searchString"
+ view-per-page="users.viewPerPageIgnored"
+ current-page="users.currentPageIgnored"
+ total-page="users.totalPageIgnored">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="th-users-0" att-table-header key="firstName" default-sort="a">{{users.usersTableHeaders[0]}}</th>
+ <th id="th-users-1" att-table-header key="lastName" sortable="true">{{users.usersTableHeaders[1]}}</th>
+ <th id="th-users-2" att-table-header key="userId" sortable="true">{{users.usersTableHeaders[2]}}</th>
+ <th id="th-users-3" att-table-header key="roles" sortable="false">{{users.usersTableHeaders[3]}}</th>
+ </tr>
+ </thead>
+ <tbody att-table-row type="body"
+ class="table-body"
+ track-by="$index"
+ row-repeat="rowData in users.accountUsers">
+ <tr id="tr-rowData" ng-click="users.openAddNewUserModal(rowData)">
+ <td class="td-first" att-table-body>
+ <div id="users-page-td-firstName-{{rowData.userId}}" ng-bind="rowData.firstName"></div>
+ </td>
+ <td att-table-body>
+ <div id="users-page-td-lastName-{{rowData.userId}}" ng-bind="rowData.lastName"></div>
+ </td>
+ <td att-table-body>
+ <div id="users-page-td-userId-{{rowData.userId}}" style="float: left;" ng-bind="rowData.orgUserId"></div>
+ <div>
+ <span style="float: left; margin-left:15px" class="ion-person" ng-click="users.openEditUserModal(rowData.orgUserId);$event.stopPropagation()"></span>
+ </div>
+ </td>
+ <td>
+ <div id="users-page-td-role-name-{{rowData.userId}}" ng-repeat="role in rowData.roles" ng-bind="role.name"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ </div>
+ <div class="error-text"
+ id="div-error-403"
+ ng-show="adminAppsIsNull==true">
+ <h1>Attention:</h1>
+ <p>&nbsp;</p>
+ <p class="error-help">It appears that you have not been added as an admin yet to an application.</p>
+ <p>&nbsp;</p>
+ <p class="error-help">Click on the Admins link to the left and check and see if you are listed as an admin for an application.
+ If not, you can add yourself to the appropriate application.</p>
+ </div>
+ </div>
+ </div>
+
+</div>
diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.js
new file mode 100644
index 00000000..f50e4683
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.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 WidgetDetailsModalCtrl {
+ constructor($scope, $log, applicationsService, widgetsService, errorMessageByCode, ECOMP_URL_REGEX, $window,userProfileService,$cookies) {
+
+ let newWidgetModel = {
+ name: null,
+ appId: null,
+ appName: null,
+ width: 360,
+ height: 300,
+ url: null
+ };
+
+ let getAvailableApps = () => {
+ applicationsService.getAppsForSuperAdminAndAccountAdmin().then(apps => {
+ this.availableApps=[];
+ for(var i=0;i<apps.length;i++) {
+ if (!apps[i].restrictedApp) {
+ $log.debug('WidgetDetailsModalCtrl::getAvailableApps: pushing {id: ', apps[i].id, 'name: ', apps[i].name,
+ 'restrictedApp: ', apps[i].restrictedApp, '}');
+ this.availableApps.push({
+ id: apps[i].id,
+ name: apps[i].name,
+ restrictedApp: apps[i].restrictedApp
+ });
+ }
+ }
+
+ if (this.isEditMode) {
+ this.selectedApp = _.find(apps, {id: this.widget.appId});
+ if(!this.selectedApp){
+ $scope.widgetForm.app.$dirty = true;
+ }
+ } else {
+ this.selectedApp = null;
+ }
+ this.updateSelectedApp();
+ }).catch(err => {
+ $log.error(err);
+ });
+ };
+ /**/
+
+ let init = () => {
+ $log.info('AppDetailsModalCtrl::init');
+ this.isSaving = false;
+ if ($scope.ngDialogData && $scope.ngDialogData.widget) {
+ $log.debug('WidgetDetailsModalCtrl::getAvailableApps: Edit widget mode for', $scope.ngDialogData.widget);
+ this.isEditMode = true;
+ this.widget = _.clone($scope.ngDialogData.widget);
+ } else {
+ $log.debug('WidgetDetailsModalCtrl::init: New app mode');
+ this.isEditMode = false;
+ this.widget = _.clone(newWidgetModel);
+ }
+ getAvailableApps();
+ };
+
+ this.ECOMP_URL_REGEX = ECOMP_URL_REGEX;
+
+ this.conflictMessages = {};
+ this.scrollApi = {};
+ let handleConflictErrors = err => {
+ if(!err.data){
+ return;
+ }
+ if(!err.data.length){
+ err.data = [err.data]
+ }
+ _.forEach(err.data, item => {
+ _.forEach(item.fields, field => {
+ this.conflictMessages[field.name] = errorMessageByCode[item.errorCode];
+ $scope.widgetForm[field.name].$setValidity('conflict', false);
+ watchOnce[field.name]();
+ });
+ });
+ this.scrollApi.scrollTop();
+ };
+
+ let resetConflict = fieldName => {
+ delete this.conflictMessages[fieldName];
+ if($scope.widgetForm[fieldName]){
+ $scope.widgetForm[fieldName].$setValidity('conflict', true);
+ }
+ };
+
+ let watchOnce = {
+ name: () => {
+ let unregisterName = $scope.$watchGroup(['widgetDetails.selectedApp','widgetDetails.widget.name'], (newVal, oldVal) => {
+ if(newVal.toLowerCase() !== oldVal.toLowerCase()){
+ resetConflict('name');
+ unregisterName();
+ }
+ });
+ },
+ url: () => {
+ let unregisterUrl = $scope.$watch('widgetDetails.widget.url', (newVal, oldVal) => {
+ if(newVal.toLowerCase() !== oldVal.toLowerCase()) {
+ resetConflict('url');
+ unregisterUrl();
+ }
+ });
+ }
+ };
+
+ this.updateSelectedApp = () => {
+ if (!this.selectedApp) {
+ return;
+ }
+ this.widget.appId = this.selectedApp.id;
+ this.widget.appName = this.selectedApp.name;
+ };
+
+ let emptyCookies = () => {
+ userProfileService.getUserProfile()
+ .then(profile=> {
+ $log.info('AppDetailsModalCtrl::emptyCookies profile: ', profile);
+ $scope.userId = profile.orgUserId;
+ $log.info('user has the following userId: ' + profile.userId);
+ if ($cookies.getObject($scope.userId + '_widget') != undefined && $cookies.getObject($scope.userId + '_widget') != null) {
+ $cookies.remove($scope.userId + '_widget');
+ }
+ });
+ };
+
+ this.saveChanges = () => {
+ if($scope.widgetForm.$invalid){
+ return;
+ }
+ this.isSaving = true;
+ if(this.isEditMode){
+ widgetsService.updateWidget(this.widget.id, this.widget)
+ .then(() => {
+ $log.debug('WidgetDetailsModalCtrl::saveChanges: Widget update succeeded!');
+ $scope.closeThisDialog(true);
+ emptyCookies();
+ }).catch(err => {
+ if(err.status === 409){
+ handleConflictErrors(err);
+ }
+ $log.error(err);
+ }).finally(()=>{
+ this.isSaving = false;
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('WidgetDetailsModalCtrl::saveChanges: Browser is IE, forcing Refresh');
+ $window.location.reload();
+ }
+ });
+ }else{
+ widgetsService.createWidget(this.widget)
+ .then(() => {
+ $log.debug('WidgetDetailsModalCtrl::createWidget: Widget creation succeeded!');
+ $scope.closeThisDialog(true);
+ emptyCookies();
+ }).catch(err => {
+ if(err.status === 409){
+ handleConflictErrors('WidgetDetailsModalCtrl::createWidget error: ',err);
+ }
+ $log.error('WidgetDetailsModalCtrl::createWidget error: ',err);
+ }).finally(()=>{
+ this.isSaving = false;
+ var objOffsetVersion = objAgent.indexOf("MSIE");
+ if (objOffsetVersion != -1) {
+ $log.debug('WidgetDetailsModalCtrl::createWidget: Browser is IE, forcing Refresh');
+ $window.location.reload();
+ }
+ });
+ }
+ };
+
+ init();
+
+ $scope.$on('$stateChangeStart', e => {
+ e.preventDefault();
+ });
+ }
+ }
+ WidgetDetailsModalCtrl.$inject = ['$scope', '$log', 'applicationsService', 'widgetsService', 'errorMessageByCode', 'ECOMP_URL_REGEX', '$window','userProfileService','$cookies'];
+ angular.module('ecompApp').controller('WidgetDetailsModalCtrl', WidgetDetailsModalCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-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/client/app/views/widgets/widget-details-dialog/widget-details.modal.html b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html
new file mode 100644
index 00000000..87be6f15
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.html
@@ -0,0 +1,132 @@
+<!--
+ ================================================================================
+ 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="widget-details-modal">
+ <div id="'widgets-details-title" class="title">Widget Details</div>
+ <div class="widget-properties-main" scroll-top="widgetDetails.scrollApi">
+ <form id="widgets-details-form" name="widgetForm" novalidate autocomplete="off">
+ <script type="text/javascript">
+ document.getElementById("appForm").reset();
+ </script>
+ <div class="item required">
+ <div class="item-label">Application Name</div>
+ <div class="custom-select-wrap">
+ <select id="widgets-details-select-app"
+ class="select-field"
+ ng-model="widgetDetails.selectedApp"
+ ng-change="widgetDetails.updateSelectedApp();"
+ ng-options="app.name for app in widgetDetails.availableApps track by app.id"
+ ng-disabled="!widgetDetails.availableApps || !widgetDetails.availableApps.length"
+ name="app"
+ required>
+ <option id="widgets-details-select-app-disabled" value="" disabled style="display: none;">Select application</option>
+ </select>
+ </div>
+ <div class="error-container" ng-show="widgetForm.app.$dirty">
+ <div ng-messages="widgetForm.app.$error" class="error-container">
+ <small id="widgets-details-select-app-error-required" class="err-message" ng-message="required">Application is required</small>
+ </div>
+ </div>
+ </div>
+ <div class="item required">
+ <div class="item-label">Widget Name</div>
+ <input id="widgets-details-input-name"
+ class="input-field"
+ type="text"
+ ng-model="widgetDetails.widget.name"
+ name="name"
+ maxlength="100"
+ ng-pattern="/^[a-zA-Z0-9_\s\&]*$/"
+ required/>
+ <div class="error-container" ng-show="widgetDetails.conflictMessages.name">
+ <small id="widgets-details-input-name-conflict" class="err-message" ng-bind="widgetDetails.conflictMessages.name"></small>
+ </div>
+ <div class="error-container" ng-show="widgetForm.name.$dirty || widgetDetails.isEditMode">
+ <div ng-messages="widgetForm.name.$error" class="error-container">
+ <small id="widgets-details-input-name-required" class="err-message" ng-message="required">Widget Name is required</small>
+ <small id="widgets-details-input-name-pattern" class="err-message" ng-message="pattern">Widget Name must be letters, numbers, or underscore</small>
+ </div>
+ </div>
+ </div>
+ <div class="item required">
+ <div class="left-item">
+ <div class="item-label">Width</div>
+ <input id="widgets-details-input-width"
+ class="input-field"
+ type="number"
+ ng-model="widgetDetails.widget.width"
+ name="width"
+ min="300"
+ required
+ disabled/>
+ <div class="error-container" ng-show="widgetForm.width.$dirty || widgetDetails.isEditMode">
+ <div ng-messages="widgetForm.width.$error" class="error-container">
+ <small id="widgets-details-input-width-required" class="err-message" ng-message="required">Widget width is required</small>
+ <small id="widgets-details-input-min-width" class="err-message" ng-message="min">Minimum width is 300</small>
+ </div>
+ </div>
+ </div>
+ <div class="right-item required">
+ <div class="item-label">Height</div>
+ <input id="widgets-details-input-height"
+ class="input-field"
+ type="number"
+ ng-model="widgetDetails.widget.height"
+ name="height"
+ min="200"
+ required
+ disabled/>
+ <div class="error-container" ng-show="widgetForm.height.$dirty || widgetDetails.isEditMode">
+ <div ng-messages="widgetForm.height.$error" class="error-container">
+ <small id="widgets-details-input-height-required" class="err-message" ng-message="required">Widget height is required</small>
+ <small id="widgets-details-input-height-minimum" class="err-message" ng-message="min">Minimum height is 200</small>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="item required">
+ <div class="item-label">URL</div>
+ <input id="widgets-details-input-URL"
+ class="input-field"
+ type="url"
+ ng-model="widgetDetails.widget.url"
+ name="url"
+ maxlength="256"
+ ng-pattern="widgetDetails.ECOMP_URL_REGEX"
+ required/>
+ <div class="error-container" ng-show="widgetDetails.conflictMessages.url">
+ <small id="widgets-details-input-URL-conflict" class="err-message" ng-bind="widgetDetails.conflictMessages.url"></small>
+ </div>
+ <div class="error-container" ng-show="widgetForm.url.$dirty || widgetDetails.isEditMode">
+ <div ng-messages="widgetForm.url.$error" class="error-container">
+ <small id="widgets-details-input-URL-required" class="err-message" ng-message="required">Widget URL is required</small>
+ <small id="widgets-details-input-URL-pattern" class="err-message" ng-message="pattern">Incorrect URL pattern</small>
+ </div>
+ </div>
+
+ </div>
+ </form>
+ </div>
+ <div class="dialog-control">
+ <span class="ecomp-save-spinner" ng-show="widgetDetails.isSaving"></span>
+ <div id="widgets-details-next-button" class="next-button"
+ ng-class="{disabled: widgetForm.$invalid}" ng-click="widgetDetails.saveChanges()">Save</div>
+ <div id="widgets-details-cancel-button" class="cancel-button" ng-click="closeThisDialog()">Cancel</div>
+ </div>
+</div>
diff --git a/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less
new file mode 100644
index 00000000..6e031b1d
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widget-details-dialog/widget-details.modal.less
@@ -0,0 +1,94 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .widget-details-modal {
+ height: 430px;
+
+ .title {
+ .n18r;
+ border-bottom: @a 3px solid;
+
+ }
+
+ .widget-properties-main {
+ padding: 16px;
+ height: 306px;
+ overflow-y: auto;
+
+ .item{
+ position: relative;
+ margin-bottom: 18px;
+
+ .input-field{
+ .custom-input-field;
+ width: 100%;
+ &.url{
+ width: 78%;
+ display: inline-block;
+ }
+ }
+
+ .select-field {
+ .custom-select-field;
+ }
+
+ .item-label{
+ .o14r;
+ }
+
+ .right-item{
+ position: relative;
+ display: inline-block;
+ width: 48%;
+ float: right;
+ }
+ .left-item{
+ display: inline-block;
+ width: 48%;
+ }
+
+ .url-validation-button{
+ .btn-blue;
+ width: 20%;
+ display: inline-block;
+ float: right;
+ }
+
+ .error-container{
+ position: absolute;
+ width: 280px;
+ display: block;
+ height: 12px;
+ line-height: 12px;
+
+ .err-message{
+ color: red;
+ font-size: 9px;
+ }
+ .valid-message{
+ color: green;
+ font-size: 9px;
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js
new file mode 100644
index 00000000..c24f49cd
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.js
@@ -0,0 +1,151 @@
+/*-
+ * ================================================================================
+ * 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 WidgetsCtrl {
+ constructor($log, applicationsService, widgetsService, ngDialog, confirmBoxService,
+ userProfileService, $cookies, $scope) {
+ $log.info('WidgetsCtrl::init: Starting Up');
+ $scope.infoMessage = true;
+
+ let populateAvailableApps = widgets => {
+ let allPortalsFilterObject = {index: 0, title: 'All applications', value: ''};
+ this.availableApps = [allPortalsFilterObject];
+ this.filterByApp = this.availableApps[0];
+ applicationsService.getAppsForSuperAdminAndAccountAdmin().then(myApps => {
+ var reSortedApp = myApps.sort(getSortOrder("name"));
+ var realAppIndex = 1;
+ for (let i = 1; i <= reSortedApp.length; i++) {
+ if (!reSortedApp[i-1].restrictedApp) {
+ $log.debug('WidgetsCtrl::populateAvailableApps: pushing {index: ', realAppIndex, 'title: ', reSortedApp[i - 1].name,
+ 'value: ', reSortedApp[i - 1].name, '}');
+ this.availableApps.push({
+ index: realAppIndex,
+ title: reSortedApp[i - 1].name,
+ value: reSortedApp[i - 1].name
+ })
+ realAppIndex = realAppIndex + 1;
+ }
+ }
+ }).catch(err => {
+ $log.error(err);
+ });
+ };
+
+ let getOnboardingWidgets = () => {
+ this.isLoadingTable = true;
+ widgetsService.getManagedWidgets().then(res => {
+ var reSortedWidget = res.sort(getSortOrder("name"));
+ this.widgetsList = reSortedWidget;
+ populateAvailableApps(reSortedWidget);
+ }).catch(err => {
+ $log.error('WidgetsCtrl::getOnboardingWidgets error: ' + err);
+ }).finally(()=> {
+ this.isLoadingTable = false;
+ });
+ };
+
+ let getSortOrder = (prop) => {
+ return function(a, b) {
+ if (a[prop].toLowerCase() > b[prop].toLowerCase()) {
+ return 1;
+ } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+
+ $scope.hideMe = function () {
+ $scope.infoMessage = false;
+ }
+
+ let init = () => {
+ this.isLoadingTable = false;
+ getOnboardingWidgets();
+
+
+ this.searchString = '';
+
+ this.widgetsTableHeaders = [
+ {name: 'Widget Name', value: 'name', isSortable: false},
+ {name: 'Application', value: 'appName', isSortable: true},
+ {name: 'Width', value: 'width', isSortable: false},
+ {name: 'Height', value: 'height', isSortable: false}
+ ];
+ this.widgetsList = [];
+ };
+
+ this.filterByDropdownValue = item => {
+ if(this.filterByApp.value === ''){
+ return true;
+ }
+ return item.appName === this.filterByApp.value;
+ };
+
+ this.openWidgetDetailsModal = (selectedWidget) => {
+ let data = null;
+ if(selectedWidget){
+ if(!selectedWidget.id){
+ $log.error('Widget id not found');
+ return;
+ }
+ data = {
+ widget: selectedWidget
+ }
+ }
+ ngDialog.open({
+ templateUrl: 'app/views/widgets/widget-details-dialog/widget-details.modal.html',
+ controller: 'WidgetDetailsModalCtrl',
+ controllerAs: 'widgetDetails',
+ data: data
+ }).closePromise.then(needUpdate => {
+ if(needUpdate.value === true){
+ $log.debug('WidgetsCtrl::openWidgetDetailsModal: updating table data...');
+ getOnboardingWidgets();
+ }
+ });
+ };
+
+ this.deleteWidget = widget => {
+ confirmBoxService.deleteItem(widget.name).then(isConfirmed => {
+ if(isConfirmed){
+ if(!widget || !widget.id){
+ $log.error('WidgetsCtrl::deleteWidget: No widget or ID... cannot delete');
+ return;
+ }
+ widgetsService.deleteWidget(widget.id).then(() => {
+ this.widgetsList.splice(this.widgetsList.indexOf(widget), 1);
+ }).catch(err => {
+ $log.error('WidgetsCtrl::deleteWidget error:',err);
+ });
+ }
+ }).catch(err => {
+ $log.error('WidgetsCtrl::deleteWidget error:',err);
+ });
+ };
+
+ init();
+ }
+ }
+ WidgetsCtrl.$inject = ['$log', 'applicationsService', 'widgetsService', 'ngDialog', 'confirmBoxService',
+ 'userProfileService','$cookies', '$scope'];
+ angular.module('ecompApp').controller('WidgetsCtrl', WidgetsCtrl);
+})();
diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js b/ecomp-portal-FE/client/app/views/widgets/widgets.controller.spec.js
new file mode 100644
index 00000000..34042c14
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widgets.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/client/app/views/widgets/widgets.less b/ecomp-portal-FE/client/app/views/widgets/widgets.less
new file mode 100644
index 00000000..4b83b66f
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widgets.less
@@ -0,0 +1,60 @@
+/*-
+ * ================================================================================
+ * 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.
+ * ================================================================================
+ */
+ .widgets-page-main{
+ .bg_w;
+ position: @page-main-position;
+ top: @page-main-top;
+ left: @page-main-left;
+ right: @page-main-right;
+ bottom: @page-main-bottom;
+ padding-top: @padding-top;
+ overflow-y: @page-main-overflow-y;
+ padding-left: @padding-left-side;
+
+ .widgets-table{
+ width: @table-width;
+ margin: @table-margin;
+
+ .table-control{
+
+ }
+
+ .delete-widget{
+ .ico_trash_default;
+ }
+ }
+ .error-text {
+ width: 1170px;
+ margin: auto;
+ padding: 20px;
+ left: 20px;
+ font-weight: bold;
+ font-size: 16px;
+ text-align: left;
+ color: @err;
+ background-color: @u;
+
+ .error-help {
+ color: @o;
+ font-weight: normal;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html b/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html
new file mode 100644
index 00000000..d255174f
--- /dev/null
+++ b/ecomp-portal-FE/client/app/views/widgets/widgets.tpl.html
@@ -0,0 +1,77 @@
+<!--
+ ================================================================================
+ 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="widgets-page-main" id="contentId">
+ <div id="widget-onboarding-title" class="w-ecomp-main-view-title">Widget Onboarding</div>
+ <div class="widgets-table">
+ <div class="table-control">
+ <div class="c-ecomp-att-abs-select default">
+ <div class="form-field" id="widegets-available-apps"
+ att-select="widgets.availableApps"
+ ng-model="widgets.filterByApp"></div>
+ </div>
+ <input class="table-search" type="text" id="widget-onboarding-table-search"
+ placeholder="Search in entire table"
+ ng-model="widgets.searchString"/>
+
+ <div id="widget-onboarding-button-add" class="add-button" ng-click="widgets.openWidgetDetailsModal()">Add Widget</div>
+ </div>
+ <div class="error-text" ng-show="infoMessage">
+ <span class="error-help">Only widgets for active applications are displayed.</span>
+ <button type="button" class="close" ng-click="hideMe()">&times;</button>
+ </div>
+
+ <span class="ecomp-spinner" ng-show="widgets.isLoadingTable"></span>
+ <div class="c-ecomp-att-abs-table default" ng-hide="widgets.isLoadingTable">
+ <table att-table
+ table-data="widgets.widgetsList"
+ search-string="widgets.searchString"
+ view-per-page="widgets.viewPerPageIgnored"
+ current-page="widgets.currentPageIgnored"
+ total-page="widgets.totalPageIgnored">
+ <thead att-table-row type="header">
+ <tr>
+ <th id="widget-onboarding-th-header-name" ng-repeat="header in widgets.widgetsTableHeaders" att-table-header key="{{header.value}}" sortable="{{header.isSortable}}">{{header.name}}</th>
+ <th id="widget-onboarding-th-header-url" att-table-header key="url" sortable="{{false}}">URL</th>
+ <th id="widget-onboarding-th-header-delete" att-table-header sortable="{{false}}">Delete</th>
+ </tr>
+ </thead>
+ <tbody att-table-row type="body"
+ class="table-body"
+ row-repeat="rowData in widgets.widgetsList | filter:widgets.filterByDropdownValue">
+ <tr >
+ <td att-table-body ng-repeat="header in widgets.widgetsTableHeaders" ng-click="widgets.openWidgetDetailsModal(rowData)">
+ <div id="widget-onboarding-div-{{rowData[header.value].split(' ').join('-')}}" ng-bind="rowData[header.value]"></div>
+ </td>
+ <td att-table-body ng-click="widgets.openWidgetDetailsModal(rowData)">
+ <div id="widget-onboarding-div-url-{{rowData[header.value].split(' ').join('-')}}" ng-bind="rowData.url | trusted"></div>
+ </td>
+ <td att-table-body>
+ <div id="widget-onboarding-div-delete-widget-{{$index}}" class="delete-widget" ng-click="widgets.deleteWidget(rowData)"><span class="ion-trash-b"></span></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>