Name

sn_cd.cd_ContentDelivery

Description

API for getting content to display on a portal.

Script

var cd_ContentDelivery = Class.create();
cd_ContentDelivery.prototype = {
  type: 'cd_ContentDelivery',
  useCache: true // attempt to find audience evaluation result in cache
};
/* Get content for the specified sp_instance
* @param instanceId String The ID of the sp_instance for which we want content
* @return Array A sorted array of objects containing information about the content specified by @function _addContentToResult
*/
cd_ContentDelivery.getContentForWidgetInstance = function (instanceId, topicId) {
  var results = {
      contentMap: {},
      contentArray: []
  };
  cd_ContentDelivery._getStandardPortalContentForWidgetInstance(instanceId, topicId, results);
  cd_ContentDelivery._getCampaignPortalContentForWidgetInstance(instanceId, topicId, results);
  return results.contentArray.sort(function (a, b) {
      var i = Number(a.order);
      var j = Number(b.order);
      var k = new GlideDateTime(a.availability_start_date.length > 0 ? a.availability_start_date : a.sys_created_on);
      var l = new GlideDateTime(b.availability_start_date.length > 0 ? b.availability_start_date : b.sys_created_on);
      if (i < j)
          return -1;
      if (i > j)
          return 1;
      if (k.after(l))
          return -1;
      if (k.before(l))
          return 1;
      return 0;
  });
};
/* Get standard content for the specified sp_instance
* @param instanceId String The ID of the sp_instance for which we want content
* @param results Array Objects containing information about the content and to which we will append more objects
*/
cd_ContentDelivery._getStandardPortalContentForWidgetInstance = function (instanceId, topicId, results) {
  var cd_Audience = new sn_cd.cd_Audience();
  var gdtNow = new GlideDateTime();
  var user = gs.getUserID();
  // get all active content associated with this sp_instance
  var grContentVisibility = new GlideRecord('sn_cd_content_visibility');
  grContentVisibility.addActiveQuery();
  grContentVisibility.addNullQuery('content_location').addOrCondition('content_location', 'service_portal');
  grContentVisibility.addQuery('sp_instance', instanceId);
  grContentVisibility.addQuery('content.active', true);
  grContentVisibility.addQuery('content.sys_class_name', 'INSTANCEOF', 'sn_cd_content_portal');
  grContentVisibility.addNullQuery('approvers').addOrCondition('state', 'published');
  grContentVisibility.addNullQuery('availability_start_date').addOrCondition('availability_start_date', '<=', gdtNow);
  grContentVisibility.addNullQuery('availability_end_date').addOrCondition('availability_end_date', '>=', gdtNow);
  // NOTE: we rely on event end being null if the content type is not event (BR enforces this)
  grContentVisibility.addNullQuery('content.event_end').addOrCondition('content.event_end', '>=', gdtNow);
  // query topic if it's a query param
  if (topicId != null)
      grContentVisibility.addQuery('topic', 'CONTAINS', topicId);
  grContentVisibility.query();
  while (grContentVisibility.next()) {
      var useAdhocUsers = !!grContentVisibility.use_adhoc_users;
      if (((useAdhocUsers && grContentVisibility.getValue('users').indexOf(user) !== -1) || (!useAdhocUsers && cd_Audience.isUserInAudience(grContentVisibility.getValue("audience"), user, null, null, this.useCache))) && !cd_ContentDelivery._hasUnprocessedTodoScheduledContent(grContentVisibility))
          cd_ContentDelivery._addContentToResult(grContentVisibility.content.getRefRecord(), results, grContentVisibility);
  }
};

