Name

sn_hr_sp.hr_PortalUtil

Description

No description available

Script

var GLIDE_RECORD_ERROR_MESSAGE = 'Glide record cannot be empty';
var hrPluginActive = GlidePluginManager.isActive("com.sn_hr_core");
var replaceFieldTokens = function(value, record) {
  return hrPluginActive ? new sn_hr_core.VariableReplacer().replaceFieldTokens(value, record) : value;
};
var hr_PortalUtil = Class.create();
// Get the id for a parent case, given sys_id and table name from URL
hr_PortalUtil.getParentFromUrl = function(sysIdParam, tableParam) {
  var parent = sysIdParam;

  if (tableParam == 'sn_hr_core_task') {
      var task = new GlideRecord('sn_hr_core_task');
      if (task.get(parent))
          parent = String(task.parent);
  }
  return parent;
};

hr_PortalUtil.getParentJourney = function(sys_id, table) {
  if (GlidePluginManager.isActive("com.sn_jny") && table == 'sn_hr_le_case') {
      var jnyGr = new GlideRecord('sn_jny_journey');
      jnyGr.addQuery('le_case', sys_id);
      jnyGr.setLimit(1);
      jnyGr.query();
      if (jnyGr.next())
          return jnyGr.getUniqueValue();
  }
  return "";
};

hr_PortalUtil.getActualCaseRecord = function(hrCaseId) {
  var hrCase = new GlideRecord('sn_hr_core_case');
  if (hrCase.get(hrCaseId)) {
      if (hrCase.sys_class_name != 'sn_hr_core_case') {
          hrCase = new GlideRecord(hrCase.sys_class_name.toString());
          hrCase.get(hrCaseId);
      }
      return hrCase;
  }
  gs.warn('hr_PortalUtil.getActualCaseRecord: cannot get HR Case with sys_id ' + hrCaseId);
};
//Default tables
hr_PortalUtil._hrCaseTables = new GlideTableHierarchy('sn_hr_core_case').getAllExtensions();
hr_PortalUtil._hrTables = hr_PortalUtil._hrCaseTables.slice();
hr_PortalUtil._hrTables.push('sn_hr_core_task');

