
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
05-23-2018 01:28 PM
Our Service Portal is very public by design. We just discovered that searching in the Typeahead Search widget will not yield results from the Service Catalog unless you're logged in. How do I fix that so it will show catalog item matches regardless if you're logged in? In cloning the widget I don't see where that's being set.
You may be asking yourself, "why show something that don't have access to anyway?" Our strategy is to heavily use Content Items to populate our Service Catalog so the SC is the full picture of the services we provide whether you have to request them or not. Requestable items are visible but a user must login to order the service (see graphic).
Solved! Go to Solution.
- Labels:
-
Service Portal

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-04-2019 11:38 AM
Yes! Sorry I never came back to update this issue. Here is the Typeahead Search code that works for me:
HTML
<form ng-submit="c.submitSearch()">
<input type="hidden" name="id" value="search"/>
<input type="hidden" name="t" value="{{data.searchType}}"/>
<div class="input-group input-group-{{::c.options.size}}">
<!-- uses ui.bootstrap.typeahead -->
<!-- replaced `uib-typeahead="item for item in c.getResults($viewValue)"`
in the element below to fix the [object Object] issue -->
<input id="search-input"
name="q" type="text" placeholder="{{::c.options.title}}" ng-model="c.selectedState"
ng-model-options="{debounce: 250}" autocomplete="off"
uib-typeahead="item as item.label for item in c.getResults($viewValue)"
typeahead-focus-first="false"
typeahead-on-select="c.onSelect($item, $model, $label)"
typeahead-template-url="sp-typeahead-auburn.html"
typeahead-popup-template-url="sp-typeahead-popup-auburn.html"
class="form-control input-typeahead"
title="{{::c.options.title}}"
role="textbox"
aria-label="{{::c.options.title}}" tabindex="0" aria-haspopup="true">
<span class="input-group-btn">
<button name="search" type="submit" class="btn btn-{{::c.options.color}}"
title="{{::c.options.title}}"
aria-label="{{::c.options.title}}">
<i ng-if="::c.options.glyph" class="fa fa-{{::c.options.glyph}}"></i>
</button>
</span>
</div>
</form>
CSS
ul.dropdown-menu {
min-width: 100%;
border-radius: 0px 0px 4px 4px;
margin:0px;
}
ul.dropdown-menu a.ta-item {
line-height: 20px;
}
ul.dropdown-menu li h4 {
background-color:#efefef;
margin-left:0;
padding:10px;
}
span.ta-item-header{
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
-o-user-select: none;
user-select: none;
pointer-events:none;
}
ul.dropdown-menu i.ta-icon, i.ta-img {
width: 20px;
height: 20px;
background-size: contain;
display: inline-block;
background-repeat: no-repeat;
background-position: center center;
text-align: center;
line-height: 20px;
float:left;
margin-right: 8px;
}
input[name="q"] {
color: black;
}
.ta-item-header h4 {
margin-left: 7px;
}
.no-result {
margin-left: 7px;
padding-left: 20px;
}
Client Script
function ($filter, $location,spAriaUtil, $window) {
var c = this;
c.options.glyph = c.options.glyph || 'search';
c.options.title = c.options.title || c.data.searchMsg;
c.options.color = c.options.color || "default";
c.onSelect = function($item, $model, $label) {
if ($item.target)
window.open($item.url, $item.target);
else
$location.url($item.url);
};
c.getResults = function(query) {
return c.server.get({q: query}).then(function(response) {
var a = $filter('orderBy')(response.data.results, '-order');
var resultCount = $filter('limitTo')(a, c.data.limit).length;
spAriaUtil.sendLiveMessage(parseInt(resultCount) + " " +
c.data.resultMsg + " " +
c.data.navigationMsg +
getNavigationKeys());
return $filter('limitTo')(a, c.data.limit);
});
}
c.submitSearch = function() {
if (c.selectedState) {
$location.search({
id: 'search',
t: c.data.searchType,
q: c.selectedState
});
console.log(c.selectedState);
}
}
function getNavigationKeys() {
if($window.navigator.userAgent.indexOf("Mac OS X") > -1)
return '⌘';
return 'Control';
}
}
Server Script
(function() {
//var order = 0;// ordering articles and catalog
data.searchType = $sp.getParameter("t");
data.results = [];
data.searchMsg = gs.getMessage("Search");
data.limit = options.limit || 15;
var order = data.limit;
var textQuery = '123TEXTQUERY321';
if (!input)
return;
data.q = input.q;
getCatalogItems();
getKnowledge();
// add in additional search tables from sp_search_groups
var portalGR = $sp.getPortalRecord();
var portalID = portalGR.getDisplayValue('sys_id');
var sg = GlideRecord('sp_search_group');
sg.addQuery('sp_portal',portalID);
sg.addQuery('active',true);
sg.orderBy('order');
sg.query();
while (sg.next())
addSearchTable(sg);
// typeahead search generates multiple "Your text query contained only
// common words..." msgs, we don't want them
gs.flushMessages();
function addSearchTable(sg) {
var table = sg.getValue('name');
var condition = sg.getValue('condition');
var gr = GlideRecord(table);
if (condition)
gr.addEncodedQuery(condition);
gr.addQuery(textQuery, data.q);
gr.query();
var searchTableCount = 10; // serach count
var header = {}; // begin header label for catalog and article
header.type="header";
header.label = "<h4>"+sg.tables.getDisplayValue()+"</h4>";
header.score = 0;
header.order=order;
header.text= sg.tables.getDisplayValue();
data.results.push(header);
//order++; // end
order--;
while (gr.next() && searchTableCount < data.limit) {
var rec = {};
rec.type = "rec";
rec.table = table;
rec.sys_id = gr.getDisplayValue('sys_id');
rec.page = sg.getDisplayValue('sp_page');
if (!rec.page)
rec.page = "form";
rec.label = "<b>"+table+"</b> "+gr.getDisplayValue(); // displaying label
rec.score = parseInt(gr.ir_query_score.getDisplayValue());
rec.order=order; // order begin
data.results.push(rec);
searchTableCount++;
//order++; // order end
order--;
}
}
function getKnowledge() {
var kb = new GlideRecord('kb_knowledge');
kb.addQuery('workflow_state', 'published');
kb.addQuery('valid_to', '>=', (new GlideDate()).getLocalDate().getValue());
kb.addQuery(textQuery, data.q);
//kb.addQuery('kb_knowledge_base', $sp.getDisplayValue('kb_knowledge_base'));
kb.addQuery('kb_knowledge_base.title', 'Information Technology');
kb.query();
var kbCount = 10; // count list
var header = {}; // begin header label for article
header.type = "header";
header.label = "<h4>Knowledge Base</h4>";
header.score = 10;
header.order = order;
header.text = "Knowledge Article";
header.hasItems = kb.hasNext();
data.results.push(header);
//order++; //order end
order--;
if (!kb.hasNext()) {
header.label += '<i class="no-result">No results of this type.</i>';
}
while (kb.next() && kbCount < data.limit) {
if (!$sp.canReadRecord(kb))
continue;
var article = {};
article.type = "kb";
$sp.getRecordDisplayValues(article, kb, 'sys_id,number,short_description,description,picture,published,text');
if (!article.text)
article.text = "";
article.glyph = 'file-text-o';
article.text = $sp.stripHTML(article.text);
article.text = article.text.substring(0, 200);
article.score = parseInt(kb.ir_query_score.getDisplayValue());
article.label = article.short_description; // highlight KB in bold fornt of KB article
article.order = order; // order begin
article.url = '/it?id=' + 'kb_article' + '&sys_id=' + article.sys_id;
data.results.push(article);
kbCount++;
//order++; // order ends
order--;
}
}
function getCatalogItems() {
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', $sp.getValue('sc_catalog'));
sc.query();
var catCount = 10; // limit the search to 5
var header = {}; // begin of header and label for catalog
header.type = "header";
header.label = "<h4>Service Catalog</h4>";
header.score = 10;
header.order = order;
header.text = "Service Catalog";
data.results.push(header);
//order++;
order--;
if (!sc.hasNext()) {
header.label += '<i class="no-result">No results of this type.</i>';
}
while (sc.next() && catCount < data.limit) {
if (!$sp.canReadRecord(sc))
continue;
var item = {};
item.fullResult = $sp;
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,description,picture,price,sys_id');
item.score = parseInt(sc.ir_query_score.getDisplayValue());
item.order = order; //order begin
item.label = item.name; // highlight SC in bold infront of catlog name ("Service Catalog "+)
var urlid;
if (sc.getRecordClassName() === 'sc_cat_item_guide')
urlid = 'sc_cat_item_guide';
else
urlid = 'sc_cat_item'
item.url = '/it?id=' + urlid + '&sys_id=' + item.sys_id;
data.results.push(item);
catCount++;
//order++; // order ends
order--;
}
}
function getQuestions() {
var questionGR = new GlideRecord("kb_social_qa_question");
questionGR.addActiveQuery();
questionGR.addQuery(textQuery, data.q);
questionGR.query();
var qCount = 0;
var header = {}; // begin of header for questions
header.type = "header";
header.label = "<h4>Questions</h4>";
header.score = 0;
header.order = order;
header.text = "Questions";
data.results.push(header);
//order++;
order--;
while (questionGR.next() && qCount < data.limit) {
if (!$sp.canReadRecord(questionGR))
continue;
var question = {};
question.type = "qa";
$sp.getRecordDisplayValues(question, questionGR, 'question,question_details,sys_created_on,sys_id');
question.text = (question.question_details) ? $sp.stripHTML(question.question_details) : "";
question.text = question.text.substring(0, 200);
question.label = "<b>Question</b> " + question.question;
question.score = 0;
question.order = order; //order begin
data.results.push(question);
qCount++;
//order++; // order ends
order--;
}
}
})();
Link Function
function(scope) {
var lazyLoader = $injector.get("lazyLoader");
lazyLoader.putTemplates(scope.data.typeaheadTemplates);
}
Template - sp-typeahead-popup-auburn.html
<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+'px', left: position().left+'px'}" role="listbox" aria-hidden="{{!isOpen()}}">
<li role='option' aria-hidden='true' style='display: none'></li>
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index)}"
ng-mouseenter="selectActive($index)"
ng-click="match.model.type === 'header' ? '' : selectMatch($index, $event)"
role="option" id="{{::match.id}}">
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
</li>
</ul>
Template - sp-typeahead-auburn.html
<!-- replaced instances `match.label.label` with `match.label` [object Object] issue,
since we changed the value of the `uib-typeahead` attribute in the HTML template -->
<!-- added to show category headers as simple spans instead of a elements -->
<span class="ta-item-header ta-item ng-scope"
ng-if="match.model.type === 'header'" ng-bind-html="match.label">
</span>
<!--<a class="ta-item" ng-href="{{match.model.target != '_blank' ? match.model.url : ''}}" target="{{match.model.target}}">-->
<a ng-if="match.model.type !== 'header'" class="ta-item" ng-href="{{ match.model.url }}" target="{{match.model.target}}">
<div ng-if="!match.model.templateID">
<!-- if service catalog and has icon -->
<i class="ta-img ng-scope" ng-if="match.model.type.includes('sc') && match.model.picture.length > 0"
ng-style="{'background-image':'url({{match.model.picture}})'}"></i>
<!-- if service catalog and has no icon -->
<i class="ta-img ng-scope ta-icon fa fa-file-text-o" ng-if="match.model.type.includes('sc') && match.model.picture.length === 0"></i>
<!-- if knowledge base -->
<i class="ta-img ng-scope ta-icon fa fa-file-text-o" ng-if="match.model.type.includes('kb')"></i>
<span ng-bind-html="match.label | uibTypeaheadHighlight:query"></span>
</div>
<div ng-if="match.model.templateID" ng-include="match.model.templateID"></div>
</a>

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
05-25-2018 07:00 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
05-25-2018 07:19 AM
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-09-2019 02:59 AM

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-04-2019 10:53 AM
Hey Seth,
Did you ever get this sorted out? We're in the middle of a huge push to get CSM up and running and we're hitting the same limitation.