/* Get campaign content for the specified sp_instance
* @param instanceId String The ID of the sp_instance for which we want content
* @param results Array Objects containing information about the content and to which we will append more objects
*/
cd_ContentDelivery._getCampaignPortalContentForWidgetInstance = function (instanceId, topicId, results) {
  if (!instanceId || !(new GlidePluginManager().isActive('com.sn_content_automation')))
      return;
  var campaignContent = new sn_ca.ca_CampaignContent();
  var contentIds = [];	
  if (!topicId)
      contentIds = campaignContent.getCampaignContentForUserAndWidgetInstance(gs.getUserID(), instanceId);	
  else if (typeof campaignContent.getCampaignContentForUserWidgetInstanceAndTopic !== "undefined")
      contentIds = campaignContent.getCampaignContentForUserWidgetInstanceAndTopic(gs.getUserID(), instanceId, topicId);	
  if (contentIds && contentIds.length > 0) {
      var grContentPortal = new GlideRecord('sn_cd_content_portal');
      var grCampaignContent = new GlideRecord('sn_ca_campaign_item');
      for (var i = 0; i < contentIds.length; i+=1)
          if (grContentPortal.get(contentIds[i].id) && grCampaignContent.get(contentIds[i].campaignItemId) && !cd_ContentDelivery._hasUnprocessedTodoCampaignContent(grCampaignContent)) {
              var idx = cd_ContentDelivery._addContentToResult(grContentPortal, results, null, grCampaignContent);
              var contentJson = results.contentArray[idx];
              // the keys of campaignSet will be the ids
              // of the campaigns that are publishing the content
              if (!contentJson.hasOwnProperty('campaignSet'))
                  contentJson.campaignSet = {};
              var campaignId = contentIds[i].campaignId;
              contentJson.campaignSet[campaignId] = null;
          }
  }
};
/* Get content for the specified sys_ux_custom_content_root_elem
* @param contentIdentifier String The identifier tag for which we want content
* @return Array A sorted array of objects containing information about the content
*/
cd_ContentDelivery.getContentForWorkspaceInstance = function (contentIdentifier, isUIB) {
  var results = {
      contentMap: {},
      contentArray: []
  };
  cd_ContentDelivery._getStandardPortalContentForWorkspaceInstance(contentIdentifier, results, isUIB);
  return results.contentArray.sort(function (a, b) {
      var i = Number(a.order);
      var j = Number(b.order);
      if (i < j)
          return -1;
      if (i > j)
          return 1;
      return 0;
  });
};
/* Get standard content for the specified sys_ux_custom_content_root_elem
* @param contentIdentifier String The identifier tag for which we want content
* @param results Array Objects containing information about the content and to which we will append more objects
*/
cd_ContentDelivery._getStandardPortalContentForWorkspaceInstance = function (contentIdentifier, results, isUIB) {
  var cd_Audience = new sn_cd.cd_Audience();
  var gdtNow = new GlideDateTime();
  var user = gs.getUserID();
  // get all active content associated with this sp_instance
  var grContentVisibility = new GlideRecord('sn_cd_content_visibility');
  if (!grContentVisibility.isValidField("content_identifier") && !isUIB)
      return;
  grContentVisibility.addActiveQuery();
  if (isUIB) {
      grContentVisibility.addQuery('content_location', 'uib_workspace');
      grContentVisibility.addQuery('content_group', contentIdentifier);
  } else {
      grContentVisibility.addQuery('content_location', 'workspace');
      grContentVisibility.addQuery('content_identifier', contentIdentifier);
  }
  grContentVisibility.addQuery('content.active', true);
  grContentVisibility.addQuery('content.sys_class_name', 'INSTANCEOF', 'sn_cd_content_portal');
  grContentVisibility.addNullQuery('approvers').addOrCondition('state', 'published');
  grContentVisibility.addNullQuery('availability_start_date').addOrCondition('availability_start_date', '<=', gdtNow);
  grContentVisibility.addNullQuery('availability_end_date').addOrCondition('availability_end_date', '>=', gdtNow);
  grContentVisibility.addNotNullQuery('audience');
  // NOTE: we rely on event end being null if the content type is not event (BR enforces this)
  grContentVisibility.addNullQuery('content.event_end').addOrCondition('content.event_end', '>=', gdtNow);
  grContentVisibility.orderBy('title');
  grContentVisibility.query();
  while (grContentVisibility.next())
      if (cd_Audience.isUserInAudience(grContentVisibility.getValue("audience"), user))
          if (isUIB)
              cd_ContentDelivery._addContentToResultForUIB(grContentVisibility.content.getRefRecord(), results, grContentVisibility.getValue('component_type'));
          else
              cd_ContentDelivery._addContentToResult(grContentVisibility.content.getRefRecord(), results);
};
cd_ContentDelivery._getLinkObj = function (grContent, label, type, opensWindow, download) {
  return {
      label: label,
      type: type,
      value: {
          href: grContent.url_asset.url.toString()
      },
      opensWindow: opensWindow,
      configAria: {},
      download: download
  };
};
cd_ContentDelivery._getAnnouncementObj = function (grContent, linkType) {
  return {
      customHTML: grContent.getDisplayValue("use_custom_html") || false,
      richText: grContent.getDisplayValue("rich_text") || "",
      headingText: grContent.getDisplayValue("heading_text") || "",
      bodyText: grContent.getDisplayValue("body_text") || "",
      showButton: grContent.getDisplayValue("show_button") || false,
      buttonText: grContent.getDisplayValue("button_text") || "",
      buttonLink: cd_ContentDelivery._getLinkObj(grContent, '', linkType, true, false),
  };
};
cd_ContentDelivery._addContentToResultForUIB = function (grContent, accumulatedContent, componentType) {
  var id = grContent.getUniqueValue();
  var content = {};
  var linkType = "external";
  switch (componentType) {
      case "announcements": // Banner (Announcement section)
          content = cd_ContentDelivery._getAnnouncementObj(grContent, linkType);
          break;
      case "frequently_used_apps": // Image-based Link (Frequently used apps section)
          content = {
              heading: {
                  type: "link",
                  hasIcon: false,
                  icon: {},
                  label: "",
                  link: cd_ContentDelivery._getLinkObj(grContent, '', linkType, true, false),
                  hasTile: true,
                  tile: {
                      type: "image",
                      content: {
                          image: {
                              url: grContent.getDisplayValue('image'),
                              altText: grContent.getDisplayValue('title'),
                              alignment: "center",
                              headerOverImage: false,
                              fit: "contain",
                          }
                      }
                  }
              }
          };
          break;
      case "quick_links": // URL (Quick links section)
          content = cd_ContentDelivery._getLinkObj(grContent, grContent.getDisplayValue('title'), linkType, false, false);
          break;
      default:
  }
  accumulatedContent.contentArray.push(content);
  return accumulatedContent.contentMap[id];
};
/**
* Get an object describing the specified content
* @param string contentId The sys id of the portal content record we want
* @return Array An array (max length one in this case) of content object information
*/
cd_ContentDelivery.getPortalContentById = function (contentId) {
  if (!contentId)
      return [];
  var results = {
      contentMap: {},
      contentArray: []
  };
  var grContentPortal = new GlideRecord('sn_cd_content_portal');
  if (grContentPortal.get(contentId))
      cd_ContentDelivery._addContentToResult(grContentPortal, results);
  return results.contentArray;
};
/** Get portal content referencing the specified schedule
* @param scheduleId string cmn_schedule sys_id to get portal content by
* @return Array Objects representing a portal content record specified by @function _addContentToResult
*/
cd_ContentDelivery.getPortalContentByScheduleId = function (scheduleId) {
  if (!scheduleId)
      return [];
  var results = {
      contentMap: {},
      contentArray: []
  };
  var grContentPortal = new GlideRecord('sn_cd_content_portal');
  grContentPortal.addQuery('schedule', scheduleId);
  grContentPortal.query();
  while (grContentPortal.next())
      cd_ContentDelivery._addContentToResult(grContentPortal, results);
  return results.contentArray;
};
/**
* This gets content using the availability dates and end dates, but ignores the audience.
* It is expected to be called by the Holiday Calendar widget to get other calendars
* that can be displayed via the dropdown,
* @param instanceId String The ID of the sp_instance for which we want content
* @return Array A sorted array of objects containing information about the content specified by @function _addContentToResult
*/
cd_ContentDelivery.getContentForWidgetInstanceIgnoreAudience = function (instanceId) {
  var gdtNow = new GlideDateTime();
  var results = {
      contentMap: {},
      contentArray: []
  };
  var grContentVisibility = new GlideRecord('sn_cd_content_visibility');
  grContentVisibility.addActiveQuery();
  grContentVisibility.addQuery('sp_instance', instanceId);
  grContentVisibility.addQuery('content.active', true);
  grContentVisibility.addNullQuery('availability_start_date').addOrCondition('availability_start_date', '<=', gdtNow);
  grContentVisibility.addNullQuery('availability_end_date').addOrCondition('availability_end_date', '>=', gdtNow);
  // NOTE: we rely on event end being null if the content type is not event (BR enforces this)
  grContentVisibility.addNullQuery('content.event_end').addOrCondition('content.event_end', '>=', gdtNow);
  grContentVisibility.query();
  while (grContentVisibility.next()) {
      var grContentPortal = new GlideRecord('sn_cd_content_portal');
      if (grContentPortal.get(grContentVisibility.content))
          cd_ContentDelivery._addContentToResult(grContentPortal, results);
  }
  return results.contentArray.sort(function (a, b) {
      var i = Number(a.order);
      var j = Number(b.order);
      if (i < j)
          return -1;
      if (i > j)
          return 1;
      return 0;
  });
};
/**
* Add information about a portal content record to an array
* @param grContent GlideRecord [sn_cd_content_portal] Content record from which we will get information
* @param accumulatedContent Array An array of objects to which we will add information about the portal content
* @param grContentVisibility GlideRecord [sn_cd_content_visibility] Optional, used to generate link to user todo if applicable
* @param grCampaignContent GlideRecord [sn_ca_campaign_item] Optional, used to generate link to user todo if applicable
* @return the index of the content
*/
cd_ContentDelivery._addContentToResult = function (grContent, accumulatedContent, grContentVisibility, grCampaignContent) {
  var id = grContent.getUniqueValue();
  var hasCV = grContentVisibility != null;
  var supportedContentTypes = ['a673597a0b4303008cd6e7ae37673a6f', '41040dd7237e2300fb0c949e27bf652a', 'a342197a0b4303008cd6e7ae37673a19', '3893597a0b4303008cd6e7ae37673a4c', 'ae935d7a0b4303008cd6e7ae37673a9a', '97a35d7a0b4303008cd6e7ae37673a47', 'afb39d7a0b4303008cd6e7ae37673a16', 'f8c39d7a0b4303008cd6e7ae37673a55'];
  var isValidContentType = supportedContentTypes.indexOf(grContent.content_type.toString()) > -1;
  var isValid = hasCV && isValidContentType;
  if (!accumulatedContent.contentMap.hasOwnProperty(id)) {
      accumulatedContent.contentArray.push({
          title: grContent.getDisplayValue('title'),
          url: cd_ContentDelivery._getUrlFromContent(grContent, grContentVisibility, grCampaignContent),
          open_in: grContent.url_asset.open_in.toString(),
          order: grContent.order.toString(),
          user_ref_table: grContent.user_reference_table.toString(),
          schedule: grContent.schedule.toString(),
          rich_text: cd_ContentDelivery.getRichTextForContent(grContent, 'rich_text'),
          rich_content_html: cd_ContentDelivery.getRichTextForContent(grContent, 'rich_content_html'),
          rich_content_css: grContent.rich_content_css.toString(),
          event_start: grContent.event_start.toString(),
          event_end: grContent.event_end.toString(),
          sys_id: id,
          add_icon: grContent.add_icon.toString() === 'true',
          icon: grContent.icon.toString(),
          icon_size: grContent.icon_size.toString(),
          add_background_image: grContent.add_background_image.toString() === 'true',
          background_image: grContent.getDisplayValue('image'),
          background_color: grContent.background_color.toString(),
          background_size: grContent.background_size.toString(),
          background_position: grContent.background_position.toString(),
          add_gradient: grContent.add_gradient.toString() === 'true',
          text_over_background: grContent.text_over_background.toString() === 'true',
          text_color: grContent.text_color.toString(),
          text_alignment: grContent.text_alignment.toString(),
          headline_text: cd_ContentDelivery.getRichTextForContent(grContent, 'headline'),
          heading_text: cd_ContentDelivery.getRichTextForContent(grContent, 'heading_text'),
          body_text: cd_ContentDelivery.getRichTextForContent(grContent, 'body_text'),
          action: grContent.call_to_action.toString(),
          show_button: grContent.show_button.toString(),
          link_text: grContent.link_text.toString(),
          button_text: grContent.button_text.toString(),
          use_custom_html: grContent.use_custom_html.toString() === 'true',
          use_block: grContent.use_block.toString() === 'true',
          image_src: grContent.getDisplayValue('image'),
          is_clickable: cd_ContentDelivery.isClickable(grContent),
          is_todo_link: grContent.url_asset.type.toString() === 'todo',
          type: grContent.type.toString(),
          video_link_id: grContent.video_link.toString(),
          video_url: cd_ContentDelivery._getVideoUrlFromContent(grContent),
          content_type: grContent.content_type.toString(),
          sys_created_on: isValid && grContentVisibility.sys_created_on != null ? grContentVisibility.sys_created_on.toString() : 0,
          availability_start_date: isValid && grContentVisibility.availability_start_date != null ? grContentVisibility.availability_start_date.toString() : 0
      });
      accumulatedContent.contentMap[id] = accumulatedContent.contentArray.length - 1;
  }
  return accumulatedContent.contentMap[id];
};
/* Get the rich text from a content record
* @param grContent GlideRecord [sn_cd_content_portal] Content record to get rich text from
* @param field string The field on grContent to get rich text for
* @return String Parsed rich text from @param grContent
*/
cd_ContentDelivery.getRichTextForContent = function (grContent, field) {
  if (gs.nil(grContent[field]) && gs.nil(grContent.block))
      return null;
  var userTable;
  var userRefField;
  // TODO: Add condition to test if the content type is valid and has Use block content allowed
  if (grContent.use_block) {
      userTable = grContent.block.user_reference_table.toString();
      userRefField = grContent.block.user_column.toString();
  } else {
      userTable = grContent.user_reference_table.toString();
      userRefField = grContent.user_column.toString();
  }
  var glideRec;
  if (userTable && new GlideRecord(userTable).isValid()) {
      var grsUserTable = new GlideRecordSecure(userTable);
      if (new GlideTableHierarchy('sys_user').getAllExtensions().indexOf(userTable) !== -1) {
          if (grsUserTable.get(gs.getUserID()))
              glideRec = grsUserTable;
      } else if (userRefField && grsUserTable.isValidField(userRefField)) {
          if (grsUserTable.isValidField("active"))
              grsUserTable.addActiveQuery();
          grsUserTable.addQuery(userRefField, gs.getUserID());
          grsUserTable.query();
          if (grsUserTable.next())
              glideRec = grsUserTable;
      }
  }
  var contentBody;
  if (grContent.use_block && !gs.nil(grContent.block))
      if (grContent.block.user_specific)
          contentBody = grContent.block.rich_text_user_specific.toString();
      else
          contentBody = grContent.block.rich_text.toString();
  else if (grContent[field])
      contentBody = grContent[field].toString();
return new sn_cd.cd_ContentParser().parseRichTextHtmlBody(contentBody, glideRec) + cd_ContentDelivery._processRCScript(grContent);
};