hr_PortalUtil._approvalTables = hr_PortalUtil._hrTables.slice();
hr_PortalUtil._approvalTables.push('sc_request');
hr_PortalUtil._itTables = new GlideTableHierarchy('sc_request').getAllExtensions();
hr_PortalUtil._smTables = new GlideTableHierarchy('sm_order').getAllExtensions();
hr_PortalUtil.isChatEnabled = function() {
  var result = GlidePluginManager.isActive('com.glide.connect.sp_widgets') &&
      gs.getProperty('glide.connect.chat.disabled', 'true') == 'false';

  result = result && hr_PortalUtil.askQuestionPropEnabled();

  return result;
};
hr_PortalUtil.askQuestionPropEnabled = function() {
  return gs.getProperty('sn_hr_sp.hr_ask_a_question_enabled', 'true') == 'true';
};
hr_PortalUtil.isPreChatQueue = function(chatQueueId) {
  var gr = new GlideRecord('sn_hr_sp_pre_chat');
  if (gr.isValid()) {
      gr.setLimit(1);
      gr.addQuery('queue', chatQueueId);
      gr.query();
      return gr.next();
  }
  return false;
};
hr_PortalUtil.prototype = {
  initialize: function(_gr) {
      this._gr = _gr;
      this._user = gs.getUserID();
      this._isAuthorizedSubjectPerson = _gr ? sn_hr_core.hr_Case.userHasSubjectPersonAccess(_gr) : false;
      this._isLEInstalled = GlidePluginManager.isActive('com.sn_hr_lifecycle_events');
      this._isSMInstalled = GlidePluginManager.isActive('com.snc.service_management.core');
      this.DELEGATION_PLUGIN_ACTIVE = GlidePluginManager.isActive('com.glide.granular_service_delegation');
      this._caseConfigForTicketPage = this._getCaseConfiguration(_gr);

      this._maxPersonFields = 6; // Maximum number of Person fields that can be configured
      this._maxDetailFields = 6; // Maximum number of Detail fields that can be configured

      this._awaitingAcceptanceState = 20; // Integer for awaiting acceptance state
      this._draftState = 1; // Integer for draft state

      this._defaultChildTodoLevels = 1; // Default number of levels to display the child todos
      this._maxChildTodoLevels = 3; // Maximum number of levels to display the child todos
      this.SKIPPED_TODO_TABLES = "sn_uni_task_universal_task"; //while fetching todos for a HR Case (to render on ticket page) these tables will be skipped
  },

  /**
   * Chat support is enabled if following plugin combinations are present
   * 1. com.glide.service-portal.agent-chat, com.glide.connect and com.glide.connect.support(deprecated)
   * 2. com.glide.service-portal.agent-chat and com.glide.awa
   * 3. com.glide.cs.chatbot
   */
  isChatSupportEnabled: function() {
      if (GlidePluginManager.isActive("com.glide.cs.chatbot"))
          return true;
      else if (GlidePluginManager.isActive("com.glide.service-portal.agent-chat")) {
          var isConnectEnabled = gs.getProperty('glide.connect.enabled', 'false') == 'true';
          var isConnectSupportEnabled = gs.getProperty('glide.connect.support.enabled', 'false') == 'true';
          if (isConnectEnabled && isConnectSupportEnabled)
              return true;
          else if (GlidePluginManager.isActive("com.glide.awa"))
              return true;
      }
      return false;
  },

  getHeaderOptions: function(widgetData) {
      if (gs.nil(widgetData) || gs.nil(widgetData.recordInfo))
          return {};

      var recordInfo = widgetData.recordInfo;

      if (recordInfo.sys_class_name == 'sc_request')
          return this.getHeaderOptionsRequests(widgetData);

      var headerOptions = {
          sys_id: String(recordInfo.sys_id)
      };
      if (!widgetData.onTicketPage)
          headerOptions.parentHeader = true;

      if (!gs.nil(widgetData.hasContent))
          headerOptions.hasContent = widgetData.hasContent;

      headerOptions.title = this._getHeaderTitle(recordInfo);

      if (widgetData.hasContent)
          headerOptions.panelToCollapse = widgetData.contentPanelId;

      headerOptions.headerLink = '?id=hrj_ticket_page&sys_id=' + recordInfo.sys_id;

      // For ESC portal, use M ticket page
      var ticketPageId = new sn_hr_sp.hr_TicketPageConfigUtil().getActiveTicketPageId();
      if (ticketPageId != null)
          headerOptions.headerLink = '?id=' + ticketPageId + '&sys_id=' + recordInfo.sys_id;

      if (widgetData.onTicketPage && this.canViewAssignmentFilter())
          headerOptions.showFilter = true;

      if (widgetData.isCompletedItem)
          headerOptions.completedItem = true;

      headerOptions.recordInfo = recordInfo;
      if (widgetData.isHeaderList && widgetData.hasTodo)
          headerOptions.showTodoPill = true;
      if (widgetData.isHeaderCaseList)
          headerOptions.isHeaderCaseList = true;
      return headerOptions;
  },

  /** Function to determine if a task/case needs to use global descriptions
   * @Param gr: GlideRecord of the case or a task.
   * @Return : Boolean.
   */
  _shouldGetGlobalDescriptions: function(gr) {
      if (gs.nil(gr)) throw new Error(this.GLIDE_RECORD_ERROR_MESSAGE);
      try {
          if (gr.template && gr.template.show_translated_fields)
              return true;
          return false;
      } catch (exception) {
          gs.error(exception.message);
          throw new Error(exception.message);
      }
  },

  /** Function to return short description for a case/task
   * @Param gr: GlideRecord of the case or a task.
   * @Return : String
   */
  getRecordShortDescription: function(gr) {
      var shortDesc;
      try {
          if (this._shouldGetGlobalDescriptions(gr))
              shortDesc = gr.getDisplayValue('template.short_description_for_employee');
          else
              shortDesc = gr.getValue('short_description');

          if (!shortDesc)
              return '';
          return replaceFieldTokens(shortDesc, gr);
      } catch (exception) {
          gs.error(exception.message);
          throw new Error(exception.message);
      }
  },

  /** Function to return global description/rich description/description for a case/task
   * @Param gr: GlideRecord of the case or a task.
   * @Return : String
   */
  getRecordDescription: function(gr) {
      var desc;
      try {
          if (this._shouldGetGlobalDescriptions(gr))
              desc = gr.getDisplayValue('template.description_for_employee');
          else
              desc = (gr.getValue('rich_description') || gr.getValue('description'));

          if (!desc)
              return '';
          return replaceFieldTokens(desc, gr);
      } catch (exception) {
          gs.error(exception.message);
          throw new Error(exception.message);
      }
  },

  getHeaderOptionsRequests: function(widgetData) {
      var recordInfo = widgetData.recordInfo;
      var sysApprovalId = this.getSysApprovalId(recordInfo.sys_id);
      var headerOptions = {};

      headerOptions.recordInfo = widgetData.recordInfo;
      headerOptions.parentHeader = true;
      headerOptions.sys_id = String(recordInfo.sys_id);
      headerOptions.title = this.getRequestTitle(recordInfo);
      if (!sysApprovalId)
          headerOptions.headerLink = '?id=sc_request&table=sc_request&sys_id=' + recordInfo.sys_id;
      else
          headerOptions.headerLink = '?id=approval&table=sysapproval_approver&sys_id=' + sysApprovalId;

      if (widgetData && widgetData.hasContent)
          headerOptions.panelToCollapse = widgetData.contentPanelId;

      if (widgetData.isCompletedItem)
          headerOptions.completedItem = true;
      if (widgetData.isHeaderList && widgetData.hasTodo)
          headerOptions.showTodoPill = true;

      if (!gs.nil(widgetData.hasContent))
          headerOptions.hasContent = widgetData.hasContent;
      return headerOptions;
  },

  getSysApprovalId: function(caseId) {
      var approvalId = new GlideRecord('sysapproval_approver');
      approvalId.addQuery('sysapproval', caseId);
      approvalId.addQuery('approver', new sn_hr_core.hr_Utils().getApprovals(gs.getUserID()));
      approvalId.query();
      if (approvalId.next())
          return approvalId.getUniqueValue();

      return false;
  },

  getRequestTitle: function(recordInfo) {
      var requested_items = new GlideRecord('sc_req_item');
      requested_items.addQuery('request', recordInfo.sys_id);
      requested_items.addActiveQuery();
      requested_items.query();

      if (requested_items.getRowCount() > 1)
          return gs.getMessage('{0} requested items', requested_items.getRowCount());
      else if (requested_items.next() && requested_items.canRead())
          return requested_items.cat_item.getDisplayValue();
      else
          return recordInfo.number + '';
  },

  _getHeaderTitle: function(recordInfo) {
      if (String(hr_PortalUtil._hrCaseTables).indexOf(recordInfo.sys_class_name) >= 0)
          return this._getCaseDescription(recordInfo.sys_id);
      else
          return replaceFieldTokens(recordInfo.short_description, recordInfo) || recordInfo.number;
  },

  getCaseTitle: function() {
      var title = this._getTitleFromCaseConfiguration(this._gr, this._caseConfigForTicketPage);
      if (!gs.nil(title))
          return title;
  },

  _getCaseTitle: function(gr) {
      var caseConfig;
      if (gr.sys_id != this._gr.sys_id)
          caseConfig = this._getCaseConfiguration(gr);
      else
          caseConfig = this._caseConfigForTicketPage;
      var title = this._getTitleFromCaseConfiguration(gr, caseConfig);
      if (!gs.nil(title))
          return title;
  },

  /*
  	Return the HR Case Configuration for the user based on conditions :
  	 - If User is Opened For or Approver.
  	 - If User is Subject Person or Task Assignee (or delegated).
  	 
  */
  _getCaseConfiguration: function(gr) {
      if (gs.nil(gr) || hr_PortalUtil._hrCaseTables.indexOf(gr.sys_class_name.toString()) == -1)
          return null;
      var userCaseConfig = new GlideRecord('sn_hr_core_config_case');
      var isOpenedForUser = !gs.nil(gr.getElement('opened_for')) ? gs.getUserID() == gr.getElement('opened_for') :
          false;
      if (isOpenedForUser || this.isApprovalRequired(String(gr.sys_id), gs.getUserID())) {
          if (!gs.nil(gr.hr_service) && !gs.nil(gr.hr_service.header_config_opened_for)) {
              userCaseConfig.get(String(gr.hr_service.header_config_opened_for));
              return userCaseConfig;
          }
      }

      var isSubjectPerson = !gs.nil(gr.getElement('subject_person')) ? gs.getUserID() == gr.getElement('subject_person') : false;
      if (isSubjectPerson || this._isTaskAssignee(String(gr.sys_id))) {
          if (!gs.nil(gr.hr_service) && !gs.nil(gr.hr_service.header_config_subject_person)) {
              userCaseConfig.get(String(gr.hr_service.header_config_subject_person));
              return userCaseConfig;
          }
      }
      if (!gs.nil(userCaseConfig) && !gs.nil(gr.parent) && !gs.nil(gr.hr_service.header_config_subject_person))
          userCaseConfig.get(String(gr.hr_service.header_config_subject_person));

      return userCaseConfig;
  },

  _isTaskAssignee: function(caseId) {
      if (this.DELEGATION_PLUGIN_ACTIVE)
          return this._getDelegatedTasks({
              tables: ['sn_hr_core_task'],
              encoded_query: 'parent=' + caseId
          }, gs.getUserID(), true).length > 0;

      var tasks = new GlideRecord('sn_hr_core_task');
      tasks.addQuery('parent', caseId);
      tasks.addQuery('assigned_to', gs.getUserID());
      tasks.setLimit(1);
      tasks.query();
      return tasks.hasNext();
  },

  _getTitleFromCaseConfiguration: function(caseGr, caseConfiguration) {
      if (!gs.nil(caseConfiguration)) {
          if (!gs.nil(caseConfiguration.getValue('custom_title')))
              return new sn_hr_core.hr_Utils().sanitize(String(caseConfiguration.custom_title));

          if (!gs.nil(caseConfiguration.title)) {
              var titles = String(caseConfiguration.title).split(',');
              var fieldTitleString = [];
              for (var i = 0; i < titles.length; i++) {
                  var ge = caseGr.getElement(titles[i] + '');
                  if (ge != null && ge.toString() != null && ge.canRead())
                      fieldTitleString.push(ge.getDisplayValue());
              }
              return String(fieldTitleString.join(' - '));
          }
      }
      return this._getCaseDescription(String(caseGr.sys_id));
  },

  _getCaseDescription: function(caseId) {
      var gr = new GlideRecord('sn_hr_core_case');
      var description;
      if (gr.get(caseId)) {
          var hr_service = gr.getDisplayValue('hr_service');
          var subject_person = gr.getDisplayValue('subject_person');
          var opened_for = gr.getDisplayValue('opened_for');
          if (!gs.nil(hr_service)) {
              description = hr_service;
              if (!gs.nil(subject_person))
                  description = description + ' - ' + subject_person;
              else if (!gs.nil(opened_for))
                  description = description + ' - ' + opened_for;
          }
      }
      return description;
  },

  /* Gets the description for to-dos of type HR case (used by ticket page)
  @param 
  	caseId - sys id (String) of parent LE case
  	hrServiceSysId - sys id (String) of HR service associated with the child activity
  @return
  	description - String 
  */
  getCaseDescriptionFutureTodo: function(caseId, hrServiceSysId) {
      var gr = new GlideRecord('sn_hr_core_case');
      var description;
      if (gr.get(caseId)) {
          var subject_person = gr.getDisplayValue('subject_person');
          var opened_for = gr.getDisplayValue('opened_for');
          var grService = new GlideRecord('sn_hr_core_service');
          if (grService.get(hrServiceSysId)) {
              description = grService.getDisplayValue();
              if (!gs.nil(subject_person))
                  description = description + ' - ' + subject_person;
              else if (!gs.nil(opened_for))
                  description = description + ' - ' + opened_for;
          }
      }
      return description;
  },

  getPeopleInfoFromCaseConfig: function() {
      var peoplesInfoJson = {
          peoplesInfo: []
      };

      if (!gs.nil(this._caseConfigForTicketPage)) {
          var config = this._caseConfigForTicketPage;
          for (var i = 1; i <= this._maxPersonFields; i++) {
              var personField = this._getFieldDetail(config.getValue('person_column_' + i),
                  config.getValue('person_' + i), config.getDisplayValue('p_custom_label_' + i));
              if (!this._isEmptyJsonObject(personField))
                  peoplesInfoJson.peoplesInfo.push(personField);
          }
      }
      return peoplesInfoJson;
  },

  _isEmptyJsonObject: function(obj) {
      return Object.keys(obj).length === 0;
  },

  getAdditionalFieldsInfo: function() {
      var additionalFieldsInfo = {
          additionalFields: []
      };

      if (!gs.nil(this._caseConfigForTicketPage)) {
          var config = this._caseConfigForTicketPage;

          for (var i = 1; i <= this._maxDetailFields; i++) {
              var additionalField = this._getFieldDetail(config.getValue('detail_column_' + i),
                  config.getValue('detail_' + i), config.getDisplayValue('d_custom_label_' + i));
              if (!this._isEmptyJsonObject(additionalField))
                  additionalFieldsInfo.additionalFields.push(additionalField);
          }
      }
      return additionalFieldsInfo;
  },

  processRecordsForSummary: function(tasks) {
      var processedToDos = [];
      var todoPageUtils = new sn_hr_sp.todoPageUtils();
      for (var i = 0; i < tasks.length; i++) {
          var task = tasks[i];
          var recordToShow = {};
          var tableName = task.recordInfo.sys_class_name;
          if (task.recordInfo) {
              recordToShow.future = false;
              recordToShow.sysId = task.recordInfo.uniqueId;
              recordToShow.tableName = tableName;
              recordToShow.closedAt = task.recordInfo.closed_at;
              recordToShow.isApprovalTable = (tableName == 'sysapproval_approver') ? true : false;
              recordToShow.todoNumber = task.recordInfo.number;
              recordToShow.state_num_value = task.recordInfo.state_num_value;
              recordToShow.state = task.recordInfo.state;
              recordToShow.url = '';
              recordToShow.taskType = task.recordInfo.type;
              recordToShow.todoConfigurationSysId = '';
              recordToShow.hasDueDate = task.recordInfo.due_date ? true : false;
              recordToShow.due_date = task.recordInfo.due_date;
              recordToShow.hasDueDateWarning = task.recordInfo.hasDueDateWarning;
              var dateInfo = tableName == 'sn_hr_core_task' ? this._getTodoDateInfo(task.recordInfo) : {
                  label: task.recordInfo.state,
                  numDays: ''
              };



              recordToShow.stateLabel = task.recordInfo.stateLabel;

              recordToShow.dueDateDisplayValue = dateInfo.label;
              recordToShow.dueDateDays = dateInfo.numDays;
              recordToShow.createdOn = task.recordInfo.sys_updated_on;
              recordToShow.isHRCase = task.recordInfo.isHRCase;
              recordToShow.onTicketPage = true;
              recordToShow.level = task.recordInfo.level;
              recordToShow.isCompleted = task.recordInfo.filter_info.pane_info.type == 'completed_todos';

              recordToShow.optionalLabel = task.recordInfo.optionalLabel;
              recordToShow.recordInfo = task.recordInfo;

              //Set the table name to sysapproval_approver for approver record 
              if (task.recordInfo.type == 'approval')
                  recordToShow.tableName = 'sysapproval_approver';

              if (this._isLEInstalled && !data.future) {
                  var taskGr = new GlideRecord(tableName);
                  taskGr.get(task.recordInfo.sys_id);
                  var activitySetInfo = this._getActivitySetRecordInfo(taskGr, task.recordInfo.parent);
                  if (activitySetInfo && activitySetInfo.is_adhoc)
                      task.recordInfo.future = activitySetInfo.state === 'awaiting_trigger';
              }

              //set widgetmappings display value list and due dates for all the records which respects the to-do
              // config
              if (task.recordInfo.metTodoConfig) {
                  recordToShow.isHRCase = false;
                  recordToShow.widgetMappings = task.recordInfo.toDoSummaryWidgetMappings;
                  recordToShow.displayValueList = task.recordInfo.toDoSummarydisplayValueList;
                  recordToShow.dueDateDisplayValue = task.recordInfo.toDoSummarydueDateDisplayValue;
              } else if (task.recordInfo.future) {
                  recordToShow.future = true;
                  recordToShow.dueDateDisplayValue = gs.getMessage('Upcoming to-do');
                  recordToShow.assigned_to_id = task.recordInfo.assigned_to_id;
                  recordToShow.displayValueList = [this._getTodoDescription(task.recordInfo), ''];
                  recordToShow.widgetMappings = [{
                      'condition': '',
                      'widgetSysId': '',
                      'widgetId': 'hro-future-todo'
                  }];
              } else {
                  recordToShow.displayValueList = [this._getTodoDescription(task.recordInfo), ''];
                  var widgetId = '';
                  if (task.recordInfo.type == 'task' && task.recordInfo.isHRCase)
                      widgetId = 'hrm-case-summary';
                  else
                      widgetId = 'hrm-task-activity';
                  recordToShow.widgetMappings = [{
                      'condition': '',
                      'widgetSysId': '',
                      'widgetId': widgetId
                  }];
              }

          }
          processedToDos.push(recordToShow);
      }
      return processedToDos;
  },

  _getTodoDescription: function(recordInfo) {
      var hrTables = new GlideTableHierarchy(recordInfo.sys_class_name).getTables();

      if (hrTables.indexOf('sn_hr_core_case') > -1) {
          var grCase = new GlideRecord(recordInfo.sys_class_name);
          var caseExists = grCase.get(recordInfo.sys_id);

          if (caseExists) {
              var util = new hr_PortalUtil(grCase);
              return util.getCaseTitle();
          }
      }

      return this._getTodoTitle(recordInfo);
  },

  _getTodoTitle: function(recordInfo) {
      if (recordInfo.sys_class_name == 'sc_request')
          return this.getRequestTitle(recordInfo);
      return this._getHeaderTitle(recordInfo);
  },
  getUsersForPortalDisplay: function(term, field_name) {
      var userList = {
          uList: []
      };

      var hrCase = new GlideRecord('sn_hr_core_case');
      hrCase.setLimit(1);
      hrCase.query();

      var users = new GlideRecord('sys_user');
      if (term.charAt(0) == '*') {
          term = term.substr(1);
          users.addQuery('name', 'CONTAINS', term);
      } else
          users.addQuery('name', 'STARTSWITH', term);
      users.addActiveQuery();
      if (field_name && hrCase.next() && hrCase.getElement(field_name).getED().getReferenceQualifier())
          users.addEncodedQuery(hrCase.getElement(field_name).getED().getReferenceQualifier());
      users.orderBy(users.getDisplayName());
      users.query();
      while (users.next()) {
          var u = {};
          u.text = users.name.getDisplayValue();
          u.id = users.getUniqueValue();
          u.department = users.department.getDisplayValue();
          userList.uList.push(u);
      }
      return userList;
  },

  _getTodoDateInfo: function(recordInfo) {
      var days = '';
      var message = '';
      var isApproval = recordInfo.type == 'approval';
      var notComplete = isApproval ? recordInfo.state_value == 'requested' : recordInfo.active;
      var taskUtil = new sn_hr_core.hr_Task();
      var todoPageUtils = new sn_hr_sp.todoPageUtils();
      if (recordInfo.future) {
          message = gs.getMessage('Upcoming to-do');
          return {
              label: message,
              numDays: 0
          };
      }

      if (notComplete)
          days = taskUtil.getDueDays(recordInfo.due_date);
      else if (isApproval)
          days = taskUtil.getDueDays(recordInfo.sys_updated_on);
      else
          days = taskUtil.getDueDays(recordInfo.closed_at);

      if (notComplete)
          message = todoPageUtils.getTaskDueDateDisplayValue(recordInfo.due_date);
      else
          message = recordInfo.stateLabel;
      return {
          label: message,
          numDays: days
      };
  },

  _getFieldDetail: function(isFieldSelected, fieldMapping, fieldLabel) {
      var field = {};
      var config = this._caseConfigForTicketPage;
      var hrCase = this._gr;
      var translatedFields = ['hr_service.template.short_description_for_employee',
          'template.short_description_for_employee', 'template.description_for_employee',
          'hr_service.template.description_for_employee'
      ];
      var htmlFields = ['html', 'translated_html', 'html_template', 'html_script'];
      if (isFieldSelected == '1' && !gs.nil(fieldMapping)) {
          if(fieldMapping=='rich_description' && gs.nil(hrCase.getElement(fieldMapping).toString()))
  	fieldMapping='description';
  			
          var ge = hrCase.getElement(fieldMapping);
          if (ge != null && ge.toString() != null && (ge.canRead() || translatedFields.indexOf(fieldMapping) >= 0)) {
              field.label = !gs.nil(fieldLabel) ? this.sanitize(String(fieldLabel)) : ge.getLabel();
              field.type = ge.getED().getInternalType();
              field.fieldDisplayValue = ge.getDisplayValue();
              field.fieldValue = ge.toString();
              field.isHTML = htmlFields.indexOf(field.type) >= 0;
              var user = new GlideRecord('sys_user');
              field.isUserField = user.get('sys_id', ge.toString());
              field.isWritable = ge.canWrite();
              field.fieldMapping = fieldMapping;
              if (field.type == 'glide_list') {
                  field.listDetails = [];
                  if (field.fieldValue) {
                      var userArray = String(field.fieldValue).split(',');
                      for (var i = 0; i < userArray.length; i++) {
                          var list_user = new GlideRecord('sys_user');
                          var userInfo = {};
                          if (list_user.get(userArray[i])) {
                              userInfo.id = String(list_user.sys_id);
                              userInfo.name = String(list_user.name);
                              userInfo.department = list_user.department.getDisplayValue();
                          }
                          field.listDetails.push(userInfo);
                      }
                  }
              }
          }
      }
      return field;
  },

  /*	
   * Return Reference qualifier for Hr Case Header configuration that would be valid for 
   * the particular HR Service.
   *
   * @param hrService - which has reference to 'sn_hr_core_config_case' record.
   * @return - encoded query containing list of COE String that matches COE from passed HR Service with  existing HR Case Configuration Records. 
   */
  getCaseHeaderConfigurations: function(hrService) {
      var coeArray = [];
      var queryString = 'coeIN';
      var caseHeaderConfig = new GlideRecord('sn_hr_core_config_case');
      caseHeaderConfig.addActiveQuery();
      caseHeaderConfig.addQuery('coe', 'IN',
          new GlideTableHierarchy(hrService.topic_detail.topic_category.coe).getTables());
      caseHeaderConfig.query();
      while (caseHeaderConfig.next())
          coeArray.push(String(caseHeaderConfig.coe));
      return queryString + String(coeArray);
  },

  /**
   * Check whether or not the given HR Case has any lifecycle events associated with it.
   * Will return false if the lifecycle plugin is not installed.
   */
  hasLifecycleEvents: function() {
      if (this._isLEInstalled && this._gr &&
          hr_PortalUtil._hrCaseTables.indexOf(this._gr.sys_class_name.toString()) >= 0) {
          var grContext = new GlideRecord('sn_hr_le_activity_set_context');
          grContext.addQuery('hr_case', this._gr.getUniqueValue());
          grContext.addQuery('workflow_context.table', this._gr.sys_class_name.toString());
          grContext.setLimit(1);
          grContext.query();

          return grContext.hasNext();
      }
      return false;
  },

  getFilterPanels: function(onTicketPage) {
      var overdue, dueSoon, completed, all, future;
      var panels = [];

      overdue = {
          label: gs.getMessage('Overdue'),
          panelName: 'overdue',
          indicatorClass: 'overdue',
          count: 0
      };
      dueSoon = {
          label: gs.getMessage('Due Soon'),
          panelName: 'due_soon',
          indicatorClass: 'due-today',
          count: 0
      };
      completed = {
          label: gs.getMessage('Completed'),
          panelName: 'completed_todos',
          indicatorClass: 'complete',
          count: 0
      };
      future = {
          label: gs.getMessage('Future'),
          panelName: 'future_todos',
          indicatorClass: 'future',
          count: 0
      };
      all = {
          label: gs.getMessage('All'),
          panelName: 'all_todos',
          count: 0
      };
      panels.push(overdue);
      panels.push(dueSoon);
      panels.push(completed);
      panels.push(future);
      if (onTicketPage)
          panels.push(all);

      return panels;
  },

  isOpenedForView: function() {
      return this._isOpenedForView(this._gr);
  },
  isSubjectPersonView: function() {
      return this._isSubjectPersonView(this._gr);
  },

  allowDisplayActivitySet: function() {
      return this._allowDisplayActivitySet(this._gr);
  },

  allowDisplayActivitySetToSubjectPerson: function() {
      return this._allowDisplayActivitySetToSubjectPerson(this._gr);
  },

  /**
   * The assignmentfilter will be viewable for Opened for or Subject Persons viewing a case with the property checked
   * Will return false for other users (task assignee, approvers, etc)
   */
  canViewAssignmentFilter: function() {
      return this._gr && (this._isAuthorizedSubjectPerson || this.isOpenedForView());
  },

  getApprovalCount: function(caseId) {
      var grApproval = new GlideRecordSecure('sysapproval_approver');
      grApproval.addQuery('sysapproval', caseId);
      grApproval.query();
      return grApproval.getRowCount();
  },

  isApprovalRequired: function(caseId, userId) {
      // Check for granular delegations
      var config = {
          'encoded_query': 'sysapproval=' + caseId + '^ORdocument_id=' + caseId,
          'limit': 1
      };
      if (new sn_hr_core.hr_Delegation().getDelegatedApprovals(config, userId, true).length > 0)
          return true;
  
      var grApprovalRequire = new GlideRecordSecure('sysapproval_approver');
      grApprovalRequire.addQuery('sysapproval', caseId);
      grApprovalRequire.addQuery('approver', new sn_hr_core.hr_Utils().getApprovals(userId));
      grApprovalRequire.setLimit(1);
      grApprovalRequire.query();
      return grApprovalRequire.hasNext();
  },

  ticketPage: function() {
      var page = this.isLegacyPortal() ? 'hri_task_details' : 'hrj_ticket_page';

      // For ESC portal, use M ticket page
      if (page == 'hrj_ticket_page') {
          var ticketPageId = new sn_hr_sp.hr_TicketPageConfigUtil().getActiveTicketPageId();
          if (ticketPageId != null)
              page = ticketPageId;
      }

      return page;
  },

  // return true if Istanbul (legacy) portal is still in use
  isLegacyPortal: function() {
      var portal = new GlideRecord('sp_portal');
      return portal.get('31e232209f22120047a2d126c42e70b4') && portal.url_suffix == 'hrportal';
  },

  hasAttachments: function(tableSysId) {
      var gr = new GlideRecord('sys_attachment');
      return gr.get('table_sys_id', tableSysId);
  },

  /* Start of Refactoring */

  getActivitySetContexts: function(caseId) {
      var contextValues = [];
      var hiddenContextValues = [];
      var firstRunningActvitySet = {};
      var lastFinishedActivitySet = {};

      var grContext = new GlideRecord('sn_hr_le_activity_set_context');
      grContext.addQuery('hr_case', caseId);
      grContext.orderBy('activity_set.display_order');
      grContext.query();

      while (grContext.next()) {
          var activeProgress = this._isActivitySetActive(grContext.state) ? true : false;
          if (contextValues.length > 0) {
              if (activeProgress && this._isActivitySetActive(contextValues[contextValues.length - 1].state))
                  contextValues[contextValues.length - 1].activeProgress = true;
              else
                  contextValues[contextValues.length - 1].activeProgress = false;
          }

          var activitySet = {};
          activitySet.id = grContext.getUniqueValue();
          activitySet.name = grContext.activity_set.display_title.toString();
          activitySet.state = grContext.state.toString();
          activitySet.stateDisplay = grContext.state.getDisplayValue();
          activitySet.order = Number(grContext.activity_set.display_order);

          // check if the current activity set needs to be skipped (hidden)
          if (this._hideActivitySet(grContext)) {
              hiddenContextValues.push(activitySet);
              continue;
          }

          if (this._isEmptyJsonObject(firstRunningActvitySet) && grContext.state == 'running_activities') {
              firstRunningActvitySet.id = activitySet.id;
              firstRunningActvitySet.name = activitySet.name;
              firstRunningActvitySet.state = activitySet.state;
          }
          if (grContext.state == 'finished') {
              lastFinishedActivitySet.id = activitySet.id;
              lastFinishedActivitySet.name = activitySet.name;
              lastFinishedActivitySet.state = activitySet.state;
          }
          contextValues.push(activitySet);
      }

      //select the 1st running activity set by defalult
      if (!this._isEmptyJsonObject(firstRunningActvitySet))
          return {
              viewAllToDos: false,
              firstOpen: firstRunningActvitySet.id,
              firstOpenName: firstRunningActvitySet.name,
              firstOpenState: firstRunningActvitySet.state,
              contexts: contextValues,
              hiddenContexts: hiddenContextValues
          };

      //select the lastest finished Activity Set when it is not the last activity set and there is no any running
      // activity set
      if (!this._isEmptyJsonObject(lastFinishedActivitySet) && lastFinishedActivitySet.id !=
          contextValues[contextValues.length - 1].id)
          return {
              viewAllToDos: false,
              firstOpen: lastFinishedActivitySet.id,
              firstOpenName: lastFinishedActivitySet.name,
              firstOpenState: lastFinishedActivitySet.state,
              contexts: contextValues,
              hiddenContexts: hiddenContextValues
          };

      //view all to-dos is checked by default if all activity sets are finished. Last activity set is used for toggle
      // -  when uncheck view all to-dos,  the last activity set is selected
      return {
          viewAllToDos: true,
          firstOpen: contextValues.length > 0 ? contextValues[contextValues.length - 1].id : '',
          firstOpenName: contextValues.length > 0 ? contextValues[contextValues.length - 1].name : '',
          firstOpenState: contextValues.length > 0 ? contextValues[contextValues.length - 1].state : '',
          contexts: contextValues,
          hiddenContexts: hiddenContextValues
      };
  },

  getBadgeForRecord: function(gr, isAcceptanceOrApproval) {
      var isHRTask = !isAcceptanceOrApproval && gr.sys_class_name == 'sn_hr_core_task';
      var isCase = this._isCase(String(gr.sys_class_name));
      var activityGr = null;
      var USER_BADGE_PREFIX = 'user_';

      var badgeInfo = {};

      if (!isHRTask && this._isLEInstalled)
          if (gr.sn_hr_le_activity) {
              activityGr = this._getActivityOrEmployeeRequestGr(gr.getValue(sn_hr_core.hr.TABLE_ACTIVITY));
          } else {
              var activityStatusRecord = new GlideRecord('sn_hr_le_activity_status');
              if (activityStatusRecord.get('generated_record', gr.getUniqueValue()))
                  activityGr = this._getActivityOrEmployeeRequestGr(activityStatusRecord.getValue(sn_hr_core.hr.ACTIVITY));
          }

      if (isAcceptanceOrApproval) {
          badgeInfo.filter = USER_BADGE_PREFIX + gs.getUserID();
          badgeInfo.display_name = gs.getUserDisplayName();
      } else if (isHRTask && !gs.nil(gr.assigned_to)) {
          badgeInfo.filter = USER_BADGE_PREFIX + gr.assigned_to;
          badgeInfo.display_name = gr.getDisplayValue('assigned_to');
      } else if (gr.sys_class_name == 'sn_lc_learning_task') {
          badgeInfo.filter = USER_BADGE_PREFIX + gr.assigned_to;
          badgeInfo.display_name = gr.getDisplayValue('assigned_to');
      } else if (!gs.nil(activityGr))
          badgeInfo = this._setBadgeValues(activityGr, badgeInfo);
      else if (isCase)
          badgeInfo = this._setBadgeValues(gr.hr_service, badgeInfo);
      else if (gr.getTableName().equals('sn_doc_task')) {
          badgeInfo.filter = USER_BADGE_PREFIX + gr.assigned_to;
          badgeInfo.display_name = gr.getDisplayValue('assigned_to');
      }

      return badgeInfo;
  },

  /** Private Methods **/
  /**
   * Method to return the activity glide record  or employee request glide record 
   * depending on the sys_class_name. if the sys_class name does not exist 
   * or the sys_id does not exist in base null is returned. 
   * @param {string} activityBaseId: sys id of the activity base 
   * @return {GlideRecord}: Glide record of activity or employee request or null. 
   */
  _getActivityOrEmployeeRequestGr: function(activityBaseId) {
      try {
          var activityBaseGr = new GlideRecord("sn_hr_le_activity_base");
          if (!activityBaseGr.get(activityBaseId))
              return null;
          var sysClassName = activityBaseGr.getValue("sys_class_name");
          if (sysClassName == "sn_hr_le_activity")
              return this._getLEActivityGr(activityBaseId);
          else if (sysClassName == "sn_hr_le_employee_request")
              return this._getEmployeeREquestGR(activityBaseId);
          return null;
      } catch (error) {
          gs.error(error + '');
          throw new Error(error + '');
      }

  },

  /**
   * Method to return the activity glide record, or null if the record does not exist
   * @param {string} activityId: sys id of the activity
   * @return {GlideRecord}: Glide record of activity or null. 
   */
  _getLEActivityGr: function(activityId) {
      try {
          var activityGr = new GlideRecord("sn_hr_le_activity");
          return activityGr.get(activityId) ? activityGr : null;
      } catch (error) {
          gs.error(error + '');
          throw new Error(error + '');
      }
  },

  /**
   * Method to return the employee request glide record, or null if the record does not exist
   * @param {string} employeeRequestId: sys id of the employee request
   * @return {GlideRecord}: Glide record of employee request or null. 
   */
  _getEmployeeREquestGR: function(employeeRequestId) {
      try {
          var employeeRequestGr = new GlideRecord("sn_hr_le_employee_request");
          return employeeRequestGr.get(employeeRequestId) ? employeeRequestGr : null;
      } catch (error) {
          gs.error(error + '');
          throw new Error(error + '');
      }

  },

  _setBadgeValues: function(hrService, result) {
      var badgeDisplayValue = '';
      var badgeDescription = '';
      var CASE_BADGE_PREFIX = 'case_badge_';
      result.filter = '';
      result.display_name = '';
      result.description = '';

      if (gs.nil(hrService.badge)) {
          return result;
      }

      badgeDisplayValue = String(hrService.badge.name).toUpperCase();
      badgeDescription = String(hrService.badge.badge_description);
      if (badgeDisplayValue) {
          result.filter = CASE_BADGE_PREFIX + badgeDisplayValue;
          result.display_name = badgeDisplayValue;
          result.description = badgeDescription;
      }

      return result;
  },

  _getOwnerGroupInitials: function(ownerGroupGr) {
      var groupName = ownerGroupGr.getDisplayValue().toUpperCase();
      if (groupName.length >= 4)
          groupName = groupName.substring(0, 3);
      return groupName;
  },

  getTaskInfo: function() {
      var taskInfo = [];
      taskInfo.push(this._getRelevantInfoForRecord(this._gr, false));
      return taskInfo;
  },

  getChildTodoInfo: function(mineOnly) {
      var levelsToDisplay = this.canViewAssignmentFilter() ? this._maxChildTodoLevels : this._defaultChildTodoLevels;
      var tasksInfo = [];

      this._anyTodosForMe = false;

      //Add Acceptance for the parent case. Suppress Awaiting acceptance todo when associated to universal request.
      var hasAcceptanceTodo = hr_PortalUtil._hrCaseTables.indexOf(this._gr.getRecordClassName()) >= 0 &&
          this._gr.state == this._awaitingAcceptanceState && this._gr.opened_for == this._user &&
          this._gr.universal_request == '';
      if (hasAcceptanceTodo) {
          tasksInfo.push(this._getRelevantInfoForRecord(this._gr, true));
          this._anyTodosForMe = true;
      }

      //Add Task and Approval To-dos for each level
      for (var level = 1; level <= levelsToDisplay; level++) {
          var taskTables = this._getLineItemTaskTables(level);
          for (var i = 0; i < taskTables.length; i++) {
              this._addChildrenTasksForLevel(taskTables[i], level, tasksInfo, mineOnly);
          }
          this._addChildrenApprovalsForLevel(level, tasksInfo);
      }

      return tasksInfo;

  },

  _addChildrenTasksForLevel: function(className, level, todoList, mineOnly) {
      var parentId = String(this._gr.sys_id);
      var grTask = new GlideRecord(className);

      //Restrict To-dos to Acceptance HR Cases and self-assigned HR Tasks if mineOnly is true (such as on dashboard)
      if (mineOnly) {
          if (className == 'sn_hr_core_case') {
              grTask.addQuery('opened_for', this._user);
              grTask.addQuery('state', this._awaitingAcceptanceState);
          }
          if (className == 'sn_hr_core_task') {
              var qc = grTask.addQuery('assigned_to', this._user);
              qc.addOrCondition('sys_id', 'IN', this._getDelegatedTasks({
                  tables: [className],
                  encoded_query: this._getParentQueryStringForLevel(level) + '=' + parentId
              }));
          }
      }
      grTask.addQuery(this._getParentQueryStringForLevel(level), parentId);
      //Suppress Awaiting acceptance todo when associated to universal request.
      if (this._gr.universal_request != '')
          grTask.addQuery('state', '!=', this._awaitingAcceptanceState);
      //Filter out draft HR Cases/Tasks
      if (hr_PortalUtil._hrTables.indexOf(className) >= 0)
          grTask.addQuery('state', '!=', this._draftState);

      grTask.query();

      while (grTask.next()) {
          if (grTask.canRead()) {
              var hasAcceptanceTodo = hr_PortalUtil._hrCaseTables.indexOf(className) >= 0 && grTask.state ==
                  this._awaitingAcceptanceState && grTask.opened_for == this._user &&
                  (grTask.parent.parent.parent.parent != parentId) && this._gr.universal_request == '';
              todoList.push(this._getRelevantInfoForRecord(grTask));

              if (hasAcceptanceTodo) {
                  todoList.push(this._getRelevantInfoForRecord(grTask, true));
                  this._anyTodosForMe = true;
              }
          }
      }
  },

  /* Get the delegated assignments for a user
  	@param config - Object - Object describing delegation records to retrieve
  		{
  			@param tables - Array - (optional) - The tables to get delegated records from
  			@param start_date - String - (optional) - Get delegate records at or after this date
  			@param encoded_query - String - (optional) - The condition to filter delegated records
  			@param limit - Number - (optional) - The maximum number of records to get
  		}
  	@param userId - String - (optional) - The user to get delegated records for
  	@param includeUsersRecords - boolean (optional) - Include the users assigned records as well as delegated records
  	@return Array - An array of delegated record sys_id's
  */
  _getDelegatedTasks: function(config, userId, includeUserRecords) {
      var delegatedRecords = [];

      if (!this.DELEGATION_PLUGIN_ACTIVE)
          return delegatedRecords;

      if (!userId)
          userId = gs.getUserID();

      var grTasks = new sn_delegation.DelegationUtil().getDelegatedAssignmentsForUser(userId, !!includeUserRecords, config);
      while (grTasks.next())
          delegatedRecords.push(grTasks.getUniqueValue());

      return delegatedRecords;
  },

  _addChildrenApprovalsForLevel: function(level, todoList) {
      var parentId = String(this._gr.sys_id);
      var grApproval = new GlideRecord('sysapproval_approver');

      //Approval To-dos only appear for the logged-in user
      grApproval.addQuery('approver', new sn_hr_core.hr_Utils().getApprovals(this._user));
      grApproval.addQuery(this._getParentQueryStringForLevel(level, true), parentId);
      grApproval.addQuery('sysapproval.sys_class_name', 'IN', hr_PortalUtil._approvalTables);
      grApproval.addQuery('state', '!=', 'not requested');

      //Add parent query based on level

      grApproval.query();

      while (grApproval.next()) {
          if (grApproval.canRead()) {
              todoList.push(this._getRelevantInfoForRecord(grApproval));
              this._anyTodosForMe = true;
          }
      }
  },

  _getRelevantInfoForRecord: function(gr, isAcceptance) {
      var className = gr.getRecordClassName();
      var isApproval = className == 'sysapproval_approver';
      var recordInfo = {};
      var todoPageUtils = new sn_hr_sp.todoPageUtils();
      if (this._gr)
          recordInfo['rootParent'] = this._gr.sys_id.toString();

      if (isApproval) {
          //Approval display fields
          recordInfo['state'] = gr.getDisplayValue('state');
          recordInfo['state_num_value'] = gr.getValue('state');
          recordInfo['number'] = gr.sysapproval.number.toString();
          recordInfo['sys_updated_on'] = gr.sys_updated_on.toString();
          recordInfo['grandParent'] = gr.sysapproval.parent.toString();
          recordInfo['parent'] = gr.sysapproval.toString();
          recordInfo['parentDisplayName'] = gr.sysapproval.number.toString();
          //Approval logical fields
          recordInfo['state_value'] = gr.state.toString();
          recordInfo['sysapproval'] = gr.sysapproval.toString();
          recordInfo['sysapproval_parent'] = gr.sysapproval.parent.toString();
          recordInfo['sys_class_name'] = gr.sysapproval.sys_class_name.toString();
          recordInfo['sys_id'] = gr.getUniqueValue();
          recordInfo['type'] = 'approval';
          recordInfo['title'] = this._getHeaderTitle(recordInfo);
          recordInfo['isHRCase'] = false;
      } else if (className == 'sn_doc_task') {
          recordInfo['assigned_to'] = gr.getDisplayValue('assigned_to');
          recordInfo['assigned_to_id'] = gr.assigned_to.toString();
          recordInfo['number'] = gr.number.toString();
          recordInfo['short_description'] = replaceFieldTokens(gr.short_description.toString(), gr);
          recordInfo['state'] = gr.getDisplayValue('state');
          recordInfo['state_num_value'] = gr.getValue('state');
          //Task logical fields
          recordInfo['active'] = gr.active == true;
          recordInfo['closed_at'] = gr.closed_at.toString();
          recordInfo['parent'] = isAcceptance ? gr.getUniqueValue() : gr.parent.toString();
          recordInfo['parentDisplayName'] = gr.getDisplayValue('parent');
          recordInfo['sys_class_name'] = className;
          recordInfo['sys_updated_on'] = gr.sys_updated_on.toString();
          recordInfo['sys_id'] = gr.getUniqueValue();
          recordInfo['type'] = isAcceptance ? 'acceptance' : 'task';
          recordInfo['isHRCase'] = this._isCase(className);
          recordInfo['chatWidgetParams'] = this.getChatWidgetParams(gr);
      } else {
          //Task display fields
          recordInfo['assigned_to'] = gr.getDisplayValue('assigned_to');
          recordInfo['assigned_to_id'] = gr.assigned_to.toString();
          recordInfo['number'] = gr.number.toString();
          recordInfo['short_description'] = this.getRecordShortDescription(gr);
          recordInfo['state'] = gr.getDisplayValue('state');
          recordInfo['state_num_value'] = gr.getValue('state');
          //Task logical fields
          recordInfo['active'] = gr.active == true;
          recordInfo['closed_at'] = gr.closed_at.toString();
          recordInfo['due_date'] = gr.due_date.toString();
          recordInfo['grandParent'] = isAcceptance ? gr.parent.toString() : gr.parent.parent.toString();
          recordInfo['parent'] = isAcceptance ? gr.getUniqueValue() : gr.parent.toString();
          recordInfo['parentDisplayName'] = gr.getDisplayValue('parent');
          recordInfo['sys_class_name'] = className;
          recordInfo['sys_updated_on'] = gr.sys_updated_on.toString();
          recordInfo['sys_id'] = gr.getUniqueValue();
          recordInfo['type'] = isAcceptance ? 'acceptance' : 'task';
          recordInfo['isHRCase'] = this._isCase(className);
          recordInfo['chatWidgetParams'] = this.getChatWidgetParams(gr);
          recordInfo['hasDueDateWarning'] = todoPageUtils._getDueDateWarning(gr.due_date);
          recordInfo['optionalLabel'] = todoPageUtils._getOptionalLabel(gr);

          //HR Case specific fields
          if (hr_PortalUtil._hrCaseTables.indexOf(className) >= 0) {
              recordInfo['hr_service'] = gr.hr_service.getDisplayValue();
              recordInfo['opened_for'] = gr.opened_for.getDisplayValue();
              recordInfo['opened_for_id'] = gr.opened_for.toString();
              recordInfo['subject_person'] = gr.subject_person.getDisplayValue();
              recordInfo['showAllLevels'] = gr.opened_for == this._user ||
                  (gr.subject_person == this._user && gr.hr_service.subject_person_access);
              recordInfo['title'] = this._getCaseTitle(gr);

          } else
              recordInfo['title'] = this._getTodoTitle(recordInfo);


          if (className == 'sn_hr_core_task' && this._isAssignedOrDelegated(gr, this._user))
              this._anyTodosForMe = true;

      }

      recordInfo['stateLabel'] = todoPageUtils.getStateLabel(gr);

      //Add information needed for client filtering
      recordInfo['filter_info'] = {
          'badge_info': this.getBadgeForRecord(gr, isAcceptance || isApproval),
          'pane_info': this._lineItemType(gr, isAcceptance)
      };
      if (this._isLEInstalled) {
          var activitySetInfo = this._getActivitySetRecordInfo(gr);
          recordInfo['filter_info']['activity_set_info'] = activitySetInfo;
          recordInfo.future = activitySetInfo && activitySetInfo.is_adhoc && activitySetInfo.state === 'awaiting_trigger';
      }

      return recordInfo;
  },

  /* Determine if user is assigned or delegated to a record
  	@param record - GlideRecord - The record to check if user is assigned or delegated to
  	@param userId - String - (optional) - The user to check if record is assigned or delegated to
  	@return Boolean - Whether or not the record is assigned or delegated to @param user
  */
  _isAssignedOrDelegated: function(record, userId) {
      if (!record || !record.isValid())
          return false;

      if (!userId)
          userId = gs.getUserID();

      if (record.getElement('assigned_to').toString() == userId)
          return true;

      if (this.DELEGATION_PLUGIN_ACTIVE && new sn_delegation.DelegationUtil().isRecordDelegatedToUser(userId, record))
          return true;

      return false;
  },

  // construct Activity Set information
  _constructActivitySetInfo: function(gr) {
      if (gr)
          return {
              'activity_set_context': gr.activity_set_context.sys_id + '',
              'activity_set_sys_id': gr.activity_set_context.activity_set.sys_id + '',
              'state': gr.activity_set_context.state.toString(),
              'state_display': gr.activity_set_context.state.getDisplayValue(),
              'hide_activity_set': this._hideActivitySet(gr.activity_set_context),
              'activity_display_order': this._getDisplayOrder(gr),
              'is_adhoc': !gr.activity
          };
      else
          return {
              'activity_set_context': '',
              'activity_set_sys_id': '',
              'state': '',
              'state_display': '',
              'hide_activity_set': '',
              'activity_display_order': '',
              'is_adhoc': false
          };
  },

  /**
   * @param {GlideRecord} gr - an activity status record
   * @returns {string} - the display order for the related record either
   *    from the related sn_hr_le_activity or the related task record
   */
  _getDisplayOrder: function(gr) {
      var activityDisplayOrder = gr.getElement('activity.display_order');
      if (activityDisplayOrder)
          return activityDisplayOrder.toString();

      var relatedRecord = new GlideRecord(gr.table.toString());
      if (!relatedRecord.get(gr.related_record.toString()))
          return '';

      if (!relatedRecord.isValidField('display_order'))
          return '';

      var displayOrder = relatedRecord.getValue('display_order');
      if (!displayOrder)
          return '';

      return displayOrder.toString();
  },

  _getActivitySetRecordInfo: function(taskRec, leCaseId) {
      if (!leCaseId)
          leCaseId = this._gr ? this._gr.getUniqueValue() : leCaseId;

      if (gs.nil(taskRec.sys_id) || !leCaseId)
          return '';
      //Current Record 
      var gr = new GlideRecord(sn_hr_core.hr.TABLE_ACTIVITY_STATUS);
      gr.addQuery('activity_set_context.hr_case', leCaseId);
      gr.addQuery('related_record', taskRec.sys_id).addOrCondition('generated_record', taskRec.sys_id);
      gr.query();

      if (gr.next())
          return this._constructActivitySetInfo(gr);

      //Set activity set of the parent as this record's activity set
      if (gs.nil(taskRec.parent))
          return '';

      gr = new GlideRecord(sn_hr_core.hr.TABLE_ACTIVITY_STATUS);
      gr.addQuery('activity_set_context.hr_case', leCaseId);
      gr.addQuery('related_record', taskRec.parent.sys_id).addOrCondition('generated_record', taskRec.parent.sys_id);
      gr.query();

      if (gr.next())
          return this._constructActivitySetInfo(gr);

      //Set activity set of the grand parent as this record's activity set
      if (gs.nil(taskRec.parent.parent))
          return '';

      gr = new GlideRecord(sn_hr_core.hr.TABLE_ACTIVITY_STATUS);
      gr.addQuery('activity_set_context.hr_case', leCaseId);
      gr.addQuery('related_record', taskRec.parent.parent.sys_id)
          .addOrCondition('generated_record', taskRec.parent.parent.sys_id);
      gr.query();

      if (gr.next())
          return this._constructActivitySetInfo(gr);

      return '';
  },

  _getLineItemTaskTables: function(level) {
      var parentId = String(this._gr.sys_id);
      var taskTables = [];

      var aggregate = new GlideAggregate('task');
      aggregate.addQuery(this._getParentQueryStringForLevel(level), parentId);
      //Skipping Universal tasks on ticket page. These tasks will be rendered on todos page (hrm_todos_page)
      aggregate.addQuery('sys_class_name', 'NOT IN', this.SKIPPED_TODO_TABLES);
      aggregate.groupBy('sys_class_name');
      aggregate.query();

      while (aggregate.next()) {
          taskTables.push(String(aggregate.sys_class_name));
      }

      return taskTables;
  },

  _getParentQueryStringForLevel: function(level, isApproval) {
      var parentString = isApproval ? 'sysapproval' : 'parent';
      for (var i = 1; i < level; i++)
          parentString = parentString + '.parent';

      return parentString;
  },

  _lineItemType: function(gr, isAcceptanceItem) {
      var isApprovalItem = !isAcceptanceItem && gr.getTableName() == 'sysapproval_approver';
      var completedApproval = isApprovalItem && gr.getValue('state') != 'requested';
      var completedTask = !isApprovalItem && !gr.active;

      var object = {
          id: gr.getUniqueValue(),
          isAcceptance: isAcceptanceItem,
          isApproval: isApprovalItem,
          isOptional: gr.getValue("optional") == 1
      };

      if (completedApproval || completedTask) {
          object.type = 'completed_todos';
          object.timestamp = new GlideDateTime(completedTask ? gr.closed_at : gr.sys_updated_on).getNumericValue();
      } else if (isAcceptanceItem) {
          object.type = 'overdue';
          object.timestamp = new GlideDateTime().getNumericValue();
      } else if (!gs.nil(gr.due_date)) {
          object.type = (new sn_hr_core.hr_Task().getDueDays(gr.due_date + '') < 0) ? 'overdue' : 'due_soon';
          object.timestamp = new GlideDateTime(gr.due_date).getNumericValue();
      } else {
          object.type = 'due_later';
          object.timestamp = new GlideDateTime(gr.sys_created_on).getNumericValue();
      }

      return object;
  },

  _getChatSetupHrFulfillerValue: function() {
      var hrFulfiller;

      if (!this.isSpAgentChatInstalled()) {
          hrFulfiller = 'connect';
      } else {
          var chatSetup = new GlideRecord('sys_cs_live_agent_setup');
          chatSetup.addQuery('name', 'Chat Setup');
          chatSetup.query();
          while (chatSetup.next())
              hrFulfiller = chatSetup.getValue('hr_fulfiller');
      }

      return hrFulfiller;
  },

  _isAWAInstalled: function() {
      return GlidePluginManager.isActive('com.glide.awa');
  },
  _isConnectSupportInstalled: function() {
      return GlidePluginManager.isActive('com.glide.connect.support');
  },
  isConnectSPWidgetsInstalled: function() {
      return GlidePluginManager.isActive('com.glide.connect.sp_widgets');
  },
  isConnectInstalled: function() {
      return GlidePluginManager.isActive('com.glide.connect');
  },
  isSpAgentChatInstalled: function() {
      return GlidePluginManager.isActive('com.glide.service-portal.agent-chat');
  },

  /* check the configuration whether connect widget needs to be loaded or not */
  getConnectChatConfiguration: function(taskGr) {
      if (taskGr.sys_class_name == sn_hr_core.hr.TABLE_TASK) {

          var chatType = taskGr.task_support_team;
          if (chatType == 'users_and_groups' && this.isConnectInstalled() && hr_PortalUtil.isChatEnabled())
              return true;
          else {
              var hrFulfiller = this._getChatSetupHrFulfillerValue();

              if (chatType == 'queue' && this._isAWAInstalled() && hrFulfiller == 'service_workspace')
                  return null;
              else if (chatType == 'queue' && this._isConnectSupportInstalled() && hr_PortalUtil.isChatEnabled() && hrFulfiller == 'connect')
                  return true;
              else if (chatType == 'agent_workspace' && this._isAWAInstalled() && this.isSpAgentChatInstalled() && hrFulfiller == 'service_workspace')
                  return false;
              else if (chatType == 'agent_workspace' && this._isConnectSupportInstalled() && hrFulfiller == 'connect')
                  return null;
          }
      } else {
          var hrFulfiller = this._getChatSetupHrFulfillerValue();

          if (this._isAWAInstalled() && this.isSpAgentChatInstalled() && hrFulfiller == 'service_workspace')
              return false;
          else if (this._isConnectSupportInstalled() && hr_PortalUtil.isChatEnabled() && hrFulfiller == 'connect')
              return true;
      }
      return null;
  },

  isAskAQuestionVisible: function(taskGr) {
      if (taskGr.sys_class_name == sn_hr_core.hr.TABLE_TASK) {
          var chatType = taskGr.task_support_team;
          if (chatType == 'users_and_groups' && this.isConnectInstalled() && hr_PortalUtil.isChatEnabled())
              return true;
          else if (chatType == 'queue' && this._isConnectSupportInstalled() && hr_PortalUtil.isChatEnabled())
              return true;
          else if (chatType == 'agent_workspace' && this._isAWAInstalled() && this.isSpAgentChatInstalled() && hr_PortalUtil.askQuestionPropEnabled())
              return true;
      } else {
          var hrFulfiller = this._getChatSetupHrFulfillerValue();
          if (this._isAWAInstalled() && this.isSpAgentChatInstalled() && hrFulfiller == 'service_workspace' && hr_PortalUtil.askQuestionPropEnabled())
              return true;
          else if (this._isConnectSupportInstalled() && hr_PortalUtil.isChatEnabled() && hrFulfiller == 'connect' && hr_PortalUtil.askQuestionPropEnabled())
              return true;
      }
      return false;
  },

  /* Check whether any chat is not in ClosedComplete/ClosedAbandoned state in interaction record for current user */
  checkForActiveChat: function() {
      var gr = new GlideRecord('interaction');
      gr.addQuery('opened_for', this._user);
      gr.addEncodedQuery('stateNOT INclosed_complete,closed_abandoned');
      gr.query();
      return gr.hasNext();
  },

  /* Returns the Live agent topic name from VA General Settings */
  getLiveAgentTopicName: function() {
      if (GlidePluginManager.isActive('com.glide.cs.chatbot')) {
          var generalSettingsGr = new GlideRecord('sys_cs_general_settings');
          if (generalSettingsGr.get('sys_domain', 'global'))
              return generalSettingsGr.getDisplayValue('live_agent_topic');
      }

      return null;
  },

  /* To get the AWA queue id configured for particular task. */
  getTaskAWAQueue: function() {
      if (this._gr.sys_class_name == sn_hr_core.hr.TABLE_TASK && this._gr.awa_queue)
          return this._gr.awa_queue.number.toString();
      else
          return '';
  },

  _getCaseGlideRecord: function(case_sys_id) {
      var caseGr = new GlideRecord('sn_hr_core_case');
      if (caseGr.get(case_sys_id) != null)
          return caseGr;

      return null;
  },

  getHrCaseCoe: function(case_sys_id) {
      var caseGr = this._getCaseGlideRecord(case_sys_id);
      if (caseGr != null)
          return caseGr.hr_service.service_table.toString();

      return null;
  },

  getHrCaseTableName: function(case_sys_id) {
      var caseGr = this._getCaseGlideRecord(case_sys_id);
      if (caseGr != null)
          return caseGr.sys_class_name.toString();

      return null;
  },

  /* Setup the widget params for chat widget */
  getChatWidgetParams: function(gr) {

      if (!gr)
          gr = this._gr;

      if (!gr)
          return;

      if (!this.isAskAQuestionVisible(gr))
          return '';


      var groupUsers, parent;
      var options = {
          'allow_attachments': true,
          'show_card_details': true,
          'show_system_messages': false,
          'record_table': gr.getValue('sys_class_name'),
          'record_id': gr.getUniqueValue(),
          'welcome': gs.getMessage('Hello, how can I help you?')
      };
      if (gr.sys_class_name == sn_hr_core.hr.TABLE_TASK) { // HR task recipients are determined by the configuration on the task
          var hr_Task = new sn_hr_core.hr_Task(gr);
          options.title = gs.getMessage('Ask a question about this task');
          var recipients;
          var chatType = gr.task_support_team;
          if (chatType == 'queue' && !gs.nil(gr.queue)) {
              options.type = 'queue';
              options.queue = gr.getValue('queue');
              options.owner_table = sn_hr_core.hr.TABLE_TASK;
              options.owner_id = gr.getUniqueValue();
          } else if (chatType == 'users_and_groups' &&
              (!gs.nil(gr.getValue('parent_case_users')) || !gs.nil(gr.getValue('groups')))) {
              options.type = 'record';
              if (!gs.nil(gr.getValue('parent_case_users'))) {
                  var fields = gr.getValue('parent_case_users');
                  fields = fields.split(',');
                  for (var i in fields) {
                      var fieldName = new GlideRecord('sn_hr_core_service_approval_option');
                      fieldName.get(fields[i]);
                      parent = gr.parent.getRefRecord();
                      var field = parent.getElement(fieldName.getValue('approval_assign_to')).getRefRecord();
                      if (field.getTableName() == 'sys_user') {
                          // If anyone in recipients doesn't have access to the record, the chat will be peer to peer
                          if (!hr_Task.canEditTask(field.getUniqueValue()))
                              options.type = 'user';
                          recipients = this._addUserToRecipients(field.getUniqueValue(), recipients);
                      } else if (field.getTableName() == 'sys_user_group') {
                          groupUsers = this._addUserFromGroup(field.getUniqueValue(), recipients, hr_Task);
                          recipients = groupUsers.userList;
                          options.type = options.type != 'user' ? groupUsers.chatType : options.type;
                      }
                  }
              }
              if (!gs.nil(gr.getValue('groups'))) {
                  var groups = gr.getValue('groups');
                  groups = groups.split(',');
                  for (var grp in groups) {
                      groupUsers = this._addUserFromGroup(groups[grp], recipients, hr_Task);
                      recipients = groupUsers.userList;
                      options.type = options.type != 'user' ? groupUsers.chatType : options.type;
                  }
              }
          }

          if (gs.nil(options.queue) && gs.nil(recipients)) {
              options.type = 'record';
              parent = gr.parent.getRefRecord();
              recipients = this._addUserToRecipients(parent.getValue('assigned_to'), recipients);
          }
          recipients = this._addUserToRecipients(gr.getValue('assigned_to'), recipients);
          options.recipients = recipients;
      } else if (gr.sys_class_name == 'sn_doc_task') {
          // For document task, connect to parent case assigned to or assignment group.
          parent = gr.parent.getRefRecord();
          if (parent && parent.getValue('assigned_to')) {
              options.type = 'record';
              options.recipients = parent.getValue('assigned_to');
          } else if (parent && parent.assignment_group) {
              recipients = '';
              options.type = 'user';
              var groupGr = new GlideRecord('sys_user_grmember');
              groupGr.addQuery('group', parent.assignment_group);
              groupGr.query();
              while (groupGr.next())
                  recipients = this._addUserToRecipients(groupGr.getValue('user'), recipients);
              options.recipients = recipients;
          } else
              gs.error('assigned_to and assignment_group not present for parent of document task: {0}',
                  gr.getUniqueValue());
      } else { //Chat for non hr tasks always go to assigned to
          if (this._isCase(gr.getValue('sys_class_name')))
              options.title = gs.getMessage('Ask a question about this case');
          else
              options.title = gs.getMessage('Ask a question about this task');
          options.type = 'record';
          options.recipients = gr.getValue('assigned_to');
      }
      return options;
  },

  isConnectUsed: function(task) {
      var gr = new GlideRecord('live_group_profile');
      gr.addQuery('document', task);
      gr.query();
      if (gr.next())
          return true;
      var grChatEntry = new GlideRecord('chat_queue_entry');
      grChatEntry.addQuery('owner_id', task);
      grChatEntry.addEncodedQuery('stateIN1,2');
      grChatEntry.query();
      if (grChatEntry.next())
          return true;

      return false;
  },

  _addUserFromGroup: function(grp, recipients, hr_Task) {
      var group = new GlideRecord('sys_user_grmember');
      var type = 'record';
      group.addQuery('group', grp);
      group.query();
      while (group.next()) {
          // If anyone in group doesn't have access to the record, the chat will be peer to peer
          if (!hr_Task.canEditTask(group.getValue('user')))
              type = 'user';

          recipients = this._addUserToRecipients(group.getValue('user'), recipients);
      }

      return {
          chatType: type,
          userList: recipients
      };
  },

  _addUserToRecipients: function(user, recipients) {
      if (gs.nil(user))
          return;

      if (gs.nil(recipients))
          recipients = user;
      else if (recipients.indexOf(user) < 0)
          recipients += ',' + user;
      return recipients;
  },

  _isActivitySetActive: function(state) {
      return (state == 'running_activities' || state == 'finished');
  },

  /*
   ** check if the current activity set needs to be skipped. 
   *** if activity-set-context state is `skipped`
   *** if it is opened for view, the activity set needs to be skipped
   ****      if the display_to_opened_for in activity set is set to false
   *** if it is subject person view, the activity set needs to be skipped
   ****      if the display_to_subject_person is false or "display is not allowed for subject person"
   *** otherwise, the current activity set should not be skipped.
   */
  _hideActivitySet: function(grContext) {
      var hideOpenedFor = !grContext.activity_set.display_to_opened_for;
      var hideSubjectPerson = !grContext.activity_set.display_to_subject_person;

      if (grContext.state == 'skipped')
          return true;

      //if the case is for both "opened for" and "subjectr person", only hide the acticity set when both hide
      // conditions are true
      if (this._isOpenedForView(grContext.hr_case) && this._isSubjectPersonView(grContext.hr_case))
          return hideOpenedFor && hideSubjectPerson;

      //if the case is for opened for only, check the hide option for opened for only
      if (this._isOpenedForView(grContext.hr_case) && !this._isSubjectPersonView(grContext.hr_case))
          return hideOpenedFor;

      //if the case is for subject person only, check the hide option for subject person only.
      if (!this._isOpenedForView(grContext.hr_case) && this._isSubjectPersonView(grContext.hr_case))
          return hideSubjectPerson;

      return false;
  },

  // private function that takes an HR case parameter
  _isOpenedForView: function(gr) {
      if (gs.nil(gr))
          return false;
      var opened_for = String(gr.opened_for);
      return opened_for == this._user;
  },

  // private function that takes an HR case parameter
  _isSubjectPersonView: function(gr) {
      if (gs.nil(gr))
          return false;
      var subject_person = String(gr.subject_person);
      return subject_person == this._user;
  },

  // private function that takes an HR case parameter
  _allowDisplayActivitySet: function(gr) {
      if (gs.nil(gr))
          return false;
      if (!gs.nil(gr.hr_service.fulfillment_type) && gr.hr_service.fulfillment_type == 'lifecycle_event') {
          if (!gs.nil(gr.hr_service.le_type) && gr.hr_service.le_type.display_activity_set)
              return true;
      }

      return false;
  },

  // private function that takes an HR case parameter
  _allowDisplayActivitySetToSubjectPerson: function(gr) {
      if (gs.nil(gr))
          return false;
      else
          return this._isSubjectPersonView(gr) && gr.hr_service.subject_person_access &&
              this._allowDisplayActivitySet(gr);
  },

  _isCase: function(tableName) {
      return hr_PortalUtil._hrCaseTables.indexOf(tableName) >= 0;
  },
  
  /**
   * Remove the unwanted speacial characters from the String
   * Characters inside regex array will be removed from string.
   */
  sanitize : function(str){
  	var cleanStr = str.replace(/[()]/g,'');
  	return cleanStr;
  },

  type: 'hr_PortalUtil'
};

Sys ID

3c764fda534032003585c3c606dc34e9

Offical Documentation

Official Docs: