We're reclaiming inactive PDIs to keep them available for active builders. Learn what's changing, who's affected, and how to protect your work. Read More

dvp
Mega Sage

Finally I got sometime to finish off the Multiple Catalog series. In this part let's delve into search and breadcrumbs.

As we know the OOB Homepage and typeahead Search widget supports only single catalog. In order to support the search for multiple catalogs, we need to clone typeahead search widget and give an unique name to widget id.

And in server script getCatalogItems function need to updated.

function getCatalogItems() {

  var catalogs = [];

  var sc_cat = new GlideRecordSecure('sc_catalog');

  sc_cat.addQuery('active', true);

  sc_cat.query();

  while(sc_cat.next())

  {

  catalogs.push(sc_cat.sys_id.toString());

  }

  var sc = new GlideRecord('sc_cat_item');

  sc.addQuery(textQuery, data.q);

  sc.addQuery('active',true);

  sc.addQuery('no_search', '!=', true);

  sc.addQuery('visible_standalone', true);

  sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');

  sc.addQuery('sc_catalogs', catalogs);

  sc.query();

  var catCount = 0;

  while (sc.next() && catCount < data.limit) {

  if (!$sp.canReadRecord(sc))

  continue;

  var item = {};

  if (sc.getRecordClassName() == "sc_cat_item_guide")

  item.type = "sc_guide";

  else if (sc.getRecordClassName() == "sc_cat_item_content") {

  var gr = new GlideRecord('sc_cat_item_content');

  gr.get(sc.getUniqueValue());

  $sp.getRecordValues(item, gr, 'url,content_type,kb_article');

  item.type = "sc_content";

  }

  else

  item.type = "sc";

  $sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id');

  item.score = parseInt(sc.ir_query_score.getDisplayValue());

  item.label = item.name;

  data.results.push(item);

  catCount++;

  }

}

Once this widget is created, use the new widget ID in homepage search widget

data.typeAheadSearch = $sp.getWidget('new-typeahead-search', typeAheadSearchOpts);

Once the widget is successfully created use the new typeahead search widget across all the pages.

Breadcrumbs

To point the correct breadcrumbs we have update the client controller in following widgets

  • Multi SC categories widget that was created earlier
  • SC Category Page widget
  • SC catalog Item widget

Here is the sample script for multi SC categories client controller

var bc = [{label: 'Catalogs', url: '?id=multi_catalog'}];

if ($scope.data.sc_catalog) {

bc[bc.length] = {label: $scope.data.catalog_name, url: '#'};

}        

$rootScope.$broadcast('sp.update.breadcrumbs', bc);

find_real_file.png

find_real_file.png

This concludes multiple catalog functionality in Service Portal.

Previous Blogs in this series

Portal diaries: Service Portal — Multiple Catalogs (Part 1)

Portal diaries: Service Portal — Multiple Catalogs (Part 2)

9 Comments
conradw
Kilo Contributor

Thanks for a great series of articles. I'm trying to update the type-ahead search widget, but I don't find the getCatalogItems function in the server script on the OOTB type-ahead-search widget.



Any ideas why that function is not in the widget?



Thanks for your help.


dvp
Mega Sage

If I'm correct you are on Istanbul version. Am I?



Earlier is Helsinki all the search queries resided in type-ahead search widget. In Istanbul, SN re designed the way search is handled using Search Source feature(sp_search_source).



find_real_file.png


neharao
Giga Contributor

hi,



I am in Istanbul Version and found the same issue. I followed your post to show multiple catalogs in my service portal. I am almost there. Only the typeahead search and breadcrumb i am stuck. So what needs to be done for this sp_search_source table. i did nt get you ,can you please elaborate, where we should modify the getCatalogItems function. Kindly help



One more thing in breadcrumb if i traverse forward everything works fine. If i traverse in reverse order in breadcrumb i am not seeing the desired output.



dvp
Mega Sage

Use the following code in Data fetch script field in service catalog search source



(function(query) {


  var results = [];


  //Here goes the logic. Compute results however you want!


  if (!gs.isLoggedIn())


  return results;




  var gr = new GlideRecordSecure('sc_catalog'); // enforeces acl operations


    gr.addQuery('active', true);


    gr.query();


var catalog_list = [];



    while(gr.next()){


  catalog_list.push(gr.sys_id.toString());


    }


 


  var sc = new GlideRecord('sc_cat_item');


  sc.addQuery('123TEXTQUERY321', query);


  sc.addQuery('active',true);


  sc.addQuery('no_search', '!=', true);


  sc.addQuery('visible_standalone', true);


  sc.addQuery('sys_class_name', 'NOT IN', 'sc_cat_item_wizard');


  sc.addQuery('sc_catalogs', 'IN', catalog_list);


  sc.query();


  var catCount = 0;


  while (sc.next() && catCount < data.limit) {


  gs.log('Loop first')


  if (!$sp.canReadRecord(sc))


  continue;




  var item = {};


  item.type = "sc";


  item.page = "sc_cat_item";




  if (sc.getRecordClassName() == "sc_cat_item_guide")


  item.page = "sc_cat_item_guide";


  else if (sc.getRecordClassName() == "sc_cat_item_content") {


  var gr = new GlideRecord('sc_cat_item_content');


  gr.get(sc.getUniqueValue());


  $sp.getRecordValues(item, gr, 'url,content_type,kb_article');


  item.type = "sc_content";


  }


  else


  item.type = "sc";




  $sp.getRecordDisplayValues(item, sc, 'name,short_description,picture,price,sys_id,sys_class_name');


  item.score = parseInt(sc.ir_query_score.getDisplayValue());


  item.label = item.name;


  item.primary = item.name;




  //calculating URL


  if (item.type == "sc")


  item.url = '?id=' + item.page + '&sys_id=' + item.sys_id;


  if (item.type == "sc_content") {


  if (item.content_type == "kb")


  item.url = '?id=kb_article&sys_id=' + item.kb_article;


  else if (item.content_type == "external")


  item.target = '_blank';


  else


  item.url = '?id=sc_cat_item&sys_id=' + item.sys_id;


  }


  if (item.type == "sc_guide")


  item.url = '?id=sc_cat_item_guide&sys_id=' + item.sys_id;




  results.push(item);


  catCount++;


  }




  return results;


})(query);




I'm not sure what do you mean by traversing reverse order..


neharao
Giga Contributor

hi ,


Thanks for the reply. Multi catalog search worked. I am facing issue in breadcrumb thing. when i traverse for the first time i.e, home>catalogs>Technical catalog and Once i click on Emergency changes it is displaying as Home>Servicecatalog in breadcrumb which is shown as in my second screenshot. I think bread crumb is not updating properly.But again if I traverse from home>Catalogs >Technical Catalog and click on emergency changes it is updating the breadcrumb properly which is shown in my third screenshot. Only for the first time it is not updating the breadcrumb properly. Your help is appreciated.


find_real_file.png




find_real_file.png



find_real_file.png



Here is my breadcrumb code which i have written on Multi Sc CategoryPage widget on client controller section :



var bc = [{label: 'Catalogs', url: '?id=multi_catalog'}];


     


      if ($scope.data.sc_catalog) {


              bc[bc.length] = {label: $scope.data.catalog_name, url:$scope.data.catalog_url};


      }      


      if ($scope.data.category) {


              console.log('SCOPE DATA',$scope.data);


              bc[bc.length] = {label: $scope.data.category, url: '#'};


             


      }



      $rootScope.$broadcast('sp.update.breadcrumbs', bc);




Server script on Multi SC category Page widget goes here:




(function() {


      data.category_id = $sp.getParameter("sys_id");


      data.showPrices = $sp.showCatalogPrices();


      if (options && options.sys_id)


              data.category_id = options.sys_id;


     


     


      data.sc_catalog = $sp.getParameter('catalog_sys_id');


     


      var sc_cat = new GlideRecord('sc_catalog');


      sc_cat.addQuery('sys_id', data.sc_catalog);


      sc_cat.query();



      while(sc_cat.next())


      {


              data.catalog_name = sc_cat.title.toString();


              data.catalog_url = '?id=sc_home2&catalog_sys_id=' + sc_cat.sys_id.toString();



      }


     


//       data.sc_catalog_page = $sp.getDisplayValue("sc_catalog_page") || "oa_sc_home2";


      // Does user have permission to see this category?


      if (!$sp.canReadRecord("sc_category", data.category_id)) {


              data.error = "You do not have permission to see this category";


              return;


      }



      var cat = new GlideRecord('sc_category');


      cat.get(data.category_id);


      data.category = cat.getDisplayValue('title');


      //data.category_url = '?id=sc_category&sys_id='+ cat.sys_id +'&catalog_sys_id='+sc_cat.sys_id.toString();


      var items = data.items = [];


      var sc = new GlideRecord('sc_cat_item_category');


      if (data.category_id)


              sc.addQuery('sc_category', data.category_id);



      sc.addQuery('sc_cat_item.active',true);


      sc.addQuery('sc_cat_item.sys_class_name', 'NOT IN', 'sc_cat_item_wizard');


      sc.orderBy('sc_cat_item.order');


      sc.orderBy('sc_cat_item.name');


      sc.query();


      while (sc.next()) {


              // Does user have permission to see this item?


              if (!$sp.canReadRecord("sc_cat_item", sc.sc_cat_item.sys_id.getDisplayValue()))


                      continue;



              var item = {};


              var gr = new GlideRecord('sc_cat_item');


              gr.get(sc.sc_cat_item);


              gr = GlideScriptRecordUtil.get(gr).getRealRecord();


              $sp.getRecordDisplayValues(item, gr, 'name,short_description,picture,price,sys_id');


              item.sys_class_name = sc.sc_cat_item.sys_class_name + "";


              item.page = 'sc_cat_item';


              if (item.sys_class_name == 'sc_cat_item_guide')


                      item.page = 'sc_cat_item_guide';


              else if (item.sys_class_name == 'sc_cat_item_content') {


                      $sp.getRecordValues(item, gr, 'url,content_type,kb_article');


                      if (item.content_type == 'kb') {


                              item.page = 'kb_article';


                              item.sys_id = item.kb_article;


                      } else if (item.content_type == 'literal') {


                              item.page = 'sc_cat_item';


                      } else if (item.content_type == 'external')


                              item.target = '_blank';


              }



              items.push(item);


      }


})()


dvp
Mega Sage

Can you replace


data.sc_catalog = $sp.getParameter('catalog_sys_id');



with



var cat = new GlideRecord('sc_category');


cat.addQuery('sys_id', data.category_id);


cat.query();



if (cat.next()) {


        data.sc_catalog = cat.sc_catalog;


}




if(categryID == '' || categryID == null )


        data.sc_catalog = $sp.getParameter("catalog_sys_id");



and see if it works.


John Phifer
Tera Expert

Great solution, I'm just having trouble getting the breadcrumbs to work properly on my catalog item page. Every page leading up to this has all the correct levels, but this one drops out the catalog name:


find_real_file.png


Here's my breadcrumb code in the client controller:



if ($scope.data.sc_cat_item) {


var bc = [{label: 'Catalogs', url: '?id=multi_catalog'}];



//var bc = [{label: $scope.page.title, url: '?id=' + $scope.data.sc_catalog_page}];



if ($scope.data.catalog_name)


bc[bc.length] = {label: $scope.data.catalog_name, url: '?id=' + $scope.data.catalog_url};


if ($scope.data.category)


bc[bc.length] = {label: $scope.data.category.name, url: $scope.data.category.url};



bc[bc.length] = {label: $scope.data.sc_cat_item.name, url: '#'};


$rootScope.$broadcast('sp.update.breadcrumbs', bc);


spUtil.setSearchPage('sc');


}



I'm pretty sure I'm close, but missed something obvious.


dvp
Mega Sage

Were you able to find the issue? I tried the same in the my instance and i do not see any issues with the above code


mshah2
Tera Contributor

If you have not got it till now then you can see your modified SC category page widget. In that there will be ng-click inside the <a> tag. You can remove it or comment out the redirect part from the client script.

Mark Helpful if it helps!