/* Process the rich component script
* @param grContent GlideRecord [sn_cd_content_portal] Content record
* @return element of the script
*/
cd_ContentDelivery._processRCScript = function(grContent) {
  var rcScripts = [];
  if (grContent.getValue("content_type") === cd_CommonConstants.CONTENT_TYPE_RICH_CONTENT)
      try {
          var rcComponents = JSON.parse(grContent.rich_content_components);
          rcComponents.forEach(function(idx) {
              var script = cd_ContentDelivery._getRichContentComponentScript(idx);
              if (script) {
                  var elm = "var elements = document.querySelectorAll('." + idx + "'); elements.forEach(function(element) {(" + script + ".bind(element)())});" ;
                  rcScripts.push("<script> " + elm + "</script>");
              }
          });
      } catch (error) {
          gs.error(error);
      }
  return rcScripts.toString();
};

/* Get the rich component script from a wb_component record
* @param grContent GlideRecord [wb_component] Content record to get script from
* @param field string The field on wbCompGR to get script 
* @return String of the script from wbCompGR
*/
cd_ContentDelivery._getRichContentComponentScript = function(component) {
  // Keeping for when component table is reimplemented
  // var scriptContent;
  // var wbCompGR = new GlideRecord("wb_component");
  // wbCompGR.addQuery("name", component);
  // wbCompGR.query();
  // if (wbCompGR.next())
  //     scriptContent = wbCompGR.getValue("script");
  // return scriptContent;
  if (component === 'wb-tab-container')
      return "function wbTabContainer() {" +
          "const el = this;" +
          "el.addEventListener('keydown', onKeydown);" +
          "el.addEventListener('click', onClick);" +
          "const activeTab = el.firstElementChild.querySelector('.active[role=tab]');" +
          "if (activeTab) selectTab(activeTab);" +
          "else selectTab(el.firstElementChild.querySelector('[role=tab]'));" +
          "function onClick(event) {" +
          "    var target = event.target;" +
          "    if (target.role !== 'tab') target = getTargetedTab(target);" +
          "    if (target) {" +
          "        event.preventDefault();" +
          "        selectTab(target);" +
          "    }" +
          "}" +
          "function onKeydown(event) {" +
          "    var target = event.target;" +
          "    var flag = false;" +
          "    if (target.role !== 'tab') target = getTargetedTab(target);" +
          "    if (target) {" +
          "        switch (event.key) {" +
          "            case 'ArrowLeft':" +
          "                setSelectedToPreviousTab(target);" +
          "                flag = true;" +
          "                break;" +
          "            case 'ArrowRight':" +
          "                setSelectedToNextTab(target);" +
          "                flag = true;" +
          "                break;" +
          "            case 'Home':" +
          "                selectTab(target.parentNode.firstChild, true);" +
          "                flag = true;" +
          "                break;" +
          "            case 'End':" +
          "                selectTab(target.parentNode.lastChild, true);" +
          "                flag = true;" +
          "                break;" +
          "            default:" +
          "                break;" +
          "        }" +
          "        if (flag) {" +
          "            event.stopPropagation();" +
          "            event.preventDefault();" +
          "        }" +
          "    }" +
          "}" +
          "function selectTab(target, setFocus) {" +
          "    const tabs = el.firstElementChild.querySelectorAll('[role=tab]');" +
          "    tabs.forEach(function (tab) {" +
          "        tab.tabIndex = -1;" +
          "        tab.className = tab.className.replace('active', '').trim();" +
          "        tab.ariaSelected = false;" +
          "        const panel = el.querySelector('#' + tab.getAttribute('aria-controls'));" +
          "        if (panel) {" +
          "            panel.hidden = true;" +
          "            panel.tabIndex = -1;" +
          "        }" +
          "    });" +
          "    target.tabIndex = 0;" +
          "    target.className += ' active';" +
          "    target.ariaSelected = true;" +
          "    const panel = el.querySelector('#' + target.getAttribute('aria-controls'));" +
          "    panel.hidden = false;" +
          "    panel.tabIndex = 0;" +
          "    if (setFocus) target.focus();" +
          "}" +
          "function setSelectedToPreviousTab(currentTab) {" +
          "    var previousTab = currentTab.previousElementSibling;" +
          "    if (previousTab) selectTab(previousTab, true);" +
          "    else selectTab(currentTab.parentNode.lastChild, true);" +
          "}" +
          "function setSelectedToNextTab(currentTab) {" +
          "    var nextTab = currentTab.nextElementSibling;" +
          "    if (nextTab) selectTab(nextTab, true);" +
          "    else selectTab(currentTab.parentNode.firstChild, true);" +
          "}" +
          "function getTargetedTab(target) {" +
          "    var tabTarget;" +
          "    el.firstElementChild.querySelectorAll('[role=tab]').forEach(function (tab) {" +
          "        if (tab.contains(target)) tabTarget = tab;" +
          "    });" +
          "    return tabTarget;" +
          "}" +
      "}";
  return '';
};

/* Get the URL to from the portal content record
* @param grContent GlideRecord [sn_cd_content_portal] Content record to get the linked content from
* @param grContentVisibility GlideRecord [sn_cd_content_visibility] Optional, if provided check for linked todo content
* @param grCampaignContent GlideRecord [sn_ca_campaign_item] Optional, if provided check for linked todo content
* @return String The correct URL to pass to widget
*/
cd_ContentDelivery._getUrlFromContent = function (grContent, grContentVisibility, grCampaignContent) {
  var todoTaskGr;
  var portalGr;
  var portalSuffix;
  var taskSysId;
  // Check linked todo
  if (grContentVisibility && !grContentVisibility.todo_scheduled_content.nil()) {
      const todoScheduledContentSysId = grContentVisibility.todo_scheduled_content.toString();
      todoTaskGr = new GlideRecord("sn_cd_task");
      todoTaskGr.addQuery("assigned_to", gs.getUserID());
      todoTaskGr.addQuery("created_from_scheduled_content", todoScheduledContentSysId);
      todoTaskGr.query();
      portalGr = $sp.getPortalRecord();	
      portalSuffix = portalGr.getDisplayValue("url_suffix");
      if (todoTaskGr.next()) {
          taskSysId = todoTaskGr.getUniqueValue();
          return "/" + portalSuffix + "?id=hrm_todos_page&todo=" + taskSysId;
      }
  } else if (grCampaignContent && !grCampaignContent.todo_campaign_content.nil()) {
      const todoCampaignItemSysId = grCampaignContent.todo_campaign_content.toString();
      todoTaskGr = new GlideRecord("sn_cd_task");
      todoTaskGr.addQuery("assigned_to", gs.getUserID());
      todoTaskGr.addQuery("created_from_campaign_content", todoCampaignItemSysId);
      todoTaskGr.query();
      portalGr = $sp.getPortalRecord();	
      portalSuffix = portalGr.getDisplayValue("url_suffix");
      if (todoTaskGr.next()) {
          taskSysId = todoTaskGr.getUniqueValue();
          return "/" + portalSuffix + "?id=hrm_todos_page&todo=" + taskSysId;
      }
  }
  // Else check kb article
  var Kb = new cd_KnowledgeBase();
  return Kb.getUrlAsset(grContent.url_asset);
};
/* Get the video  URL to from the portal content record
* @param grContent GlideRecord [sn_cd_content_portal] Content record to get the linked content from
* @return String The correct video URL to pass to widget
*/
cd_ContentDelivery._getVideoUrlFromContent = function (grContent) {
  if (grContent && grContent.video_link) {
  	var videoId = grContent.video_link;
  	var urlAssetGr = new GlideRecord("sn_cd_url_asset");
  	if (urlAssetGr.get(videoId) && (urlAssetGr.getValue('type') === 'video'))
  		return urlAssetGr.getValue('url');
  }
  return '';
};
/*
* Checks if content is associated to scheduled to-do content and if that content has been processed
*   and posted to user inboxes yet.
* @param grContentVisibility GlideRecord [sn_cd_content_visibility] A scheduled content record
* @return boolean true if the content has associated to-do content that has not yet been processed, false otherwise
*/
cd_ContentDelivery._hasUnprocessedTodoScheduledContent = function (grContentVisibility) {
  if (grContentVisibility.todo_scheduled_content.nil())
      return false;
  var todoScheduledContentId = grContentVisibility.todo_scheduled_content.toString();
  var todoScheduledContentStatus = grContentVisibility.todo_scheduled_content.notification_status.toString();
  // If all are unprocessed or an error occured, don't bother querying tasks
  if (["pending", "error"].indexOf(todoScheduledContentStatus) > -1)
      return true;
  // Check if individual user todo has been processed
  var grTask = new GlideRecord("sn_cd_task");
  grTask.addQuery("created_from_scheduled_content", todoScheduledContentId);
  grTask.addQuery("assigned_to", gs.getUserID());
  grTask.query();
  return !grTask.hasNext();
};
cd_ContentDelivery._hasUnprocessedTodoCampaignContent = function (grCampaignItem) {
  if (grCampaignItem.todo_campaign_content.nil())
      return false;
  var todoCampaignContentId = grCampaignItem.todo_campaign_content.toString();
  // Check if individual user todo has been processed
  var grTask = new GlideRecord("sn_cd_task");
  grTask.addQuery("created_from_campaign_content", todoCampaignContentId);
  grTask.addQuery("assigned_to", gs.getUserID());
  grTask.query();
  return !grTask.hasNext();
};
/*
* @param grContent GlideRecord a content record
* @return boolean true if the content is clickable (i.e. it has a button)
*   or a link in it. false otherwise
*/
cd_ContentDelivery.isClickable = function (grContent) {
  if (grContent.sys_class_name === 'sn_cd_content_todo')
      return true;
  var isClickableHTML = function (text) {
      return text.indexOf('</a>') >= 0;
  };
  var isBanner = grContent.content_type === 'a673597a0b4303008cd6e7ae37673a6f';
  var isRichText = grContent.content_type === '97a35d7a0b4303008cd6e7ae37673a47';
  var isEmail = grContent.content_type === '02dccfc33b630300d901655593efc4df';
  if (isBanner) {
      if (grContent.use_custom_html)
          return isClickableHTML(grContent.rich_text + '');
      return grContent.show_button.toString() === "true";
  }
  if (isRichText) {
      if (grContent.use_block)
          return isClickableHTML(grContent.block.rich_text + '');
      return isClickableHTML(grContent.rich_text + '');
  }
  if (isEmail) {
      if (grContent.use_block)
          return isClickableHTML(grContent.block.rich_text + '');
      return isClickableHTML(grContent.rich_text_plain_html + '');
  }
  if (grContent.content_type.category === 'portal_content')
      return true;
  return false;
};
/* Given a service portal context, returns true if the content widget is in preview mode
* @param Object widget $sp context object
* @return true if the content is being previewed, false otherwise
*/
cd_ContentDelivery.isContentPreview = function ($sp) {
  if (!$sp || typeof $sp.getParameter !== 'function')
      return false;
  return $sp.getParameter('is_preview') === 'true';
};
/* Get the widget that should rendered for this preview called by CD Preview Widget
* @param Object widget $sp context object
* @return Object the widget object to render
*/
cd_ContentDelivery.getWidgetForPreview = function ($sp) {
  // TODO: allow preview of standard portal content from Content Publishing, STRY51126195
  var contentItemId = $sp.getParameter('sca_content_id');
  if (contentItemId)
      return cd_ContentDelivery.getWidgetForSCAPreview($sp);
  if (new GlidePluginManager().isActive('com.sn_content_automation'))
      return sn_ca.ca_CampaignPortalPreview.getWidgetForPreview($sp);
  return null;
};
cd_ContentDelivery.getWidgetForSCAPreview = function ($sp) {
  var contentId = $sp.getParameter('sca_content_id');
  var previewObj = {};
  var grContentItem = new GlideRecord('sn_cd_content_portal');
  if (grContentItem.get(contentId)) {
      var grContentType = grContentItem.getValue('content_type');
      if (grContentType === 'a673597a0b4303008cd6e7ae37673a6f')
          previewObj.widget = $sp.getWidget('cd-banner', {
              content_id: grContentItem.getUniqueValue()
          });
      else if (grContentType === '41040dd7237e2300fb0c949e27bf652a')
          previewObj.widget = $sp.getWidget('cd-styled-content', {
              content_id: grContentItem.getUniqueValue()
          });
      else if (grContentType === 'f8c39d7a0b4303008cd6e7ae37673a55')
          previewObj.widget = $sp.getWidget('cd-video-carousel', {
              content_id: grContentItem.getUniqueValue()
          });
      else if (grContentType === 'afb39d7a0b4303008cd6e7ae37673a16')
          previewObj.widget = $sp.getWidget('cd-information-links', {
              content_id: grContentItem.getUniqueValue()
          });
      else if (grContentType === 'ae935d7a0b4303008cd6e7ae37673a9a')
          previewObj.widget = $sp.getWidget('cd-quick-links', {
              content_id : grContentItem.getUniqueValue()
          });
      else if (grContentType === '3893597a0b4303008cd6e7ae37673a4c')
          previewObj.widget = $sp.getWidget('cd-upcoming-events', {
              content_id : grContentItem.getUniqueValue()
          });
      else if (grContentType === '97a35d7a0b4303008cd6e7ae37673a47')
          previewObj.widget = $sp.getWidget('cd-announcements', {
              content_id : grContentItem.getUniqueValue()
          });
      else if (grContentType === '181d586d77741110a10440483f5a9933')
          previewObj.widget = $sp.getWidget('cd-richcontent', {
              content_id : grContentItem.getUniqueValue()
          });
  } else
      gs.addErrorMessage(gs.getMessage("Could not find sn_cd_content_portal {0}", contentId));
  return previewObj;
};

