define(function(require){ var $ = require('jquery'), _ = require('underscore'), chosen = require('chosen'), monster = require('monster'), timezone = require('monster-timezone'), toastr = require('toastr'); var app = { requests: {}, subscribe: { 'voip.users.render': 'usersRender' }, deviceIcons: { 'cellphone': 'icon-phone', 'smartphone': 'icon-telicon-mobile-phone', 'landline': 'icon-telicon-home-phone', 'mobile': 'icon-telicon-sprint-phone', 'softphone': 'icon-telicon-soft-phone', 'sip_device': 'icon-telicon-voip-phone', 'sip_uri': 'icon-telicon-voip-phone', 'fax': 'icon-telicon-fax', 'ata': 'icon-telicon-fax' }, /* Users */ /* args: parent and userId */ usersRender: function(args) { var self = this, args = args || {}, parent = args.parent || $('.right-content'), _userId = args.userId, _openedTab = args.openedTab; self.usersRemoveOverlay(); self.usersGetData(function(data) { var dataTemplate = self.usersFormatListData(data), template = $(monster.template(self, 'users-layout', dataTemplate)), templateUser; _.each(dataTemplate.users, function(user) { templateUser = monster.template(self, 'users-row', user); template.find('.user-rows').append(templateUser); }); template.find('[data-toggle="tooltip"]').tooltip({ container: 'body'}); template.find('[data-toggle="popover"]').popover({ container: 'body'}); self.usersBindEvents(template, parent, dataTemplate); parent .empty() .append(template); if(_userId) { var cells = parent.find('.grid-row[data-id=' + _userId + '] .grid-cell'); monster.ui.highlight(cells); } if ( dataTemplate.users.length == 0) { parent.find('.grid-row.title').css('display', 'none'); parent.find('.no-users-row').css('display', 'block'); } else { parent.find('.grid-row.title').css('display', 'block'); parent.find('.no-users-row').css('display', 'none'); } if(_userId && _openedTab) { template.find('.grid-row[data-id="'+ _userId +'"] .grid-cell[data-type="' + _openedTab + '"]').click(); args.callback && args.callback(); } }); }, usersFormatUserData: function(dataUser, _mainDirectory, _mainCallflow, _vmbox, _vmboxes) { var self = this, formattedUser = { additionalDevices: 0, additionalExtensions: 0, additionalNumbers: 0, devices: [], extension: '', hasFeatures: false, isAdmin: dataUser.priv_level === 'admin', listCallerId: [], listExtensions: [], listNumbers: [], phoneNumber: '', differentEmail: dataUser.email !== dataUser.username, mapFeatures: { caller_id: { icon: 'icon-user', iconColor: 'icon-blue', title: self.i18n.active().users.caller_id.title }, call_forward: { icon: 'icon-mail-forward', iconColor: 'icon-yellow', title: self.i18n.active().users.call_forward.title }, hotdesk: { icon: 'icon-fire', iconColor: 'icon-orange', title: self.i18n.active().users.hotdesk.title }, vm_to_email: { icon: 'icon-telicon-voicemail', iconColor: 'icon-green', title: self.i18n.active().users.vm_to_email.title }, faxing: { icon: 'icon-telicon-fax', iconColor: 'icon-red', title: self.i18n.active().users.faxing.title }, conferencing: { icon: 'icon-comments', iconColor: 'icon-gray', title: self.i18n.active().users.conferencing.title }, find_me_follow_me: { icon: 'icon-sitemap', iconColor: 'icon-purple', title: self.i18n.active().users.find_me_follow_me.title }, music_on_hold: { icon: 'icon-music', iconColor: 'icon-pink', title: self.i18n.active().users.music_on_hold.title }, call_recording: { icon: 'icon-microphone', iconColor: 'icon-blue', title: self.i18n.active().users.callRecording.title } } }; if(!('extra' in dataUser)) { dataUser.extra = formattedUser; } dataUser.extra.countFeatures = 0; _.each(dataUser.features, function(v) { if(v in dataUser.extra.mapFeatures) { dataUser.extra.countFeatures++; dataUser.extra.mapFeatures[v].active = true; } }); if(dataUser.extra.countFeatures > 0) { dataUser.extra.hasFeatures = true; } if(_mainDirectory) { dataUser.extra.mainDirectoryId = _mainDirectory.id; if('directories' in dataUser && _mainDirectory.id in dataUser.directories) { dataUser.extra.includeInDirectory = true; } else { dataUser.extra.includeInDirectory = false; } } if(_mainCallflow) { dataUser.extra.mainCallflowId = _mainCallflow.id; if('flow' in _mainCallflow) { var flow = _mainCallflow.flow, module = 'user'; if(dataUser.features.indexOf('find_me_follow_me') >= 0) { module = 'ring_group'; dataUser.extra.groupTimeout = true; } while(flow.module != module && '_' in flow.children) { flow = flow.children['_']; } dataUser.extra.ringingTimeout = flow.data.timeout; } } if(_vmbox) { dataUser.extra.vmbox = _vmbox; } if(!_.isEmpty(_vmbox) && !_.isEmpty(_vmboxes)) { var i = _vmboxes.indexOf(_vmbox.mailbox); _vmboxes.splice(i, 1); dataUser.extra.existingVmboxes = _vmboxes; } dataUser.extra.adminId = self.userId; dataUser.extra.presenceIdOptions = [{ key: 'unset', value: self.i18n.active().users.editionForm.noPresenceID }]; var temp, addNumberToPresenceOptions = function(number) { temp = { key: number, value: monster.util.formatPhoneNumber(number) }; dataUser.extra.presenceIdOptions.push(temp); }; _.each(dataUser.extra.listExtensions, function(extension) { addNumberToPresenceOptions(extension); }) _.each(dataUser.extra.listNumbers, function(number) { addNumberToPresenceOptions(number); }); return dataUser; }, usersFormatListData: function(data) { var self = this, dataTemplate = { existingExtensions: [], countUsers: data.users.length }, mapUsers = {}; _.each(data.users, function(user) { mapUsers[user.id] = self.usersFormatUserData(user); }); _.each(data.callflows, function(callflow) { if(callflow.type !== 'faxing') { var userId = callflow.owner_id; _.each(callflow.numbers, function(number) { if(number && number.length < 7) { dataTemplate.existingExtensions.push(number); } }); if(userId in mapUsers) { var user = mapUsers[userId]; //User can only have one phoneNumber and one extension displayed with this code _.each(callflow.numbers, function(number) { if(number.length < 7) { user.extra.listExtensions.push(number); if(user.extra.extension === '') { user.extra.extension = number; } else { user.extra.additionalExtensions++; } } else { user.extra.listCallerId.push(number); user.extra.listNumbers.push(number); if(user.extra.phoneNumber === '') { user.extra.phoneNumber = number; } else { user.extra.additionalNumbers++; } } }); } } }); dataTemplate.existingExtensions.sort(self.usersSortExtensions); _.each(data.devices, function(device) { var userId = device.owner_id; if(userId in mapUsers) { var isRegistered = _.find(data.deviceStatus, function(status){ return (status.device_id === device.id && status.registered === true); }) ? true : false; if(mapUsers[userId].extra.devices.length == 2) { if(mapUsers[userId].extra.additionalDevices) { mapUsers[userId].extra.additionalDevices.count++; mapUsers[userId].extra.additionalDevices.tooltip += '
' + device.name + ' (' + device.device_type.replace('_', ' ') + ')'; } else { mapUsers[userId].extra.additionalDevices = { count: 1, tooltip: '' + device.name + ' (' + device.device_type.replace('_', ' ') + ')' }; } } else { mapUsers[userId].extra.devices.push({ id: device.id, name: device.name + ' (' + device.device_type.replace('_', ' ') + ')', type: device.device_type, registered: isRegistered, icon: self.deviceIcons[device.device_type] }); } } }); var sortedUsers = []; _.each(mapUsers, function(user) { sortedUsers.push(user); }); sortedUsers = monster.util.sort(sortedUsers, 'last_name'); dataTemplate.users = sortedUsers; return dataTemplate; }, usersBindEvents: function(template, parent, data) { var self = this, currentNumberSearch = '', currentUser, currentCallflow, existingExtensions = data.existingExtensions, extensionsToSave, numbersToSave, toastrMessages = self.i18n.active().users.toastrMessages, mainDirectoryId, mainCallflowId, listUsers = data, renderFindMeFollowMeFeature = function(featureCallback) { monster.parallel({ userDevices: function(callback) { self.usersListDeviceUser(currentUser.id, function(devices) { callback(null, devices); }); }, userCallflow: function(callback) { self.usersGetMainCallflow(currentUser.id, function(callflow) { callback(null, callflow); }); } }, function(error, results) { self.usersRenderFindMeFollowMe($.extend(true, results, { currentUser: currentUser, saveCallback: featureCallback })); } ); }; template.find('.grid-row:not(.title) .grid-cell').on('click', function() { var cell = $(this), type = cell.data('type'), row = cell.parents('.grid-row'), userId = row.data('id'); template.find('.edit-user').slideUp("400", function() { $(this).empty(); }); if(cell.hasClass('active')) { template.find('.grid-cell').removeClass('active'); template.find('.grid-row').removeClass('active'); self.usersRemoveOverlay(); cell.css({ 'position': 'initial', 'z-index': '0' }); cell.parent().siblings('.edit-user').css({ 'position': 'initial', 'z-index': '0' }); } else { template.find('.grid-cell').removeClass('active'); template.find('.grid-row').removeClass('active'); cell.toggleClass('active'); row.toggleClass('active'); cell.css({ 'position': 'relative', 'z-index': '3' }); cell.parent().siblings('.edit-user').css({ 'position': 'relative', 'z-index': '2' }); self.usersGetTemplate(type, userId, listUsers, function(template, data) { if(type === 'name') { currentUser = data; template.find('#user_timezone').chosen({search_contains: true, width: "61%"}); data.extra.differentEmail ? template.find('.email-group').show() : template.find('.email-group').hide(); if(data.extra.mainDirectoryId) { mainDirectoryId = data.extra.mainDirectoryId; } if(data.extra.mainCallflowId) { mainCallflowId = data.extra.mainCallflowId; } } else if(type === 'numbers') { extensionsToSave = []; currentCallflow = data.callflow; currentUser = data.user; _.each(data.extensions, function(number) { extensionsToSave.push(number); }); } else if(type === 'extensions') { existingExtensions = data.allExtensions; currentCallflow = data.callflow; currentUser = data.user; numbersToSave = []; _.each(data.assignedNumbers, function(v) { numbersToSave.push(v.phoneNumber); }); } else if(type === 'features') { currentUser = data; } //FancyCheckboxes. monster.ui.prettyCheck.create(template); row.find('.edit-user').append(template).slideDown(400, function() { $('body').animate({ scrollTop: row.offset().top - (window.innerHeight - row.height() - 10) }); }); $('body').append($('
')); }); } }); template.find('.users-header .search-query').on('keyup', function() { var searchString = $(this).val().toLowerCase(), rows = template.find('.user-rows .grid-row:not(.title)'), emptySearch = template.find('.user-rows .empty-search-row'); _.each(rows, function(row) { var row = $(row); row.data('search').toLowerCase().indexOf(searchString) < 0 ? row.hide() : row.show(); }); if(rows.size() > 0) { rows.is(':visible') ? emptySearch.hide() : emptySearch.show(); } }); template.find('.users-header .add-user').on('click', function() { monster.parallel({ callflows: function(callback) { self.usersListCallflows(function(callflows) { callback(null, callflows); }); }, vmboxes: function(callback) { self.usersListVMBoxes(function(vmboxes) { callback(null, vmboxes); }); } }, function(err, results) { var originalData = self.usersFormatAddUser(results), userTemplate = $(monster.template(self, 'users-creation', originalData)); monster.ui.prettyCheck.create(userTemplate); monster.ui.validate(userTemplate.find('#form_user_creation'), { rules: { 'callflow.extension': { checkList: originalData.listExtensions }, 'vmbox.number': { checkList: originalData.listVMBoxes } }, messages: { 'user.first_name': { required: self.i18n.active().validation.required }, 'user.last_name': { required: self.i18n.active().validation.required }, 'callflow.extension': { required: self.i18n.active().validation.required } } }); userTemplate.find('#create_user').on('click', function() { if(monster.ui.valid(userTemplate.find('#form_user_creation'))) { var dataForm = monster.ui.getFormData('form_user_creation'), formattedData = self.usersFormatCreationData(dataForm); self.usersCreate(formattedData, function(data) { popup.dialog('close').remove(); self.usersRender({ userId: data.user.id }); }); } }); userTemplate.find('#notification_email').on('ifChanged', function() { userTemplate.find('.email-group').toggleClass('hidden'); }); var popup = monster.ui.dialog(userTemplate, { title: self.i18n.active().users.dialogCreationUser.title }); } ); }); template.on('click', '.cancel-link', function() { template.find('.edit-user').slideUp("400", function() { $(this).empty(); template.find('.grid-cell.active').css({ 'position': 'inline-block', 'z-index': '0' }); template.find('.grid-row.active .edit-user').css({ 'position': 'block', 'z-index': '0' }); template.find('.grid-row.active').removeClass('active'); self.usersRemoveOverlay(); template.find('.grid-cell.active').removeClass('active'); }); }); /* Events for Extensions details */ template.on('click', '.save-extensions', function() { var $this = $(this), numbers = $.extend(true, [], numbersToSave), name = $this.parents('.grid-row').find('.grid-cell.name').text(), userId = $this.parents('.grid-row').data('id'); template.find('.extensions .list-assigned-items .item-row').each(function(k, row) { var row = $(row), number; number = (row.data('id') ? row.data('id') : row.find('.input-extension').val()) + ''; numbers.push(number); }); if(numbers.length > 0) { var updateCallflow = function() { self.usersUpdateCallflowNumbers(userId, (currentCallflow || {}).id, numbers, function(callflowData) { toastr.success(monster.template(self, '!' + toastrMessages.numbersUpdated, { name: name })); self.usersRender({ userId: callflowData.owner_id }); }); }; if(self.usersHasProperPresenceId(numbers, currentUser)) { updateCallflow(); } else { self.usersUpdatePresenceIDPopup(numbers, currentUser, function(user) { self.usersUpdateUser(user, function() { updateCallflow(); }); }); } } else { monster.ui.alert('warning', self.i18n.active().users.noNumberCallflow); } }); template.on('click', '#add_extensions', function() { var nextExtension = parseInt(self.usersGetNextInt(existingExtensions)) + '', dataTemplate = { recommendedExtension: nextExtension }, newLineTemplate = $(monster.template(self, 'users-newExtension', dataTemplate)), listExtensions = template.find('.extensions .list-assigned-items'); listExtensions.find('.empty-row').hide(); listExtensions.append(newLineTemplate); existingExtensions.push(nextExtension); }); template.on('click', '.remove-extension', function() { var phoneRow = $(this).parents('.item-row'), emptyRow = phoneRow.siblings('.empty-row'); if(phoneRow.siblings('.item-row').size() === 0) { emptyRow.show(); } phoneRow.remove(); }); template.on('click', '.cancel-extension-link', function() { var extension = $(this).siblings('input').val(), index = existingExtensions.indexOf(extension); if(index > -1) { existingExtensions.splice(index, 1); } $(this).parents('.item-row').remove(); }); template.on('click', '#delete_user', function() { var dataUser = $(this).parents('.grid-row').data(), deleteType = dataUser.priv_level === 'admin' ? 'confirmDeleteAdmin' : 'confirmDeleteUser'; monster.ui.confirm(self.i18n.active().users[deleteType], function() { self.usersDelete(dataUser.id, function(data) { toastr.success(monster.template(self, '!' + toastrMessages.userDeleted)); }); }); }); template.on('ifChanged', '#notification_email', function() { if ( template.find('.email-border').hasClass('open') ) { template.find('.email-border').removeClass('open', 400); template.find('.email-group').slideUp(); } else { template.find('.email-group').slideDown(); template.find('.email-border').addClass('open', 400); } }); template.on('click', '.save-user', function() { var formData = monster.ui.getFormData('form-'+currentUser.id), form = template.find('#form-'+currentUser.id); monster.util.checkVersion(currentUser, function() { if(monster.ui.valid(form)) { currentUser.extra.vmbox.mailbox = formData.extra.vmboxNumber; currentUser.extra.vmbox.timezone = formData.timezone; var userToSave = $.extend(true, {}, currentUser, formData); monster.parallel({ vmbox: function(callback) { self.usersSmartUpdateVMBox(userToSave, true, function(vmbox) { callback(null, vmbox); }); }, user: function(callback) { self.usersUpdateUser(userToSave, function(userData) { callback(null, userData.data); }); }, callflow: function(callback) { if(userToSave.extra.ringingTimeout && userToSave.features.indexOf('find_me_follow_me') < 0) { self.usersGetMainCallflow(userToSave.id, function(mainCallflow) { if('flow' in mainCallflow) { var flow = mainCallflow.flow; while(flow.module != 'user' && '_' in flow.children) { flow = flow.children['_']; } flow.data.timeout = parseInt(userToSave.extra.ringingTimeout); self.usersUpdateCallflow(mainCallflow, function(updatedCallflow) { callback(null, updatedCallflow); }); } else { callback(null, null); } }); } else { callback(null, null); } } }, function(error, results) { toastr.success(monster.template(self, '!' + toastrMessages.userUpdated, { name: results.user.first_name + ' ' + results.user.last_name })); self.usersRender({ userId: results.user.id }); } ); } }); }); template.on('click', '#change_pin', function() { var pinTemplate = $(monster.template(self, 'users-changePin')), form = pinTemplate.find('#form_new_pin'); //monster.ui.validate(form); monster.ui.validate(form, { rules: { 'pin': { number: true, minlength:4, min: 0 } } }); pinTemplate.find('.save-new-pin').on('click', function() { var formData = monster.ui.getFormData('form_new_pin'), vmboxData = $.extend(true, currentUser.extra.vmbox, formData); if(monster.ui.valid(form)) { self.usersUpdateVMBox(vmboxData, function(data) { popup.dialog('close').remove(); toastr.success(monster.template(self, '!' + toastrMessages.pinUpdated, { name: currentUser.first_name + ' ' + currentUser.last_name })); }); } }); pinTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); var popup = monster.ui.dialog(pinTemplate, { title: self.i18n.active().users.dialogChangePin.title }); }); template.on('click', '#change_username', function() { var passwordTemplate = $(monster.template(self, 'users-changePassword', currentUser)), form = passwordTemplate.find('#form_new_username'); passwordTemplate.find('.save-new-username').on('click', function() { var formData = monster.ui.getFormData('form_new_username'), userToSave = $.extend(true, {}, currentUser, formData); if(monster.ui.valid(form)) { if(!currentUser.extra.differentEmail) { userToSave.email = userToSave.username; } self.usersUpdateUser(userToSave, function(userData) { currentUser.username = userData.data.username; template.find('#username').html(userData.data.username); if(!currentUser.extra.differentEmail) { template.find('#email').val(userData.data.email); currentUser.email = userData.username; } popup.dialog('close').remove(); toastr.success(monster.template(self, '!' + toastrMessages.userUpdated, { name: userData.data.first_name + ' ' + userData.data.last_name })); }); } }); passwordTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); var popup = monster.ui.dialog(passwordTemplate, { title: self.i18n.active().users.dialogChangePassword.title }); }); template.on('click', '#open_fmfm_link', function() { renderFindMeFollowMeFeature(function(usersRenderArgs) { usersRenderArgs.openedTab = 'name'; self.usersRender(usersRenderArgs); }); }); template.on('focus', '.ringing-timeout.disabled #ringing_timeout', function() { $(this).blur(); }); /* Events for Devices in Users */ template.on('click', '.create-device', function() { var $this = $(this), type = $this.data('type'), userId = $this.parents('.grid-row').data('id'); monster.pub('voip.devices.renderAdd', { type: type, callback: function(device) { var rowDevice = monster.template(self, 'users-rowSpareDevice', device), listAssigned = template.find('.list-assigned-items'); listAssigned.find('.empty-row').hide(); /* reset search */ listAssigned.find('.empty-search-row').hide(); template.find('.unassigned-list-header .search-query').val(''); listAssigned.find('.item-row').show(); /* Add row */ listAssigned.append(rowDevice) .find('.item-row[data-id="' + device.id + '"]') .toggleClass('updated') .find('button') .removeClass('add-device btn-primary') .addClass('remove-device btn-danger') .text(self.i18n.active().remove); } }); }); template.on('click', '.save-devices', function() { var dataDevices = { new: [], old: [] }, name = $(this).parents('.grid-row').find('.grid-cell.name').text(), userId = $(this).parents('.grid-row').data('id'); template.find('.detail-devices .list-assigned-items .item-row.updated').each(function(k, row) { dataDevices.new.push($(row).data('id')); }); template.find('.detail-devices .list-unassigned-items .item-row.updated').each(function(k, row) { dataDevices.old.push($(row).data('id')); }); self.usersUpdateDevices(dataDevices, userId, function() { toastr.success(monster.template(self, '!' + toastrMessages.devicesUpdated, { name: name })); self.usersRender({ userId: userId }); }); }); template.on('click', '.detail-devices .edit-device-link', function() { var row = $(this).parents('.item-row'), id = row.data('id') monster.pub('voip.devices.editDevice', { data: { id: id }, callbackSave: function(device) { row.find('.edit-device').html(device.name); }, callbackDelete: function(device) { row.remove(); } } ); }); template.on('click', '.detail-devices .list-unassigned-items .add-device', function() { var row = $(this).parents('.item-row'), spare = template.find('.count-spare'), countSpare = spare.data('count') - 1, assignedList = template.find('.detail-devices .list-assigned-items'); spare .html(countSpare) .data('count', countSpare); row.toggleClass('updated') .find('button') .removeClass('add-device btn-primary') .addClass('remove-device btn-danger') .text(self.i18n.active().remove); assignedList.find('.empty-row').hide(); assignedList.append(row); var rows = template.find('.list-unassigned-items .item-row'); if(rows.size() === 0) { template.find('.detail-devices .list-unassigned-items .empty-row').show(); } else if(rows.is(':visible') === false) { template.find('.detail-devices .list-unassigned-items .empty-search-row').show(); } }); template.on('click', '.detail-devices .list-assigned-items .remove-device', function() { var row = $(this).parents('.item-row'), spare = template.find('.count-spare'), countSpare = spare.data('count') + 1, unassignedList = template.find('.detail-devices .list-unassigned-items'); /* Alter the html */ row.hide(); row.toggleClass('updated') .find('button') .removeClass('remove-device btn-danger') .addClass('add-device btn-primary') .text(self.i18n.active().add); unassignedList.append(row); unassignedList.find('.empty-row').hide(); spare .html(countSpare) .data('count', countSpare); var rows = template.find('.detail-devices .list-assigned-items .item-row'); /* If no rows beside the clicked one, display empty row */ if(rows.is(':visible') === false) { template.find('.detail-devices .list-assigned-items .empty-row').show(); } /* If it matches the search string, show it */ if(row.data('search').indexOf(currentNumberSearch) >= 0) { row.show(); unassignedList.find('.empty-search-row').hide(); } }); template.on('keyup', '.detail-devices .search-query', function() { var searchString = $(this).val().toLowerCase(), rows = template.find('.list-unassigned-items .item-row'), emptySearch = template.find('.list-unassigned-items .empty-search-row'); _.each(rows, function(row) { var row = $(row); row.data('search').toLowerCase().indexOf(searchString) < 0 ? row.hide() : row.show(); }); if(rows.size() > 0) { rows.is(':visible') ? emptySearch.hide() : emptySearch.show(); } }); /* Events for Numbers in Users */ template.on('click', '.detail-numbers .list-assigned-items .remove-number', function() { var $this = $(this), userName = currentUser.first_name + ' ' + currentUser.last_name, dataNumbers = $.extend(true, [], extensionsToSave), userId = currentUser.id, row = $this.parents('.item-row'); row.slideUp(function() { row.remove(); if ( !template.find('.list-assigned-items .item-row').is(':visible') ) { template.find('.list-assigned-items .empty-row').slideDown(); } template.find('.item-row').each(function(idx, elem) { dataNumbers.push($(elem).data('id')); }); if(dataNumbers.length > 0) { var updateCallflow = function() { self.usersUpdateCallflowNumbers(userId, (currentCallflow || {}).id, dataNumbers, function(callflowData) { toastr.success(monster.template(self, '!' + toastrMessages.numbersUpdated, { name: userName })); self.usersRender({ userId: userId }); }); }, updateUserAndCallflow = function(user) { self.usersUpdateUser(user, function() { updateCallflow(); }); }; // If we deleted a number that was used as the Caller-ID , disable the Caller-ID feature. var needUpdateUser = false; if(currentUser.caller_id.hasOwnProperty('internal') && dataNumbers.indexOf(currentUser.caller_id.internal.number) < 0) { delete currentUser.caller_id.internal.number; needUpdateUser = true; } if(currentUser.caller_id.hasOwnProperty('external') && dataNumbers.indexOf(currentUser.caller_id.external.number) < 0) { delete currentUser.caller_id.external.number; needUpdateUser = true; } if(!self.usersHasProperPresenceId(dataNumbers, currentUser)) { self.usersUpdatePresenceIDPopup(dataNumbers, currentUser, function(user) { updateUserAndCallflow(user); }); } else { if(needUpdateUser) { updateUserAndCallflow(currentUser); } else { updateCallflow(); } } } else { monster.ui.alert('warning', self.i18n.active().users.noNumberCallflow); self.usersRender({ userId: userId }); } }); }); template.on('click', '.callerId-number', function() { var cnamCell = $(this).parents('.item-row').first(), phoneNumber = cnamCell.data('id'); if(phoneNumber) { var args = { phoneNumber: phoneNumber, callbacks: { success: function(data) { if('cnam' in data.data && data.data.cnam.display_name) { cnamCell.find('.features i.feature-outbound_cnam').addClass('active'); } else { cnamCell.find('.features i.feature-outbound_cnam').removeClass('active'); } if('cnam' in data.data && data.data.cnam.inbound_lookup) { cnamCell.find('.features i.feature-inbound_cnam').addClass('active'); } else { cnamCell.find('.features i.feature-inbound_cnam').removeClass('active'); } } } }; monster.pub('common.callerId.renderPopup', args); } }); template.on('click', '.e911-number', function() { var e911Cell = $(this).parents('.item-row').first(), phoneNumber = e911Cell.data('id'); if(phoneNumber) { var args = { phoneNumber: phoneNumber, callbacks: { success: function(data) { if(!($.isEmptyObject(data.data.dash_e911))) { e911Cell.find('.features i.feature-dash_e911').addClass('active'); } else { e911Cell.find('.features i.feature-dash_e911').removeClass('active'); } } } }; monster.pub('common.e911.renderPopup', args); } }); template.on('click', '.prepend-number', function() { var prependCell = $(this).parents('.item-row').first(), phoneNumber = prependCell.data('id'); if(phoneNumber) { var args = { phoneNumber: phoneNumber, callbacks: { success: function(data) { if('prepend' in data.data && data.data.prepend.enabled) { prependCell.find('.features i.feature-prepend').addClass('active'); } else { prependCell.find('.features i.feature-prepend').removeClass('active'); } } } }; monster.pub('common.numberPrepend.renderPopup', args); } }); template.on('click', '.feature[data-feature="caller_id"]', function() { self.usersRenderCallerId(currentUser); }); template.on('click', '.feature[data-feature="call_forward"]', function() { if(currentUser.features.indexOf('find_me_follow_me') < 0) { var featureUser = $.extend(true, {}, currentUser); self.usersGetMainCallflow(featureUser.id, function(mainCallflow) { if(mainCallflow && 'flow' in mainCallflow) { var flow = mainCallflow.flow; while(flow.module != 'user' && '_' in flow.children) { flow = flow.children['_']; } if(flow.data.timeout < 30) { featureUser.extra.timeoutTooShort = true; } } self.usersRenderCallForward(featureUser); }); } else { self.usersRenderCallForward(currentUser); } }); template.on('click', '.feature[data-feature="hotdesk"]', function() { self.usersRenderHotdesk(currentUser); }); template.on('click', '.feature[data-feature="find_me_follow_me"]', function() { renderFindMeFollowMeFeature(); }); template.on('click', '.feature[data-feature="call_recording"]', function() { self.usersGetMainCallflow(currentUser.id, function(callflow) { if(callflow) { self.usersRenderCallRecording({ userCallflow: callflow, currentUser: currentUser }); } else { monster.ui.alert('error', self.i18n.active().users.call_recording.noNumber); } }); }); template.on('click', '.feature[data-feature="music_on_hold"]', function() { self.usersRenderMusicOnHold(currentUser); }); template.on('click', '.feature[data-feature="vm_to_email"]', function() { self.usersListVMBoxesUser(currentUser.id, function(vmboxes) { currentUser.extra.deleteAfterNotify = true; if(vmboxes.length > 0) { self.usersGetVMBox(vmboxes[0].id, function(data) { currentUser.extra.deleteAfterNotify = data.delete_after_notify; self.usersRenderVMToEmail(currentUser); }); } else { self.usersRenderVMToEmail(currentUser); } }); }); template.on('click', '.feature[data-feature="conferencing"]', function() { self.usersGetConferenceFeature(currentUser.id, function(dataConf) { var data = { listConferences: dataConf.listConfNumbers, user: currentUser, conference: dataConf.conference }; if(_.isEmpty(data.listConferences)) { monster.ui.alert('error', self.i18n.active().users.conferencing.noConfNumbers); } else { self.usersRenderConferencing(data); } }); }); template.on('click', '.feature[data-feature="faxing"]', function() { monster.parallel({ numbers: function(callback) { self.usersListNumbers(function(listNumbers) { var spareNumbers = {}; _.each(listNumbers.numbers, function(number, key) { if(number.used_by === '') { spareNumbers[key] = number; } }); callback && callback(null, spareNumbers); }); }, callflows: function(callback) { self.usersListCallflowsUser(currentUser.id, function(callflows) { var existingCallflow; _.each(callflows, function(callflow) { if(callflow.type === 'faxing') { existingCallflow = callflow; return false; } }); if ( existingCallflow ) { self.callApi({ resource: 'callflow.get', data: { accountId: self.accountId, callflowId: existingCallflow.id }, success: function(data, status) { callback && callback(null, data.data); } }); } else { callback && callback(null, existingCallflow); } }); } }, function(err, results) { results.user = currentUser; if ( typeof results.callflows !== 'undefined' ) { // Compatibility with old version var faxboxId = results.callflows.flow.data.hasOwnProperty('faxbox_id') ? results.callflows.flow.data.faxbox_id : results.callflows.flow.data.id; self.callApi({ resource: 'faxbox.get', data: { accountId: self.accountId, faxboxId: faxboxId }, success: function(_data) { results.faxbox = _data.data; results.faxbox.id = faxboxId; self.usersRenderFaxboxes(results); }, error: function() { self.usersRenderFaxboxes(results); } }); } else { self.usersRenderFaxboxes(results); } } ); }); template.on('click', '.actions .spare-link:not(.disabled)', function(e) { e.preventDefault(); var $this = $(this), args = { accountName: monster.apps['auth'].currentAccount.name, accountId: self.accountId, callback: function(numberList) { var name = $this.parents('.grid-row').find('.grid-cell.name').text(), dataNumbers = $.extend(true, [], extensionsToSave), userId = $this.parents('.grid-row').data('id'); template.find('.empty-row').hide(); template.find('.item-row').each(function(idx, elem) { dataNumbers.push($(elem).data('id')); }); _.each(numberList, function(val, idx) { dataNumbers.push(val.phoneNumber); val.isLocal = val.features.indexOf('local') > -1; template .find('.list-assigned-items') .append($(monster.template(self, 'users-numbersItemRow', { number: val }))); }); self.usersUpdateCallflowNumbers(userId, (currentCallflow || {}).id, dataNumbers, function(callflowData) { toastr.success(monster.template(self, '!' + toastrMessages.numbersUpdated, { name: name })); self.usersRender({ userId: callflowData.owner_id }); }); } } monster.pub('common.numbers.dialogSpare', args); }); template.on('click', '.actions .buy-link', function(e) { e.preventDefault(); monster.pub('common.buyNumbers', { searchType: $(this).data('type'), callbacks: { success: function(numbers) { var countNew = 0; monster.pub('common.numbers.getListFeatures', function(features) { _.each(numbers, function(number, k) { countNew++; /* Formating number */ number.viewFeatures = $.extend(true, {}, features); var rowTemplate = monster.template(self, 'users-rowSpareNumber', number); template.find('.list-unassigned-items .empty-row').hide(); template.find('.list-unassigned-items').append(rowTemplate); }); var previous = parseInt(template.find('.unassigned-list-header .count-spare').data('count')), newTotal = previous + countNew; template.find('.unassigned-list-header .count-spare') .data('count', newTotal) .html(newTotal); template.find('.spare-link.disabled').removeClass('disabled'); }); }, error: function(error) { } } }); }); $('body').on('click', '#users_container_overlay', function() { template.find('.edit-user').slideUp("400", function() { $(this).empty(); }); self.usersRemoveOverlay(); template.find('.grid-cell.active').css({ 'position': 'inline-block', 'z-index': '0' }); template.find('.grid-row.active').parent().siblings('.edit-user').css({ 'position': 'block', 'z-index': '0' }); template.find('.grid-cell.active').removeClass('active'); template.find('.grid-row.active').removeClass('active'); }); }, usersUpdatePresenceIDPopup: function(numbers, user, callback) { var self = this, dataTemplate = { numbers: numbers }, template = $(monster.template(self, 'users-changePresenceIDPopup', dataTemplate)), $options = template.find('.presence-id-option'); $options.on('click', function() { $options.removeClass('active'); $(this).addClass('active'); }); template.find('.save-presence-id').on('click', function() { var newPresenceID = template.find('.presence-id-option.active').data('number'); if(newPresenceID !== 'none') { user.presence_id = newPresenceID; } else { delete user.presence_id; } popup.dialog('close').remove(); callback && callback(user); }); template.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); var popup = monster.ui.dialog(template, { title: self.i18n.active().users.presenceIDPopup.title, position: ['center', 20] }); }, usersHasProperPresenceId: function(listNumbers, user) { var self = this; if(user.presence_id) { var found = false, formattedPresenceID = '' + user.presence_id; _.each(listNumbers, function(number) { if(number === formattedPresenceID) { found = true; } }); return found; } else { return true; } }, /* Helper function that takes an array of number in parameter, sorts it, and returns the first number not in the array, greater than the minVal */ usersGetNextInt: function(listNumbers) { var orderedArray = listNumbers, previousIterationNumber, minNumber = 2000, increment = 1; orderedArray.sort(function(a,b) { var parsedA = parseInt(a), parsedB = parseInt(b); if(isNaN(parsedA)) { return -1; } else if(isNaN(parsedB)) { return 1; } else { return parsedA > parsedB ? 1 : -1; } }); _.each(orderedArray, function(number) { var currentNumber = parseInt(number); // First we make sure it's a valid number, if not we move on to the next number if(!isNaN(currentNumber)) { // If we went through this loop already, previousIterationNumber will be set to the number of the previous iteration if(previousIterationNumber) { if(currentNumber - previousIterationNumber !== increment) { return previousIterationNumber + increment; } } // else, it's the first iteration, we initialize the minValue to the first number in the ordered array else { minNumber = currentNumber; } // We store current as the previous number for the next iteration previousIterationNumber = currentNumber; } }); return (previousIterationNumber) ? previousIterationNumber + increment : minNumber; }, usersFormatAddUser: function(data) { var self = this, formattedData = { sendToSameEmail: true, nextExtension: '', nextVMBox: '', listExtensions: {}, listVMBoxes:{}, }, arrayExtensions = [], arrayVMBoxes = []; _.each(data.callflows, function(callflow) { _.each(callflow.numbers, function(number) { if(number.length < 7) { formattedData.listExtensions[number] = callflow; arrayExtensions.push(number); } }); }); formattedData.nextExtension = parseInt(self.usersGetNextInt(arrayExtensions)) + ''; _.each(data.vmboxes, function(vmbox) { formattedData.listVMBoxes[vmbox.mailbox] = vmbox; arrayVMBoxes.push(vmbox.mailbox); }); //Set the VMBox Number to 2001 if there are no VMBox in the system, or to the latest vmbox number + 1 if there are formattedData.nextVMBox = parseInt(self.usersGetNextInt(arrayVMBoxes)) + ''; return formattedData; }, usersFormatFaxingData: function(data) { var tempList = [], listNumbers = {}; _.each(data.numbers, function(val, key){ tempList.push(key); }); tempList.sort(function(a, b) { return a < b ? -1 : 1; }); if(data.callflows) { if(data.callflows.numbers.length > 0) { listNumbers[data.callflows.numbers[0]] = data.callflows.numbers[0] } } _.each(tempList, function(val, key) { listNumbers[val] = val; }); data.extra = $.extend(true, {}, data.extra, { listNumbers: listNumbers }); return data; }, usersRenderConferencing: function(data) { var self = this, data = self.usersFormatConferencingData(data), featureTemplate = $(monster.template(self, 'users-feature-conferencing', data)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), featureForm = featureTemplate.find('#conferencing_form'); monster.ui.validate(featureForm); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.save').on('click', function() { var args = { openedTab: 'features', callback: function() { popup.dialog('close').remove(); } }; if(monster.ui.valid(featureForm)) { data.conference = monster.ui.getFormData('conferencing_form'); if(switchFeature.bootstrapSwitch('status')) { self.usersUpdateConferencing(data, function(data) { args.userId = data.user.id; self.usersRender(args); }); } else { self.usersDeleteConferencing(data.user.id, function(data) { args.userId = data.user.data.id; self.usersRender(args); }); } } }); monster.ui.prettyCheck.create(featureTemplate.find('.content')); var popup = monster.ui.dialog(featureTemplate, { title: data.user.extra.mapFeatures.conferencing.title, position: ['center', 20] }); }, usersFormatConferencingData: function(data) { return data; }, usersRenderFaxboxes: function(data) { var self = this, data = self.usersFormatFaxingData(data), featureTemplate = $(monster.template(self, 'users-feature-faxing', data)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(); monster.ui.prettyCheck.create(featureTemplate.find('.content')); if ( !_.isEmpty(data.extra.listNumbers) ) { var popup = monster.ui.dialog(featureTemplate, { title: data.user.extra.mapFeatures.faxing.title, position: ['center', 20] }); } else { monster.ui.alert('error', self.i18n.active().users.errorNumberFaxing); } featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.save').on('click', function() { var newNumber = featureTemplate.find('#caller_id').val(), args = { openedTab: 'features', callback: function() { popup.dialog('close').remove(); } }; if ( switchFeature.bootstrapSwitch('status') ) { self.usersUpdateFaxing(data, newNumber, function(results) { args.userId = results.callflow.owner_id; self.usersRender(args); }); } else { self.usersDeleteFaxing(data.user.id, function() { args.userId = data.user.id; self.usersRender(args); }); } }); }, usersRenderHotdesk: function(currentUser) { var self = this, featureTemplate = $(monster.template(self, 'users-feature-hotdesk', currentUser)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), requirePin = featureTemplate.find('[name="require_pin"]'), featureForm = featureTemplate.find('#hotdesk_form'); monster.ui.validate(featureForm); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); requirePin.on('ifChanged', function() { if(requirePin.is(':checked')) { featureTemplate.find('#pin') .removeAttr('disabled', 'disabled') .focus(); } else { featureTemplate.find('#pin') .val('') .attr('disabled', 'disabled'); } }); featureTemplate.find('.save').on('click', function() { if(monster.ui.valid(featureForm)) { var formData = monster.ui.getFormData('hotdesk_form'), args = { openedTab: 'features', callback: function() { popup.dialog('close').remove(); } }; formData.enabled = switchFeature.bootstrapSwitch('status'); if(formData.require_pin === false) { delete formData.pin; } delete currentUser.hotdesk; userToSave = $.extend(true, {}, currentUser, { hotdesk: formData }); self.usersUpdateUser(userToSave, function(data) { args.userId = data.data.id; self.usersRender(args); }); } }); monster.ui.prettyCheck.create(featureTemplate.find('.content')); var popup = monster.ui.dialog(featureTemplate, { title: currentUser.extra.mapFeatures.hotdesk.title, position: ['center', 20] }); }, usersRenderVMToEmail: function(currentUser) { var self = this, featureTemplate = $(monster.template(self, 'users-feature-vm_to_email', currentUser)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), featureForm = featureTemplate.find('#vm_to_email_form'); monster.ui.validate(featureForm); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.save').on('click', function() { var formData = monster.ui.getFormData('vm_to_email_form'), userToSave = $.extend(true, {}, currentUser), enabled = switchFeature.bootstrapSwitch('status'), args = { callback: function() { popup.dialog('close').remove(); }, openedTab: 'features' }, updateUser = function(user) { self.usersUpdateUser(user, function(data) { args.userId = data.data.id; self.usersRender(args); }); }, /* updates all the vmboxes with the new delete after notify setting, and then calls the callback*/ updateVMsDeleteAfterNotify = function(val, userId, callbackAfterUpdate) { self.usersListVMBoxesUser(userId, function(vmboxes) { var listFnParallel = []; _.each(vmboxes, function(vm) { listFnParallel.push(function(callback) { self.usersGetVMBox(vm.id, function(data) { /* Only update vms if the deleteAfterNotify value is different than before */ if(data.delete_after_notify !== val) { data.delete_after_notify = val; self.usersUpdateVMBox(data, function(data) { callback(null, data); }); } else { callback(null, data); } }); }); }); monster.parallel(listFnParallel, function(err, results) { callbackAfterUpdate && callbackAfterUpdate(results); }); }); }; userToSave.vm_to_email_enabled = enabled; /* Only update the email and the checkboxes if the setting is enabled */ if(enabled === true) { if(monster.ui.valid(featureForm)) { userToSave.email = formData.email; /* Update VMBoxes, then update user and finally close the popup */ updateVMsDeleteAfterNotify(formData.delete_after_notify, userToSave.id, function() { updateUser(userToSave); }); } } else { updateUser(userToSave); } }); monster.ui.prettyCheck.create(featureTemplate.find('.content')); var popup = monster.ui.dialog(featureTemplate, { title: currentUser.extra.mapFeatures.vm_to_email.title, position: ['center', 20] }); }, usersRenderCallerId: function(currentUser) { var self = this, featureTemplate = $(monster.template(self, 'users-feature-caller_id', currentUser)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.save').on('click', function() { var switchCallerId = featureTemplate.find('.switch'), userData = currentUser, userToSave = $.extend(true, {}, { caller_id: { internal: {}, external: {}, } }, currentUser), args = { openedTab: 'features', callback: function() { popup.dialog('close').remove(); } }; if(switchCallerId.bootstrapSwitch('status') === false) { if(userToSave.caller_id.hasOwnProperty('internal')) { delete userToSave.caller_id.internal.number; } if(userToSave.caller_id.hasOwnProperty('external')) { delete userToSave.caller_id.external.number; } } else { var callerIdValue = featureTemplate.find('.caller-id-select').val(); userToSave.caller_id.internal.number = callerIdValue; userToSave.caller_id.external.number = callerIdValue; } self.usersUpdateUser(userToSave, function(data) { args.userId = data.data.id; self.usersRender(args); }); }); if(currentUser.extra.listCallerId.length > 0){ var popup = monster.ui.dialog(featureTemplate, { title: currentUser.extra.mapFeatures.caller_id.title, position: ['center', 20] }); } else { monster.ui.alert('error', self.i18n.active().users.errorCallerId); } }, usersRenderCallForward: function(currentUser) { var self = this, featureTemplate = $(monster.template(self, 'users-feature-call_forward', currentUser)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), featureForm = featureTemplate.find('#call_forward_form'), args = { callback: function() { popup.dialog('close').remove(); }, openedTab: 'features' }, timeoutWarningBox = featureTemplate.find('.timeout-warning'); if(currentUser.hasOwnProperty('call_forward') && currentUser.call_forward.require_keypress) { timeoutWarningBox.hide(); } monster.ui.validate(featureForm); monster.ui.prettyCheck.create(featureTemplate.find('.content')); featureTemplate.find('input[name="require_keypress"]').on('ifToggled', function() { timeoutWarningBox.toggle(); }); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('#phoneType').on('change', function() { if ( $(this).val() === 'mobile' ) { featureTemplate.find('#number').mask('+1 (999) 999-9999'); } else if ( $(this).val() === 'deskphone' ) { featureTemplate.find('#number').mask('(999) 999-9999'); } }); featureTemplate.find('.save').on('click', function() { if(monster.ui.valid(featureForm)) { var formData = monster.ui.getFormData('call_forward_form'); formData.require_keypress = !formData.require_keypress; formData.enabled = switchFeature.bootstrapSwitch('status'); formData.number = monster.util.unformatPhoneNumber(formData.number, 'keepPlus'); delete formData.phoneType; var userToSave = $.extend(true, {}, currentUser, { call_forward: formData}); if(timeoutWarningBox.is(':visible')) { args.openedTab = 'name'; } self.usersUpdateUser(userToSave, function(data) { args.userId = data.data.id; self.usersRender(args); }); } }); if (currentUser.hasOwnProperty('call_forward') && currentUser.call_forward.number && /^(\+1)/.test(currentUser.call_forward.number)) { featureTemplate.find('#phoneType').val('mobile'); featureTemplate.find('#number').mask('+1 (999) 999-9999'); } else { featureTemplate.find('#phoneType').val('deskphone'); featureTemplate.find('#number').mask('(999) 999-9999'); } var popup = monster.ui.dialog(featureTemplate, { title: currentUser.extra.mapFeatures.call_forward.title, position: ['center', 20] }); }, usersRenderFindMeFollowMe: function(params) { var self = this; if(!params.userCallflow) { monster.ui.alert('error', self.i18n.active().users.find_me_follow_me.noNumber); } else if(!params.userDevices || params.userDevices.length === 0) { monster.ui.alert('error', self.i18n.active().users.find_me_follow_me.noDevice); } else { var currentUser = params.currentUser, userCallflow = params.userCallflow, featureTemplate = $(monster.template(self, 'users-feature-find_me_follow_me', { currentUser: currentUser })), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), featureForm = featureTemplate.find('#find_me_follow_me_form'), args = { callback: function() { popup.dialog('close').remove(); }, openedTab: 'features' }, endpoints = (userCallflow.flow.module === 'ring_group' ? userCallflow.flow.data.endpoints : []), userDevices = {}; _.each(params.userDevices, function(val) { userDevices[val.id] = val; }); endpoints = $.map(endpoints, function(endpoint) { if(userDevices[endpoint.id]) { var device = userDevices[endpoint.id]; delete userDevices[endpoint.id]; return { id: endpoint.id, delay: endpoint.delay, timeout: endpoint.timeout, name: device.name, icon: self.deviceIcons[device.device_type], disabled: false } } }); _.each(userDevices, function(device) { endpoints.push({ id: device.id, delay: 0, timeout: 0, name: device.name, icon: self.deviceIcons[device.device_type], disabled: true }) }); monster.pub('common.ringingDurationControl.render', { container: featureForm, endpoints: endpoints, hasDisableColumn: true }); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.save').on('click', function() { var enabled = switchFeature.bootstrapSwitch('status'); monster.pub('common.ringingDurationControl.getEndpoints', { container: featureForm, callback: function(endpoints) { currentUser.smartpbx = currentUser.smartpbx || {}; currentUser.smartpbx.find_me_follow_me = currentUser.smartpbx.find_me_follow_me || {}; currentUser.smartpbx.find_me_follow_me.enabled = (enabled && endpoints.length > 0); if(enabled && endpoints.length > 0) { userCallflow.flow.module = 'ring_group'; userCallflow.flow.data = { strategy: "simultaneous", timeout: 20, endpoints: [] } _.each(endpoints, function(endpoint) { userCallflow.flow.data.endpoints.push({ id: endpoint.id, endpoint_type: "device", delay: endpoint.delay, timeout: endpoint.timeout }); if((endpoint.delay+endpoint.timeout) > userCallflow.flow.data.timeout) { userCallflow.flow.data.timeout = (endpoint.delay+endpoint.timeout); } }); } else { userCallflow.flow.module = 'user'; userCallflow.flow.data = { can_call_self: false, id: currentUser.id, timeout: 20 } } monster.parallel({ callflow: function(callbackParallel) { self.usersUpdateCallflow(userCallflow, function(data) { callbackParallel && callbackParallel(null, data.data); }); }, user: function(callbackParallel) { self.usersUpdateUser(currentUser, function(data) { callbackParallel && callbackParallel(null, data.data); }); } }, function(err, results) { args.userId = results.user.id; if(typeof params.saveCallback === 'function') { params.saveCallback(args); } else { self.usersRender(args); } } ); } }); }); var popup = monster.ui.dialog(featureTemplate, { title: currentUser.extra.mapFeatures.find_me_follow_me.title, position: ['center', 20] }); } }, usersRenderFindMeFollowMeSliders: function(template, deviceList, ringGroup, maxSeconds) { var self = this, scaleSections = 6, //Number of 'sections' in the time scales for the sliders scaleMaxSeconds = maxSeconds && maxSeconds >= 30 ? maxSeconds : 120; //Maximum of seconds, corresponding to the end of the scale if(!maxSeconds) { var currentMax = 0; _.each(ringGroup, function(endpoint) { currentMax = (endpoint.delay+endpoint.timeout > currentMax) ? endpoint.delay+endpoint.timeout : currentMax; }); scaleMaxSeconds = currentMax > scaleMaxSeconds ? Math.ceil(currentMax/60)*60 : scaleMaxSeconds; } var sliderTooltip = function(event, ui) { var val = ui.value, tooltip = '
' + val + '
'; $(ui.handle).html(tooltip); }, createTooltip = function(event, ui, deviceId, sliderObj) { var val1 = sliderObj.slider('values', 0), val2 = sliderObj.slider('values', 1), tooltip1 = '
' + val1 + '
', tooltip2 = '
' + val2 + '
'; template.find('.device-row[data-device_id="'+ deviceId + '"] .slider-time .ui-slider-handle').first().html(tooltip1); template.find('.device-row[data-device_id="'+ deviceId + '"] .slider-time .ui-slider-handle').last().html(tooltip2); }, createSlider = function(device) { var deviceRow = template.find('.device-row[data-device_id="'+ device.id +'"]'); deviceRow.find('.slider-time').slider({ range: true, min: 0, max: scaleMaxSeconds, values: device.id in ringGroup ? [ ringGroup[device.id].delay, ringGroup[device.id].delay+ringGroup[device.id].timeout ] : [0,0], slide: sliderTooltip, change: sliderTooltip, create: function(event, ui) { createTooltip(event, ui, device.id, $(this)); }, }); createSliderScale(deviceRow); }, createSliderScale = function(container, isHeader) { var scaleContainer = container.find('.scale-container') isHeader = isHeader || false; scaleContainer.empty(); for(var i=1; i<=scaleSections; i++) { var toAppend = '
' + (isHeader ? (i==scaleSections ? '' :'') + Math.floor(i*scaleMaxSeconds/scaleSections) + ' Sec' : '') + '
'; scaleContainer.append(toAppend); } if(isHeader) { scaleContainer.append('0 Sec'); } }; _.each(deviceList, function(device) { createSlider(device); if(!(device.id in ringGroup)) { monster.ui.prettyCheck.action(template.find('.device-row[data-device_id="'+device.id+'"] .disable-device'), 'check'); } }); createSliderScale(template.find('.device-row.title'), true); }, usersRenderCallRecording: function(params) { var self = this, templateData = $.extend(true, { user: params.currentUser }, (params.currentUser.extra.mapFeatures.call_recording.active ? { url: params.userCallflow.flow.data.url, format: params.userCallflow.flow.data.format, timeLimit: params.userCallflow.flow.data.time_limit } : {}) ), featureTemplate = $(monster.template(self, 'users-feature-call_recording', templateData)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), featureForm = featureTemplate.find('#call_recording_form'), popup; monster.ui.validate(featureForm, { rules: { 'time_limit': { digits: true } } }); featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.save').on('click', function() { if(monster.ui.valid(featureForm)) { var formData = monster.ui.getFormData('call_recording_form'), enabled = switchFeature.bootstrapSwitch('status'); if(!('smartpbx' in params.currentUser)) { params.currentUser.smartpbx = {}; } if(!('call_recording' in params.currentUser.smartpbx)) { params.currentUser.smartpbx.call_recording = { enabled: false }; } if(params.currentUser.smartpbx.call_recording.enabled || enabled) { params.currentUser.smartpbx.call_recording.enabled = enabled; var newCallflow = $.extend(true, {}, params.userCallflow); if(enabled) { if(newCallflow.flow.module === 'record_call') { newCallflow.flow.data = $.extend(true, { action: "start" }, formData); } else { newCallflow.flow = { children: { "_": $.extend(true, {}, params.userCallflow.flow) }, module: "record_call", data: $.extend(true, { action: "start" }, formData) } var flow = newCallflow.flow; while(flow.children && '_' in flow.children) { if(flow.children['_'].module === 'record_call' && flow.children['_'].data.action === 'stop') { break; // If there is already a Stop Record Call } else if(flow.children['_'].module === 'voicemail') { var voicemailNode = $.extend(true, {}, flow.children['_']); flow.children['_'] = { module: 'record_call', data: { action: "stop" }, children: { '_': voicemailNode } } break; } else { flow = flow.children['_']; } } } } else { newCallflow.flow = $.extend(true, {}, params.userCallflow.flow.children["_"]); var flow = newCallflow.flow; while(flow.children && '_' in flow.children) { if(flow.children['_'].module === 'record_call') { flow.children = flow.children['_'].children; break; } else { flow = flow.children['_']; } } } self.usersUpdateCallflow(newCallflow, function(updatedCallflow) { self.usersUpdateUser(params.currentUser, function(updatedUser) { popup.dialog('close').remove(); self.usersRender({ userId: params.currentUser.id }); }); }); } else { popup.dialog('close').remove(); self.usersRender({ groupId: params.currentUser.id }); } } }); popup = monster.ui.dialog(featureTemplate, { title: params.currentUser.extra.mapFeatures.call_recording.title, position: ['center', 20] }); }, usersRenderMusicOnHold: function(currentUser) { var self = this, silenceMediaId = 'silence_stream://300000'; self.usersListMedias(function(medias) { var templateData = { user: currentUser, silenceMedia: silenceMediaId, mediaList: medias, media: 'music_on_hold' in currentUser && 'media_id' in currentUser.music_on_hold ? currentUser.music_on_hold.media_id : silenceMediaId }, featureTemplate = $(monster.template(self, 'users-feature-music_on_hold', templateData)), switchFeature = featureTemplate.find('.switch').bootstrapSwitch(), popup, closeUploadDiv = function(newMedia) { var uploadInput = featureTemplate.find('.upload-input'); uploadInput.wrap('
').closest('form').get(0).reset(); uploadInput.unwrap(); featureTemplate.find('.upload-div').slideUp(function() { featureTemplate.find('.upload-toggle').removeClass('active'); }); if(newMedia) { var mediaSelect = featureTemplate.find('.media-dropdown'); mediaSelect.append(''); mediaSelect.val(newMedia.id); } }; featureTemplate.find('.cancel-link').on('click', function() { popup.dialog('close').remove(); }); switchFeature.on('switch-change', function(e, data) { data.value ? featureTemplate.find('.content').slideDown() : featureTemplate.find('.content').slideUp(); }); featureTemplate.find('.upload-toggle').on('click', function() { if($(this).hasClass('active')) { featureTemplate.find('.upload-div').stop(true, true).slideUp(); } else { featureTemplate.find('.upload-div').stop(true, true).slideDown(); } }); featureTemplate.find('.upload-cancel').on('click', function() { closeUploadDiv(); }); featureTemplate.find('.upload-submit').on('click', function() { var file = featureTemplate.find('.upload-input')[0].files[0]; fileReader = new FileReader(); fileReader.onloadend = function(evt) { self.callApi({ resource: 'media.create', data: { accountId: self.accountId, data: { streamable: true, name: file.name, media_source: "upload", description: file.name } }, success: function(data, status) { var media = data.data; self.callApi({ resource: 'media.upload', data: { accountId: self.accountId, mediaId: media.id, data: evt.target.result }, success: function(data, status) { closeUploadDiv(media); }, error: function(data, status) { self.callApi({ resource: 'media.delete', data: { accountId: self.accountId, mediaId: media.id, data: {} }, success: function(data, status) { } }); } }); } }); }; if(file) { if(file.size >= (Math.pow(2,20) * 5)) { //If size bigger than 5MB monster.ui.alert(self.i18n.active().users.music_on_hold.fileTooBigAlert); } else { fileReader.readAsDataURL(file); } } else { monster.ui.alert(self.i18n.active().users.music_on_hold.emptyUploadAlert); } }); featureTemplate.find('.save').on('click', function() { var selectedMedia = featureTemplate.find('.media-dropdown option:selected').val(), enabled = switchFeature.bootstrapSwitch('status'); if(!('music_on_hold' in currentUser)) { currentUser.music_on_hold = {}; } if('media_id' in currentUser.music_on_hold || enabled) { if(enabled) { currentUser.music_on_hold = { media_id: selectedMedia }; } else { currentUser.music_on_hold = {}; } self.usersUpdateUser(currentUser, function(updatedUser) { popup.dialog('close').remove(); self.usersRender({ userId: currentUser.id }); }); } else { popup.dialog('close').remove(); self.usersRender({ userId: currentUser.id }); } }); popup = monster.ui.dialog(featureTemplate, { title: currentUser.extra.mapFeatures.music_on_hold.title, position: ['center', 20] }); }); }, usersCleanUserData: function(userData) { var userData = $.extend(true, {}, userData), fullName = userData.first_name + ' ' + userData.last_name, defaultCallerIdName = fullName.substring(0, 15), newCallerIDs = { caller_id: { internal: { name: defaultCallerIdName }, external: { name: defaultCallerIdName } } }; userData = $.extend(true, userData, newCallerIDs); /* If the user has been removed from the directory */ if(userData.extra) { if(userData.extra.includeInDirectory === false) { if('directories' in userData && userData.extra.mainDirectoryId && userData.extra.mainDirectoryId in userData.directories) { delete userData.directories[userData.extra.mainDirectoryId]; } } else { userData.directories = userData.directories || {}; if(userData.extra.mainCallflowId) { userData.directories[userData.extra.mainDirectoryId] = userData.extra.mainCallflowId; } } if('differentEmail' in userData.extra && userData.extra.differentEmail) { if ( 'email' in userData.extra ) { userData.email = userData.extra.email } } else { userData.email = userData.username; } if('language' in userData.extra) { if(userData.extra.language !== 'auto') { userData.language = userData.extra.language; } else { delete userData.language; } } } if(userData.hasOwnProperty('call_forward')) { if(userData.call_forward.number === '') { delete userData.call_forward.number; } } if(userData.presence_id === 'unset') { delete userData.presence_id; } delete userData.include_directory; delete userData.features; delete userData.extra; delete userData['']; return userData; }, usersGetTemplate: function(type, userId, listUsers, callbackAfterData) { var self = this, template; if(type === 'name') { self.usersGetNameTemplate(userId, listUsers, callbackAfterData); } else if(type === 'numbers') { self.usersGetNumbersTemplate(userId, callbackAfterData); } else if(type === 'extensions') { self.usersGetExtensionsTemplate(userId, callbackAfterData); } else if(type === 'features') { self.usersGetFeaturesTemplate(userId, listUsers, callbackAfterData); } else if(type === 'devices') { self.usersGetDevicesTemplate(userId, callbackAfterData); } }, usersGetFeaturesTemplate: function(userId, listUsers, callback) { var self = this; self.usersGetUser(userId, function(userData) { _.each(listUsers.users, function(user) { if(user.id === userData.id) { userData = $.extend(true, userData, user); } }); var dataTemplate = self.usersFormatUserData(userData); template = $(monster.template(self, 'users-features', dataTemplate)); callback && callback(template, dataTemplate); }); }, usersGetNameTemplate: function(userId, listUsers, callbackAfterFormat) { var self = this; monster.parallel({ mainCallflow: function(callback) { self.usersGetMainCallflow(userId, function(mainCallflow) { callback(null, mainCallflow); }); }, mainDirectory: function(callback) { self.usersGetMainDirectory(function(mainDirectory) { callback(null, mainDirectory); }); }, user: function(callback) { self.usersGetUser(userId, function(userData) { callback(null, userData); }); }, vmboxes: function(callback) { self.usersListVMBoxes(function(vmboxes) { var firstVmboxId, results = { listExisting: [], userVM: {} }; _.each(vmboxes, function(vmbox) { results.listExisting.push(vmbox.mailbox); if(vmbox.owner_id === userId && !firstVmboxId) { firstVmboxId = vmbox.id; } }); if(firstVmboxId) { self.usersGetVMBox(firstVmboxId, function(vmbox) { results.userVM = vmbox; callback(null, results); }); } else { callback(null, results); } }); } }, function(error, results) { var userData = results.user; _.each(listUsers.users, function(user) { if(user.id === results.user.id) { userData = $.extend(true, user, userData); return false; } }); var dataTemplate = self.usersFormatUserData(userData, results.mainDirectory, results.mainCallflow, results.vmboxes.userVM, results.vmboxes.listExisting); template = $(monster.template(self, 'users-name', dataTemplate)); monster.ui.validate(template.find('form.user-fields'), { rules: { 'extra.vmboxNumber': { checkList: dataTemplate.extra.existingVmboxes }, 'extra.ringingTimeout': { digits: true } }, messages: { 'first_name': { required: self.i18n.active().validation.required }, 'last_name': { required: self.i18n.active().validation.required } } }); timezone.populateDropdown(template.find('#user_timezone'), dataTemplate.timezone); template.find('[data-toggle="tooltip"]').tooltip({ container: 'body'}); callbackAfterFormat && callbackAfterFormat(template, dataTemplate); } ); }, usersGetDevicesData: function(callback) { var self = this; self.callApi({ resource: 'device.list', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(data) { callback && callback(data.data); } }); }, usersGetNumbersData: function(userId, callback) { var self = this; monster.parallel({ user: function(callbackParallel) { self.usersGetUser(userId, function(user) { callbackParallel && callbackParallel(null, user); }); }, callflow: function(callbackParallel) { var response = {}; self.usersListCallflows(function(callflows) { response.list = callflows; var callflowId; $.each(callflows, function(k, callflowLoop) { /* Find Smart PBX Callflow of this user */ if(callflowLoop.owner_id === userId && callflowLoop.type !== 'faxing') { callflowId = callflowLoop.id; return false; } }); if(callflowId) { self.callApi({ resource: 'callflow.get', data: { accountId: self.accountId, callflowId: callflowId }, success: function(callflow) { response.userCallflow = callflow.data; callbackParallel && callbackParallel(null, response); } }); } else { callbackParallel && callbackParallel(null, response); } }); }, numbers: function(callbackParallel) { self.usersListNumbers(function(listNumbers) { callbackParallel && callbackParallel(null, listNumbers); }); } }, function(err, results) { callback && callback(results); } ); }, usersGetNumbersTemplate: function(userId, callback) { var self = this; self.usersGetNumbersData(userId, function(results) { self.usersFormatNumbersData(userId, results, function(results) { template = $(monster.template(self, 'users-numbers', results)); callback && callback(template, results); }); }); }, usersGetDevicesTemplate: function(userId, callback) { var self = this; self.usersGetDevicesData(function(results) { var results = self.usersFormatDevicesData(userId, results); template = $(monster.template(self, 'users-devices', results)); callback && callback(template, results); }); }, usersGetExtensionsTemplate: function(userId, callback) { var self = this; self.usersGetNumbersData(userId, function(results) { self.usersFormatNumbersData(userId, results, function(results) { template = $(monster.template(self, 'users-extensions', results)); callback && callback(template, results); }); }); }, usersFormatDevicesData: function(userId, data) { var self = this, formattedData = { countSpare: 0, assignedDevices: {}, unassignedDevices: {} }; _.each(data, function(device) { if(device.owner_id === userId) { formattedData.assignedDevices[device.id] = device; } else if(device.owner_id === '' || !('owner_id' in device)) { formattedData.countSpare++; formattedData.unassignedDevices[device.id] = device; } }); formattedData.emptyAssigned = _.isEmpty(formattedData.assignedDevices); formattedData.emptySpare = _.isEmpty(formattedData.unassignedDevices); return formattedData; }, usersFormatNumbersData: function(userId, data, callback) { var self = this, response = { countSpare: 0, assignedNumbers: [], unassignedNumbers: {}, callflow: data.callflow.userCallflow, extensions: [], user: data.user || {} }; monster.pub('common.numbers.getListFeatures', function(features) { if('numbers' in data.numbers) { _.each(data.numbers.numbers, function(number, k) { /* Formating number */ number.viewFeatures = $.extend(true, {}, features); /* TODO: Once locality is enabled, we need to remove it */ number.localityEnabled = 'locality' in number ? true : false; _.each(number.features, function(feature) { if(feature in number.viewFeatures) { number.viewFeatures[feature].active = 'active'; } }); /* Adding to spare numbers */ if(number.used_by === '') { response.countSpare++; response.unassignedNumbers[k] = number; } }); } if(response.callflow) { /* If a number is in a callflow and is returned by the phone_numbers, add it to the assigned numbers */ _.each(response.callflow.numbers, function(number) { if(number in data.numbers.numbers) { var numberElement = data.numbers.numbers[number]; numberElement.phoneNumber = number; numberElement.isLocal = numberElement.features.indexOf('local') > -1; response.assignedNumbers.push(numberElement); } else { response.extensions.push(number); } }); } response.assignedNumbers = monster.util.sort(response.assignedNumbers, 'phoneNumber'); /* List of extensions */ response.allExtensions = []; _.each(data.callflow.list, function(callflow) { _.each(callflow.numbers, function(number) { /* If it's a valid extension number (ie: a number that's not in the number database) */ if(!(number in data.numbers.numbers) && !(_.isNaN(parseInt(number)))) { response.allExtensions.push(number); } }); }); /* Sort extensions so that we can recommend an available extension to a user whom would add a new one */ response.allExtensions.sort(function(a, b) { var parsedA = parseInt(a), parsedB = parseInt(b), result = -1; if(parsedA > 0 && parsedB > 0) { result = parsedA > parsedB; } return result; }); response.emptyAssigned = _.isEmpty(response.assignedNumbers); response.emptySpare = _.isEmpty(response.unassignedNumbers); response.emptyExtensions = _.isEmpty(response.extensions); callback && callback(response); }); }, usersFormatCreationData: function(data, callback) { var self = this, fullName = data.user.first_name + ' ' + data.user.last_name, callerIdName = fullName.substring(0, 15), defaultTimezone = timezone.getLocaleTimezone(), formattedData = { user: $.extend(true, {}, { caller_id: { internal: { name: callerIdName }, external: { name: callerIdName } }, presence_id: data.callflow.extension, email: data.extra.differentEmail ? data.extra.email : data.user.username, priv_level: 'user', timezone: defaultTimezone }, data.user), vmbox: { //mailbox: (data.callflow || {}).extension, mailbox: data.vmbox.number, name: fullName + '\'s VMBox', timezone: defaultTimezone }, callflow: { contact_list: { exclude: false }, flow: { children: { _: { children: {}, data: {}, module: 'voicemail' } }, data: { can_call_self: false, timeout: 20 }, module: 'user' }, name: fullName + ' SmartPBX\'s Callflow', numbers: [ (data.callflow || {}).extension ] }, extra: data.extra }; return formattedData; }, /* Utils */ usersDelete: function(userId, callback) { var self = this; monster.parallel({ devices: function(callback) { self.usersListDeviceUser(userId, function(devices) { callback(null, devices); }); }, vmbox: function(callback) { self.usersListVMBoxesUser(userId, function(data) { callback(null, data); }); }, callflows: function(callback) { self.usersListCallflowsUser(userId, function(data) { callback(null, data); }); } }, function(error, results) { var listFnDelete = []; _.each(results.devices, function(device) { listFnDelete.push(function(callback) { // self.usersDeleteDevice(device.id, function(data) { self.usersUnassignDevice(device.id, function(data) { callback(null, ''); }); }); }); _.each(results.callflows, function(callflow) { listFnDelete.push(function(callback) { self.usersDeleteCallflow(callflow.id, function(data) { callback(null, ''); }); }); }); _.each(results.vmbox, function(vmbox) { listFnDelete.push(function(callback) { self.usersDeleteVMBox(vmbox.id, function(data) { callback(null, ''); }); }); }); monster.parallel(listFnDelete, function(err, resultsDelete) { self.usersDeleteUser(userId, function(data) { toastr.success(monster.template(self, '!' + self.i18n.active().users.toastrMessages.userDelete, { name: data.first_name + ' ' + data.last_name })); self.usersRender(); }); }); } ); }, usersDeleteUser: function(userId, callback) { var self = this; self.callApi({ resource: 'user.delete', data: { userId: userId, accountId: self.accountId, data: {} }, success: function(data) { callback(data.data); } }); }, usersDeleteVMBox: function(vmboxId, callback) { var self = this; self.callApi({ resource: 'voicemail.delete', data: { voicemailId: vmboxId, accountId: self.accountId, data: {} }, success: function(data) { callback(data.data); } }); }, usersUnassignDevice: function(deviceId, callback) { var self = this; self.usersGetDevice(deviceId, function(deviceGet) { delete deviceGet.owner_id; self.usersUpdateDevice(deviceGet, function(updatedDevice) { callback && callback(updatedDevice); }); }); }, usersDeleteDevice: function(deviceId, callback) { var self = this; self.callApi({ resource: 'device.delete', data: { deviceId: deviceId, accountId: self.accountId, data: {} }, success: function(data) { callback(data.data); } }); }, usersDeleteCallflow: function(callflowId, callback) { var self = this; self.callApi({ resource: 'callflow.delete', data: { callflowId: callflowId, accountId: self.accountId, data: {} }, success: function(data) { callback(data.data); } }); }, usersCreate: function(data, callback) { var self = this; self.callApi({ resource: 'user.create', data: { accountId: self.accountId, data: data.user }, success: function(_dataUser) { var userId = _dataUser.data.id; data.user.id = userId; data.vmbox.owner_id = userId; self.usersCreateVMBox(data.vmbox, function(_dataVM) { data.callflow.owner_id = userId; data.callflow.type = 'mainUserCallflow'; data.callflow.flow.data.id = userId; data.callflow.flow.children['_'].data.id = _dataVM.id; self.usersCreateCallflow(data.callflow, function(_dataCF) { if(data.extra.includeInDirectory) { self.usersAddUserToMainDirectory(_dataUser.data, _dataCF.id, function(dataDirectory) { callback(data); }); } else { callback(data); } }); }); } }); }, /* Hack to support users from previous version */ usersMigrateFromExtensions: function(userId, listExtensions, callback) { var self = this; self.usersGetUser(userId, function(user) { user.extra = { vmbox: { mailbox: listExtensions[0] } }; var fullName = user.first_name + ' ' + user.last_name, callflow = { contact_list: { exclude: false }, flow: { children: { _: { children: {}, data: { }, module: 'voicemail' } }, data: { id: user.id, can_call_self: false, timeout: 20 }, module: 'user' }, name: fullName + ' SmartPBX\'s Callflow', numbers: listExtensions, owner_id: user.id, type: 'mainUserCallflow' }; self.usersSmartUpdateVMBox(user, false, function(_dataVM) { callflow.flow.children['_'].data.id = _dataVM.id; self.usersCreateCallflow(callflow, function(_dataCF) { callback && callback(_dataCF); }, function(errorPayload, globalHandler) { var errorCallback = function() { globalHandler && globalHandler(errorPayload, { generateError: true }); }; if(errorPayload.error === '400' && errorPayload.hasOwnProperty('data') && errorPayload.data.hasOwnProperty('numbers') && errorPayload.data.numbers.hasOwnProperty('unique')) { self.usersHasKazooUICallflow(callflow, function(existingCallflow) { self.usersMigrateKazooUIUser(callflow, existingCallflow, callback); }, errorCallback); } else { errorCallback(); } }, false ); }); }); }, usersGetCallflowFromNumber: function(number, callback) { var self = this, found = false; self.callApi({ resource: 'callflow.searchByNumber', data: { accountId: self.accountId, value: number }, success: function(results) { if(results.data.length > 0) { _.each(results.data, function(callflow) { _.each(callflow.numbers, function(n) { if(n === number && found === false) { found = true; self.callApi({ resource: 'callflow.get', data: { accountId: self.accountId, callflowId: callflow.id }, success: function(callflow) { callback && callback(callflow.data); } }) } }); }); if(found === false) { callback && callback({}); } } else { callback && callback({}); } } }); }, usersHasKazooUICallflow: function(callflow, success, error) { var self = this, parallelRequests = {}, kazooUICallflowFound = 0, kazooUICallflow; // Check if we find a number that belong to a Kazoo-UI callflow _.each(callflow.numbers, function(number) { parallelRequests[number] = function(callback) { self.usersGetCallflowFromNumber(number, function(callflow) { if(!(callflow.hasOwnProperty('ui_metadata') && callflow.ui_metadata.hasOwnProperty('ui') && callflow.ui_metadata.ui === 'monster-ui')) { // If we already found a callflow if(typeof kazooUICallflow !== 'undefined') { // If it's not the same Callflow that we found before, we increment the # of callflows found, which will trigger an error later // If it's the same as before we do nothing if(callflow.id !== kazooUICallflow.id) { kazooUICallflowFound++; } } else { kazooUICallflowFound++; kazooUICallflow = callflow; } } callback && callback(null, {}); }); } }); monster.parallel(parallelRequests, function(err, results) { // If we didn't find a single non-Monster-UI Callflow, then we trigger the error if(kazooUICallflowFound === 0) { error && error(); } // If we had more than 1 Kazoo UI callflow, show an error saying the migration is impossible else if(kazooUICallflowFound > 1) { monster.ui.alert(self.i18n.active().users.migration.tooManyCallflows); } // Else, we have found 1 callflow from Kazoo-UI, migration is possible, we continue with the success callback else { success && success(kazooUICallflow); } }); }, usersMigrateKazooUIUser: function(callflowToCreate, existingCallflow, callback) { var self = this, newNumbers = []; // copy all the existing callflow numbers to the callflow we're about to create callflowToCreate.numbers = existingCallflow.numbers; // Update the numbers of the existing callflow so that we keep a trace of the migration _.each(existingCallflow.numbers, function(number) { newNumbers.push('old_' + number + '_r' + monster.util.randomString(6)); }); existingCallflow.numbers = newNumbers; // Make sure the User knows what's going to happen monster.ui.confirm(self.i18n.active().users.migration.confirmMigration, function() { // First update the existing callflow with its new fake numbers self.usersUpdateCallflow(existingCallflow, function(oldCallflow) { // Now that the numbers have been changed, we can create the new Monster UI Callflow self.usersCreateCallflow(callflowToCreate, function(newCallflow) { // Once all this is done, continue normally to the SmartPBX normal update callback && callback(newCallflow); }) }); }); }, usersAddUserToMainDirectory: function(dataUser, callflowId, callback) { var self = this; self.usersGetMainDirectory(function(directory) { dataUser.directories = dataUser.directories || {}; dataUser.directories[directory.id] = callflowId; self.usersUpdateUser(dataUser, function(data) { callback && callback(data); }); }); }, usersGetMainCallflow: function(userId, callback) { var self = this; self.usersListCallflowsUser(userId, function(listCallflows) { var indexMain = -1; _.each(listCallflows, function(callflow, index) { if(callflow.owner_id === userId && callflow.type === 'mainUserCallflow' || !('type' in callflow)) { indexMain = index; return false; } }); if(indexMain === -1) { //toastr.error(self.i18n.active().users.noUserCallflow); callback(null); } else { self.callApi({ resource: 'callflow.get', data: { accountId: self.accountId, callflowId: listCallflows[indexMain].id }, success: function(data) { callback(data.data); }, error: function() { callback(listCallflows[indexMain]); } }); } }); }, usersGetMainDirectory: function(callback) { var self = this; self.usersListDirectories(function(listDirectories) { var indexMain = -1; _.each(listDirectories, function(directory, index) { if(directory.name === 'SmartPBX Directory') { indexMain = index; return false; } }); if(indexMain === -1) { self.usersCreateMainDirectory(function(data) { callback(data); }); } else { callback(listDirectories[indexMain]); } }); }, usersListDirectories: function(callback) { var self = this; self.callApi({ resource: 'directory.list', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(data) { callback && callback(data.data); } }); }, usersCreateMainDirectory: function(callback) { var self = this, dataDirectory = { confirm_match: false, max_dtmf: 0, min_dtmf: 3, name: 'SmartPBX Directory', sort_by: 'last_name' }; self.callApi({ resource: 'directory.create', data: { accountId: self.accountId, data: dataDirectory }, success: function(data) { callback && callback(data.data); } }); }, usersListCallflows: function(callback) { var self = this; self.callApi({ resource: 'callflow.list', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(data) { callback(data.data); } }); }, usersListCallflowsUser: function(userId, callback) { var self = this; self.callApi({ resource: 'callflow.list', data: { accountId: self.accountId, filters: { filter_owner_id: userId, paginate: 'false' } }, success: function(data) { callback(data.data); } }); }, usersListVMBoxes: function(callback) { var self = this; self.callApi({ resource: 'voicemail.list', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(data) { callback(data.data); } }); }, usersListVMBoxesUser: function(userId, callback) { var self = this; self.callApi({ resource: 'voicemail.list', data: { accountId: self.accountId, filters: { filter_owner_id: userId, paginate: 'false' } }, success: function(data) { callback(data.data); } }); }, usersGetVMBox: function(vmboxId, callback) { var self = this; self.callApi({ resource: 'voicemail.get', data: { accountId: self.accountId, voicemailId: vmboxId }, success: function(data) { callback(data.data); } }); }, usersCreateVMBox: function(vmData, callback) { var self = this; self.callApi({ resource: 'voicemail.create', data: { accountId: self.accountId, data: vmData }, success: function(data) { callback(data.data); } }); }, usersUpdateVMBox: function(vmData, callback) { var self = this; self.callApi({ resource: 'voicemail.update', data: { accountId: self.accountId, data: vmData, voicemailId: vmData.id }, success: function(data) { callback(data.data); } }); }, usersGetUser: function(userId, callback) { var self = this; self.callApi({ resource: 'user.get', data: { accountId: self.accountId, userId: userId }, success: function(user) { callback && callback(user.data); } }); }, usersUpdateUser: function(userData, callback) { var self = this; userData = self.usersCleanUserData(userData); self.callApi({ resource: 'user.update', data: { accountId: self.accountId, userId: userData.id, data: userData }, success: function(userData) { callback && callback(userData); } }); }, usersGetConferenceFeature: function(userId, globalCallback) { var self = this, dataResponse = { conference: {}, listConfNumbers: [] }; monster.parallel({ confNumbers: function(callback) { self.usersListConfNumbers(function(numbers) { callback && callback(null, numbers); }); }, listConferences: function(callback) { self.usersListConferences(userId, function(conferences) { if(conferences.length > 0) { self.usersGetConference(conferences[0].id, function(conference) { callback && callback(null, conference); }); } else { callback && callback(null, {}); } }); } }, function(err, results) { dataResponse.conference = results.listConferences; dataResponse.listConfNumbers = results.confNumbers; globalCallback && globalCallback(dataResponse); } ); }, usersListConfNumbers: function(callback) { var self = this; self.callApi({ resource: 'callflow.list', data: { accountId: self.accountId, filters: { filter_type: 'conference', paginate: 'false' } }, success: function(data) { var numbers = []; _.each(data.data, function(conf) { if(conf.name === 'MainConference') { if(conf.numbers.length > 0 && conf.numbers[0] !== 'undefinedconf') { numbers = numbers.concat(conf.numbers); } } }); callback && callback(numbers); } }); }, usersListConferences: function(userId, callback) { var self = this; self.callApi({ resource: 'conference.list', data: { accountId: self.accountId, filters: { filter_owner_id: userId, paginate: 'false' } }, success: function(data) { callback(data.data); } }); }, usersGetConference: function(conferenceId, callback) { var self = this; self.callApi({ resource: 'conference.get', data: { accountId: self.accountId, conferenceId: conferenceId }, success: function(data) { callback(data.data); } }); }, usersUpdateConference: function(conference, callback) { var self = this; self.callApi({ resource: 'conference.update', data: { accountId: self.accountId, conferenceId: conference.id, data: conference }, success: function(data) { callback(data.data); } }); }, usersCreateConference: function(conference, callback) { var self = this; self.callApi({ resource: 'conference.create', data: { accountId: self.accountId, data: conference }, success: function(data) { callback(data.data); } }); }, usersDeleteConference: function(conferenceId, callback) { var self = this; self.callApi({ resource: 'conference.delete', data: { accountId: self.accountId, conferenceId: conferenceId, data: {} }, success: function(data) { callback(data.data); } }); }, usersGetDevice: function(deviceId, callback) { var self = this; self.callApi({ resource: 'device.get', data: { accountId: self.accountId, deviceId: deviceId }, success: function(data) { callback(data.data); } }); }, usersUpdateDevice: function(data, callback) { var self = this; self.callApi({ resource: 'device.update', data: { accountId: self.accountId, data: data, deviceId: data.id }, success: function(data) { callback(data.data); } }); }, usersListDeviceUser: function(userId, callback) { var self = this; self.callApi({ resource: 'device.list', data: { accountId: self.accountId, filters: { filter_owner_id: userId, paginate: 'false' } }, success: function(data) { callback(data.data); } }); }, usersListMedias: function(callback) { var self = this; self.callApi({ resource: 'media.list', data: { accountId: self.accountId }, success: function(data) { callback(data.data); } }); }, usersGetData: function(callback) { var self = this; monster.parallel({ users: function(callback) { self.callApi({ resource: 'user.list', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(dataUsers) { callback(null, dataUsers.data); } }); }, callflows: function(callback) { self.usersListCallflows(function(callflows) { callback(null, callflows); }); }, devices: function(callback) { self.usersGetDevicesData(function(devices) { callback(null, devices); }); }, deviceStatus: function(callback) { self.callApi({ resource: 'device.getStatus', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(data, status) { callback(null, data.data); } }); } }, function(err, results) { callback && callback(results); } ); }, usersUpdateDevices: function(data, userId, callbackAfterUpdate) { var self = this, updateDevices = function(userCallflow) { var listFnParallel = []; _.each(data.new, function(deviceId) { listFnParallel.push(function(callback) { self.usersGetDevice(deviceId, function(data) { data.owner_id = userId; self.usersUpdateDevice(data, function(data) { callback(null, data); }); }); }); }); _.each(data.old, function(deviceId) { listFnParallel.push(function(callback) { self.usersGetDevice(deviceId, function(data) { delete data.owner_id; self.usersUpdateDevice(data, function(data) { callback(null, data); }); }); }); }); if(data.old.length > 0 && userCallflow && userCallflow.flow.module === 'ring_group') { var endpointsCount = userCallflow.flow.data.endpoints.length; userCallflow.flow.data.endpoints =_.filter(userCallflow.flow.data.endpoints, function(endpoint) { return (data.old.indexOf(endpoint.id) < 0); }); if(userCallflow.flow.data.endpoints.length < endpointsCount) { if(userCallflow.flow.data.endpoints.length === 0) { userCallflow.flow.module = 'user'; userCallflow.flow.data = { can_call_self: false, id: userId, timeout: "20" } listFnParallel.push(function(callback) { self.usersGetUser(userId, function(user) { user.smartpbx.find_me_follow_me.enabled = false; self.usersUpdateUser(user, function(data) { callback(null, data) }); }); }); } listFnParallel.push(function(callback) { self.usersUpdateCallflow(userCallflow, function(data) { callback(null, data); }); }); } } monster.parallel(listFnParallel, function(err, results) { callbackAfterUpdate && callbackAfterUpdate(results); }); }; self.usersGetMainCallflow(userId, function(callflow) { updateDevices(callflow) }); }, usersUpdateCallflowNumbers: function(userId, callflowId, numbers, callback) { var self = this; if(numbers.length > 0) { if(callflowId) { self.callApi({ resource: 'callflow.get', data: { accountId: self.accountId, callflowId: callflowId }, success: function(getCallflowData) { getCallflowData.data.numbers = numbers; self.usersUpdateCallflow(getCallflowData.data, function(callflowData) { callback && callback(callflowData); }); } }); } else { if(numbers[0].length < 7) { self.usersMigrateFromExtensions(userId, numbers, function(data) { callback && callback(data); }); } else { toastr.error(self.i18n.active().users.needExtensionFirst); } } } else { toastr.error(self.i18n.active().users.noNumberCallflow); } }, usersListNumbers: function(callback) { var self = this; self.callApi({ resource: 'numbers.list', data: { accountId: self.accountId, filters: { paginate: 'false' } }, success: function(numbers) { callback && callback(numbers.data); } }); }, usersUpdateCallflow: function(callflow, callback) { var self = this; self.callApi({ resource: 'callflow.update', data: { accountId: self.accountId, callflowId: callflow.id, data: callflow }, success: function(callflowData) { callback && callback(callflowData.data); } }); }, usersCreateCallflow: function(callflow, success, error, generateError) { var self = this; self.callApi({ resource: 'callflow.create', data: { accountId: self.accountId, data: callflow, generateError: generateError === false ? false : true }, success: function(callflowData) { success && success(callflowData.data); }, error: function(callflowData, junk, globalHandler) { error && error(callflowData, globalHandler); } }); }, /* If user has a vmbox, then only update it if it comes from the user form. If the check comes from the extension page, where we create a callflow, then only update the vmbox if there are no vmbox attached to this user */ usersSmartUpdateVMBox: function(user, needVMUpdate, callback) { var self = this; self.usersListVMBoxesUser(user.id, function(vmboxes) { if(vmboxes.length > 0) { if(needVMUpdate) { self.usersGetVMBox(vmboxes[0].id, function(vmbox) { vmbox = $.extend(true, {}, vmbox, user.extra.vmbox); vmbox.name = user.first_name + ' ' + user.last_name + '\'s VMBox'; self.usersUpdateVMBox(vmbox, function(vmbox) { callback && callback(vmbox); }); }); } else { callback && callback(vmboxes[0]); } } else { var vmbox = { owner_id: user.id, mailbox: user.extra.vmbox.mailbox, name: user.first_name + ' ' + user.last_name + '\'s VMBox', timezone: user.timezone }; self.usersCreateVMBox(vmbox, function(vmbox) { callback && callback(vmbox); }); } }); }, usersUpdateConferencing: function(data, globalCallback) { var self = this; monster.parallel({ conference: function(callback) { var baseConference = { name: data.user.first_name + ' ' + data.user.last_name + ' SmartPBX Conference', owner_id: data.user.id, play_name_on_join: true, member: { numbers: [], join_muted: false } }; baseConference = $.extend(true, {}, baseConference, data.conference); self.usersListConferences(data.user.id, function(conferences) { var conferenceToSave = baseConference; if(conferences.length > 0) { conferenceToSave = $.extend(true, {}, conferences[0], baseConference); self.usersUpdateConference(conferenceToSave, function(conference) { callback && callback(null, conference); }); } else { self.usersCreateConference(conferenceToSave, function(conference) { callback && callback(null, conference); }); } }); }, user: function(callback) { if(data.user.smartpbx && data.user.smartpbx.conferencing && data.user.smartpbx.conferencing.enabled === true) { callback && callback(null, data.user); } else { data.user.smartpbx = data.user.smartpbx || {}; data.user.smartpbx.conferencing = data.user.smartpbx.conferencing || {}; data.user.smartpbx.conferencing.enabled = true; self.usersUpdateUser(data.user, function(user) { callback && callback(null, user.data); }); } } }, function(err, results) { globalCallback && globalCallback(results); } ); }, usersUpdateFaxing: function(data, newNumber, globalCallback) { var self = this; monster.parallel({ callflow: function(callback) { var baseCallflow = {}; self.usersListCallflowsUser(data.user.id, function(callflows) { _.each(callflows, function(callflow) { if(callflow.type === 'faxing') { baseCallflow.id = callflow.id; return false; } }); self.usersUpdateCallflowFaxing(data, newNumber, baseCallflow, function(callflow) { callback && callback(null, callflow); }); }); }, user: function(callback) { if(data.user.smartpbx && data.user.smartpbx.faxing && data.user.smartpbx.faxing.enabled === true) { callback && callback(null, data.user); } else { data.user.smartpbx = data.user.smartpbx || {}; data.user.smartpbx.faxing = data.user.smartpbx.faxing || {}; data.user.smartpbx.faxing.enabled = true; self.usersUpdateUser(data.user, function(user) { callback && callback(null, user.data); }); } } }, function(err, results) { globalCallback && globalCallback(results); } ); }, usersUpdateCallflowFaxing: function(data, newNumber, callflow, callback) { var self = this, user = data.user, faxbox = data.faxbox, baseCallflow = { type: 'faxing', owner_id: user.id, numbers: [ newNumber ], flow: { module: 'faxbox', children: {}, data: { id: data.hasOwnProperty('faxbox') ? faxbox.id : '' } } }, number = { caller_id: newNumber, fax_identity: monster.util.formatPhoneNumber(newNumber) }; callflow = $.extend(true, {}, baseCallflow, callflow); if( callflow.hasOwnProperty('id') ) { faxbox = $.extend(true, {}, faxbox, number); self.callApi({ resource: 'faxbox.update', data:{ accountId: self.accountId, faxboxId: faxbox.id, data: faxbox }, success: function(_data, status) { self.usersUpdateCallflow(callflow, function(callflow) { callback && callback(callflow); }); } }); } else { var defaultFaxbox = { name: user.first_name.concat(' ', user.last_name, self.i18n.active().users.faxing.defaultSettings.nameExtension), caller_name: user.first_name.concat(' ', user.last_name), fax_header: monster.config.whitelabel.companyName.concat(self.i18n.active().users.faxing.defaultSettings.headerExtension), fax_timezone: user.timezone, owner_id: user.id, notifications: { inbound: { email: { send_to: user.email } }, outbound: { email: { send_to: user.email } } } }; faxbox = $.extend(true, {}, defaultFaxbox, number); self.callApi({ resource: 'faxbox.create', data: { accountId: self.accountId, userId: user.id, data: faxbox }, success: function(_data, status) { callflow.flow.data.id = _data.data.id; self.usersCreateCallflow(callflow, function(callflow) { callback && callback(callflow); }); } }); } }, usersDeleteConferencing: function(userId, globalCallback) { var self = this; monster.parallel({ conferences: function(callback) { self.usersListConferences(userId, function(conferences) { var listRequests = []; _.each(conferences, function(conference) { listRequests.push(function(subCallback) { self.usersDeleteConference(conference.id, function(data) { subCallback(null, data); }); }); }); monster.parallel(listRequests, function(err, results) { callback && callback(results); }); }); }, user: function(callback) { self.usersGetUser(userId, function(user) { //user.conferencing_enabled = false; user.smartpbx = user.smartpbx || {}; user.smartpbx.conferencing = user.smartpbx.conferencing || {}; user.smartpbx.conferencing.enabled = false; self.usersUpdateUser(user, function(user) { callback(null, user); }); }); } }, function(err, results) { globalCallback && globalCallback(results); } ); }, usersDeleteFaxing: function(userId, globalCallback) { var self = this; monster.parallel({ callflows: function(callback) { self.usersListCallflowsUser(userId, function(callflows) { var listRequests = []; _.each(callflows, function(callflow) { if(callflow.type === 'faxing') { listRequests.push(function(subCallback) { self.callApi({ resource: 'callflow.get', data: { accountId: self.accountId, callflowId: callflow.id }, success: function(data, status) { self.callApi({ resource: 'faxbox.delete', data: { accountId: self.accountId, faxboxId: data.data.flow.data.faxbox_id || data.data.flow.data.id, generateError: false }, success: function(_data, status) { self.usersDeleteCallflow(callflow.id, function(results) { subCallback(null, results); }); }, error: function(_data, error) { self.usersDeleteCallflow(callflow.id, function(results) { subCallback(null, results); }); } }); } }); }); } }); monster.parallel(listRequests, function(err, results) { callback && callback(results); }); }); }, user: function(callback) { self.usersGetUser(userId, function(user) { //user.faxing_enabled = false; user.smartpbx = user.smartpbx || {}; user.smartpbx.faxing = user.smartpbx.faxing || {}; user.smartpbx.faxing.enabled = false; self.usersUpdateUser(user, function(user) { callback(null, user); }); }); } }, function(err, results) { globalCallback && globalCallback(results); } ); }, usersSortExtensions: function(a, b) { var parsedA = parseInt(a), parsedB = parseInt(b), result = -1; if(parsedA > 0 && parsedB > 0) { result = parsedA > parsedB; } return result; }, usersRemoveOverlay: function() { $('body').find('#users_container_overlay').remove(); } }; return app; });