define(function(require) { var $ = require('jquery'), _ = require('lodash'), monster = require('monster'); var app = { requests: { 'callflows.device.getProvisionerPhones': { 'apiRoot': monster.config.api.provisioner, 'url': 'phones/', 'verb': 'GET', 'headers': { 'Accept': '*/*' } } }, subscribe: { 'callflows.fetchActions': 'deviceDefineActions', 'callflows.device.popupEdit': 'devicePopupEdit', 'callflows.device.edit': '_deviceEdit' }, devicePopupEdit: function(args) { var self = this, popup, popup_html, data = args.data, callback = args.callback, data_defaults = args.data_defaults; popup_html = $('
'); self.deviceEdit(data, popup_html, $('.inline_content', popup_html), { save_success: function(_data) { popup.dialog('close'); if (typeof callback === 'function') { callback(_data); } }, delete_success: function() { popup.dialog('close'); if (typeof callback === 'function') { callback({ data: {} }); } }, after_render: function() { popup = monster.ui.dialog(popup_html, { title: (data.id) ? self.i18n.active().callflows.device.edit_device : self.i18n.active().callflows.device.create_device }); } }, data_defaults); }, // Added for the subscribed event to avoid refactoring deviceEdit _deviceEdit: function(args) { var self = this; self.deviceEdit(args.data, args.parent, args.target, args.callbacks, args.data_defaults); }, deviceEdit: function(data, _parent, _target, _callbacks, data_defaults) { var self = this, parent = _parent || $('#device-content'), target = _target || $('#device-view', parent), _callbacks = _callbacks || {}, callbacks = { save_success: _callbacks.save_success, save_error: _callbacks.save_error || function(_data, status, type) { if (status === 200 && type === 'mac_address') { monster.ui.alert(self.i18n.active().callflows.device.this_mac_address_is_already_in_use); } }, delete_success: _callbacks.delete_success, delete_error: _callbacks.delete_error, after_render: _callbacks.after_render }, defaults = { data: $.extend(true, { enabled: true, caller_id: { external: {}, internal: {}, emergency: {} }, ringtones: {}, ignore_completed_elsewhere: true, call_restriction: { closed_groups: 'inherit' }, media: { secure_rtp: 'none', audio: { codecs: ['PCMU', 'PCMA'] }, video: { codecs: [] }, fax: { option: 'false' }, fax_option: false }, sip: { method: 'password', invite_format: 'contact', username: 'user_' + monster.util.randomString(6), password: monster.util.randomString(12), expire_seconds: '360' }, contact_list: { exclude: false }, call_forward: {}, music_on_hold: {} }, data_defaults || {}), field_data: { users: [], call_restriction: {}, sip: { methods: { 'password': self.i18n.active().callflows.device.password, 'ip': 'IP' }, invite_formats: { 'username': 'Username', 'npan': 'NPA NXX XXXX', 'e164': 'E. 164', '1npan': '1npan', 'route': 'Route', 'contact': 'Contact' } }, media: { secure_rtp: { value: 'none', options: { 'none': self.i18n.active().callflows.device.none, 'srtp': self.i18n.active().callflows.device.srtp, 'zrtp': self.i18n.active().callflows.device.zrtp } }, fax: { options: { 'auto': self.i18n.active().callflows.device.auto_detect, 'true': self.i18n.active().callflows.device.always_force, 'false': self.i18n.active().callflows.device.disabled } }, audio: { codecs: { 'OPUS': 'OPUS', 'CELT@32000h': 'Siren @ 32Khz', 'G7221@32000h': 'G722.1 @ 32khz', 'G7221@16000h': 'G722.1 @ 16khz', 'G722': 'G722', 'speex@32000h': 'Speex @ 32khz', 'speex@16000h': 'Speex @ 16khz', 'PCMU': 'G711u / PCMU - 64kbps (North America)', 'PCMA': 'G711a / PCMA - 64kbps (Elsewhere)', 'G729': 'G729 - 8kbps (Requires License)', 'GSM': 'GSM', 'CELT@48000h': 'Siren (HD) @ 48kHz', 'CELT@64000h': 'Siren (HD) @ 64kHz' } }, video: { codecs: { 'VP8': 'VP8', 'H264': 'H264', 'H263': 'H263', 'H261': 'H261' } } }, hide_owner: data.hide_owner || false, outbound_flags: data.outbound_flags ? data.outbound_flags.join(', ') : data.outbound_flags }, functions: { inArray: function(value, array) { if (array) { return ($.inArray(value, array) === -1) ? false : true; } else { return false; } } } }, parallelRequests = function(deviceData) { monster.parallel({ list_classifier: function(callback) { self.callApi({ resource: 'numbers.listClassifiers', data: { accountId: self.accountId, filters: { paginate: false } }, success: function(_data_classifiers) { if ('data' in _data_classifiers) { $.each(_data_classifiers.data, function(k, v) { defaults.field_data.call_restriction[k] = { friendly_name: v.friendly_name }; defaults.data.call_restriction[k] = { action: 'inherit' }; }); } callback(null, _data_classifiers); } }); }, account: function(callback) { self.callApi({ resource: 'account.get', data: { accountId: self.accountId }, success: function(_data, status) { $.extend(defaults.field_data.sip, { realm: _data.data.realm }); callback(null, _data); } }); }, user_list: function(callback) { self.callApi({ resource: 'user.list', data: { accountId: self.accountId, filters: { paginate: false } }, success: function(_data, status) { _data.data.sort(function(a, b) { return (a.first_name + a.last_name).toLowerCase() < (b.first_name + b.last_name).toLowerCase() ? -1 : 1; }); _data.data.unshift({ id: '', first_name: '- No', last_name: 'owner -' }); if (deviceData.hasOwnProperty('device_type') && deviceData.device_type === 'mobile') { var userData = _.find(_data.data, function(user) { return user.id === deviceData.owner_id; }); if (userData) { defaults.field_data.users = userData; } else { defaults.field_data.users = { first_name: '- No', last_name: 'owner -' }; } } else { defaults.field_data.users = _data.data; } callback(null, _data); } }); }, media_list: function(callback) { self.callApi({ resource: 'media.list', data: { accountId: self.accountId, filters: { paginate: false } }, success: function(_data, status) { _data.data.unshift( { id: '', name: self.i18n.active().callflows.device.default_music }, { id: 'silence_stream://300000', name: self.i18n.active().callflows.device.silence }, { id: 'shoutcast', name: self.i18n.active().callflows.accountSettings.musicOnHold.shoutcastURL } ); defaults.field_data.music_on_hold = _data.data; callback(null, _data); } }); }, provisionerData: function(callback) { if (monster.config.api.hasOwnProperty('provisioner') && monster.config.api.provisioner) { self.deviceGetDataProvisoner(function(data) { callback(null, data); }); } else { callback(null, {}); } } }, function(err, results) { var render_data = self.devicePrepareDataForTemplate(data, defaults, $.extend(true, results, { get_device: deviceData })); self.deviceRender(render_data, target, callbacks); if (typeof callbacks.after_render === 'function') { callbacks.after_render(); } }); }; if (typeof data === 'object' && data.id) { self.deviceGet(data.id, function(_data, status) { defaults.data.device_type = 'sip_device'; if ('media' in _data && 'encryption' in _data.media) { defaults.field_data.media.secure_rtp.value = _data.media.encryption.enforce_security ? _data.media.encryption.methods[0] : 'none'; } if ('sip' in _data && 'realm' in _data.sip) { defaults.field_data.sip.realm = _data.sip.realm; } self.deviceMigrateData(_data); parallelRequests(_data); }); } else { parallelRequests(defaults); } }, devicePrepareDataForTemplate: function(data, dataGlobal, results) { var self = this, dataDevice = results.get_device, dataProvisioner = results.provisionerData; if (typeof data === 'object' && data.id) { dataGlobal = $.extend(true, dataGlobal, { data: dataDevice }); } if (dataDevice.hasOwnProperty('media') && dataDevice.media.hasOwnProperty('audio')) { // If the codecs property is defined, override the defaults with it. Indeed, when an empty array is set as the // list of codecs, it gets overwritten by the extend function otherwise. if (dataDevice.media.audio.hasOwnProperty('codecs')) { dataGlobal.data.media.audio.codecs = dataDevice.media.audio.codecs; } if (dataDevice.media.video.hasOwnProperty('codecs')) { dataGlobal.data.media.video.codecs = dataDevice.media.video.codecs; } } _.each(dataGlobal.field_data.call_restriction, function(restriction, key) { restriction.value = dataGlobal.data.call_restriction[key].action; }); dataGlobal.field_data.provisioner = dataProvisioner; dataGlobal.field_data.provisioner.isEnabled = !_.isEmpty(dataProvisioner); if (dataGlobal.field_data.provisioner.isEnabled) { var default_provision_data = { voicemail_beep: 1, //ie enabled time_format: '12', hotline: '', vlan: { enable: false, number: '' }, date_format: 'middle-endian' }; dataGlobal.data.provision = $.extend(true, {}, default_provision_data, dataGlobal.data.provision); } dataGlobal.extra = dataGlobal.extra || {}; dataGlobal.extra.isShoutcast = false; // if the value is set to a stream, we need to set the value of the media_id to shoutcast so it gets selected by the old select mechanism, // but we also need to store the value so we can display it if (dataGlobal.data.hasOwnProperty('music_on_hold') && dataGlobal.data.music_on_hold.hasOwnProperty('media_id')) { if (dataGlobal.data.music_on_hold.media_id.indexOf('://') >= 0) { if (dataGlobal.data.music_on_hold.media_id !== 'silence_stream://300000') { dataGlobal.extra.isShoutcast = true; dataGlobal.extra.shoutcastValue = dataGlobal.data.music_on_hold.media_id; dataGlobal.data.music_on_hold.media_id = 'shoutcast'; } } } return dataGlobal; }, deviceGetValidationByDeviceType: function(deviceType) { var self = this, i18n = self.i18n.active(), validation = { ata: { 'sip.ip': { required: true, ipv4: true } }, sip_uri: {}, sip_device: { 'mac_address': { mac: true }, 'sip_expire_seconds': { digits: true }, 'sip.ip': { ipv4: true, required: true }, 'extra.shoutcastUrl': { protocol: true } }, fax: { 'mac_address': { mac: true }, 'sip_expire_seconds': { digits: true }, 'sip.ip': { ipv4: true, required: true } }, cellphone: {}, smartphone: { 'sip_expire_seconds': { digits: true }, 'sip.ip': { ipv4: true, required: true } }, landline: {}, softphone: { 'sip_expire_seconds': { digits: true }, 'extra.shoutcastUrl': { protocol: true } }, mobile: { 'mdn': { digits: true }, 'sip_expire_seconds': { digits: true }, 'extra.shoutcastUrl': { protocol: true } } }, deviceTypeValidation = { rules: validation[deviceType] }; if (_.includes(['ata', 'fax', 'mobile', 'sip_device', 'softphone'], deviceType)) { _.merge(deviceTypeValidation, { rules: { 'caller_id.asserted.name': { regex: /^[0-9A-Za-z ,]{0,30}$/ }, 'caller_id.asserted.number': { phoneNumber: true }, 'caller_id.asserted.realm': { realm: true } }, messages: { 'caller_id.asserted.name': { regex: i18n.callflows.device.validation.caller_id.name }, 'caller_id.asserted.number': { regex: i18n.callflows.device.validation.caller_id.number }, 'caller_id.asserted.realm': { regex: i18n.callflows.device.validation.caller_id.realm } } }); } return deviceTypeValidation; }, deviceRender: function(data, target, callbacks) { var self = this, device_html; if ('media' in data.data && 'fax_option' in data.data.media) { data.data.media.fax_option = (data.data.media.fax_option === 'auto' || data.data.media.fax_option === true); } if (typeof data.data === 'object' && data.data.device_type) { device_html = $(self.getTemplate({ name: 'device-' + data.data.device_type, data: _.merge({ showPAssertedIdentity: monster.config.whitelabel.showPAssertedIdentity }, data), submodule: 'device' })); var deviceForm = device_html.find('#device-form'); if (monster.config.api.hasOwnProperty('provisioner') && monster.config.api.provisioner) { self.deviceSetProvisionerStuff(device_html, data); } /* Do device type specific things here */ if ($.inArray(data.data.device_type, ['fax', 'softphone', 'sip_device', 'smartphone', 'mobile', 'ata']) > -1) { monster.ui.protectField(device_html.find('#sip_password'), device_html); } monster.ui.validate(deviceForm, self.deviceGetValidationByDeviceType(data.data.device_type)); if (!$('#owner_id', device_html).val()) { $('#edit_link', device_html).hide(); } device_html.find('input[data-mask]').each(function() { var $this = $(this); monster.ui.mask($this, $this.data('mask')); }); if (!$('#music_on_hold_media_id', device_html).val()) { $('#edit_link_media', device_html).hide(); } if (data.data.sip && data.data.sip.method === 'ip') { $('#username_block', device_html).hide(); } else { $('#ip_block', device_html).hide(); } } else { device_html = $(self.getTemplate({ name: 'general_edit', submodule: 'device' })); $('.media_pane', device_html).hide(); } $('*[rel=popover]:not([type="text"])', device_html).popover({ trigger: 'hover' }); $('*[rel=popover][type="text"]', device_html).popover({ trigger: 'focus' }); self.winkstartTabs(device_html); self.deviceBindEvents({ data: data, template: device_html, callbacks: callbacks }); (target) .empty() .append(device_html); $('.media_tabs .buttons[device_type="sip_device"]', device_html).trigger('click'); }, /** * Bind events for the device edit template * @param {Object} args * @param {Object} args.data * @param {Object} args.template * @param {Object} args.callbacks * @param {Function} args.callbacks.save_success * @param {Function} args.callbacks.delete_success */ deviceBindEvents: function(args) { var self = this, data = args.data, callbacks = args.callbacks, device_html = args.template; if (typeof data.data === 'object' && data.data.device_type) { var deviceForm = device_html.find('#device-form'); $('#owner_id', device_html).change(function() { !$('#owner_id option:selected', device_html).val() ? $('#edit_link', device_html).hide() : $('#edit_link', device_html).show(); }); $('.inline_action', device_html).click(function(ev) { var _data = ($(this).data('action') === 'edit') ? { id: $('#owner_id', device_html).val() } : {}, _id = _data.id; ev.preventDefault(); monster.pub('callflows.user.popupEdit', { data: _data, callback: function(user) { /* Create */ if (!_id) { $('#owner_id', device_html).append(''); $('#owner_id', device_html).val(user.id); $('#edit_link', device_html).show(); } else { /* Update */ if (_data.hasOwnProperty('id')) { $('#owner_id #' + user.id, device_html).text(user.first_name + ' ' + user.last_name); /* Delete */ } else { $('#owner_id #' + _id, device_html).remove(); $('#edit_link', device_html).hide(); } } } }); }); $('.device-save', device_html).click(function(ev) { ev.preventDefault(); var $this = $(this); if (!$this.hasClass('disabled')) { $this.addClass('disabled'); if (monster.ui.valid(deviceForm)) { var form_data = monster.ui.getFormData('device-form'); if (form_data.hasOwnProperty('music_on_hold') && form_data.music_on_hold.media_id === 'shoutcast') { form_data.music_on_hold.media_id = device_html.find('.shoutcast-url-input').val(); } self.deviceCleanFormData(form_data); if (form_data.hasOwnProperty('provision') && form_data.provision.hasOwnProperty('endpoint_brand') && form_data.provision.endpoint_brand !== 'none') { var modelArray = $('.dropdown_family[data-brand="' + form_data.provision.endpoint_brand + '"]', device_html).val().split('.'), endpoint_family = modelArray[0], endpoint_model = modelArray[1]; // We have to set this manually since we have 3 dropdown with the same name we don't know which selected one is the correct one.. form_data.provision.endpoint_model = endpoint_model; form_data.provision.endpoint_family = endpoint_family; } self.deviceSave(form_data, data, callbacks.save_success); } else { $this.removeClass('disabled'); monster.ui.alert('error', self.i18n.active().callflows.device.there_were_errors_on_the_form); } } }); if (data.device_type !== 'mobile') { $('.device-delete', device_html).click(function(ev) { ev.preventDefault(); monster.ui.confirm(self.i18n.active().callflows.device.are_you_sure_you_want_to_delete, function() { self.deviceDelete(data.data.id, callbacks.delete_success); }); }); } device_html.find('#sip_method').on('change', function() { if ($('#sip_method option:selected', device_html).val() === 'ip') { $('#ip_block', device_html).slideDown(); $('#username_block', device_html).slideUp(); } else { $('#username_block', device_html).slideDown(); $('#ip_block', device_html).slideUp(); } }); $('#music_on_hold_media_id', device_html).change(function() { !$('#music_on_hold_media_id option:selected', device_html).val() ? $('#edit_link_media', device_html).hide() : $('#edit_link_media', device_html).show(); device_html.find('.shoutcast-div').toggleClass('active', $(this).val() === 'shoutcast'); }); $('.inline_action_media', device_html).click(function(ev) { var _data = ($(this).data('action') === 'edit') ? { id: $('#music_on_hold_media_id', device_html).val() } : {}, _id = _data.id; ev.preventDefault(); monster.pub('callflows.media.editPopup', { data: _data, callback: function(media) { /* Create */ if (!_id) { $('#music_on_hold_media_id', device_html).append(''); $('#music_on_hold_media_id', device_html).val(media.id); $('#edit_link_media', device_html).show(); } else { /* Update */ if (media.hasOwnProperty('id')) { $('#music_on_hold_media_id #' + media.id, device_html).text(media.name); /* Delete */ } else { $('#music_on_hold_media_id #' + _id, device_html).remove(); $('#edit_link_media', device_html).hide(); } } } }); }); } else { $('.media_tabs .buttons', device_html).click(function() { var $this = $(this); $('.media_pane', device_html).show(); if (!$this.hasClass('current')) { $('.media_tabs .buttons').removeClass('current'); $this.addClass('current'); data.data.device_type = $this.attr('device_type'); self.deviceFormatData(data); self.deviceRender(data, $('.media_pane', device_html), callbacks); } }); } }, deviceSetProvisionerStuff: function(device_html, data) { var self = this, set_value = function(brand_name, model_family, model_name) { device_html.find('.dropdown_family').hide(); if (brand_name in data.field_data.provisioner.brands) { device_html.find('#dropdown_brand').val(brand_name); device_html .find('.dropdown_family[data-brand="' + brand_name + '"]') .show() .val(model_family + '.' + model_name); } }, provisionData = data.data.provision, regex_brands = { '00085d': 'aastra', '0010bc': 'aastra', '00036b': 'cisco', '00000c': 'cisco', '000142': 'cisco', '000143': 'cisco', '000163': 'cisco', '000164': 'cisco', '000196': 'cisco', '000197': 'cisco', '0001c7': 'cisco', '0001c9': 'cisco', '000f23': 'cisco', '0013c4': 'cisco', '0016c8': 'cisco', '001818': 'cisco', '00175a': 'cisco', '001795': 'cisco', '001A2f': 'cisco', '001c58': 'cisco', '001dA2': 'cisco', '002155': 'cisco', '000e84': 'cisco', '000e38': 'cisco', '00070e': 'cisco', '001bd4': 'cisco', '001930': 'cisco', '0019aa': 'cisco', '001d45': 'cisco', '001ef7': 'cisco', '000e08': 'cisco', '1cdf0f': 'cisco', 'e05fb9': 'cisco', '5475d0': 'cisco', 'c46413': 'cisco', '000Ffd3': 'digium', '000b82': 'grandstream', '08000f': 'mitel', '1045bE': 'norphonic', '0050c2': 'norphonic', '0004f2': 'polycom', '00907a': 'polycom', '000413': 'snom', '001f9f': 'thomson', '00147f': 'thomson', '642400': 'xorcom', '001565': 'yealink' }; set_value(provisionData.endpoint_brand, provisionData.endpoint_family, provisionData.endpoint_model); device_html.find('#dropdown_brand').on('change', function() { set_value($(this).val()); }); device_html.find('#mac_address').on('keyup', function() { var mac_address = $(this).val().replace(/[^0-9a-fA-F]/g, ''); if (mac_address in regex_brands) { set_value(regex_brands[mac_address]); } }); }, deviceFormatData: function(data) { if (data.data.device_type === 'smartphone' || data.data.device_type === 'landline' || data.data.device_type === 'cellphone') { data.data.call_forward = { enabled: true, require_keypress: true, keep_caller_id: true }; } else { data.data.call_forward = { enabled: false }; } if (data.data.device_type === 'sip_uri') { data.data.sip.invite_format = 'route'; } if (data.data.device_type === 'mobile') { if (!('mobile' in data.data)) { data.data.mobile = { mdn: '' }; } } if (data.data.device_type === 'fax') { data.data.media.fax_option = true; data.data.media.fax.option = 'true'; } else { data.data.media.fax_option = false; data.data.media.fax.option = 'false'; } }, deviceMigrateData: function(data) { var self = this; if (data.hasOwnProperty('media') && data.media.hasOwnProperty('audio') && data.media.audio.hasOwnProperty('codecs')) { var mapMigrateCodec = { 'Speex': 'speex@16000h', 'G722_16': 'G7221@16000h', 'G722_32': 'G7221@32000h', 'CELT_48': 'CELT@48000h', 'CELT_64': 'CELT@64000h' }, newCodecList = []; _.each(data.media.audio.codecs, function(codec) { mapMigrateCodec.hasOwnProperty(codec) ? newCodecList.push(mapMigrateCodec[codec]) : newCodecList.push(codec); }); data.media.audio.codecs = newCodecList; } if (data.device_type === 'cell_phone') { data.device_type = 'cellphone'; } if (typeof data.media === 'object' && typeof data.media.fax === 'object' && 'codecs' in data.media.fax) { delete data.media.fax.codecs; } if ('status' in data) { data.enabled = data.status; delete data.status; } if (data.hasOwnProperty('ignore_complete_elsewhere')) { data.ignore_completed_elsewhere = data.ignore_complete_elsewhere; delete data.ignore_complete_elsewhere; } return data; }, deviceNormalizeData: function(data) { var self = this; if (data.hasOwnProperty('provision')) { if (data.provision.endpoint_brand === 'none') { delete data.provision; } else { if (data.provision.voicemail_beep !== 0) { delete data.provision.voicemail_beep; } } } if (data.hasOwnProperty('media') && data.media.hasOwnProperty('fax_option') && data.media.fax_option === 'auto') { delete data.media.fax_option; } if ('media' in data && 'fax' in data.media && 'fax_option' in data.media) { data.media.fax.option = data.media.fax_option.toString(); } if ('media' in data && 'secure_rtp' in data.media) { delete data.media.secure_rtp; } if ('media' in data && 'bypass_media' in data.media) { delete data.media.bypass_media; } self.compactObject(data.caller_id); if (_.isEmpty(data.caller_id)) { delete data.caller_id; } if (!data.music_on_hold.media_id) { delete data.music_on_hold.media_id; } if (!data.owner_id) { delete data.owner_id; } if ($.isEmptyObject(data.call_forward)) { delete data.call_forward; } if (!data.mac_address) { delete data.mac_address; } if (data.sip.method !== 'ip') { delete data.sip.ip; } if (typeof data.outbound_flags === 'string') { data.outbound_flags = data.outbound_flags.split(/,/); /* Get rid of empty string */ var new_flags = []; $.each(data.outbound_flags, function(k, v) { if (v.replace(/\s/g, '') !== '') { new_flags.push(v); } }); data.outbound_flags = new_flags; } if (data.device_type === 'fax') { if (!('outbound_flags' in data)) { data.outbound_flags = ['fax']; } else if (data.outbound_flags.indexOf('fax') < 0) { data.outbound_flags.splice(0, 0, 'fax'); } } if (data.ringtones && 'internal' in data.ringtones && data.ringtones.internal === '') { delete data.ringtones.internal; } if (data.ringtones && 'external' in data.ringtones && data.ringtones.external === '') { delete data.ringtones.external; } // For devices who don't have sip creds, we need to use username, for sip url we already set it to "route", and for the others, the default is applied: "contact" if ($.inArray(data.device_type, ['landline', 'cellphone']) >= 0) { data.sip.invite_format = 'username'; } if ($.inArray(data.device_type, ['fax', 'mobile', 'softphone', 'sip_device', 'smartphone']) < 0) { delete data.call_restriction; } if (data.hasOwnProperty('presence_id') && data.presence_id === '') { delete data.presence_id; } return data; }, deviceCleanFormData: function(form_data) { if ('provision' in form_data && form_data.provision.voicemail_beep === true) { form_data.provision.voicemail_beep = 0; } if (form_data.mac_address) { form_data.mac_address = form_data.mac_address.toLowerCase(); if (form_data.mac_address.match(/^(((\d|([a-f]|[A-F])) {2}-) {5}(\d|([a-f]|[A-F])) {2})$/)) { form_data.mac_address = form_data.mac_address.replace(/-/g, ':'); } else if (form_data.mac_address.match(/^(((\d|([a-f]|[A-F])) {2}) {5}(\d|([a-f]|[A-F])) {2})$/)) { form_data.mac_address = form_data.mac_address.replace(/(.{2})/g, '$1:').slice(0, -1); } } if (form_data.caller_id) { form_data.caller_id.internal.number = form_data.caller_id.internal.number.replace(/\s|\(|\)|-|\./g, ''); form_data.caller_id.external.number = form_data.caller_id.external.number.replace(/\s|\(|\)|-|\./g, ''); form_data.caller_id.emergency.number = form_data.caller_id.emergency.number.replace(/\s|\(|\)|-|\./g, ''); var assertedNumber = _.get(form_data.caller_id, 'asserted.number', ''); if(!_.isEmpty(assertedNumber)) { assertedNumber = monster.util.getFormatPhoneNumber(assertedNumber).e164Number; } // Need to keep the empty string so the new value is not lost when merging the form data with the original data _.set(form_data.caller_id, 'asserted.number', assertedNumber); } if ('media' in form_data && 'audio' in form_data.media) { form_data.media.audio.codecs = $.map(form_data.media.audio.codecs, function(val) { return (val) ? val : null; }); } if ('media' in form_data && 'video' in form_data.media) { form_data.media.video.codecs = $.map(form_data.media.video.codecs, function(val) { return (val) ? val : null; }); } if (form_data.device_type === 'smartphone' || form_data.device_type === 'landline' || form_data.device_type === 'cellphone') { form_data.call_forward.number = form_data.call_forward.number.replace(/\s|\(|\)|-|\./g, ''); form_data.enabled = form_data.call_forward.enabled; } if ('extra' in form_data && form_data.extra.notify_unregister === true) { form_data.suppress_unregister_notifications = false; } else { form_data.suppress_unregister_notifications = true; } if ('extra' in form_data && 'closed_groups' in form_data.extra) { form_data.call_restriction.closed_groups = { action: form_data.extra.closed_groups ? 'deny' : 'inherit' }; } if ($.inArray(form_data.device_type, ['sip_device', 'mobile', 'softphone']) > -1) { if ('extra' in form_data) { form_data.media.encryption = form_data.media.encryption || {}; if ($.inArray(form_data.extra.encryptionMethod, ['srtp', 'zrtp']) > -1) { form_data.media.encryption.enforce_security = true; form_data.media.encryption.methods = [form_data.extra.encryptionMethod]; } else { form_data.media.encryption.methods = []; form_data.media.encryption.enforce_security = false; } } } delete form_data.extra; return form_data; }, deviceFixArrays: function(data, data2) { if (typeof data.media === 'object' && typeof data2.media === 'object') { (data.media.audio || {}).codecs = (data2.media.audio || {}).codecs; (data.media.video || {}).codecs = (data2.media.video || {}).codecs; } if ('media' in data2 && 'encryption' in data2.media && 'methods' in data2.media.encryption) { data.media.encryption = data.media.encryption || {}; data.media.encryption.methods = data2.media.encryption.methods; } return data; }, deviceSave: function(form_data, data, success) { var self = this, id = (typeof data.data === 'object' && data.data.id) ? data.data.id : undefined, normalized_data = self.deviceFixArrays(self.deviceNormalizeData($.extend(true, {}, data.data, form_data)), form_data); if (id) { self.deviceUpdate(normalized_data, function(_data, status) { success && success(_data, status, 'update'); }); } else { self.deviceCreate(normalized_data, function(_data, status) { success && success(_data, status, 'create'); }); } }, deviceList: function(callback) { var self = this; self.callApi({ resource: 'device.list', data: { accountId: self.accountId, filters: { paginate: false } }, success: function(data) { callback && callback(data.data); } }); }, deviceGet: function(deviceId, callback) { var self = this; self.callApi({ resource: 'device.get', data: { accountId: self.accountId, deviceId: deviceId }, success: function(data) { callback && callback(data.data); } }); }, deviceCreate: function(data, callback) { var self = this; self.callApi({ resource: 'device.create', data: { accountId: self.accountId, data: data }, success: function(data) { callback && callback(data.data); } }); }, deviceUpdate: function(data, callback) { var self = this; self.callApi({ resource: 'device.update', data: { accountId: self.accountId, deviceId: data.id, data: data }, success: function(data) { callback && callback(data.data); } }); }, deviceDelete: function(deviceId, callback) { var self = this; self.callApi({ resource: 'device.delete', data: { accountId: self.accountId, deviceId: deviceId }, success: function(data) { callback && callback(data.data); } }); }, deviceGetDataProvisoner: function(callback) { var self = this; monster.request({ resource: 'callflows.device.getProvisionerPhones', data: { }, success: function(data) { data = self.deviceFormatDataProvisioner(data.data); callback && callback(data); } }); }, deviceFormatDataProvisioner: function(data) { var self = this, formattedData = { brands: data }; return formattedData; }, deviceDefineActions: function(args) { var self = this, callflow_nodes = args.actions; $.extend(callflow_nodes, { 'device[id=*]': { name: self.i18n.active().callflows.device.device, icon: 'phone', category: self.i18n.active().oldCallflows.advanced_cat, module: 'device', tip: self.i18n.active().callflows.device.device_tip, data: { id: 'null' }, rules: [ { type: 'quantity', maxSize: '1' } ], isUsable: 'true', weight: 10, caption: function(node, caption_map) { var id = node.getMetadata('id'), returned_value = ''; if (id in caption_map) { returned_value = caption_map[id].name; } return returned_value; }, edit: function(node, callback) { var _this = this; self.deviceList(function(devices) { var popup, popup_html; popup_html = $(self.getTemplate({ name: 'callflowEdit', data: { can_call_self: node.getMetadata('can_call_self') || false, parameter: { name: 'timeout (s)', value: node.getMetadata('timeout') || '20' }, objects: { items: _.sortBy(devices, 'name'), selected: node.getMetadata('id') || '' } }, submodule: 'device' })); if ($('#device_selector option:selected', popup_html).val() === undefined) { $('#edit_link', popup_html).hide(); } $('.inline_action', popup_html).click(function(ev) { var _data = ($(this).data('action') === 'edit') ? { id: $('#device_selector', popup_html).val() } : {}; ev.preventDefault(); self.devicePopupEdit({ data: _data, callback: function(device) { node.setMetadata('id', device.id || 'null'); node.setMetadata('timeout', $('#parameter_input', popup_html).val()); node.setMetadata('can_call_self', $('#device_can_call_self', popup_html).is(':checked')); node.caption = device.name || ''; popup.dialog('close'); } }); }); $('#add', popup_html).click(function() { node.setMetadata('id', $('#device_selector', popup_html).val()); node.setMetadata('timeout', $('#parameter_input', popup_html).val()); node.setMetadata('can_call_self', $('#device_can_call_self', popup_html).is(':checked')); node.caption = $('#device_selector option:selected', popup_html).text(); popup.dialog('close'); }); popup = monster.ui.dialog(popup_html, { title: self.i18n.active().callflows.device.device_title, beforeClose: function() { if (typeof callback === 'function') { callback(); } } }); }); }, listEntities: function(callback) { monster.parallel({ device: function(callback) { self.callApi({ resource: 'device.list', data: { accountId: self.accountId, filters: { paginate: false } }, success: function(data, status) { callback && callback(null, data.data); } }); }, status: function(callback) { self.callApi({ resource: 'device.getStatus', data: { accountId: self.accountId, filters: { paginate: false } }, success: function(data, status) { callback && callback(null, data.data); } }); } }, function(err, results) { var registeredDevices = _.map(results.status, function(registration) { if (registration.registered === true) { return registration.device_id; } }), deviceIcons = { 'cellphone': 'fa fa-phone', 'smartphone': 'icon-telicon-mobile-phone', 'landline': 'icon-telicon-home', '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-ata', 'unknown': 'fa fa-circle' }; _.each(results.device, function(device) { var dataTemplate = device; dataTemplate.extra = { deviceIcon: deviceIcons.hasOwnProperty(device.device_type) ? deviceIcons[device.device_type] : deviceIcons.unknown, isRegistered: device.enabled ? (['sip_device', 'smartphone', 'softphone', 'fax', 'ata'].indexOf(device.device_type) >= 0 ? registeredDevices.indexOf(device.id) >= 0 : true) : false }; // no jQuery wrapper since this template will be inserted directly with Handlebars device.customEntityTemplate = self.getTemplate({ name: 'entity-element', data: dataTemplate, submodule: 'device' }); }); callback && callback(results.device); }); }, editEntity: 'callflows.device.edit' } }); } }; return app; });