cd_ContentDelivery.getScaPreviewContent = function ($sp, portalPreview) {
  var content_id = $sp.getParameter('sca_content_id');
  var result = [];
  var user = gs.getUser();
  if (user.hasRole('sn_cd.content_approver'))
     result = cd_ContentDelivery._buildScaPreviewContent($sp, portalPreview);
  else
      if (new GlidePluginManager().isActive('com.sn_content_governance')) {
          var contentGovernance = new sn_cg.cg_PermissionUtils();
          if (contentGovernance.isRequestor(content_id, gs.getUserID()) ||
              contentGovernance.isApprover(content_id, gs.getUserID()))
              result = cd_ContentDelivery._buildScaPreviewContent($sp, portalPreview);
          else {
              gs.addErrorMessage(gs.getMessage("You do not have permission to preview this content."));
              return result;
          }          
      } 
  return result;
};

cd_ContentDelivery._buildScaPreviewContent = function ($sp, portalPreview) {
  var grContent = new GlideRecord('sn_cd_content_portal');
  var content_id = $sp.getParameter('sca_content_id');
  var result = [];
  if (grContent.get(content_id)) {
      var accumulatedContent = {
          contentMap: {},
          contentArray: []
      };
      cd_ContentDelivery._addContentToResult(grContent, accumulatedContent);
      if (portalPreview) {
      if ($sp.getParameter('attachment_id'))
          accumulatedContent.contentArray[0].background_image = $sp.getParameter('attachment_id') + '.iix';
      accumulatedContent.contentArray[0].is_preview = true;
      accumulatedContent.contentArray[0].is_content_active = grContent.getValue('active');
      if (grContent.getValue('active') === '1')
          accumulatedContent.contentArray[0].content_title = grContent.getDisplayValue('title');
      else
          accumulatedContent.contentArray[0].content_title = gs.getMessage('(Inactive) {0}', grContent.getDisplayValue('title'));
      }
      result = accumulatedContent.contentArray;
  }
  if (portalPreview) {
      var grInstanceRecord = $sp.getInstanceRecord();
      var spInstanceId = grInstanceRecord.getUniqueValue();
      var topicId = $sp.getParameter('topic_id');
      var contentList = cd_ContentDelivery.getContentForWidgetInstance(spInstanceId, topicId);
      result = result.concat(cd_ContentDelivery.getContentForWidgetInstance(spInstanceId, topicId).filter(function (content) {
          return content.sys_id !== content_id;
      }));
          var publishedContent = contentList.filter(function (content) {
          return content.sys_id === content_id;
      });
      if (publishedContent.length === 1)
          gs.addInfoMessage(gs.getMessage("Previewed content is highlighted below, but please note it is currently published and visible to applicable employees"));
      else {
          var sysIds = result.map(function(item) {
              return item.sys_id
          });
          result = result.concat(contentList.filter(function (content) {
              return content.sys_id !== content_id && sysIds.indexOf(content.sys_id) === -1;
          }));
      }
  }
  return result;
};

