Name

global.TopicList

Description

No description available

Script

var TopicList = Class.create();
TopicList.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

  childTopics: function(taxonomy, topic) {
      var childTopics = this.getChildTopics(taxonomy, topic, false);
      return 'taxonomy=' + taxonomy + '^sys_idNOT IN' + childTopics.join();
  },

  getChildTopics: function(taxonomy, topic, activeRecordsOnly) {
      var taxonomyGr = new GlideRecord('taxonomy');
      taxonomyGr.get(taxonomy);
      if (!taxonomyGr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have access to the given taxonomy and its topic", [gs.getUserID()]));
          return;
      }
      var gr = new GlideRecord('topic');
      gr.addQuery('sys_id', '!=', topic);
      gr.addQuery('taxonomy', taxonomy);
      if (activeRecordsOnly) {
          gr.addActiveQuery();
      }
      gr.addNotNullQuery('parent_topic');
      gr.query();
      this.childTopics = [];
      this.topicList = {};
      this.childTopics.push(topic);
      while (gr.next()) {
          var parentTopic = gr.getValue('parent_topic');
          if (!this.topicList.hasOwnProperty(parentTopic))
              this.topicList[parentTopic] = [];
          this.topicList[parentTopic].push(gr.getValue('sys_id'));
      }
      this.getChildren(topic);
      return this.childTopics;
  },

  getChildren: function(parent) {
      if (!this.topicList.hasOwnProperty(parent))
          return;
      var children = this.topicList[parent];
      this.childTopics = this.childTopics.concat(children);
      for (var i = 0; i < children.length; i++)
          this.getChildren(children[i]);
  },

  modifyChildTopics: function() {
      var taxonomy = this.getParameter('sysparm_taxomomySysId');
      var topic = this.getParameter('sysparm_topicSysId');
      var isDeactivation = this.getParameter('sysparm_deactivate');
      var childTopics = this.getChildTopics(taxonomy, topic, isDeactivation);

      if (!(gs.hasRole("taxonomy_admin") || new global.TaxonomyUtil().isTaxonomyManager() || new global.TopicPermissionUtil().isAnyTopicManager())) {
          gs.info(gs.getMessage("User {0} does not have write access for the given topic and its children", [gs.getUserID()]));
          return false;
      }
      // If parent is not active during activation, Stop!
      if (!isDeactivation && !this.isParentActive(topic)) {
          return "false";
      }

      var gr = new GlideRecord('topic');
      gr.addQuery('sys_id', childTopics);
      gr.query();

      while (gr.next()) {
          gr.active = !isDeactivation;
          gr.update();
      }
      return "true";
  },

  /*
   * Deactivates/Activates all the topics under a given taxonomy, if the taxonomy is deactivated/activated
   * @param- taxonomySysId
   * @param- deactivationParam
   */

  modifyTaxonomyTopics: function() {
      var taxonomy = this.getParameter('sysparm_taxomomySysId');
      var deactivationParam = this.getParameter('sysparm_deactivate');
      var isDeactivation = deactivationParam == 'true';
      if (!(gs.hasRole("taxonomy_admin") || new global.TaxonomyUtil().isTaxonomyManager())) {
          gs.info(gs.getMessage("User {0} does not have write access for the given taxonomy and its topics", [gs.getUserID()]));
          return;
      }
      var topicGr = new GlideRecord('topic');
      topicGr.addQuery('taxonomy', taxonomy);
      topicGr.addQuery('active', isDeactivation);
      topicGr.query();
      topicGr.setValue('active', !isDeactivation);
      topicGr.updateMultiple();
  },

  /*
   * Moves a topic and its children to another taxonomy/parent topic
   * Deletes all those featured content from the parent topic tree, where the content was featured from curent topic and its children.
   * For example in a normal heirarchy of topics HR -> HR1 -> HR2 -> HR3, where HR is the topmost topic in heirarchy, 
   * if we move HR2 to another taxonomy/parent topic, then the content from HR2 and HR3 which are featured on HR or HR1 will be deleted
   * Activates or deactivates the topic tree (current topic and its children) on the basis of toggling of "active" field or 
   * on the basis of destination taxonomy/parent topic while moving topic to another taxonomy/parent topic
   */
  moveTopicTree: function() {
      var topic = this.getParameter('sysparm_topicSysId');
      var topicState = this.getParameter('sysparm_topicState');
      var taxonomy = this.getParameter('sysparm_taxomomySysId');
      var parentTopic = String(this.getParameter('sysparm_parentTopicId').toString());
      var isTaxonomyChanged = this.getParameter('sysparm_isTaxonomyChanged');
      var isActiveToggled = this.getParameter('sysparm_isActiveToggled');
      var topicGr = new GlideRecord('topic');
      topicGr.get(topic);
      if (!topicGr.isValidRecord()) {
          gs.info("topic provided is not valid");
          return;
      }
      if (!topicGr.canWrite()) {
          gs.info(gs.getMessage("User {0} does not have access to modify the given topic", [gs.getUserID()]));
          return;
      }

      if (!(gs.hasRole("taxonomy_admin") || new global.TaxonomyUtil().isTaxonomyManager()) && parentTopic == '') {
          gs.info(gs.getMessage("User {0} cannot create a root topic", [gs.getUserID()]));
          return; 
      }
      
      var topicParent = new GlideRecord('topic');
      topicParent.get(parentTopic);
      
      var applyParentTemplate = false;
      var parentTemplate;
      
      if(topicParent.isValidRecord() && topicParent.getValue('apply_to_child_topics') == true) {
          applyParentTemplate = true;
          parentTemplate = topicParent.getValue('template');
      }

      var childTopics = this.getChildTopics(topicGr.taxonomy, topic, false);

      this.deleteFeaturedContent(topic);

      var statusMap = this._getStatusChange(topic, parentTopic, taxonomy, isActiveToggled, topicState);
      var shouldUpdateTreeState = statusMap['shouldUpdateTreeState'];
      var finalTreeState = statusMap['finalTreeState'];
      
      var topicCurrent = topicGr;

      topicGr = new GlideRecord('topic');
      topicGr.addQuery('sys_id', childTopics);
      topicGr.query();
              
      if(applyParentTemplate) {
          topicGr.setValue("template", parentTemplate);
      }
      if (isTaxonomyChanged)
          topicGr.setValue("taxonomy", taxonomy);
      if (shouldUpdateTreeState)
          topicGr.setValue("active", finalTreeState);
      
      // Paremeters: oldParent UC, newParentUC, currentUC
      var newParentTopicManager = topicParent.isValidRecord() ? topicParent.topic_manager : "";
      var newParentTopicContirbutor = topicParent.isValidRecord() ? topicParent.topic_contributor : "";
      
      var topicManager = this.getUnifiedUserCriteria(topicCurrent.parent_topic.topic_manager, newParentTopicManager, topicCurrent.topic_manager);
      var topicContributor = this.getUnifiedUserCriteria(topicCurrent.parent_topic.topic_contributor, newParentTopicContirbutor, topicCurrent.topic_contributor);
      topicCurrent.setValue('parent_topic', parentTopic);
      topicCurrent.setValue('topic_manager', topicManager);
      topicCurrent.setValue('topic_contributor', topicContributor);
      topicCurrent.update();

      topicGr.updateMultiple();
      return finalTreeState;
  },
  /*
   * Returns the final state of the topic and tree and whether the state of the whole topic tree should be updated or 
   * not while moving topic to another taxonomy/parent topic
   * For eg. if new taxonomy/parent topic is inactive, then it returns shouldUpdateTreeState: true and
   * finalTreeState: false because we need an inactive topic/taxonomy cannot have active child topics
   * If while moving a topic to an active taxonomy/parent topic, the "active" field is toggled 
   * then the state of the whole topic tree should be the new toggled state.
   * Based on the combination of three input parameters -> state of parent topic, state of taxonomy, isActiveToggled
   */
  _getStatusChange: function(topic, parentTopic, taxonomy, isActiveToggled, topicState) {
      var parentTopicExists = true;
      if (!parentTopic)
          parentTopicExists = false;

      var parentState;

      if (parentTopicExists) {
          var parentTopicGr = new GlideRecord('topic');
          parentTopicGr.get(parentTopic);
          parentState = parentTopicGr.active;
      } else {
          var taxonomyGr = new GlideRecord('taxonomy');
          taxonomyGr.get(taxonomy);
          parentState = taxonomyGr.active;
      }
      var finalTreeState = topicState;
      var shouldUpdateTreeState = isActiveToggled;
      if (!parentState) {
          shouldUpdateTreeState = true;
          finalTreeState = false;
      }
      var statusMap = {
          "shouldUpdateTreeState": shouldUpdateTreeState,
          "finalTreeState": finalTreeState
      };
      return statusMap;
  },
  /*
   * Deletes all those featured content from the parent topic tree, where the content was featured from curent topic and its children. 
   * For example in a normal heirarchy of topics HR -> HR1 -> HR2 -> HR3, where HR is the topmost topic in heirarchy, 
   * if we move HR2 to another taxonomy/parent topic, then the content from HR2 and HR3 which are featured on HR or HR1 will be deleted
   */
  deleteFeaturedContent: function(topic) {
      var parentTopicList = [];
      var topicGr = new GlideRecord('topic');
      topicGr.get(topic);
      if (!topicGr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have access to the give topic", [gs.getUserID()]));
          return;
      }
      while (!topicGr.getElement('parent_topic').nil()) {
          var parent = topicGr.parent_topic;
          parentTopicList.push(parent);
          topicGr = new GlideRecord('topic');
          topicGr.get(parent);
      }
      var featContentList = [];
      parentTopicList.forEach(function(parentTopic) {
          var featGr = new GlideRecord('featured_content');
          featGr.addQuery('topic', parentTopic);
          featGr.addQuery('connected_content.topic', 'NOT IN', parentTopicList);
          featGr.query();
          featGr.deleteMultiple();
      });
  },

  isParentActive: function(topic) {
      topic = topic ? topic : this.getParameter('sysparm_topicSysId');
      var result = {
          "active": false,
          "type": ""
      };
      var gr = new GlideRecord('topic');
      gr.get(topic);
      if (!gr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have access to the given topic", [gs.getUserID()]));
          return;
      }
      var parent = gr.getValue('parent_topic');
      if (parent) {
          var parentTopicGr = gr.parent_topic.getRefRecord();
          result.type = "topic";
          result.active = parentTopicGr.getDisplayValue('active') === "true";
      } else {
          var taxonomy = gr.taxonomy.getRefRecord();
          result.type = "taxonomy";
          result.active = taxonomy.getDisplayValue('active') === "true";
      }

      return JSON.stringify(result);
  },

  deleteTopics: function() {
      var taxonomySysId = this.getParameter('sysparm_taxonomy_sysid');
      var topicSysId = this.getParameter('sysparm_topic_sysid');

      if (taxonomySysId) {
          return this.deleteTaxonomyTopics(taxonomySysId);
      }

      return this.deleteChildTopics(topicSysId);
  },

  deleteTaxonomyTopics: function(taxonomy) {
      if (!(gs.hasRole("taxonomy_admin") || new global.TaxonomyUtil().isTaxonomyManager() || new global.TopicPermissionUtil().isAnyTopicManager())) {
          gs.info(gs.getMessage("User {0} does not have rights to delete the topics of the given taxonomy", [gs.getUserID()]));
          return;
      }
      var gr = new GlideRecord('topic');
      gr.addQuery('taxonomy', taxonomy);
      gr.query();
      while (gr.next()) {
          var topic = gr.getUniqueValue();
          this.deleteConnectedContent(topic);
          this.deleteConnectedCategory(topic);
          this.deleteUnconnectedCategoryContent(topic);
          gr.deleteRecord();
      }
      return "true";
  },

  deleteChildTopics: function(topic) {
      if (!(gs.hasRole("taxonomy_admin") || new global.TaxonomyUtil().isTaxonomyManager() || new global.TopicPermissionUtil().isAnyTopicManager())) {
          gs.info(gs.getMessage("User {0} does not have rights to delete the given topic and its children", [gs.getUserID()]));
          return;
      }
      var gr = new GlideRecord('topic');
      gr.get(topic);
      var taxonomy = gr.getValue('taxonomy');
      var childTopics = this.getChildTopics(taxonomy, topic);

      //delete all child topics
      var cGr = new GlideRecord('topic');
      cGr.addQuery('sys_id', childTopics);
      cGr.query();
      while (cGr.next()) {
          this.deleteConnectedContent(cGr.getUniqueValue());
          this.deleteConnectedCategory(cGr.getUniqueValue());
          this.deleteUnconnectedCategoryContent(cGr.getUniqueValue());
          cGr.deleteRecord();
      }
      return "true";
  },
  
    deleteConnectedCategory: function(topic) {
      var topicGr = new GlideRecord('topic');
      topicGr.get(topic);
      if (!topicGr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have rights to delete the connected content of the give topic", [gs.getUserID()]));
          return;
      }
      var m2mGr = new GlideRecord('m2m_connected_category');
      m2mGr.addQuery('topic', topic);
      m2mGr.query();
      m2mGr.deleteMultiple();
  },
  
    deleteUnconnectedCategoryContent: function(topic) {
      var topicGr = new GlideRecord('topic');
      topicGr.get(topic);
      if (!topicGr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have rights to delete the connected content of the give topic", [gs.getUserID()]));
          return;
      }
      var m2mGr = new GlideRecord('unconnected_category_content');
      m2mGr.addQuery('topic', topic);
      m2mGr.query();
      m2mGr.deleteMultiple();
  },

  deleteConnectedContent: function(topic) {
      var topicGr = new GlideRecord('topic');
      topicGr.get(topic);
      if (!topicGr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have rights to delete the connected content of the give topic", [gs.getUserID()]));
          return;
      }
      var m2mGr = new GlideRecord('m2m_connected_content');
      m2mGr.addQuery('topic', topic);
      m2mGr.query();
      m2mGr.deleteMultiple();
  },

  /*
   * Returns all the accessible taxonomies for a logged in user
   * @return Array
   */
  getAccessibleTaxonomies: function() {
      var gr = new GlideRecord('taxonomy');
      gr.query();
      var taxonomies = [];
      while (gr.next()) {
          if (gs.hasRole("admin,taxonomy_admin") || sn_uc.UserCriteriaLoader.userMatches(gs.getUserID(), [gr.getValue('managers')]) || new global.TaxonomyUtil().isTaxonomyContributor(gr.sys_id))
              taxonomies.push(gr.getUniqueValue());
      }

      return taxonomies;
  },

  /**
   * Returns accessible topics
   */
  getAccessibleTopics: function() {
      var topics = [];
      var taxonomy = this.getAccessibleTaxonomies();

      var gr = new GlideRecord('topic');
      gr.addQuery('taxonomy', taxonomy);
      gr.query();

      while (gr.next())
          topics.push(gr.getUniqueValue());

      return topics;
  },
  /**
   * Returns accessible topics of a taxonomy
   */
  getTaxonomyTopics: function() {
      var taxonomyId = this.getParameter('sysparm_taxonomyId');
      var taxonomyGr = new GlideRecord("taxonomy");
      taxonomyGr.get(taxonomyId);
      if (!taxonomyGr.isValidRecord()) {
          gs.info(gs.getMessage("taxonomyId provided is not valid"));
          return;
      }
      if (!taxonomyGr.canRead()) {
          gs.info(gs.getMessage("User {0} does not have access to the given taxonomy", [gs.getUserID()]));
          return;
      }
      var taxonomyUtil = new TaxonomyUtil();
      var topicGr = taxonomyUtil.getTaxonomyTopics(taxonomyId, true);
      var topic = [];
      topicGr.query();
      while (topicGr.next()) {
          var topicObj = {
              "id": topicGr.getUniqueValue(),
              "name": topicGr.getValue("name")
          };
          if (topicGr.canRead())
              topic.push(topicObj);
      }
      return JSON.stringify(topic);
  },
  /**
   * Copies taxonomy and its topic, contributors, connected content and featured content.
   */
  copyTaxonomyTree: function() {
      var currentTaxonomyId = this.getParameter('sysparm_currentTaxonomyId');
      var newTaxonomyName = this.getParameter('sysparm_newTaxonomyName');
      var newTaxonomyId = '';
      var gr = new GlideRecord('taxonomy');
      gr.get(currentTaxonomyId);
      if (gr.isValidRecord() && gr.canRead())
          newTaxonomyId = new global.TaxonomyCopyUtil().copyTaxonomyTree(currentTaxonomyId, newTaxonomyName);
      return newTaxonomyId;
  },

  /**
   * Copies a topic tree and its contributors, connected content and featured content.
   */
  copyTopicTree: function() {
      var currentTopicId = this.getParameter('sysparm_currentTopicId');
      var newTaxonomyId = this.getParameter('sysparm_newTaxonomyId');
      var newParentTopicId = this.getParameter('sysparm_newParentTopicId');
      var newTopicId = '';
      var taxonomyGr = new GlideRecord('taxonomy');
      taxonomyGr.get(newTaxonomyId);
      if (taxonomyGr.isValidRecord() && taxonomyGr.canRead())
          newTopicId = new global.TaxonomyCopyUtil().copyTopicTree(currentTopicId, newTaxonomyId, newParentTopicId);
      return newTopicId;
  },

  isTaxonomyManager: function() {
      var taxonomyId = this.getParameter('sysparm_taxonomy_id');
      var taxonomyGr = new GlideRecord("taxonomy");
      taxonomyGr.get(taxonomyId);
      var taxonomyManagers = taxonomyGr.getValue("managers") || '';
      return sn_uc.UserCriteriaLoader.userMatches(gs.getUserID(), [taxonomyManagers]);
  },
  
  /**
  * Gets the final unified user criteria for topic manager and contributor
    (currentCriteria - oldParentCriteria) + newParentCriteria
  */

  getUnifiedUserCriteria: function(oldParentCriteria, newParentCriteria, currentCriteria) {
      var arrayUtil = new ArrayUtil();
      var oldParentCriteriaList = oldParentCriteria.toString().split(",");
      var newParentCriteriaList = newParentCriteria.toString().split(",");
      var currentCriteriaList = currentCriteria.toString().split(",");
      
      var diffCriteriaList = arrayUtil.diff(currentCriteriaList, oldParentCriteriaList);
      return arrayUtil.union(newParentCriteriaList, diffCriteriaList);
  },

  type: 'TopicList'
});

Sys ID

cbe7f546532200108271ddeeff7b1244

Offical Documentation

Official Docs: