/* eslint-disable handle-callback-err, no-return-assign, no-undef, standard/no-callback-literal, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ Application.Controllers.controller('ApplicationController', ['$rootScope', '$scope', '$window', '$locale', '$timeout', 'Session', 'AuthService', 'Auth', '$uibModal', '$state', 'growl', 'Notification', '$interval', 'Setting', '_t', 'Version', 'Help', function ($rootScope, $scope, $window, $locale, $timeout, Session, AuthService, Auth, $uibModal, $state, growl, Notification, $interval, Setting, _t, Version, Help) { /* PRIVATE STATIC CONSTANTS */ // User's notifications will get refreshed every 30s const NOTIFICATIONS_CHECK_PERIOD = 30000; /* PUBLIC SCOPE */ // Fab-manager's version $scope.version = { current: '' }; // currency symbol for the current locale (cf. angular-i18n) $rootScope.currencySymbol = $locale.NUMBER_FORMATS.CURRENCY_SYM; /** * Set the current user to the provided value and initialize the session * @param user {Object} Rails/Devise user */ $scope.setCurrentUser = function (user) { if (!angular.isUndefinedOrNull(user)) { $rootScope.currentUser = user; Session.create(user); getNotifications(); // Fab-manager's app-version if (user.role === 'admin') { // get the version $scope.version = Version.get({origin: window.location.origin}); } else { $scope.version = { current: '' }; } } }; /** * Login callback * @param e {Object} see https://docs.angularjs.org/guide/expression#-event- * @param callback {function} */ $scope.login = function (e, callback) { if (e) { e.preventDefault(); } return openLoginModal(null, null, callback); }; /** * Logout callback * @param e {Object} see https://docs.angularjs.org/guide/expression#-event- */ $scope.logout = function (e) { e.preventDefault(); return Auth.logout().then(function () { Session.destroy(); $rootScope.currentUser = null; $rootScope.toCheckNotifications = false; $scope.notifications = { total: 0, unread: 0 }; return $state.go('app.public.home'); }, function (error) { console.error(`An error occurred logging out: ${error}`); }); }; /** * Open the modal window allowing the user to create an account. * @param e {Object} see https://docs.angularjs.org/guide/expression#-event- */ $scope.signup = function (e) { if (e) { e.preventDefault(); } <% active_provider = AuthProvider.active %> <% if active_provider.providable_type != DatabaseProvider.name %> $window.location.href = '/sso-redirect'; <% else %> return $uibModal.open({ templateUrl: '<%= asset_path "shared/signupModal.html" %>', size: 'md', resolve: { settingsPromise: ['Setting', function (Setting) { return Setting.query({ names: "['phone_required', 'recaptcha_site_key']" }).$promise; }] }, controller: ['$scope', '$uibModalInstance', 'Group', 'CustomAsset', 'settingsPromise', 'growl', '_t', function ($scope, $uibModalInstance, Group, CustomAsset, settingsPromise, growl, _t) { // default parameters for the date picker in the account creation modal $scope.datePicker = { format: Fablab.uibDateFormat, opened: false, options: { startingDay: Fablab.weekStartingDay } }; // is the phone number required to sign-up? $scope.phoneRequired = (settingsPromise.phone_required === 'true'); // reCaptcha v2 site key (or undefined) $scope.recaptchaSiteKey = settingsPromise.recaptcha_site_key; // callback to open the date picker (account creation modal) $scope.openDatePicker = function ($event) { $event.preventDefault(); $event.stopPropagation(); return $scope.datePicker.opened = true; }; // retrieve the groups (standard, student ...) Group.query(function (groups) { $scope.groups = groups; $scope.enabledGroups = groups.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; }); }); // retrieve the CGU CustomAsset.get({ name: 'cgu-file' }, function (cgu) { $scope.cgu = cgu.custom_asset; }); // default user's parameters $scope.user = { is_allow_contact: true, is_allow_newsletter: false, // reCaptcha response, received from Google (through AJAX) and sent to server for validation recaptcha: undefined }; // Errors display $scope.alerts = []; $scope.closeAlert = function (index) { $scope.alerts.splice(index, 1); }; // callback for form validation $scope.ok = function () { // try to create the account $scope.alerts = []; // remove 'organization' attribute const orga = $scope.user.organization; delete $scope.user.organization; // register on server return Auth.register($scope.user).then(function (user) { if (user.id) { // creation successful $uibModalInstance.close(user); } else { // the user was not saved in database, something wrong occurred growl.error(_t('app.public.common.unexpected_error_occurred')); } }, function (error) { // creation failed... // restore organization param $scope.user.organization = orga; // display errors angular.forEach(error.data.errors, function (v, k) { angular.forEach(v, function (err) { $scope.alerts.push({ msg: k + ': ' + err, type: 'danger' }); }); }); }); }; }] }).result['finally'](null).then(function (user) { // when the account was created successfully, set the session to the newly created account if(Fablab.userConfirmationNeededToSignIn) { Auth._currentUser = null; growl.info(_t('app.public.common.you_will_receive_confirmation_instructions_by_email_detailed')); } else { $scope.setCurrentUser(user); } }); <% end %> }; /** * Open the modal window allowing the user to change his password. * @param token {string} security token for password changing. The user should have recieved it by mail */ $scope.editPassword = function (token) { $uibModal.open({ templateUrl: '<%= asset_path "shared/passwordEditModal.html" %>', size: 'md', controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) { $scope.user = { reset_password_token: token }; $scope.alerts = []; $scope.closeAlert = function (index) { $scope.alerts.splice(index, 1); }; return $scope.changePassword = function () { $scope.alerts = []; return $http.put('/users/password.json', { user: $scope.user }).then(function () { $uibModalInstance.close(); }).catch(function (data) { angular.forEach(data.data.errors, function (v, k) { angular.forEach(v, function (err) { $scope.alerts.push({ msg: k + ': ' + err, type: 'danger' }); }); }); }); }; }] }).result['finally'](null).then(function () { growl.success(_t('app.public.common.your_password_was_successfully_changed')); return Auth.login().then(function (user) { $scope.setCurrentUser(user); }, function (error) { console.error(`Authentication failed: ${JSON.stringify(error)}`); } ); }); }; /** * Compact/Expend the width of the left navigation bar * @param event {Object} see https://docs.angularjs.org/guide/expression#-event- */ $scope.toggleNavSize = function (event) { let $classes, $targets; if (typeof event === 'undefined') { console.error('[ApplicationController::toggleNavSize] Missing event parameter'); return; } let toggler = $(event.target); if (!toggler.data('toggle')) { toggler = toggler.closest('[data-toggle^="class"]'); } const $class = toggler.data()['toggle']; const $target = toggler.data('target') || toggler.attr('data-link'); if ($class) { const $tmp = $class.split(':')[1]; if ($tmp) { $classes = $tmp.split(','); } } if ($target) { $targets = $target.split(','); } if ($classes && $classes.length) { $.each($targets, function (index) { if ($classes[index].indexOf('*') !== -1) { const patt = new RegExp('\\s', +$classes[index].replace(/\*/g, '[A-Za-z0-9-_]+').split(' ').join('\\s|\\s'), +'\\s', 'g'); $(toggler).each(function (i, it) { let cn = ` ${it.className} `; while (patt.test(cn)) { cn = cn.replace(patt, ' '); } return it.className = $.trim(cn); }); } return (($targets[index] !== '#') && $($targets[index]).toggleClass($classes[index])) || toggler.toggleClass($classes[index]); }); } toggler.toggleClass('active'); }; /** * Open the modal dialog showing that an upgrade is available */ $scope.versionModal = function() { if ($scope.version.up_to_date) return; if ($rootScope.currentUser.role !== 'admin') return; $uibModal.open({ templateUrl: '<%= asset_path "admin/versions/upgradeModal.html" %>', controller: 'VersionModalController', resolve: { version() { return $scope.version; } } }); } /** * Trigger the contextual help "feature tour". * @param event {Object} see https://docs.angularjs.org/guide/expression#-event- */ $scope.help = function (event) { event.preventDefault(); // we wrap the event triggering into a $timeout to prevent conflicting with current $apply $timeout(function () { var evt = new KeyboardEvent('keydown', { key: 'F1' }); window.dispatchEvent(evt); }); } /* PRIVATE SCOPE */ /** * Kind of constructor: these actions will be realized first when the controller is loaded */ const initialize = function () { // try to retrieve any currently logged user Auth.login().then(function (user) { $scope.setCurrentUser(user); // force users to complete their profile if they are not if (user.need_completion) { return $state.transitionTo('app.logged.profileCompletion'); } }, function () { console.info('No users currently logged'); $rootScope.toCheckNotifications = false; }); // bind to the $stateChangeStart event (AngularJS/UI-Router) $rootScope.$on('$stateChangeStart', function (event, toState, toParams) { if (!toState.data) { return; } const { authorizedRoles } = toState.data; if (!AuthService.isAuthorized(authorizedRoles)) { event.preventDefault(); if (AuthService.isAuthenticated()) { // user is not allowed console.error('[ApplicationController::initialize] user is not allowed'); } else { // user is not logged in openLoginModal(toState, toParams); } } }); // we stop polling notifications when the page is not in foreground onPageVisible(function (state) { $rootScope.toCheckNotifications = (state === 'visible'); }); Setting.query({ names: "['fablab_name', 'name_genre', 'link_name']" }, function (settings) { $scope.fablabName = settings.fablab_name; $scope.nameGenre = settings.name_genre; $scope.linkName = settings.link_name; }); // shorthands $scope.isAuthenticated = Auth.isAuthenticated; $scope.isAuthorized = AuthService.isAuthorized; $rootScope.login = $scope.login; // handle F1 key to trigger help window.addEventListener('keydown', Help); }; /** * Retrieve once the notifications from the server and display a message popup for each new one. * Then, periodically check for new notifications. */ const getNotifications = function () { $rootScope.toCheckNotifications = true; if (!$rootScope.checkNotificationsIsInit && !!$rootScope.currentUser) { setTimeout(function () { // we request the most recent notifications Notification.last_unread(function (notifications) { $rootScope.lastCheck = new Date(); $scope.notifications = notifications.totals; const toDisplay = []; angular.forEach(notifications.notifications, function (n) { toDisplay.push(n); }); if (toDisplay.length < notifications.totals.unread) { toDisplay.push({ message: { description: _t('app.public.common.and_NUMBER_other_notifications', { NUMBER: notifications.totals.unread - toDisplay.length }) } }); } angular.forEach(toDisplay, function (notification) { growl.info(notification.message.description); }); }); }, 2000); const checkNotifications = function () { if ($rootScope.toCheckNotifications) { return Notification.polling({ last_poll: $rootScope.lastCheck }).$promise.then(function (data) { $rootScope.lastCheck = new Date(); $scope.notifications = data.totals; angular.forEach(data.notifications, function (notification) { growl.info(notification.message.description); }); }).catch(function (error) { console.error('Error while polling notifications', error); }); } }; $interval(checkNotifications, NOTIFICATIONS_CHECK_PERIOD); $rootScope.checkNotificationsIsInit = true; } }; /** * Open the modal window allowing the user to log in. */ const openLoginModal = function (toState, toParams, callback) { <% active_provider = AuthProvider.active %> <% if active_provider.providable_type != DatabaseProvider.name %> $window.location.href = '/sso-redirect'; <% else %> return $uibModal.open({ templateUrl: '<%= asset_path "shared/deviseModal.html" %>', size: 'sm', controller: ['$scope', '$uibModalInstance', '_t', function ($scope, $uibModalInstance, _t) { const user = ($scope.user = {}); $scope.login = function () { Auth.login(user).then(function (user) { // Authentication succeeded ... $uibModalInstance.close(user); if (callback && (typeof callback === 'function')) { return callback(user); } } , function (error) { console.error(`Authentication failed: ${JSON.stringify(error)}`); $scope.alerts = []; return $scope.alerts.push({ msg: error.data.error, type: 'danger' }); }); }; // handle modal behaviors. The provided reason will be used to define the following actions $scope.dismiss = function () { $uibModalInstance.dismiss('cancel'); }; $scope.openSignup = function (e) { e.preventDefault(); return $uibModalInstance.dismiss('signup'); }; $scope.openConfirmationNewModal = function(e) { e.preventDefault(); return $uibModalInstance.dismiss('confirmationNew'); }; return $scope.openResetPassword = function (e) { e.preventDefault(); return $uibModalInstance.dismiss('resetPassword'); }; }] }).result['finally'](null).then(function (user) { // what to do when the modal is closed // authentication succeeded, set the session, gather the notifications and redirect $scope.setCurrentUser(user); if ((toState !== null) && (toParams !== null)) { return $state.go(toState, toParams); } }, function (reason) { // authentication did not end successfully if (reason === 'signup') { // open sign-up modal $scope.signup(); } else if (reason === 'resetPassword') { // open the 'reset password' modal return $uibModal.open({ templateUrl: '<%= asset_path "shared/passwordNewModal.html" %>', size: 'sm', controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) { $scope.user = { email: '' }; return $scope.sendReset = function () { $scope.alerts = []; return $http.post('/users/password.json', { user: $scope.user }).then(function () { $uibModalInstance.close(); }).catch(function () { $scope.alerts.push({ msg: _t('app.public.common.your_email_address_is_unknown'), type: 'danger' }); }); }; }] }).result['finally'](null).then(function () { growl.info(_t('app.public.common.you_will_receive_in_a_moment_an_email_with_instructions_to_reset_your_password')); }); } else if (reason === 'confirmationNew') { // open the 'reset password' modal return $uibModal.open({ templateUrl: '<%= asset_path "shared/ConfirmationNewModal.html" %>', size: 'sm', controller: ['$scope', '$uibModalInstance', '$http', function ($scope, $uibModalInstance, $http) { $scope.user = { email: '' }; return $scope.submitConfirmationNewForm = function () { $scope.alerts = []; return $http.post('/users/confirmation.json', { user: $scope.user }).then(function () { $uibModalInstance.close(); }).catch(function (res) { $scope.alerts.push({ msg: res.data.errors.email[0], type: 'danger' }); }); }; }] }).result['finally'](null).then(function () { growl.info(_t('app.public.common.you_will_receive_confirmation_instructions_by_email_detailed')); }); } }); // otherwise the user just closed the modal <% end %> }; /** * Detect if the current page (tab/window) is active of put as background. * When the status changes, the callback is triggered with the new status as parameter * Inspired by http://stackoverflow.com/questions/1060008/is-there-a-way-to-detect-if-a-browser-window-is-not-currently-active#answer-1060034 */ const onPageVisible = function (callback) { let hidden = 'hidden'; const onchange = function (evt) { const v = 'visible'; const h = 'hidden'; const evtMap = { focus: v, focusin: v, pageshow: v, blur: h, focusout: h, pagehide: h }; evt = evt || window.event; if (evt.type in evtMap) { if (typeof callback === 'function') { callback(evtMap[evt.type]); } } else { if (typeof callback === 'function') { callback(this[hidden] ? 'hidden' : 'visible'); } } }; // Standards: if (hidden in document) { document.addEventListener('visibilitychange', onchange); } else if ((hidden = 'mozHidden') in document) { document.addEventListener('mozvisibilitychange', onchange); } else if ((hidden = 'webkitHidden') in document) { document.addEventListener('webkitvisibilitychange', onchange); } else if ((hidden = 'msHidden') in document) { document.addEventListener('msvisibilitychange', onchange); // IE 9 and lower } else if ('onfocusin' in document) { document.onfocusin = (document.onfocusout = onchange); // All others } else { window.onpageshow = (window.onpagehide = (window.onfocus = (window.onblur = onchange))); } // set the initial state (but only if browser supports the Page Visibility API) if (document[hidden] !== undefined) { return onchange({ type: document[hidden] ? 'blur' : 'focus' }); } }; // !!! MUST BE CALLED AT THE END of the controller return initialize(); } ]); /** * Controller used in the modal showing details about the version and the upgrades */ Application.Controllers.controller('VersionModalController', ['$scope', '$uibModalInstance', 'version', function ($scope, $uibModalInstance, version) { // version infos (current version + upgrade infos from hub) $scope.version = version; // callback to close the modal $scope.close = function () { $uibModalInstance.dismiss(); } }]);