/* Determine if the widget is for portal or preview and return the appropriate content
* @param Object widget $sp context object
* @return Array the content item(s) for data.items
*/
cd_ContentDelivery.getContent = function ($sp, instanceId) {
  var grInstanceRecord = $sp.getInstanceRecord();
  var spInstanceId = grInstanceRecord.getUniqueValue();
  var topicId = $sp.getParameter("topic_id");
  if ($sp.getParameter('sca_content_id') && $sp.getParameter('sca_instance_id') && $sp.getParameter('sca_instance_id') === instanceId)
      return cd_ContentDelivery.getScaPreviewContent($sp, true);
  if ($sp.getParameter('sca_content_id') && !$sp.getParameter('sca_instance_id'))
      return cd_ContentDelivery.getScaPreviewContent($sp);
  if (cd_ContentDelivery.isContentPreview($sp) && !$sp.getParameter('sca_instance_id'))
      return sn_ca.ca_CampaignPortalPreview.getContentForPreview($sp);
  return cd_ContentDelivery.getContentForWidgetInstance(spInstanceId, topicId);
};
/* Creates the info object that holds the display info for the content popover in portal preview
* @param data Object the data object that has the items info
* @param additionalDetails Array objects with keys label and value
* @return Object the object with items with the info objects
*/
cd_ContentDelivery.setInfoObjectOnContentItem = function (item, additionalDetails) {
  item.infoObject = {
      title: gs.getMessage('Content Details'),
      sections: [{
          sectionTitle: '',
          details: [{
                  label: gs.getMessage('Title'),
                  value: item.content_title
              },
              {
                  label: gs.getMessage('Order'),
                  value: item.order
              },
          ].concat(additionalDetails),
      }],
  };
  return item;
};

Sys ID

0f3c54bb537132003585c3c606dc341f

Offical Documentation

Official Docs: