Name

sn_comm_management.CommunicationManagementUtilSNC

Description

Utilities for Task Communication Management Flow

Script

var context = this;
var CommunicationManagementUtilSNC = Class.create();
CommunicationManagementUtilSNC.prototype = {
  initialize: function() {

      this.arrayUtils = new global.ArrayUtil();
      this.notifyUtils = (GlidePluginManager.isActive('com.snc.notify')) ? this.notifyUtils = new global.NotifyUtils() : null;
      this.notifyUtil = null;
      if (typeof global.NotifyUtil == 'function')
          this.notifyUtil = (GlidePluginManager.isActive('com.snc.notify')) ? this.notifyUtil = new global.NotifyUtil() : null;
      this.ocr = (GlidePluginManager.isActive('com.snc.on_call_rotation')) ? this.ocr = new global.OnCallRotation() : null;
  },

  TABLES: {
      COMM_CHANNEL_CONFIG: 'comm_channel_config',
      COMM_CHANNEL_DEFINITION: 'comm_channel_definition',
      COMM_CHANNEL: 'comm_channel',
      COMM_PLAN_DEFINITION: 'comm_plan_definition',
      COMM_TASK_DEFINITION: 'comm_task_definition',
      COMM_CHANNEL_SMS: 'comm_channel_sms',
      COMM_CHANNEL_EMAIL: 'comm_channel_email',
      COMM_CHANNEL_CONFERENCE: 'comm_channel_conference',
      COMM_CHANNEL_MS_TEAMS_MESSAGE: 'comm_channel_ms_teams_message',
      COMM_CHANNEL_DEF_EMAIL: 'comm_channel_def_email',
      COMM_CHANNEL_DEF_SMS: 'comm_channel_def_sms',
      COMM_CHANNEL_DEF_CONFERENCE: 'comm_channel_def_conference',
      COMM_PLAN: 'comm_plan',
      COMM_TASK: 'comm_task',
      NOTIFY_SMS_TEMPLATE: 'notify_sms_template',
      CONTACT: 'contact',
      SYS_CHOICE: 'sys_choice',
      PUBLICATIONS_RECIPIENT_LIST: 'sn_publications_recipients_list',
      SYS_USER_GROUP_MEMBERS: 'sys_user_grmember',
      INCIDENT_ALERT: 'incident_alert',
      INCIDENT_ALERT_TASK: 'incident_alert_task',
      INCIDENT: 'incident',
      COMM_TASK_HANDLER: 'comm_task_handler',
      SYS_USER: 'sys_user'
  },

  COLUMNS: {
      COMM_PLAN_DEFINITION: 'comm_plan_definition',
      COMM_TASK_DEFINITION: 'comm_task_definition',
      COMM_PLAN: 'comm_plan',
      COMM_TASK: 'comm_task',
      TYPE: 'type',
      CLASSIFICATION: 'classification',
      NAME: 'name',
      ELEMENT: 'element',
      INACTIVE: 'inactive',
      LANGUAGE: 'language',
      TABLE: 'table',
      DOCUMENT: 'document',
      COMM_CHANNEL_TYPE: 'comm_channel_config.type',
      COMM_CHANNEL_CLASSIFICATION: 'comm_channel_config.classification',
      RECIPIENT_LIST: 'recipient_list',
      SOURCE: 'source'
  },

  CHANNELS: {
      EMAIL: 'e-mail',
      SMS: 'sms',
      CONFERENCE: 'conference',
      CHAT: 'chat',
      MS_TEAMS_MESSAGE: 'ms-teams-message'
  },

  CHANNEL_ACTIONS: {
      SENT: 'sent',
      STARTED: 'started',
      COMPLETED: 'completed'
  },

  CHANNEL_STATES: {
      SENT: 'sent',
      STARTED: 'started',
      COMPLETED: 'completed'
  },

  CHANNEL_CLASSIFICATIONS: {
      UPDATE: 'update',
      BRIDGE: 'bridge'
  },

  MAX_DEFAULT_EMAIL: 4000,
  MAX_DEFAULT_SMS: 1000,

  DEFAULT_EMAIL_RECIPIENT_FIELD: 'bcc', // Used when 'sn_comm_management.default_email_recipient_field' is not defined.

  ACTIVE_RECIPIENT_LIST: "20467c40b71111102503ec16de11a9fc",

  // Check if atleast one channel is available for a table and classification (ex. bridge)
  getChannelAvailabilityForClassification: function(table, classification) {
      if (!classification || !table) {
          return false;
      }
      var configGr = new GlideRecord(this.TABLES.COMM_CHANNEL_CONFIG);
      configGr.addActiveQuery();
      configGr.addQuery(this.COLUMNS.CLASSIFICATION, classification);
      configGr.query();
      while (configGr.next()) {
          var channelMap = this.getChannelMap(table, configGr.type);
          if (channelMap && channelMap[configGr.type] && channelMap[configGr.type].available)
              return true;
      }
      return false;
  },

  getChannelMap: function(sourceTable, channelTypes /* single channel or array of channels */ ) {
      var configGr = new GlideRecord(this.TABLES.COMM_CHANNEL_CONFIG);
      configGr.addActiveQuery();
      if (channelTypes) {
          if (!Array.isArray(channelTypes)) // if single string, making it an array and continuing the flow
              channelTypes = [channelTypes];
          configGr.addQuery(this.COLUMNS.TYPE, 'IN', channelTypes.join());
      }
      configGr.query();
      var channelMap = {};
      while (configGr.next()) {
          var type = configGr.type;
          var label = configGr.type.getDisplayValue();
          var apiName = configGr.config_script.api_name + '';
          var channelConfigScriptInstance = this.getScriptInstanceFromApiName(context, apiName);
          var config = {};
          config = channelConfigScriptInstance.getConfig(sourceTable);
          channelMap[type] = config;
          if (!config.label) {
              channelMap[type].label = label;
          }
          channelMap[type].api_name = apiName;
      }
      return channelMap;
  },

  getChannelConfigScriptMap: function(channelTypes /* single channel or array of channels */ ) {
      var configGr = new GlideRecord(this.TABLES.COMM_CHANNEL_CONFIG);
      configGr.addActiveQuery();
      if (channelTypes) {
          if (!Array.isArray(channelTypes)) // if single string, making it an array and continuing the flow
              channelTypes = [channelTypes];
          configGr.addQuery(this.COLUMNS.TYPE, 'IN', channelTypes.join());
      }
      configGr.query();
      var channelMap = {};
      while (configGr.next()) {
          var type = configGr.type + '';
          channelMap[type] = {};
          var apiName = configGr.config_script.api_name + '';
          channelMap[type].apiName = apiName;
      }
      return channelMap;
  },

  validateCommunicationFrequencyForChannel: function(channelGr) {
      if (!channelGr) {
          return false;
      }

      var frequency = channelGr.comm_task.communication_frequency + '';
      if (frequency == 'recurring') {
          return this.isRecurringAllowedForChannel(channelGr);
      }
      return true;
  },

  validateCommunicationFrequencyForCommTask: function(commTaskGr) {
      if (!commTaskGr) {
          return false;
      }

      var frequency = commTaskGr.communication_frequency + '';
      if (frequency == 'recurring') {
          var channelGr = this.getCommTaskChannels(commTaskGr.getUniqueValue());
          while (channelGr.next()) {
              if (!this.isRecurringAllowedForChannel(channelGr)) {
                  gs.addErrorMessage(gs.getMessage('Recurring communication frequency is not supported for {0}', [channelGr.getDisplayValue('comm_channel_config')]));
                  return false;
              }
          }
      }

      return true;
  },

  isRecurringAllowedForChannel: function(channelGr) {
      var configScriptApiName = channelGr.comm_channel_config.config_script.api_name + '';
      channelConfigScriptInstance = this.getScriptInstanceFromApiName(context, configScriptApiName);
      var channelConfig = channelConfigScriptInstance.getConfig();
      return !channelConfig.hideRecurringFrequency;
  },

  getCommTaskChannels: function(commTaskSysId) {
      var channelGr = new GlideRecord(this.TABLES.COMM_CHANNEL);
      channelGr.addQuery(this.COLUMNS.COMM_TASK, commTaskSysId);
      channelGr.query();
      return channelGr;
  },

  validateCommunicationFrequencyForChannelDef: function(channelDefGr) {
      if (!channelDefGr) {
          return false;
      }

      var frequency = channelDefGr.comm_task_definition.communication_frequency + '';
      if (frequency == 'recurring') {
          return this.isRecurringAllowedForChannelDefinition(channelDefGr);
      }
      return true;
  },

  validateCommunicationFrequencyForCommTaskDef: function(commTaskDefGr) {
      if (!commTaskDefGr) {
          return false;
      }

      var frequency = commTaskDefGr.communication_frequency + '';
      if (frequency == 'recurring') {
          var channelDefGr = this.getCommTaskDefChannels(commTaskDefGr.getUniqueValue());
          while (channelDefGr.next()) {
              if (!this.isRecurringAllowedForChannelDefinition(channelDefGr)) {
                  gs.addErrorMessage(gs.getMessage('Recurring communication frequency is not supported for {0}', [channelDefGr.getDisplayValue('channel')]));
                  return false;
              }
          }
      }

      return true;
  },

  isRecurringAllowedForChannelDefinition: function(channelDefGr) {
      var configScriptApiName = channelDefGr.channel.config_script.api_name + '';
      channelConfigScriptInstance = this.getScriptInstanceFromApiName(context, configScriptApiName);
      var channelConfig = channelConfigScriptInstance.getConfig();
      return !channelConfig.hideRecurringFrequency;
  },

  getCommTaskDefChannels: function(commTaskDefSysId) {
      var channelDefGr = new GlideRecord(this.TABLES.COMM_CHANNEL_DEFINITION);
      channelDefGr.addQuery(this.COLUMNS.COMM_TASK_DEFINITION, commTaskDefSysId);
      channelDefGr.query();
      return channelDefGr;
  },

  canCreateChannelOnRelatedDef: function(channelType, relatedDefGr, ignoreACL) {
      if (!ignoreACL && !relatedDefGr.canWrite())
          return false;

      var currentChannelConfig;
      var recordClass = relatedDefGr.getRecordClassName();
      var channelDefGr = new GlideRecord(this.TABLES.COMM_CHANNEL_DEFINITION);
      currentChannelConfig = this.getChannelMap(relatedDefGr.comm_plan_definition.table, channelType)[channelType];

      if (!currentChannelConfig)
          return false;
      if (currentChannelConfig && !currentChannelConfig.available)
          return false;

      channelDefGr.addQuery(this.COLUMNS.COMM_TASK_DEFINITION, relatedDefGr.getUniqueValue());
      channelDefGr.query();

      var channelConfig;
      var channelConfigScriptInstance;
      var apiName;

      while (channelDefGr.next()) {
          // Only one channel allowed for bridge classification
          if (channelDefGr.channel.type + '' == channelType || channelDefGr.channel.classification == this.CHANNEL_CLASSIFICATIONS.BRIDGE)
              return false;
          apiName = channelDefGr.channel.config_script.api_name;
          channelConfigScriptInstance = this.getScriptInstanceFromApiName(context, apiName);
          channelConfig = channelConfigScriptInstance.getConfig();
          if (!ignoreACL && !new GlideRecord(channelConfig.def_table).canCreate())
              return false;
          if (channelConfig.not_compatible_with.indexOf(channelType) > -1)
              return false;
      }
      return true;
  },

  isChannelDefValidSelfCheck: function(channelDefGr) {
      if (channelDefGr && channelDefGr.comm_task_definition) {
          if (channelDefGr.channel && channelDefGr.channel.type)
              return this.canCreateChannelOnRelatedDef(channelDefGr.channel.type, channelDefGr.comm_task_definition.getRefRecord(), true);

      } else
          return false;
  },

  canCreateChannelOnRelatedInstance: function(channelType, relatedInstanceGr, sourceTable /*e.g for incident_alert take in incident always*/ , ignoreACL) {
      var currentChannelConfig;
      var recordClass = relatedInstanceGr.getRecordClassName();
      var table = sourceTable;

      if (!sourceTable && relatedInstanceGr.comm_plan && relatedInstanceGr.comm_plan.source && relatedInstanceGr.comm_plan.source.getRefRecord().getRecordClassName())
          table = relatedInstanceGr.comm_plan.source.getRefRecord().getRecordClassName();

      if (!table)
          table = new CommunicationManagementBridgeSNC().getTaskTableGivenCommTaskTable(recordClass);

      currentChannelConfig = this.getChannelMap(table, channelType)[channelType];
      if (!currentChannelConfig)
          return false;
      if (currentChannelConfig && !currentChannelConfig.available)
          return false;

      var handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(table);
      if (!handlerInstance.allowAddChannelOnCommTask(relatedInstanceGr, ignoreACL, currentChannelConfig.inst_table))
          return false;

      var channelGr = new GlideRecord(this.TABLES.COMM_CHANNEL);
      channelGr.addQuery(this.COLUMNS.COMM_TASK, relatedInstanceGr.getUniqueValue());
      channelGr.query();
      var channelConfig;
      var channelConfigScriptInstance;
      var apiName;
      while (channelGr.next()) {
          // Only one channel allowed for bridge classification
          if (channelGr.comm_channel_config.type + '' == channelType || channelGr.comm_channel_config.classification == 'bridge')
              return false;
          apiName = channelGr.comm_channel_config.config_script.api_name;
          channelConfigScriptInstance = this.getScriptInstanceFromApiName(context, apiName);
          channelConfig = channelConfigScriptInstance.getConfig();
          if (channelConfig.not_compatible_with.indexOf(channelType) > -1)
              return false;
      }
      return true;
  },

  isChannelValidSelfCheck: function(channelGr) {
      if (channelGr && channelGr.comm_task && channelGr.comm_task.getRefRecord().getRecordClassName() == this.TABLES.INCIDENT_ALERT_TASK)
          return this.canCreateChannelOnRelatedInstance(channelGr.comm_channel_config.type, channelGr.comm_task.getRefRecord(), this.TABLES.INCIDENT, true);
      else if (channelGr && channelGr.comm_task && channelGr.comm_channel_config && channelGr.comm_channel_config.type)
          return this.canCreateChannelOnRelatedInstance(channelGr.comm_channel_config.type, channelGr.comm_task.getRefRecord(), undefined /*Table resolved within function call safely*/ , true);
      else
          return false;
  },

  isChannelTypeUniqueForCommTask: function(channelGr) {
      if (!channelGr || !channelGr.comm_task || !channelGr.comm_channel_config) {
          gs.addErrorMessage(gs.getMessage('Invalid Comm Channel record'));
          return false;
      }
      var chnGr = new GlideRecord(this.TABLES.COMM_CHANNEL);
      chnGr.addQuery(this.COLUMNS.COMM_TASK, channelGr.comm_task);
      chnGr.addQuery(this.COLUMNS.COMM_CHANNEL_TYPE, channelGr.comm_channel_config.type); // Check if channel type exists
      if (!channelGr.isNewRecord())
          chnGr.addQuery('sys_id', '!=', channelGr.getUniqueValue());
      chnGr.query();
      if (chnGr.hasNext()) {
          gs.addErrorMessage(gs.getMessage('Duplicate channel type not allowed for Comm Task'));
          return false;
      }
      return true;
  },

  getScriptInstanceFromApiName: function(_ctx, apiName) {
      if (apiName && _ctx) {
          var splits = apiName.split('.');
          if (splits && splits.length == 2) {
              var scope = splits[0];
              var siName = splits[1];
              return new _ctx[scope][siName]();
          }
      }
      return;
  },

  prepareDataForCommunication: function(channelObj, commTaskGr, respData) {
      var channelApiInstance = this.getScriptInstanceFromApiName(context, channelObj.api_name);
      channelApiInstance.prepareDataForCommunication(commTaskGr, respData);
  },

  getSmsMetadata: function(commTaskGR) {
      var sms = {};
      if (!this.notifyUtils) return sms;
      var smsChannelGR = new GlideRecord(this.TABLES.COMM_CHANNEL_SMS);
      smsChannelGR.addQuery(this.COLUMNS.COMM_TASK, commTaskGR.sys_id);
      smsChannelGR.query();
      if (smsChannelGR.next()) {
          if (smsChannelGR.notify_sms_template) {
              var smsTemplateGR = new GlideRecord(this.TABLES.NOTIFY_SMS_TEMPLATE);
              smsTemplateGR.get(smsChannelGR.notify_sms_template.toString());
              sms.message = this.notifyUtils.formatContent(commTaskGR, smsTemplateGR.template);
              sms.recipients = this.getContactAccessPoints(smsChannelGR, this.CHANNELS.SMS, commTaskGR.sys_id);
              sms.notifyGroupSelector = smsChannelGR.notify_group_selector + '';
              if (typeof this.notifyUtil.getListOfNotifyNumbersAndProviders == 'function')
                  sms.notifyNumbers = this.notifyUtil.getListOfNotifyNumbersAndProviders(commTaskGR.getRecordClassName(), commTaskGR.getUniqueValue(), sms.notifyGroupSelector).numbers;
              else
                  sms.notifyNumbers = [];
          } else
              return false;
      } else
          sms.available = false;
      return sms;
  },

  getEmailMetadata: function(commTaskGR, expandGroupsAndRecipients) {
      var emailChannelGR = new GlideRecord(this.TABLES.COMM_CHANNEL_EMAIL);
      emailChannelGR.addQuery(this.COLUMNS.COMM_TASK, commTaskGR.sys_id);
      emailChannelGR.query();
      var email = {};
      if (emailChannelGR.next()) {
          if (!emailChannelGR.email_client_template)
              return false;
          email.template_id = emailChannelGR.email_client_template.toString();
          /* Keeping backward compatibility on getContactAccessPoints, as it used from other places. */
          var isRecipientItemsEnabled = null;
          if (commTaskGR.getValue('sys_class_name') === 'incident_alert_task')
              isRecipientItemsEnabled = (gs.getProperty('com.snc.iam.email_client_recipient_items_enabled') != null) ? gs.getProperty('com.snc.iam.email_client_recipient_items_enabled') : gs.getProperty('sn_comm_management.email_client_recipient_items_enabled');
          else isRecipientItemsEnabled = gs.getProperty('sn_comm_management.email_client_recipient_items_enabled');
          var config = {
              recipient_items_enabled: (expandGroupsAndRecipients == false) ? !expandGroupsAndRecipients : (gs.getProperty('glide.email_client.recipient_items_enabled') === 'true' && isRecipientItemsEnabled === 'true' && this.isActiveRecipientListConfigured()),
              expandGroupsAndRecipients: (expandGroupsAndRecipients == false) ? expandGroupsAndRecipients : true
          };
          var recipientInfo = this.getContactAccessPoints(emailChannelGR, this.CHANNELS.EMAIL, commTaskGR.sys_id, config);
          if (config.recipient_items_enabled) {
              email.recipients = recipientInfo.emailAddresses;
              email.recipientLists = recipientInfo.recipientLists;
          } else {
              email.recipients = recipientInfo;
          }
          if (!expandGroupsAndRecipients)
              email.groups = recipientInfo.groups;
          email.recipient_field = emailChannelGR.recipient_field.toString() || this.getDefaultEmailRecipientField(commTaskGR.comm_plan.source.getRefRecord().getRecordClassName());
      } else
          email.available = false;
      return email;
  },

  isActiveRecipientListConfigured: function() {
      var emailClientConfigGr = new GlideRecord("sys_email_client_configuration");
      emailClientConfigGr.addActiveQuery();
      emailClientConfigGr.addQuery('table', 'incident_alert_task');
      emailClientConfigGr.query();
      if (!emailClientConfigGr.hasNext()) {
          emailClientConfigGr = new GlideRecord("sys_email_client_configuration");
          emailClientConfigGr.addActiveQuery();
          emailClientConfigGr.addQuery('default_configuration', true);
          emailClientConfigGr.query();
      }


      if (emailClientConfigGr.next()) {
          return (emailClientConfigGr.recipient_qualifiers + "").indexOf(this.ACTIVE_RECIPIENT_LIST) != -1;
      }
      return false;
  },

  canSendUpdates: function(commTaskGR) {
      var recordClass;
      if (commTaskGR.comm_plan && commTaskGR.comm_plan.source)
          recordClass = commTaskGR.comm_plan.source.getRefRecord().getRecordClassName();
      else if (commTaskGR.getRecordClassName() == this.TABLES.INCIDENT_ALERT_TASK)
          recordClass = this.TABLES.INCIDENT;
      var commTaskHandler = new CommunicationManagementBridgeSNC().getHandlerInstance(recordClass);
      return commTaskHandler.canSendUpdates(commTaskGR);
  },

  updateChannelState: function(channelActionMap, handlerInstance, commTaskGr) {
      var channelGr;
      var currentTime = new GlideDateTime().getDisplayValue();
      var channelUpdateConsolidated = {
          update_last_communication: false,
          update_terminal_state: false
      };
      var channelMap = this.getChannelMap();
      for (var channel in channelActionMap) {
          var channelUpdate = {};
          var channelObj = channelMap[channel];
          var channelApiInstance = this.getScriptInstanceFromApiName(context, channelObj.api_name);
          channelUpdate = channelApiInstance.prepareChannelUpdates(commTaskGr, channelActionMap[channel], currentTime, channelUpdate);
          if (channelUpdate && channelUpdate.update_last_communication)
              channelUpdateConsolidated.update_last_communication = true;
          if (channelUpdate && channelUpdate.update_terminal_state)
              channelUpdateConsolidated.update_terminal_state = true;
      }
      if (channelUpdateConsolidated.update_last_communication || channelUpdateConsolidated.update_terminal_state)
          handlerInstance.updateTaskPerChannelCommunication(commTaskGr, channelUpdateConsolidated, currentTime);
  },

  findAndUpdateChannelEmailState: function(commTaskGr, channelAction, currentTime) {
      if (channelAction == this.CHANNEL_ACTIONS.SENT) {
          var channelGr = new GlideRecord(this.TABLES.COMM_CHANNEL_EMAIL);
          channelGr.addQuery(this.COLUMNS.COMM_TASK, commTaskGr.getUniqueValue());
          channelGr.query();
          if (channelGr.next()) {
              channelGr.last_communication = currentTime;
              channelGr.state = this.CHANNEL_STATES.SENT;
              channelGr.update();
              return {
                  update_last_communication: true,
                  update_terminal_state: true
              };
          }
      }
  },

  findAndUpdateChannelSMSState: function(commTaskGr, channelAction, currentTime) {
      if (channelAction == this.CHANNEL_ACTIONS.SENT) {
          var channelGr = new GlideRecord(this.TABLES.COMM_CHANNEL_SMS);
          channelGr.addQuery(this.COLUMNS.COMM_TASK, commTaskGr.getUniqueValue());
          channelGr.query();
          if (channelGr.next()) {
              channelGr.last_communication = currentTime;
              channelGr.state = this.CHANNEL_STATES.SENT;
              channelGr.update();
              return {
                  update_last_communication: true,
                  update_terminal_state: true
              };
          }
      }
  },

  findAndUpdateChannelConferenceState: function(commTaskGr, channelAction, currentTime, confSysId) {
      var channelUpdates = {
          update_last_communication: false,
          update_terminal_state: false
      };
      if (channelAction == this.CHANNEL_ACTIONS.STARTED || channelAction == this.CHANNEL_ACTIONS.COMPLETED) {
          var channelGr = new GlideRecord(this.TABLES.COMM_CHANNEL_CONFERENCE);
          channelGr.addQuery(this.COLUMNS.COMM_TASK, commTaskGr.getUniqueValue());
          channelGr.query();
          if (channelGr.next()) {
              if (channelAction == this.CHANNEL_ACTIONS.STARTED) {
                  channelGr.state = this.CHANNEL_STATES.STARTED;
                  channelGr.last_communication = currentTime;
                  channelUpdates.update_last_communication = true;
              }
              if (channelAction == this.CHANNEL_ACTIONS.COMPLETED) {
                  channelGr.state = this.CHANNEL_STATES.COMPLETED;
                  channelUpdates.update_terminal_state = true;
              }
              if (confSysId)
                  channelGr.notify_conference_call = confSysId;
              channelGr.update();
              return channelUpdates;
          }
      }
  },

  sendSMS: function(notifyNumber, toNumbers, message, commTaskGR) {
      var numbers = {
          notifyNumber: notifyNumber,
          toNumbers: toNumbers
      };
      gs.eventQueue('sn_comm_management.send_bulk_sms', commTaskGR, JSON.stringify(numbers), message);
  },

  getTableFieldsMap: function(table) {
      // Always exclude meta fields
      var exclude = {
          sys_scope: true,
          sys_replace_on_upgrade: true,
          sys_policy: true,
          sys_package: true,
          sys_customer_update: true,
          sys_domain: true,
          sys_domain_path: true,
          user_password: true
      };

      var fieldsMap = {};
      var gr = new GlideRecord(table);
      gr.initialize();
      var el = gr.getElements();
      for (var i = 0; i < el.length; i++) {
          var elem = el[i];
          var elName = elem.getName() + "";
          if (exclude[elName])
              continue;
          fieldsMap[elName] = elem.getLabel();
      }
      return fieldsMap;
  },

  getStates: function(tableName, element) {
      var tableGr = new GlideRecord(tableName);
      tableGr.initialize();
      var ele = tableGr.getElement(element);
      if (!ele.canRead())
          return null;

      var states = [];
      var gr = new GlideRecord(this.TABLES.SYS_CHOICE);
      gr.addQuery(this.COLUMNS.NAME, tableName);
      gr.addQuery(this.COLUMNS.ELEMENT, element);
      var qc = gr.addNullQuery(this.COLUMNS.INACTIVE);
      qc.addOrCondition(this.COLUMNS.INACTIVE, 'false');
      gr.addQuery(this.COLUMNS.LANGUAGE, gs.getSession().getLanguage());
      gr.query();
      if (!gr.hasNext()) {
          var baseTable = this.getBase(tableName);
          if (baseTable !== tableName)
              return this.getStates(baseTable, element);
      }
      while (gr.next()) {
          var state = {};
          state.label = gr.getValue('label');
          state.value = gr.getValue('value');
          states.push(state);
      }
      return states;
  },

  getBase: function(tableName) {
      var glideTableHierarchy = new GlideTableHierarchy(tableName);
      return glideTableHierarchy.getBase();
  },

  toJS: function(gr, requiredFields) {
      if (!gr)
          return;
      if (!gr.canRead())
          return;

      // Always exclude meta fields
      var exclude = {
          sys_scope: true,
          sys_replace_on_upgrade: true,
          sys_policy: true,
          sys_package: true,
          sys_customer_update: true,
          sys_domain: true,
          sys_domain_path: true,
          user_password: true
      };

      var obj = {};
      obj.sys_id = {};
      obj.sys_id.value = gr.getUniqueValue();

      var el = gr.getElements();
      for (var i = 0; i < el.length; i++) {
          var elem = el[i];
          var elName = elem.getName() + "";

          if ((requiredFields && requiredFields.indexOf(elName) == -1) || exclude[elName])
              continue;

          obj[elName] = {};
          if (!elem.canRead()) {
              obj[elName].canRead = false;
              continue;
          }

          obj[elName].display_value = elem.getDisplayValue();
          obj[elName].value = elem.toString();

          var fieldType = elem.getED().getInternalType();
          if (fieldType == "glide_date_time" || fieldType == "glide_date" || fieldType == "glide_time")
              obj[elName].display_value_internal = this.glideDateTimeToStr(gr, elName);
      }

      return obj;
  },

  /* getContactAccessPoints gets the contact access points i.e Email addresses for Email and Numbers for SMS */
  getContactAccessPoints: function(channelGr, channelType, commTaskSysId, config) {
      var commTaskGr = new GlideRecord(this.TABLES.COMM_TASK);
      commTaskGr.get(commTaskSysId);
      var commPlanSysId = commTaskGr.comm_plan;
      var commPlanTable = channelGr.comm_task.comm_plan.getRefRecord().getRecordClassName();
      if (channelType == this.CHANNELS.EMAIL) {
          return this._getContactsForEmail(commPlanSysId, commPlanTable, config);
      } else if (channelType == this.CHANNELS.SMS) {
          return this._getContactsForSMS(commPlanSysId, commPlanTable);
      } else if (channelType == this.CHANNELS.CONFERENCE) {
          return this._getContactsForConference(commPlanSysId, commPlanTable);
      }
  },

  resolveContactsToUsers: function(commPlanSysId, commPlanTable, maxContactLimit, ignoreOnCall) {
      if (!maxContactLimit) {
          maxContactLimit = this._getMaxContactLimit();
      }

      var userIds = this.resolveContactsToUserIds(commPlanSysId, commPlanTable, maxContactLimit, ignoreOnCall);

      var usersRetrieved = (maxContactLimit - 1) + "";

      if (userIds.length === maxContactLimit) {
          gs.addInfoMessage(gs.getMessage("{0} users were retrieved for sending communication per the maximum limit set by Admin.", usersRetrieved));
          userIds.pop();
      }

      if (!userIds || userIds.length == 0) {
          return [];
      }

      return this.resolveIdsToUserDetails(userIds);
  },

  _getMaxContactLimit: function() {
      var maxContactLimitProperty = 'sn_comm_management.max_contact_limit';
      return Number(gs.getProperty(maxContactLimitProperty, 10000)) + 1;
  },

  resolveContactsToUserIds: function(commPlanSysId, commPlanTable, maxContactLimit, ignoreOnCall) {
      var userIds = [];
      if (!maxContactLimit) {
          maxContactLimit = this._getMaxContactLimit();
      }

      var contactGr = this.getContactGr(commPlanSysId, commPlanTable);

      while (contactGr.next() && userIds.length < maxContactLimit) {
          var userIdsLength = userIds.length;

          if (contactGr.user && contactGr.user.toString()) {
              userIds.push(contactGr.user.toString());
          } else if (contactGr.group && contactGr.group.toString()) {
              var onCallUsers = [];

              if (ignoreOnCall) {
                  onCallUsers = this.getGroupMembers(contactGr.group + '');
              } else {
                  onCallUsers = this.getOnCallResourcesForGroup(contactGr.group, commPlanSysId);
              }
              if (onCallUsers.length > (maxContactLimit - userIdsLength))
                  onCallUsers = onCallUsers.slice(0, (maxContactLimit - userIdsLength));
              for (var i = 0; i < onCallUsers.length; i++)
                  userIds.push(onCallUsers[i].id);
          } else if (contactGr.recipient_list && contactGr.recipient_list.toString()) {
              if (contactGr.recipient_list.type == "internal") {
                  var recipientInfo = this._getRecipientContacts(contactGr, commPlanSysId, commPlanTable, this.CHANNELS.SMS);
                  var recipient = recipientInfo.recipientUsersList;
                  if (recipient.length > (maxContactLimit - userIdsLength))
                      recipient = recipient.slice(0, (maxContactLimit - userIdsLength));
                  userIds = userIds.concat(recipient);
              }
          }
      }
      return userIds;
  },

  resolveIdsToUserDetails: function(userIds) {
      if (!userIds || userIds.length == 0)
          return [];

      var users = [];

      var userGr = new GlideRecord(this.TABLES.SYS_USER);
      userGr.addQuery('sys_id', 'IN', userIds.join(','));
      userGr.query();
      while (userGr.next()) {
          var user = {
              id: userGr.getUniqueValue(),
              email: {
                  value: userGr.email + '',
                  display_value: userGr.getDisplayValue('email')
              },
              name: {
                  value: userGr.name + '',
                  display_value: userGr.getDisplayValue('name')
              }
          };
          users.push(user);
      }
      return users;
  },

  _getContactsForEmail: function(commPlanSysId, commPlanTable, config) {
      var emailAddresses = [],
          userList = [],
          recipientLists = [],
          groupSysIds = [];
      var contactGr = this.getContactGr(commPlanSysId, commPlanTable);
      var commPlanGr = new GlideRecord(this.TABLES.COMM_PLAN);
      var handlerInstance, maxContactLimitProperty, maxContactLimit = this.MAX_DEFAULT_EMAIL,
          emailAddressLength = 0;
      if (commPlanGr.get(commPlanSysId)) {
          if (commPlanGr.sys_class_name == 'incident_alert') {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(this.TABLES.INCIDENT);
          } else {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(commPlanGr.source.getRefRecord().getRecordClassName());
          }
          maxContactLimitProperty = handlerInstance.PROPERTIES.MAX_CONTACT_LIMIT_EMAIL;
          maxContactLimit = Number(gs.getProperty(maxContactLimitProperty, this.MAX_DEFAULT_EMAIL)) + 1;
      }

      while (contactGr.next() && emailAddressLength < maxContactLimit) {
          if (contactGr.user && contactGr.user.toString()) {
              emailAddresses.push(contactGr.user.email.toString());
              emailAddressLength++;
          } else if (contactGr.group && contactGr.group.toString()) {
              if (config.expandGroupsAndRecipients) {
                  emailAddresses = emailAddresses.concat(this._getAllGroupMembersInfo(contactGr.group.toString(), this.CHANNELS.EMAIL, maxContactLimit - emailAddressLength));
                  emailAddressLength = emailAddresses.length;
              } else
                  groupSysIds.push(contactGr.group.toString());
          } else if (contactGr.consumer && contactGr.consumer.toString()) {
              // NO-OP
          } else if (contactGr.recipient_list && contactGr.recipient_list.toString()) {
              if (contactGr.recipient_list.type == "internal") {
                  var recipientInfo = this._getRecipientContacts(contactGr, commPlanSysId, commPlanTable, this.CHANNELS.EMAIL, config);
                  var userList = recipientInfo.recipientUsersList;
                  if (recipientInfo.recipientList) {
                      recipientLists.push(recipientInfo.recipientList);
                  }
                  if ((emailAddressLength + userList.length) > maxContactLimit)
                      userList = userList.slice(0, (maxContactLimit - emailAddressLength));
                  emailAddresses = emailAddresses.concat(this._getEmailFromUserId(userList));
                  emailAddressLength = emailAddresses.length;
              }
          }
      }
      if (emailAddressLength == maxContactLimit) {
          gs.addInfoMessage(gs.getMessage("{0} users were retrieved for sending Email per the maximum limit set by Admin.", (maxContactLimit - 1) + ""));
          emailAddresses.pop();
      }
      if (config && config.recipient_items_enabled) {
          var output = {
              recipientLists: recipientLists,
              emailAddresses: emailAddresses.join()
          };
          if (!config.expandGroupsAndRecipients)
              output.groups = groupSysIds;
          return output;
      } else
          return emailAddresses.join();
  },

  _getRecipientContacts: function(contactGr, commPlanSysId, commPlanTable, channelType, config) {
      var recipientId = contactGr.getValue(this.COLUMNS.RECIPIENT_LIST);
      var recipientUsersList = [];
      var recipientList;
      if (recipientId) {
          var recipientGr, commPlanGr, commPlanSourceId;
          recipientGr = new GlideRecord(this.TABLES.PUBLICATIONS_RECIPIENT_LIST);
          if (recipientGr.get(recipientId)) {
              commPlanGr = new GlideRecord(commPlanTable);
              if (commPlanGr.get(commPlanSysId)) {
                  commPlanSourceId = commPlanGr.getValue(this.COLUMNS.SOURCE);
                  var sourceTable = commPlanGr.source.getRefRecord().getRecordClassName();
                  var needOnCallUsers = false;
                  if (channelType == this.CHANNELS.SMS || channelType == this.CHANNELS.CONFERENCE)
                      needOnCallUsers = true;
                  if (channelType === this.CHANNELS.EMAIL && config && config.recipient_items_enabled) {
                      recipientList = {
                          sys_id: recipientGr.getUniqueValue(),
                          display_value: recipientGr.getDisplayValue()
                      };
                  } else {
                      recipientUsersList = new sn_publications.RecipientsListApi().refreshRecepientList(recipientGr, true, {
                          "commPlanSysId": commPlanSysId,
                          "commPlanSourceId": commPlanSourceId,
                          "sourceTable": sourceTable,
                          "needOnCallUsers": needOnCallUsers
                      });
                  }
              }
          }
      }
      return {
          recipientList: recipientList,
          recipientUsersList: recipientUsersList
      };
  },

  _getAllGroupMembersInfo: function(grpId, channelType, maxLimit) {
      var grpMembers = [];
      var grpGr = new GlideRecord(this.TABLES.SYS_USER_GROUP_MEMBERS);
      grpGr.addQuery("group", grpId);
      if (maxLimit)
          grpGr.setLimit(maxLimit);
      grpGr.query();
      while (grpGr.next()) {
          if (channelType == this.CHANNELS.EMAIL)
              grpMembers.push(grpGr.user.email.toString());
          else if (channelType == this.CHANNELS.CHAT)
              grpMembers.push(grpGr.user.toString());
      }
      return grpMembers;
  },

  _getEmailFromUserId: function(userList) {
      var userGr, emailAddresses = [];
      for (var i = 0; i < userList.length; i++) {
          userGr = this._getUserByID(userList[i]);
          if (userGr)
              emailAddresses.push(userGr.getValue('email'));
      }
      return emailAddresses;
  },

  _getContactsForSMS: function(commPlanSysId, commPlanTable) {
      var smsContacts = [],
          userList = [];
      var contactGr = this.getContactGr(commPlanSysId, commPlanTable);
      var commPlanGr = new GlideRecord(this.TABLES.COMM_PLAN);
      var handlerInstance, maxContactLimitProperty, maxContactLimit = this.MAX_DEFAULT_SMS,
          smsContactsLength = 0;
      if (commPlanGr.get(commPlanSysId)) {
          if (commPlanGr.sys_class_name == 'incident_alert') {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(this.TABLES.INCIDENT);
          } else {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(commPlanGr.source.getRefRecord().getRecordClassName());
          }
          maxContactLimitProperty = handlerInstance.PROPERTIES.MAX_CONTACT_LIMIT_SMS;
          maxContactLimit = Number(gs.getProperty(maxContactLimitProperty, this.MAX_DEFAULT_SMS)) + 1;
      }
      while (contactGr.next() && smsContactsLength < maxContactLimit) {
          if (contactGr.user && contactGr.user.toString()) {
              userList.push(contactGr.user.toString());
              smsContactsLength++;
          } else if (contactGr.group && contactGr.group.toString()) {
              var onCallUsers = this.getOnCallResourcesForGroup(contactGr.group, commPlanSysId);
              if (onCallUsers.length > (maxContactLimit - smsContactsLength))
                  onCallUsers = onCallUsers.slice(0, (maxContactLimit - smsContactsLength));
              for (var i = 0; i < onCallUsers.length; i++)
                  userList.push(onCallUsers[i].id);
              smsContactsLength = userList.length;
          } else if (contactGr.recipient_list && contactGr.recipient_list.toString()) {
              if (contactGr.recipient_list.type == "internal") {
                  var recipientInfo = this._getRecipientContacts(contactGr, commPlanSysId, commPlanTable, this.CHANNELS.SMS);
                  var recipient = recipientInfo.recipientUsersList;
                  if (recipient.length > (maxContactLimit - smsContactsLength))
                      recipient = recipient.slice(0, (maxContactLimit - smsContactsLength));
                  userList = userList.concat(recipient);
                  smsContactsLength = userList.length;
              }
          }
          // TO DO: Support for consumers can be added in London+ releases
      }
      if (smsContactsLength == maxContactLimit) {
          gs.addInfoMessage(gs.getMessage("{0} users were retrieved for sending SMS per the maximum limit set by Admin.", (maxContactLimit - 1) + ""));
          userList.pop();
      }
      smsContacts = this._getSMSfromUserIds(userList);
      return smsContacts;
  },

  _getSMSfromUserIds: function(userList) {
      var smsContacts = [];
      if (!this.notifyUtils) return smsContacts;
      for (var i = 0; i < userList.length; i++) {
          var userGr = this._getUserByID(userList[i]);
          if (userGr) {
              var contact = {};
              contact.name = userGr.name + '';
              contact.numbers = this.notifyUtils.getUniquePhoneNumbersForUsersAndGroups([], [userList[i] + ''], [], this.notifyUtils.numberType.sms);
              smsContacts.push(contact);
          }
      }
      return smsContacts;
  },

  _getContactsForConference: function(commPlanSysId, commPlanTable) {
      var contactsForConference = [];
      if (!this.notifyUtils) return contactsForConference;
      var contactGr = this.getContactGr(commPlanSysId, commPlanTable);
      while (contactGr.next()) {
          if (contactGr.user && contactGr.user.toString()) {
              contactsForConference = this.notifyUtils.appendUniquely(contactsForConference, contactGr.user.toString());
          } else if (contactGr.group && contactGr.group.toString()) {
              var onCallUsers = this.getOnCallResourcesForGroup(contactGr.group, commPlanSysId);
              contactsForConference = this.notifyUtils.appendUniquely(contactsForConference, onCallUsers);
          } else if (contactGr.recipient_list && contactGr.recipient_list.toString()) {
              if (contactGr.recipient_list.type == "internal") {
                  var recipientInfo = this._getRecipientContacts(contactGr, commPlanSysId, commPlanTable, this.CHANNELS.CONFERENCE);
                  var recipient = recipientInfo.recipientUsersList;
                  contactsForConference = this.notifyUtils.appendUniquely(contactsForConference, recipient);
              }
          }
          // TODO: Support for consumers can be added in London + releases
      }
      return contactsForConference;
  },

  isEscalateConference: function(commPlanSysId) {
      var escalateConference;
      var commPlanGr = new GlideRecord(this.TABLES.COMM_PLAN);
      if (commPlanGr.get(commPlanSysId)) {
          var handlerInstance;
          if (commPlanGr.sys_class_name == this.TABLES.INCIDENT_ALERT) {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(this.TABLES.INCIDENT);
          } else {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(commPlanGr.source.getRefRecord().getRecordClassName());
          }
          escalateConference = gs.getProperty(handlerInstance.PROPERTIES.ESCALATE_CONFERENCE);
      }
      return escalateConference == "true";
  },

  getContactsForConferenceModal: function(commPlanSysId, commPlanTable) {
      var contactsForConference = [];
      if (!this.notifyUtils) return contactsForConference;
      var contactGr = this.getContactGr(commPlanSysId, commPlanTable);
      var escalateConference = this.isEscalateConference(commPlanSysId);

      while (contactGr.next()) {
          if (contactGr.user && contactGr.user.toString()) {
              contactsForConference = this.notifyUtils.appendUniquely(contactsForConference, contactGr.user.toString());
          } else if (contactGr.group && contactGr.group.toString()) {

              var groupGr = new GlideRecord('sys_user_group');
              groupGr.addActiveQuery();
              if (groupGr.get(contactGr.group.toString())) {
                  var escalationContacts = this.processOnCallConferenceEscalatees(groupGr, commPlanSysId, escalateConference);
                  contactsForConference = this.notifyUtils.appendUniquely(contactsForConference, escalationContacts);
              }

          } else if (contactGr.recipient_list && contactGr.recipient_list.toString()) {
              if (contactGr.recipient_list.type == "internal") {
                  var recipientInfo = this._getRecipientContacts(contactGr, commPlanSysId, commPlanTable, this.CHANNELS.CONFERENCE);
                  var recipient = recipientInfo.recipientUsersList;
                  contactsForConference = this.notifyUtils.appendUniquely(contactsForConference, recipient);
              }
          }
          // TODO: Support for consumers can be added in London + releases
      }
      return contactsForConference;
  },
  processOnCallConferenceEscalatees: function(groupGr, commPlanSysId, escalateConference) {
      var escalatees = this.getOnCallResourcesForConference(groupGr, commPlanSysId);
      var escalationContacts = [];
      var groups = {};
      var notifyUtils = new global.NotifyUtils();
      for (var i = 0; i < escalatees.length; i++) {
          var escalatee = escalatees[i];
          var groupId = escalatee.groupId + '';
          if (!groups[groupId]) {
              groups[groupId] = {
                  name: escalatee.group + '',
                  id: groupId,
                  group: groupGr,
                  groupId: groupId,
                  showGroupName: escalateConference,
                  users: []
              };
          }
          var eachEscalatee = {
              name: escalatee.name + '',
              id: escalatee.id + '',
              group: groupGr,
              groupId: groupId,
              showGroupName: escalateConference,
              level: escalatee.level
          };
          if (escalatee.type == 'user') {
              var userGr = new GlideRecord("sys_user");
              if (!userGr.get(escalatee.id + ''))
                  continue;
              if (!userGr.active)
                  continue;
              var numbers = notifyUtils.getUniquePhoneNumbersForUsersAndGroups([], [userGr.getUniqueValue()], [], notifyUtils.numberType.voice);
              eachEscalatee.phoneNumber = numbers.length > 0 ? numbers[0] : '';
          } else if (escalatee.type == 'phone_number') {
              eachEscalatee.phoneNumber = escalatee.id + '';
          } else {
              // if any other type comes in future , need to be handled.
              continue;
          }
          escalationContacts.push(eachEscalatee);
      }
      return escalationContacts;
  },
  getContactsForChat: function(commPlanSysId, commPlanTable) {
      var userList = [];
      var contactGr = this.getContactGr(commPlanSysId, commPlanTable);
      while (contactGr.next()) {
          if (contactGr.user && contactGr.user.toString()) {
              userList.push(contactGr.user.toString());
          } else if (contactGr.group && contactGr.group.toString()) {
              userList = userList.concat(this._getAllGroupMembersInfo(contactGr.group.toString(), this.CHANNELS.CHAT));
          } else if (contactGr.consumer && contactGr.consumer.toString()) {
              // NO-OP
          } else if (contactGr.recipient_list && contactGr.recipient_list.toString()) {
              if (contactGr.recipient_list.type == "internal")
                  var recipientInfo = this._getRecipientContacts(contactGr, commPlanSysId, commPlanTable, this.CHANNELS.CHAT);
              userList = userList.concat(recipientInfo.recipientUsersList);
          }
      }
      userList = this.arrayUtils.unique(userList);
      return userList;
  },

  getGlideDateTimeObj: function(gr, fieldName) {
      if (gr.getValue(fieldName))
          return new GlideDateTime(gr.getValue(fieldName));
      return null;
  },

  glideDateTimeToStr: function(gr, fieldName) {
      var obj = this.getGlideDateTimeObj(gr, fieldName);
      if (obj)
          return obj.getDisplayValueInternal() + '';
      return null;
  },

  _getUserByID: function(id) {
      return this._getRecordBySysID('sys_user', id);
  },

  _getRecordBySysID: function(table, id) {
      var record = new GlideRecord(table);
      if (record.get(id))
          return record;
  },

  getContactTypes: function() {
      var contactsGr = new GlideRecord(this.TABLES.CONTACT);
      contactsGr.initialize();
      if (contactsGr.type.canRead())
          return this.getStates('contact_responsibility', 'type');
      return [];
  },

  getCommPlanRecipients: function(commPlanGr) {
      var recipientsGr = new GlideRecord(this.TABLES.CONTACT);
      recipientsGr.addQuery(this.COLUMNS.TABLE, commPlanGr.getRecordClassName());
      recipientsGr.addQuery(this.COLUMNS.DOCUMENT, commPlanGr.sys_id);
      recipientsGr.query();
      return recipientsGr;
  },

  whoIsOnCall: function(groupIds) {
      if (groupIds) {
          var escalatees = this.ocr.getEscalatees(groupIds);
          for (var i = 0; i < escalatees.length; i++) {
              var userGr = this._getUserByID(escalatees[i].userId);
              if (!userGr)
                  continue;
              escalatees[i].name = userGr.name + "";
              escalatees[i].rosterName = this._getRosterName(escalatees[i].roster);
          }
          return escalatees;
      } else {
          var onCallInfo = this.ocr.onCallGroups();
          return onCallInfo;
      }
  },

  _getRosterName: function(rosterId) {
      var rosterGr = new GlideRecord('cmn_rota_roster');
      if (rosterGr.get(rosterId))
          return rosterGr.name;
  },

  /**
   * Get all on call resources for a particular group
   *
   * @param group
   * @returns {Array}
   * @private
   */
  getOnCallResourcesForGroup: function(group, commPlanSysId) {
      var onCallResources = [];
      if (!group) return onCallResources;
      var commPlanGr = new GlideRecord(this.TABLES.COMM_PLAN);
      if (commPlanGr.get(commPlanSysId)) {
          var handlerInstance;
          if (commPlanGr.sys_class_name == 'incident_alert') {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(this.TABLES.INCIDENT);
          } else {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(commPlanGr.source.getRefRecord().getRecordClassName());
          }
          var ESCALATION_PROPERTY = handlerInstance.PROPERTIES.ESCALATION_PROPERTY;
          // check if On Call Rotation is installed
          if (this.ocr && this._groupHasRotas(group)) {
              // get the escalation plan
              var depth = (gs.getProperty(ESCALATION_PROPERTY, 2)) * 1;
              var escalationPlan = this.whoIsOnCall(group.sys_id + "");
              var planSize = escalationPlan.length;
              var displayPlanSize = ((depth == -1) || (depth >= planSize)) ? planSize : depth;
              // iterate over contacts in the escalation plan
              for (var i = 0; i < displayPlanSize; i++) {
                  var contact = escalationPlan[i];
                  var level = contact.order;
                  if (this._isUserActive(contact.userId + '')) {
                      onCallResources.push({
                          id: contact.userId + '',
                          level: level,
                          group: group.name,
                          groupId: group.sys_id
                      });
                  }
              }
          }
      }
      return onCallResources;
  },

  getGroupMembers: function(grpId) {
      var grpMembers = [];
      var grpMemberGr = new GlideRecord(this.TABLES.SYS_USER_GROUP_MEMBERS);
      grpMemberGr.addQuery("group", grpId);
      grpMemberGr.addQuery("user.active", true);
      grpMemberGr.query();
      while (grpMemberGr.next()) {
          grpMembers.push({
              id: grpMemberGr.user + ''
          });
      }
      return grpMembers;
  },

  getContactGr: function(commPlanSysId, table) {
      var contactGr = new GlideRecord(this.TABLES.CONTACT);
      contactGr.addQuery(this.COLUMNS.TABLE, table);
      contactGr.addQuery(this.COLUMNS.DOCUMENT, commPlanSysId);
      contactGr.query();
      return contactGr;
  },

  getOnCallResourcesForConference: function(group, commPlanSysId) {
      var currentOncallEscalatees = [];
      if (!group)
          return currentOncallEscalatees;
      var commPlanGr = new GlideRecord(this.TABLES.COMM_PLAN);
      if (commPlanGr.get(commPlanSysId)) {
          var handlerInstance;
          var taskGr;
          var taskDetails = {};
          if (commPlanGr.source) {
              var sourceGr = commPlanGr.source.getRefRecord();
              taskDetails.table = sourceGr.getRecordClassName();
              taskDetails.sys_id = sourceGr.getUniqueValue();
          }
          if (commPlanGr.sys_class_name == 'incident_alert') {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(this.TABLES.INCIDENT);
          } else {
              handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(commPlanGr.source.getRefRecord().getRecordClassName());
          }
          var escalateConference = gs.getProperty(handlerInstance.PROPERTIES.ESCALATE_CONFERENCE);

          if (this.ocr && this._groupHasRotas(group)) {
              var depth;
              if (escalateConference == "true")
                  depth = 1;
              else
                  depth = (gs.getProperty(handlerInstance.PROPERTIES.ESCALATION_LEVEL, 2)) * 1;

              if (taskDetails.table && taskDetails.sys_id) {
                  taskGr = new GlideRecord(taskDetails.table);
                  taskGr.get(taskDetails.sys_id);
              }
              var escalationPlan = this.ocr.getEscalationPlan(group.getUniqueValue(), null, "", taskGr);
              var planSize = escalationPlan.length;
              var displayPlanSize = ((depth == -1) || (depth >= planSize)) ? planSize : depth;
              for (var i = 0; i < displayPlanSize; i++) {
                  var escalatee = escalationPlan[i];
                  this._addEscalatee(escalatee, currentOncallEscalatees, group, escalateConference == "true");
                  for (var j = 0; j < escalatee.additionalEscalatees.length; j++) {
                      var additionalEscalatee = escalatee.additionalEscalatees[j];
                      this._addEscalatee(additionalEscalatee, currentOncallEscalatees, group, escalateConference == "true");
                  }
              }
          } else if (this.getProperty(handlerInstance.PROPERTIES.ADD_ALL_MEMBERS_ON_NO_ROTA_DEFINED))
              return this._getAllMembersInGroup(group);
      }
      return currentOncallEscalatees;
  },

  _getAllMembersInGroup: function(grpGr) {
      var userGr = new GlideRecord("sys_user");
      userGr.addActiveQuery();
      var cond = userGr.addJoinQuery("sys_user_grmember", "sys_id", "user");
      cond.addCondition("group", grpGr.getUniqueValue());
      userGr.query();
      var numbers;
      var contacts = [];
      while (userGr.next()) {
          numbers = this.notifyUtils.getUniquePhoneNumbersForUsersAndGroups([], [userGr.getUniqueValue() + ''], []);
          contacts.push({
              name: gs.getMessage("{0} ({1})", [userGr.getDisplayValue(), grpGr.getDisplayValue()]),
              id: userGr.getUniqueValue() + '',
              phoneNumber: numbers.length > 0 ? numbers[0] : ''
          });
      }
      return contacts;
  },

  _getEscalateeDisplayProps: function(userId, groupGr, escalateeType, isEscalation, rosterId) {
      var props = {
          name: '',
          color: 'black'
      };

      var rosterName = this._getRosterName(rosterId);
      if (!rosterName)
          rosterName = "";

      if (isEscalation) {
          var userGr = this._getUserByID(userId);
          if (userGr) {
              props.name = userGr.name;
          }
          return props;
      }
      var name = '';
      var additionalInformation = '';
      if (escalateeType == 'user') {
          var userGr = this._getUserByID(userId);
          additionalInformation = gs.getMessage('{0} ({1} {2})', [userGr.name, rosterName, groupGr.name]);
          // lookup the voice numbers for this contact
          numbers = this.notifyUtils.getUniquePhoneNumbersForUsersAndGroups([], [userId], [], this.notifyUtils.numberType.voice);
          if (numbers.length == 0)
              props.color = 'tomato'; // has no number, mark as red
      } else if (escalateeType == 'phone_number') {
          additionalInformation = gs.getMessage('{0} ({1} {2})', [userId, rosterName, groupGr.name]);
      } else if (escalateeType == 'group')
          additionalInformation = gs.getMessage('{0} {1}', [rosterName, groupGr.name]);

      props.name = additionalInformation;
      return props;
  },

  _addEscalatee: function(escalatee, currentOncallEscalatees, groupGr, isEscalation) {
      if (escalatee.userId && this._isUserActive(escalatee.userId)) {
          var props = this._getEscalateeDisplayProps(escalatee.userId, groupGr, 'user', isEscalation, escalatee.rosterId);
          currentOncallEscalatees.push({
              id: escalatee.userId,
              level: escalatee.order,
              group: groupGr.name,
              groupId: groupGr.sys_id,
              type: 'user',
              name: props.name,
              color: props.color
          });
      }
      for (var i = 0; i < escalatee.userIds.length; i++) {
          var user = escalatee.userIds[i];
          if (user && this._isUserActive(user)) {
              var props = this._getEscalateeDisplayProps(user, groupGr, 'user', isEscalation, escalatee.rosterId);
              currentOncallEscalatees.push({
                  id: user,
                  level: escalatee.order,
                  group: groupGr.name,
                  groupId: groupGr.sys_id,
                  type: 'user',
                  name: props.name,
                  color: props.color
              });
          }
      }
      for (var i = 0; i < escalatee.escalationGroups.length; i++) {
          var groupId = escalatee.escalationGroups[i];
          if (groupId) {
              var props = this._getEscalateeDisplayProps(groupId, groupGr, 'group', isEscalation, escalatee.rosterId);
              currentOncallEscalatees.push({
                  id: groupId,
                  level: escalatee.order,
                  group: groupGr.name,
                  groupId: groupGr.sys_id,
                  type: 'group',
                  name: props.name,
                  color: props.color
              });
          }
      }
      if (escalatee.deviceId) {
          var deviceGr1 = new GlideRecord('cmn_notif_device');
          deviceGr1.addActiveQuery();
          if (deviceGr1.get(escalatee.deviceId)) {
              if (deviceGr1.phone_number + '' && deviceGr1.type == "Voice") {
                  var props1 = this._getEscalateeDisplayProps(deviceGr1.phone_number + '', groupGr, 'phone_number', isEscalation, escalatee.rosterId);
                  currentOncallEscalatees.push({
                      id: deviceGr1.phone_number + '',
                      level: escalatee.order,
                      group: groupGr.name,
                      groupId: groupGr.sys_id,
                      type: 'phone_number',
                      name: props1.name,
                      color: props1.color
                  });
              }
          }
      }
      for (var j = 0; j < escalatee.deviceIds.length; j++) {
          var deviceSysId = escalatee.deviceIds[j];
          var deviceGr = new GlideRecord('cmn_notif_device');
          deviceGr.addActiveQuery();
          if (deviceGr.get(deviceSysId)) {
              if (deviceGr.phone_number + '' && deviceGr.type == "Voice") {
                  var props = this._getEscalateeDisplayProps(deviceGr.phone_number + '', groupGr, 'phone_number', isEscalation, escalatee.rosterId);
                  currentOncallEscalatees.push({
                      id: deviceGr.phone_number + '',
                      level: escalatee.order,
                      group: groupGr.name,
                      groupId: groupGr.sys_id,
                      type: 'phone_number',
                      name: props.name,
                      color: props.color
                  });
              }
          }
      }
  },

  _isUserActive: function(userId) {
      var gr = new GlideRecord("sys_user");
      return gr.get(userId) && gr.active == true;
  },
  /**
   * Check if a group has On Call Rota's configured
   *
   * @param group
   * @returns {*}
   * @private
   */
  _groupHasRotas: function(group) {
      if (group) {
          var rota = new GlideRecord('cmn_rota');
          rota.addActiveQuery();
          rota.addQuery('group', group.sys_id);
          rota.query();
          return rota.next();
      }
      return false;
  },

  populateConferenceVariables: function(scratchpad, current) {
      scratchpad.conferenceCallAllowed = false;
      scratchpad.allowedToJoin = false;
      scratchpad.allowedToEnd = false;
      scratchpad.setupCorrectly = false;
      scratchpad.conferenceInProgress = false;
      scratchpad.commPlanID = current.comm_plan + "";
      scratchpad.commTaskClass = current.getRecordClassName();

      var handlerInstance;
      var NOTIFY_NUMBER;
      var ENABLE_NOTIFY;
      if (current.getRecordClassName() == "incident_alert_task") {
          handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance('incident');
          NOTIFY_NUMBER = handlerInstance.PROPERTIES.NOTIFY_NUMBER;
          ENABLE_NOTIFY = handlerInstance.PROPERTIES.ENABLE_NOTIFY;
      } else {
          handlerInstance = new CommunicationManagementBridgeSNC().getHandlerInstance(current.comm_plan.source.getRefRecord().getRecordClassName());
          NOTIFY_NUMBER = handlerInstance.PROPERTIES.NOTIFY_NUMBER;
          ENABLE_NOTIFY = handlerInstance.PROPERTIES.ENABLE_NOTIFY;
      }

      scratchpad.conferenceManagementPageName = handlerInstance.getConferenceManagementPageName();
      scratchpad.conferenceEndPageName = handlerInstance.getConferenceEndPageName();
      scratchpad.setupCorrectly = (!gs.nil(gs.getProperty(NOTIFY_NUMBER, ''))) || (gs.getProperty(ENABLE_NOTIFY, '') === 'true');

      var conference = new GlideRecord('comm_channel_conference');
      conference.addQuery('comm_task', current.sys_id);
      conference.query();
      if (conference.next()) {
          scratchpad.conferenceCallAllowed = true;
          if (conference.state == "started") {
              scratchpad.conferenceInProgress = true;
          } else {
              scratchpad.conferenceInProgress = false;
          }
          scratchpad.notifyGroupSelector = conference.notify_group_selector + '';
      }
      scratchpad.allowedToJoin = false;
      scratchpad.allowedToEnd = false;
      scratchpad.isInstanceUpgrading = gs.isPaused();
      if (gs.isPaused() && typeof global.NotifyConferenceUtils != 'function')
          scratchpad.upgradeMsg = gs.getMessage('System is upgrading. Some of the functionality may not work properly until upgrade is complete');
      scratchpad.customizationMsg = gs.getMessage('NotifyOnTaskClient UI Script is customized. Please contact your System Administrator');
      scratchpad.notifyUpgradeMsg = gs.getMessage('Notify is upgrading (or) NotifyOnTaskClient UI Script is customized. Please contact your System Administrator');
      /**
       * Continuous Upgrade scenario handling. Some of the SI that "NotifyConferenceUtils" dependent may not be loaded at least for a fraction of second.
       * but this is the best we can do.
       */
      if (typeof global.NotifyConferenceUtils != 'undefined') {
          var confUtils = new global.NotifyConferenceUtils();
          var confCalls = confUtils.getConferenceDetailsFromSourceId(current.sys_id + '');
          scratchpad.conferenceCallAllowed = scratchpad.conferenceCallAllowed && confUtils.hasFullyConfiguredConferenceAvailable();
          if (confCalls.length > 0) {
              var thisCallInfo = confCalls[0];
              scratchpad.serviceProviderName = thisCallInfo.serviceProvider;
              scratchpad.activeCallId = thisCallInfo.sysId;
              scratchpad.allowedToEnd = thisCallInfo.endConferenceAllowed;
              scratchpad.allowedToJoin = thisCallInfo.joinConferenceAllowed;
          }
      } else {
          // check if this communication task has any active conference calls associated with it
          var gr = new GlideRecord('notify_conference_call');
          gr.addEncodedQuery("active=true^table=" + handlerInstance.getCommTaskInstanceForTask() + "^source=" + current.sys_id);
          gr.query();

          // check if there are any active conference calls related to the current comm task
          if (gr.hasNext()) {
              // set scratchpad value to false so the UI action to
              // initiate a conference call is hidden
              scratchpad.conferenceCallAllowed = false;
              scratchpad.allowedToEnd = true;
          }

          if (scratchpad.conferenceCallAllowed == false && gr.next()) {
              scratchpad.activeCallId = gr.sys_id + '';
              if (this.notifyUtils) {
                  var hasActiveSession = this.notifyUtils.isParticipantSessionActive(gs.getUserID(), scratchpad.activeCallId);
                  scratchpad.allowedToJoin = !hasActiveSession;
                  scratchpad.hasActiveSession = hasActiveSession;
              }
          } else {
              scratchpad.allowedToJoin = false;
              scratchpad.allowedToEnd = false;
          }
      }
      scratchpad.followOnCallEscalation = new CommTaskNotifyUtils().isEscalateConference(scratchpad.commTaskClass, current.getUniqueValue());
  },

  checkInitiateConferenceCondition: function(scratchpad, current) {
      var isInitiateConferenceAllowed = new CommTaskNotifyUtils().isConferenceLeader(current) && scratchpad.conferenceCallAllowed && scratchpad.setupCorrectly && !scratchpad.conferenceInProgress && current.getValue('active') == '1';
      return isInitiateConferenceAllowed;
  },

  checkJoinConferenceCondition: function(scratchpad, current) {
      var isJoinConferenceAllowed = scratchpad.allowedToJoin && scratchpad.setupCorrectly && current.getValue('active') == '1';
      return isJoinConferenceAllowed;
  },

  checkEndConferenceCondition: function(scratchpad, current) {
      var isEndConferenceAllowed = new CommTaskNotifyUtils().isConferenceLeader(current) && scratchpad.allowedToEnd && scratchpad.conferenceInProgress && current.getValue('active') == '1';
      return isEndConferenceAllowed;
  },

  validateNotifyGroupSelector: function(commTaskClass, current) {
      var isNotifyGroupSelectorActive = current.notify_group_selector.active + '' == 'true';
      if (!isNotifyGroupSelectorActive)
          return false;

      var sourceTable = current.notify_group_selector.source_table + '';
      if (sourceTable) {
          var isManualGroupSelector = current.notify_group_selector.manual_selection + '' == 'true';
          if (commTaskClass != current.notify_group_selector.source_table || !isManualGroupSelector) {
              return false;
          }
      }
      return true;
  },

  getDefaultEmailRecipientField: function (tableName) {
      if (tableName && tableName === "incident" && (GlideApplicationProperty.getValue('com.snc.iam.default_email_recipient_field') != null)) {
  	    return GlideApplicationProperty.getValue('com.snc.iam.default_email_recipient_field');
      }
      return GlideApplicationProperty.getValue('sn_comm_management.default_email_recipient_field') || this.DEFAULT_EMAIL_RECIPIENT_FIELD;
  },
  
  getDialInNumber: function(conferenceGr, result) {
      var providerName = conferenceGr.service_provider + '';
      var number;
      if(providerName == "Microsoft Teams") {
  	    var data = JSON.parse(conferenceGr.service_provider_data + '');
  	    number = gs.nil(data.toll_free_number) ?  data.toll_number :  data.toll_free_number;
      }
      if(!gs.nil(number)) {
  	    result['notify_number'] = {};
  	    result['notify_number'].display_value = number;
      }
      return result;
  },

  getProperty: function(prop) {
      if(gs.nil(prop))
  	    return false;
      return gs.getProperty(prop, "false") == "true";
  },

  type: 'CommunicationManagementUtilSNC'
};

Sys ID

1b91fb105350130009170ef5d5dc345d

Offical Documentation

Official Docs: