"use strict"; /** * Created by viktor.shevchenko on 7/21/15. */ angular.module('resourceModule') .directive('rsKaSearch', ['ticketService', 'ticketModel', '$state', 'metadataModel', '$q', 'knowledgeArticleModel', 'relationModel', 'categoriesService', '$modal', 'permissionModel', 'roles', '$filter', 'configurationModel', 'events', 'searchModel', function (ticketService, ticketModel, $state, metadataModel, $q, knowledgeArticleModel, relationModel, categoriesService, $modal, permissionModel, roles, $filter, configurationModel, events, searchModel) { return { require: '^rs', restrict: 'E', replace: true, templateUrl: 'views/resource/rs-ka-search.html', link: function (scope, element, attrs, rsController) { angular.extend(scope, rsController); scope.comaroundEnabled = configurationModel.comaroundEnabled; //After manual refresh of ticket rsController context is not getting updated. if (scope.context.type !== EntityVO.TYPE_KNOWLEDGE && ticketModel.cache[scope.context.id]) { scope.context = ticketModel.cache[scope.context.id]; } else if (knowledgeArticleModel.articles[scope.context.id]) { scope.context = knowledgeArticleModel.articles[scope.context.id]; } var state = { processing: false, searching: false, searchNotFound: false, watcherOn: false, optionIsCollapsed: true, canCreateKnowledge: permissionModel.hasPermission(roles.ITSM_KNOWLEDGE_USER_ROLE) && !scope.context.isDraft && scope.context.accessMappings.relationsEditAllowed }, searchItems = []; scope.articlePlaceholder = { type: EntityVO.TYPE_KNOWLEDGE, allCategories: [], customer: {}, summary: '', allCompanies: [] }; scope.titleRegExp = new RegExp(/.{1,5}/g); if (scope.context.type === EntityVO.TYPE_SMARTRECORDER) { scope.articlePlaceholder.customer.company = { name: scope.context.company }; } else if (scope.context.type === EntityVO.TYPE_PROBLEM || scope.context.type === EntityVO.TYPE_KNOWNERROR) { scope.articlePlaceholder.customer.company = scope.context.company; } else if (scope.context.customer && scope.context.customer.company) { scope.articlePlaceholder.customer.company = scope.context.customer.company; } else { scope.articlePlaceholder.customer.company = scope.context.customer || scope.context.company; } scope.contextRelations = relationModel.cache[scope.context.id]; scope.companyState = { selected: true, showFilter: configurationModel.showFilterForRecommendedKnowledge }; if (scope.context.type === EntityVO.TYPE_KNOWLEDGE) { scope.articlePlaceholder.allCompanies = scope.context.allCompanies; //SW00512025 - passing All even if its not a selected company for this KA if (!_.find(scope.articlePlaceholder.allCompanies, { name: 'All' })) { scope.articlePlaceholder.allCompanies.push({ name: 'All' }); } } else { scope.articlePlaceholder.allCompanies.push({ name: 'All' }); scope.articlePlaceholder.allCompanies.push(scope.articlePlaceholder.customer.company); } scope.toggleOptions = function () { state.optionIsCollapsed = !state.optionIsCollapsed; }; scope.createNewArticle = function () { if (scope.comaroundEnabled) { searchModel.getHKMUrls().then(function (urls) { if (urls.createUrl) { urls.createUrl = decodeURI(urls.createUrl); window.open(urls.createUrl, '_blank'); } }); } else { ticketModel.articleParent = scope.context; var modalInstance = $modal.open({ templateUrl: 'views/create/create-ka.html', controller: 'CreateKnowledgeArticleModalController', windowClass: 'modal_full-content', backdrop: false, keyboard: false }); modalInstance.result.then(function (article) { scope.$emit(events.TICKET_PROFILE_RESEND_EVENT, { eventName: events.TICKET_SHOW_LOADER }); var newResource = new RelationItemVO().build({ id: article.id, displayId: article.articleId, title: article.title, desc: article.title, type: article.type, additionalInformation: { modifiedDate: article.modifiedDate }, tag: EntityVO.TYPE_RESOURCE, relationshipType: EntityVO.TYPE_RELATEDTO, realObject: { status: article.status, version: article.version, modifiedDate: article.modifiedDate } }); scope.saveToTicket(newResource).then(function () { $state.go(newResource.type, { id: newResource.id }); }).finally(function () { scope.$emit(events.TICKET_PROFILE_RESEND_EVENT, { eventName: events.TICKET_HIDE_LOADER }); }); scope.toggleSearchKa(); }); } }; scope.setSortOption = function (option) { scope.selectedSortOption = option; scope.searchArticle(); }; scope.changeStatusOption = function (status) { scope.statusOptions = status; }; scope.changeSourceOption = function (source) { scope.sourceOptions = source; }; scope.searchArticle = function () { if (state.searching) { return; } state.searching = true; scope.searchResults = []; state.searchNotFound = false; knowledgeArticleModel.clearVisitedArticlesForTicket(scope.context.id); var searchData = { search_text: scope.articlePlaceholder.summary, suggest_search: true, chunk_index: 0, chunk_size: 15 }, searchOptions = {}, categoryList = scope.articlePlaceholder.allCategories; if (scope.statusOptions) { searchOptions.statuses = [{ value: scope.statusOptions.name }]; } if (scope.sourceOptions) { searchOptions.source = scope.sourceOptions.name; } if (scope.companyState.showFilter && scope.companyState.selected) { var companyList = fetchContextCompany(); if (companyList) { searchOptions.searchCompanies = companyList; } } searchOptions.category = []; _.each(categoryList, function (category) { if (category.listOfTiers[0] && category.listOfTiers[0].selectedValue) { var categoryData = {}; categoryData.name = category.name; categoryData.tiers = {}; _.each(category.listOfTiers, function (tier) { categoryData.tiers[tier.name] = tier.selectedValue ? tier.selectedValue : ''; }); searchOptions.category.push(categoryData); } }); findArticles(searchData, searchOptions); scope.searchData = searchData; scope.searchOptions = searchOptions; !state.watcherOn && initWatch(); }; scope.getTicketLabel = function () { return $filter('i18n')('ticket.type.' + scope.context.type); }; function fetchContextCompany() { var companies = [{ name: 'All' }]; if (scope.context.type === EntityVO.TYPE_KNOWLEDGE && scope.context.company && scope.context.company.name === 'All') { return companies; } if (scope.context.type === EntityVO.TYPE_KNOWLEDGE || scope.context.type === EntityVO.TYPE_PROBLEM || scope.context.type === EntityVO.TYPE_KNOWNERROR) { companies.push(scope.context.company); } else { companies.push(scope.context.customer ? scope.context.customer.company : { name: scope.context.company }); } return companies; } function findArticles(searchData, searchOptions) { var searchPromise; if (configurationModel.comaroundEnabled) { var params = { search_text: searchData.search_text }; if (searchOptions.category && searchOptions.category.length) { params.category = searchOptions.category; } searchPromise = knowledgeArticleModel.getHKMRecommendedKA(searchData, params); } else { searchPromise = knowledgeArticleModel.findArticleByText(searchData, searchOptions, scope.selectedSortOption); } searchPromise.then(function (articles) { searchItems = _.filter(articles, function (article) { return scope.context.id !== article.id; }); populateArticles(scope.contextRelations); }).finally(function () { state.searching = false; }); } function populateArticles(relatedItems) { if (!searchItems || !searchItems.length) { state.searchNotFound = true; return; } var relatedArticles = _.filter(relatedItems, function (relatedItem) { return _.find(searchItems, { id: relatedItem.id }); }), notRelatedArticles = _.reject(searchItems, function (searchItem) { return _.find(relatedArticles, { id: searchItem.id }); }); scope.searchResults = relatedArticles.concat(notRelatedArticles); if (scope.context.type !== EntityVO.TYPE_KNOWLEDGE) { checkForVisitedArticles(scope.searchResults); } } function initWatch() { scope.$watchCollection('contextRelations', function (relatedItems) { if (!state.watcherOn) { state.watcherOn = true; return; //Code below shouldn't run when initializing the watch } if (relationModel.unlinked.length && rsController.isKaSearchEnabled()) { //Case : Article is unlinked and advanced search articles screen relationModel.unlinked.length = 0; findArticles(scope.searchData, scope.searchOptions); return; } populateArticles(relatedItems); }, true); } scope.clearSearch = function () { scope.articlePlaceholder.summary = ''; scope.searchResults = []; searchItems.length = 0; state.searchNotFound = false; }; //Knowledge status is not getting populated when using filter in html in case of browser refresh because metadataModel.cache doesn't fetch value at that time. scope.localizedStatus = function (status) { if (scope.comaroundEnabled) { return $filter('i18n')('hkm.status.' + status); } else { return $filter('localizeLabel')(status, 'status', 'knowledge'); } }; function checkForVisitedArticles(searchResults) { var visitedArticlesList = knowledgeArticleModel.getVisitedArticlesForTicket(scope.context.id); searchResults.some(function (article) { article.visited = false; return article instanceof GlobalSearchItemVO; //RelationItemVO are taken from cache so marking only them false. }); if (!_.isEmpty(visitedArticlesList)) { _.forEach(searchResults, function (article) { article.visited = _.includes(visitedArticlesList, article.id); }); } } function init() { scope.clearSearch(); scope.state = state; scope.itemsLimit = 4; scope.sortOptions = [ { name: 'mostRelevant', sortFieldName: '', sortFieldOrder: '' }, { name: 'mostFavorite', sortFieldName: 'favorite', sortFieldOrder: 'des' }, { name: 'mostRecent', sortFieldName: 'modifiedDate', sortFieldOrder: 'des' } ]; scope.selectedSortOption = scope.sortOptions[0]; state.processing = true; var articleTemplatePromise = scope.comaroundEnabled ? $q.when([]) : knowledgeArticleModel.getKnowledgeArticleTemplates(); $q.all([metadataModel.getMetadataByType(EntityVO.TYPE_KNOWLEDGE), articleTemplatePromise]) .then(function (result) { var metaData = result[0]; var categorizations = [ { name: 'operational', tiers: {} }, { name: 'product', tiers: {} } ]; scope.availableStatuses = metaData.statuses ? metaData.statuses.slice() : []; // remove work in progress status since it is not supported by backend MTF search _.remove(scope.availableStatuses, { name: 'Work In Progress' }); scope.articlePlaceholder.allCategories = _.cloneDeep(categoriesService.populateCategories(categorizations, metaData)); scope.availableSources = result[1]; }) .finally(function () { state.processing = false; }); } init(); } }; } ]);