Name

global.KBCommonSNC

Description

Customers should not change this class directly This class is never called directly.

Script

var KBCommonSNC = Class.create();

KBCommonSNC.prototype = {
  
  VERSIONING_PLUGIN : 'com.snc.knowledge_advanced',
  INTEGRATION_PLUGIN : 'com.snc.knowledge.external_integration',
  I18N2_PLUGIN : 'com.glideapp.knowledge.i18n2',
  BLOCKS_PLUGIN : 'com.snc.knowledge_blocks',
  WORD_ADDIN_PLUGIN: 'com.snc.knowledge.ms_word',
  TRANSLATION_TASK_NEW : 1,
  TRANSLAITON_TASK_IN_PROGRESS : 2,
  TRANSLATION_TASK_CLOSED : 3,
  TRANSLATION_TASK_CANCELLED : 4,
  INDEX_FILTER_ATTR_SYS_ID : 'dafb788eb7103300abfad298ee11a9c9',
  KNOWLEDGE_CONFIG_SYS_ID : '28eefe427320330003da83a9fdf6a794',
  KNOWLEDGE_INDEX_FILTER_VALUE : 'workflow_stateNOT INretired,outdated',
  LOCALIZATION_FRAMEWORK_ID: 'com.glide.localization_framework',
  
  initialize: function() {
  	this._knowledgeHelper = new SNC.KnowledgeHelper();
  },

  /**
   * GlideRecord.get() prints a warning message match could not be found.
   * GlideRecord.next() does not. So this method is exposed for extending objects to use.
   */
  _get: function(record, fieldName, fieldData) {
  	if (record) {
  		record.addQuery(fieldName, fieldData);
  		record.query();
  		if (record.next() && record.isValidRecord())
  			return true;
  	}
  	return false;
  },

  /**
   * Determines whether the current user is a manager or owner of the knowledge base associated with the passed in record
   *
   * @param user: userid of the user to be checked
   * @param GlideRecord: record on which to search for managers to evaluate
   * @param String: (optionally dotted) path to the managers field on the passed in record
   * @return Boolean: whether the current user is a manager of the passed in record
   */
  isKnowledgeBaseOwnerOrManager: function(record,pathToUser,user){
  	var ownerOrManagers = this._getDotField(record, pathToUser);
  	var isOwnerOrManager = (ownerOrManagers.indexOf(user) >= 0);
  	return isOwnerOrManager;
  },
  
  /**
   * Determines whether the current user is a manager of the knowledge base associated with the passed in record
   *
   * @param GlideRecord: record on which to search for managers to evaluate
   * @param String: (optionally dotted) path to the managers field on the passed in record
   * @return Boolean: whether the current user is a manager of the passed in record
   */
  isKnowledgeBaseManager: function(record, pathToUser) {
  	return this.isKnowledgeBaseOwnerOrManager(record,pathToUser,gs.getUserID());
  },

  /**
   * Determines whether the current user is the owner of the knowledge base associated with the passed in record
   *
   * @param GlideRecord: record on which to search for owners to evaluate
   * @param String: (optionally dotted) path to the owner field on the passed in record
   * @return Boolean: whether the current user is the owner of the passed in record
   */
  isKnowledgeBaseOwner: function(record, pathToUser) {
  	return this.isKnowledgeBaseOwnerOrManager(record,pathToUser,gs.getUserID());
  },

  /**
   * Determine if a user has the right to access kb_category record
   *
   * @param String kbCategoryId
   * @return boolean
   */
  managerRightToKnowledgeCategory: function(kbCategoryId) {
  	var rootId = this._knowledgeHelper.getRootKBId(kbCategoryId);
  	var pathToOwner = "owner";
  	var pathToManagers = "kb_managers";
  	var kbKnowledgeBase = new GlideRecord("kb_knowledge_base");
  	if (kbKnowledgeBase.get(rootId))
  		return this.isKnowledgeBaseManager(kbKnowledgeBase, pathToManagers) || this.isKnowledgeBaseOwner(kbKnowledgeBase, pathToOwner) || this.isAdminUser(kbKnowledgeBase);

  	return false;
  },
  
  /**
   * Determine if a user has the right to access root knowledge base record
   *
   * @param String kbCategoryId
   * @return boolean
   */
  canReadRootKnowledgeBase: function(kbCategoryId) {
  	var rootId = this._knowledgeHelper.getRootKBId(kbCategoryId);
  	var kbKnowledgeBase = new GlideRecord("kb_knowledge_base");
  	if (kbKnowledgeBase.get(rootId)){
  		return kbKnowledgeBase.canRead();
  	}

  	return false;
  },

  /**
   * Determines if a given knowledge category has children(categories or articles) or is empty
   *
   * @param String sys_id of a kb_category record
   * @return boolean
   */
  isEmptyCategory: function(kbCategoryId) {
  	var kbKnowledge = new GlideRecord("kb_knowledge");
  	var category = new GlideRecord("kb_category");

  	// Check for any child categories or related knowledge articles
  	if (kbKnowledge.get("kb_category", kbCategoryId) || category.get("parent_id", kbCategoryId))
  		return false;

  	return true;
  },

  /**
   * Determines if the knowledge base is based on Knowledge Management V3.
   *
   * @param String: the sys_id of a knowledge base record to check
   * @return Boolean: true if knowledge base version is Fuji release, false otherwise
   */
  isKBVersion3: function(knowledgeBaseId) {
  	// Check if preknowledge3 property has been set, if not, knowledge base must be latest
  	var legacyKBVersion = gs.getProperty("glide.knowman.preknowledge3.kb_version", null);
  	if (JSUtil.nil(legacyKBVersion))
  		return true;

  	// Check if the knowledge base version number matches Fuji release (version 3)
  	return this._getKBVersion(knowledgeBaseId) === "3";
  },

  /**
   * Determines if the knowledge base is based on Knowledge Management V2.
   *
   * @param String: the sys_id of a knowledge base record to check
   * @return Boolean: true if knowledge base version is Fuji release, false otherwise
   */
  isKBVersion2: function(knowledgeBaseId) {
  	// Check if preknowledge3 property has been set, if not, knowledge base must be latest
  	var legacyKBVersion = gs.getProperty("glide.knowman.preknowledge3.kb_version", null);
  	if (JSUtil.nil(legacyKBVersion))
  		return false;

  	// Check if the knowledge base version number is pre Fuji (version 2)
  	return this._getKBVersion(knowledgeBaseId) === "2";
  },

  /**
   * Determines if a kb_knowledge record is based on Knowledge Management V3.
   *
   * @param GlideRecord: kb_knowledge record to evaluate
   * @return Boolean: true if record is based on latest release, false otherwise
   */
  isRecordVersion3: function(record) {
  	var knowledgeBaseId = this._getDotField(record, "kb_knowledge_base");

  	return this.isKBVersion3(knowledgeBaseId);
  },

  /**
   * Determines if a record is displayed with a GlideDialogForm
   *
   * @return Boolean: true if the nameofstack is formDialog
   */
  isStackNameDialog: function() {
  	var stack = GlideTransaction.get().getRequestParameter("sysparm_nameofstack");
  	return stack == "formDialog";
  },

  isStackNameDialogNavHandler: function(g_uri) {
  	// Convert view name to lowercase
  	var view = g_uri.get("sysparm_view");
  	if (!JSUtil.nil(view))
  		view = view.toLowerCase();

  	// Case 1: Existing record provided
  	var stack = g_uri.get("sysparm_nameofstack");
  	if (stack == "formDialog" && !JSUtil.nil(view))
  		return true;

  	return false;
  },	
  
 /**
   * Can the user create a kb_knowledge record.
   * @param GlideRecord: kb_knowledge 
   * @return Boolean: can logged in user create a kb_knowledge
   */
  _canCreateKnowledge: function(gr){
      if (this.isAdminUser(gr))
          return true;

      if (gr.isNewRecord())
  		gr = this._getKnowledgeBase(gr) || gr;

      if (this.isKnowledgeBaseOwner(gr, "kb_knowledge_base.owner"))
          return true;

      if (this.isKnowledgeBaseManager(gr, "kb_knowledge_base.kb_managers"))
          return true;

      if (this._knowledgeHelper.canContribute(gr))
          return true;

      return false;
  },
  
  
  canRetireKnowledge: function(itemGr) {
  	// Case 1: Pass in a valid value
  	if (!itemGr)
  		return false;
  
  	// Case 2: If the record is published continue
  	if (itemGr.workflow_state != "published" && itemGr.workflow_state != "draft")
  		return false;
  	
  	// Case 3: If user can contribute continue
  	if (!new SNC.KnowledgeHelper().canContribute(itemGr))
  		return false;
  	
  	// Default: Allow user to retire knowledge
  	return true;
  },
  
  /** 
  * Checks whether user can republish knowledge
  *
  * @param GlideRecord
  * @return boolean
  */

  canRepublishKnowledge: function(itemGr) {
  	// Case 1: Pass in a valid value
  	if (!itemGr)
  		return false;

  	// Case 2: If the record is retired continue
  	if (itemGr.workflow_state != "retired")
  		return false;

  	// Case 3: If user is admin continue
  	if (!this.isAdminUser(itemGr))
  		return false;

  	// Default: Allow user to republish knowledge
  	return true;
  },

  /** 
  * Republishes an article
  *
  * @param GlideRecord
  * @return GlideRecord
  */
  republishKnowledge: function(current){

  	if(this.canRepublishKnowledge(current)){
  		current.workflow_state = "published";
  		current.published = new GlideDate(); current.retired= "";
  		current.update();
  		return current;
  	}

  	return false;
  },

  /** 
  * Checks whether versioning is enabled
  *
  *
  * @return boolean
  */
  isVersioningEnabled: function(){
  	return this._knowledgeHelper.isVersioningEnabled();
  },

  /** 
  * Checks whether versioning plugin is activated
  *
  *
  * @return boolean
  */
  isVersioningInstalled : function(){
  	if(gs.nil(this._knowledgeHelper))
  		this._knowledgeHelper = new SNC.KnowledgeHelper();
  	return this._knowledgeHelper.isVersioningInstalled();
  },

  /** 
  * Returns the physical record of the given article number
  * Used for global search exact matches
  *
  *
  * @return GlideRecord
  */
  getKnowledgeRecord: function(articleNumber){
  	if(this.isVersioningEnabled())
  		return new KBVersioning().getLatestAccessibleVersion(articleNumber);
  	else {
  		var gr = new GlideRecord('kb_knowledge');
  		gr.addQuery('number', articleNumber);
  		gr.setCategory('homepage');
  		gr.query();
  		if (gr.next())
  			return gr;
  		else
  			return false;
  	}
  },
  
  /** 
  * Returns the physical record of the given article number
  * Used for global search exact matches
  *
  *
  * @return GlideRecord
  */
  getKnowledgeRecordById: function(articleId){
  	var gr = new GlideRecord('kb_knowledge');
  	gr.get(articleId);
  	return gr;
  },

  /**
   * Gets the knowledge base whose id has been passed in the URI.
   *
   * @return A kb_knowledge_base record or null
   */
  _getKnowledgeBase: function(gr) {
  	var kbId = gr.getValue("kb_knowledge_base");
  	
      if (JSUtil.nil(kbId)) {
          gs.log("[KBCommonSNC] Error - No knowledge base id found");
          return null;
      }

  	var kbGr = new GlideRecord("kb_knowledge_base");
  	if (!this._get(kbGr, "sys_id", kbId)) {
          gs.log("[KBCommonSNC] Error - No knowledge base found matching id " + kbId);
          return null;
      }

      return kbGr;
  },

  _getKBVersion: function(knowledgeBaseId) {
  	var gr = new GlideRecord("kb_knowledge_base");
  	if (gr.get(knowledgeBaseId))
  		return gr.kb_version + "";

  	return null;
  },

  _getDotField: function(gr, pathToField) {
  	if (JSUtil.nil(gr))
  		return "";

  	var arrFields = (pathToField || "").split(".");
  	var element = gr;
  	for (var i = 0; i < arrFields.length; i++) {
  		element = element[arrFields[i]];
  		if (!element)
  			return "";
  	}

  	return element;
  },

  _encode: function(object) {
  	return new JSON.encode(object);
  },

  _decode: function(string) {
  	return new JSON.decode(string);
  },

  _i18n: function(message, array) {
  	message = message || "";
  	var padded =  message;
  	var translated = gs.getMessage(padded, array);
  	var trimmed = translated.trim();
  	return trimmed;
  },

  setUniqueInfoMessage: function(message, key) {
  	this._knowledgeHelper.addUniqueInfoMessage(message, key);
  },

  getStateMessage: function(state) {
  	var message = {
  		draft: "This knowledge item is in draft state and can be edited",
  		review: "This knowledge item is in review",
  		published: "This knowledge article has been published",
  		scheduled_publish : "Article is approved and will be published on Scheduled Date",
  		pending_retirement: "This knowledge item is pending retirement",
  		retired: "This knowledge item has been retired"
  	};

  	return (this._i18n(message[state]) || "");
  },
  
  /**
   * Is it a multi update form.
   *
   * @param GlideRecord: kb_knowledge
   **/
  isMultipleKnowledgeUpdate: function(knowledgeGR) {
  	try{
  		if(RP){
  		var url = RP.getReferringURL();
  		var sys_action = RP.getParameterValue('sys_action');
  		var sysparm_multiple = RP.getParameterValue('sysparm_multiple');
  	
  		return url && url != null && !gs.nil(url) && (url.startsWith('kb_knowledge_update.do') || url.startsWith('kb_knowledge_base_update.do')) && sys_action == 'sysverb_multiple_update' && sysparm_multiple == 'true';
  		}
  	} catch(err) {
  		//gs.log("Warning: KBCommonSNC.isMultipleKnowledgeUpdate(" + err.lineNumber + "): " + err.name + " - " + err.message);
  	}
  	return false;
  },

  /** 
  * Checks whether external integration plugin is enabled
  *
  *
  * @return boolean
  */
  isExternalIntegrationInstalled: function(){
  	return GlidePluginManager.isActive(this.INTEGRATION_PLUGIN);
  },

  isAccessBlockedWithNoUC: function(){
  	if(gs.getProperty("glide.knowman.block_access_with_no_user_criteria",'false')=='true')
  		return true;
  	else
  		return false;
  },
  /** 
  * Checks whether an article is externally sourced
  *
  *
  * @return boolean
  */
  isExternalArticle: function(knowledgrGR){
  	return knowledgrGR && this.isExternalIntegrationInstalled() && knowledgrGR.external;
  },
  
  isLinkedToWordDocument: function(knowledgeGR){
  	return knowledgeGR && pm.isActive(this.WORD_ADDIN_PLUGIN) && knowledgeGR.getValue('office_doc_url');
  },
  
  //Return true if knowledge base Can Read user criteria list is empty 
  isKBReadPublic: function(kbSysId) {
  	if(!kbSysId)
  		return false;
  	var canRead = new GlideRecord('kb_uc_can_read_mtom');
  	canRead.addQuery('kb_knowledge_base', kbSysId);
  	canRead.query();
  	if(!canRead.next()) {
  		return true;
  	}

  	return false;
  },
  
  //Return true if knowledge base Can Contribute user criteria list is empty
  isKBWritePublic: function(kbSysId) {
  	if(!kbSysId)
  		return false;
  	var canWrite = new GlideRecord('kb_uc_can_contribute_mtom');
  	canWrite.addQuery('kb_knowledge_base', kbSysId);
  	canWrite.query();
  	if(!canWrite.next()) {
  		return true;
  	}
  	return false;
  },
  

  /**
  * Check whether the field exist and has value
  *
  * @param GlideRecord
  * @param FieldName
  **/
  isValidFieldWithValue: function(current,fieldName){
  	return current.isValidField(fieldName) && current[fieldName];
  },
  
  /**
  * Check whether the logged in user is a manager or a member of the group
  *
  * @param Gliderecord: group
  **/
  isGroupMemberOrManager: function(group){
  	return group.manager == gs.getUserID() || gs.getUser().isMemberOf(group);
  },
  
  /**
  * Check whether the logged in user is a manager or a member of the group if the group is valid
  *
  * @param Gliderecord
  * @param GroupFieldName
  **/
  isMemberOfValidGroup: function(current,groupfield){
  	return this.isValidFieldWithValue(current,groupfield) && this.isGroupMemberOrManager(current[groupfield]);
  },
  
  /**
  * Checks whether translations can be created thru Translated Version related list.
  *
  **/
  canCreateTranslation: function(){
  	if(pm.isActive(this.VERSIONING_PLUGIN) && pm.isActive(this.I18N2_PLUGIN))
  		return gs.getProperty('glide.knowman.translation.enable_translation_task','false') == 'false';
  	return true;
  },
  
  /** 
  * Constructs the link to a KB article
  *
  * @param articleId
  **/
  getArticleLink: function(articleId){
  	var sp = pm.isActive("com.snc.knowledge_serviceportal");
  	var url = gs.getProperty('glide.servlet.uri');
  	if(!url.endsWith("/"))
  		url = url + "/";
  	if(sp == true)
  		article_link = url+gs.getProperty('sn_km_portal.glide.knowman.serviceportal.portal_url', 'kb')+"?id=kb_article_view&sys_kb_id="+articleId;
  	else
  		article_link = url+"kb_view.do?sys_kb_id="+articleId;
  	return article_link;
  },
  
  /** 
  * Checks if the user has access to edit article based on article's domain 
  *
  * @param Gliderecord
  **/
  isArticleReadOnlyBasedOnDomain: function(current){
  	var articleDomain = current.sys_domain ? (current.sys_domain + "") : "global";
  	var userDomain = gs.getSession().getCurrentDomainID() ? (gs.getSession().getCurrentDomainID() + "") : "global"; if(articleDomain=="global" && articleDomain != userDomain)
  		return true;
  	return false;
  },
  
  /*
  * Executes the method and restores the value of answer, so that it will not override the answer in ACL
  */
  safeExecute: function(method, arg){		
  	var kbACLAnswer = null;
  	
  	try {
  		kbACLAnswer = typeof answer != 'undefined'? answer : 'BLANK';			
  	} catch(error) {
  		kbACLAnswer = answer;
  	}

  	var kbOperationResult = method(arg);
  	if(kbACLAnswer !== 'BLANK')
  		answer = kbACLAnswer;

  	return kbOperationResult;
  },
  
  /**
  * Checks if approval notification property is set and article is in review
  *
  * @param GlideRecord : current
  **/
  canNotify: function(current) {
  	if(gs.getProperty('glide.knowman.enable_approval_notification','false') == 'true') {
  		if(current.document_id && current.document_id.workflow_state == 'draft') {
  			return true;
  		}
  		else {
  			return false;
  		}
  	}
  	else {
  		return false;
  	}
      },

      /**
   * Is the logged in user admin of the given record.
   *
   * @param GlideRecord: kb_knowledge_base/kb_knowledge, for other just returns false
   * @return Boolean: true if logged in user has admin rights on the knowledge base or article
   */
  isAdminUser: function(kbRec) {
  	return this._knowledgeHelper.isAdminUser(kbRec);
  },
  
  getDefaultValidToDateFromDate: function(knowledgeBaseSysId, date) { 
  	var knowledgeBase = new GlideRecord('kb_knowledge_base');
  	var d = new GlideDate();
  	d.setValue('2100-01-01');
  	knowledgeBase.get(knowledgeBaseSysId);
  	
  	if(gs.nil(knowledgeBase.article_validity))
  		return d.getDisplayValue();
  	
  	var validity = parseInt(knowledgeBase.article_validity);
  	if(validity > 0) {
  		date.addDays(validity);
  		return date.getDisplayValue();			
  	}

  	return d.getDisplayValue();
  },
  
  getDefaultValidToDateFromCurrentDate: function(knowledgeBaseSysId) {
  	return this.getDefaultValidToDateFromDate(knowledgeBaseSysId, new GlideDate());
  },
  
  isDefaultValidToSet: function(){
  	return new GlideRecord("kb_knowledge").getElement("valid_to").getED().getDefault() == "javascript: current.kb_knowledge_base ? new global.KBCommon().getDefaultValidToDateFromCurrentDate(current.kb_knowledge_base) : '';";
  },
  
  getIndexFilterConfiguredForTable: function(tableName) {
  	var tableAttrMap = new GlideRecord('ts_table_attribute_map');
  	tableAttrMap.addQuery('ts_configuration', this.KNOWLEDGE_CONFIG_SYS_ID);
  	tableAttrMap.addQuery('table', tableName);
  	tableAttrMap.addQuery('name', this.INDEX_FILTER_ATTR_SYS_ID);
  	tableAttrMap.query();
  	if(tableAttrMap.next())
  		return tableAttrMap.value + '';
  	return null;
  },
  
  getIndexFilterForTable: function(tableName) {
  	return this.KNOWLEDGE_INDEX_FILTER_VALUE;
  },
  
  installIndexFilterForTable: function(tableName, filterValue, overwrite) {
  	if(gs.nil(tableName) || gs.nil(filterValue)) {
  		gs.error('Table name or index filter value is empty');
  		return;
  	}
  	var tableAttrMap = new GlideRecord('ts_table_attribute_map');
  	tableAttrMap.addQuery('ts_configuration', this.KNOWLEDGE_CONFIG_SYS_ID);
  	tableAttrMap.addQuery('table', tableName);
  	tableAttrMap.addQuery('name', this.INDEX_FILTER_ATTR_SYS_ID);
  	tableAttrMap.query();
  	if(!tableAttrMap.next()) {
  		gs.info('Adding index filter for ' + tableName);
  		tableAttrMap.newRecord();
  		tableAttrMap.ts_configuration = this.KNOWLEDGE_CONFIG_SYS_ID;
  		tableAttrMap.table = tableName;
  		tableAttrMap.name = this.INDEX_FILTER_ATTR_SYS_ID;
  		tableAttrMap.value = filterValue;
  		tableAttrMap.insert();
  	}
  	else if(overwrite) {
  		gs.info('Updating index filter for ' + tableName + ' current value: ' + tableAttrMap.value + ', new value : ' + filterValue);
  		tableAttrMap.value = filterValue;
  		tableAttrMap.update();
  	}
  	else {
  		gs.info(tableName + ' already has the index filter defined.');
  	}
  },
  
  installIndexFiltersForKnowledge: function(overwrite) {
  	//Make sure current scope is global same as the scope of kb_knowledge table.
  	var userScopeID = gs.getCurrentApplicationId();
  	if (userScopeID != 'global') {
  		gs.error("The kb_knowledge table is in Global, but the current application is not Global. Please change application to Global before installing index filters.");
  	} else {
  		this.installIndexFilterForTable('kb_knowledge', this.getIndexFilterForTable('kb_knowledge'), overwrite);
  		//Get kb_knowledge table extensions and add filter for each table in the hierarchy.
  		var tables = new GlideRecord('sys_dictionary');
  		tables.addQuery('name', 'IN', SNC.TableEditor.getTableExtensions('kb_knowledge'));
  		tables.addQuery('internal_type', 'collection');
  		tables.query();

  		while (tables.next()) {
  			var tableName = tables.name + '';
  			this.installIndexFilterForTable(tableName, this.getIndexFilterForTable(tableName), overwrite);
  		}
  	}
  },
  
  calculateRating: function(article) {
  	var isVersioningInstalled = this.isVersioningInstalled();
  	var kbv = new GlideAggregate("kb_feedback");
  	if(isVersioningInstalled && !article.article_id.nil()){

  		kbv.addQuery("article.article_id",article.article_id);
  		kbv.addQuery("article.sys_created_on","<=",article.sys_created_on);
  	}
  	else
  		kbv.addQuery("article",article.sys_id);
  	
  	kbv.addQuery("rating",">","0");
  	kbv.addAggregate("AVG","rating");
  	kbv.setGroup(false);
  	kbv.setCategory('homepage');
  	kbv.query();
  	var totalCount = kbv.getRowCount();
  	var num = 0;
  	if (kbv.next()) {
  		num = kbv.getAggregate("AVG","rating");
  	}
  	var dbu = new GlideRecord('kb_knowledge');
  	dbu.get(article.sys_id);
  	dbu.setValue('rating',num);
  	dbu.setSystem(true);
  	dbu.setWorkflow(false); //disable business rules
  	dbu.setUseEngines(false); //if there are data policies preventing this update bypass them
  	dbu.update();
  },
  
  updateRating: function(kb_id,fb_id) {
  	if(fb_id && fb_id != "") {
  		var gr = new GlideRecord("kb_feedback");
  		gr.addQuery("sys_id",fb_id);
  		gr.query();
  		if(gr.next())
  			return;
  	}
  	var isVersioningInstalled = this.isVersioningInstalled();
  	var article_gr = new GlideRecord("kb_knowledge");
  	article_gr.get(kb_id);
  	if(isVersioningInstalled && !article_gr.article_id.nil()){
  		var versions = new GlideRecord('kb_knowledge');
  		versions.addQuery('article_id',article_gr.article_id);
  		versions.addQuery('sys_created_on','>=',article_gr.sys_created_on);
  		versions.setCategory('homepage');
  		versions.query();
  		while(versions.next()){
  			this.calculateRating(versions);
  		}
  	}
  	else
  		this.calculateRating(article_gr);
  },

  /**
   * Checks if input record is partial since isValidRecord will return true even if it is a partial record. A use case is when dot walked fields are present in a
   * list, a join is required to fetch data from two different tables, in such scenario referred record is partially loaded from DB. This is a temporary fix until
   * a fix is provided by platform
   * @param GlideRecord: record 
   * @return Boolean: false if all fields are present otherwise true.
   */
  isPartialRecord: function(record) {
  	return !(record != null && record.sys_id != undefined && record.sys_id.__parent__ instanceof GlideRecord);
  },

  //Public method to be called by 'Count Knowledge Use' job
  updateCount: function() {
  	var isVersioningInstalled = this.isVersioningInstalled();
  	var gr = new GlideRecord('kb_knowledge');
  	gr.setQueryReferences(false);
  	gr.setCategory("list");
  	gr.query();
  	while (gr.next()) {
  		var views = this._countViews(gr,days,isVersioningInstalled);
  		gr.setValue("sys_view_count", views);
  		gr.setWorkflow(false);
  		gr.setSystem(true);
  		gr.setUseEngines(false);
  		gr.update();
  	}
  },
  
  /**
   * Returns translated articles for the given sys_ids of articles and language.
   * If no translation exists for an article, then same the same article sys_id is returned.
   *
   * @param articleIds: List of article sys_ids for which translated articles are to be retrieved.
   * @param articleLanguage: Language in which the translated articles should be retrieved.
   * @return Boolean: List of articles, which are translated versions of given articles.
   */
  getLatestArticlesByLanguage: function(articleIds, articleLanguage){
  	var allArticles = new GlideRecord('kb_knowledge');
  	allArticles.addQuery('sys_id', 'IN', articleIds);
  	allArticles.query();

  	var translationMap = {};
  	var remainingIds = [];
  	var articleParentMap = {};

  	while(allArticles.next()){
  		var article_id = allArticles.getUniqueValue();
  		var articleParent = allArticles.getValue('parent');
  		
  		if(allArticles.getValue('language') === articleLanguage){
  			translationMap[article_id] = article_id;
  			if(articleParent)
  				translationMap[articleParent] = article_id;
  		}
  		else if(allArticles.parent.language == articleLanguage)
  			translationMap[article_id] = articleParent;
  		else {
  			if(gs.nil(articleParent))
  				remainingIds.push(article_id);
  			else {
  				if(articleParentMap[articleParent])
  					articleParentMap[articleParent].push(article_id);
  				else
  					articleParentMap[articleParent] = [article_id];
  			}
  		}
  	}

  	for(var parentArticle in articleParentMap){
  		if(translationMap[parentArticle]){
  			articleParentMap[parentArticle].forEach(function(child_article){
  				if(!translationMap[child_article])
  					translationMap[child_article] = translationMap[parentId];
  			});
  			delete articleParentMap[parentArticle];
  		} else {
  			remainingIds.push(parentArticle);
  		}
  	}
  	
  	if(remainingIds.length != 0){
  		var translatedChildArticles = new GlideRecord('kb_knowledge');
  		translatedChildArticles.addQuery('parent', 'IN', remainingIds);
  		translatedChildArticles.addQuery('language', articleLanguage);
  		translatedChildArticles.orderByDesc('sys_updated_on');
  		translatedChildArticles.addActiveQuery();
  		translatedChildArticles.query();

  		while(translatedChildArticles.next()){
  			var parentId = translatedChildArticles.getValue('parent');
  			if(!translationMap[parentId])
  				translationMap[parentId] = translatedChildArticles.getUniqueValue();
  			if(articleParentMap[parentId]){
  				articleParentMap[parentId].forEach(function(child_article){
  					if(!translationMap[child_article])
  						translationMap[child_article] = translationMap[parentId];
  				});
  			}
  		}
  	}
  	return articleIds.map(function(id){
  		if(translationMap[id])
  			return translationMap[id];
  		else
  			return id;
  	});
  },

  _countViews: function (gr,days,isVersioningInstalled) {
  	var kbv = new GlideAggregate("kb_use");
  	if(isVersioningInstalled && !gr.summary.nil()){
  		// Count views happened on all versions less than or equal to this version.
  		kbv.addQuery("article.summary",gr.summary);
  		kbv.addQuery("article.sys_created_on","<=",gr.sys_created_on);
  	}
  	else
  		kbv.addQuery("article",gr.sys_id);

  	kbv.addQuery("viewed", "true");
  	if (days > 0)
  		kbv.addQuery("sys_created_on", ">=", gs.daysAgoStart(days));
  	kbv.addAggregate("SUM", "times_viewed");
  	kbv.setGroup(false);
  	kbv.setCategory("list");
  	kbv.query();

  	var views = 0;
  	if (kbv.next())
  		views = kbv.getAggregate("SUM", "times_viewed") - 0;
  	return views;
  },
  	
  _updateKB: function(sysId, field, value) {
  	var dbu = new GlideDBUpdate("kb_knowledge");
  	dbu.setPrimaryKeyValue(sysId);
  	dbu.setValue(field, value + "");
  	dbu.setSystem(true);
  	dbu.execute();
  },
  
  getWordAddinContent: function() {
  	var jr = new GlideJellyRunner();
  	return jr.runMacro("sn_km_word_addin");
  },
  
  /**
   * Is the action performed in Workspace or platform.
   **/
  isWorkspaceURI: function(){
  	return gs.action.getGlideURI().toString().contains('api/now');
  },
  
  /**
   * Checks if user has access to Knowledge Article or not
   * @param viewAsUserSysId: sysId of view as user
   * @param articleId: sysId of article
   * @return Boolean: false if user does not have access otherwise true.
   */
  isViewAsUserHasArticleAccess: function(viewAsUserSysId, articleId) {
  	return new SNC.KnowledgeHelper().canUserReadArticle(viewAsUserSysId, articleId);
  },

  /**
   * This function returns whether the user can provide a replacement article to a retiring article or not.
   *
   * @return {boolean}
   **/
  canProvideReplacementArticle: function() {
  	return gs.getProperty('glide.knowman.enable_article_replacement_on_retire') === 'true';
  },
  
  /**
   * This function returns whether the Knowledge Blocks plugin is active or not.
   *
   * @return {boolean}
   **/
  isBlocksPluginActive: function() {
  	return new GlidePluginManager().isActive(this.BLOCKS_PLUGIN);
  },
  
  
  /**
   * returns true if guest user has read access to knowledge article after publishing
   * @param kbId
   * @return boolean
   */
  checkGuestUserHasAccess: function(kbId) {
  	return this._knowledgeHelper.canUserRead("guest",kbId);
  },

  /**
   * returns true if scheduled publish date is valid
   * @param articleGR
   * @return boolean
   */
  validateScheduledPublishDate: function(articleGR) {
      // Create a GlideDateTime object for the today date
  	var todayDate = new GlideDateTime();
  	//Create a GlideDateTime object for the scheduled publish date
  	var publishDate = new GlideDateTime(articleGR.getValue('scheduled_publish_date'));
  	//the difference between today date and scheduled publish date as string
  	var diffInDates = GlideDateTime.subtract(todayDate, publishDate);
  	//date difference in Numeric value in Millisecond
  	var diffInMillisecond = diffInDates.getNumericValue();
  	//The difference between dates must be less than 24 hrs
  	return diffInMillisecond < 86400000;
  },
  type: "KBCommonSNC"
};

Sys ID

39e84ab2d72121004792a1737e610306

Offical Documentation

Official Docs: