Fixes #3559 - mentioned KB entries with the same title in different categories can not distinguished.

This commit is contained in:
Rolf Schmidt 2021-05-31 12:59:49 +00:00 committed by Thorsten Eckel
parent 205c717f4d
commit 328845c283
11 changed files with 108 additions and 14 deletions

View file

@ -90,9 +90,17 @@ class App.SearchableSelect extends Spine.Controller
renderOptions: (options) -> renderOptions: (options) ->
html = '' html = ''
for option in options for option in options
classes = 'u-textTruncate'
if option.children
classes += ' js-enter'
else
classes += ' js-option'
if option.category
classes += ' with-category'
html += App.view('generic/searchable_select_option') html += App.view('generic/searchable_select_option')
option: option option: option
class: if option.children then 'js-enter' else 'js-option' class: classes
html html
renderAllOptions: (parentName, options, level) -> renderAllOptions: (parentName, options, level) ->

View file

@ -78,8 +78,13 @@ class App.SearchableAjaxSelect extends App.SearchableSelect
renderResponseItemAjax: (elem, data) -> renderResponseItemAjax: (elem, data) ->
result = _.find(data.details, (detailElem) -> detailElem.type == elem.type and detailElem.id == elem.id) result = _.find(data.details, (detailElem) -> detailElem.type == elem.type and detailElem.id == elem.id)
category = undefined
if result.type is 'KnowledgeBase::Answer::Translation' && result.subtitle
category = result.subtitle
if result if result
{ {
category: category
name: result.title name: result.title
value: elem.id value: elem.id
} }

View file

@ -577,7 +577,8 @@
'flavor': 'agent', 'flavor': 'agent',
'index': 'KnowledgeBase::Answer::Translation', 'index': 'KnowledgeBase::Answer::Translation',
'url_type': 'agent', 'url_type': 'agent',
'highlight_enabled': false 'highlight_enabled': false,
'include_locale': true,
}), }),
processData: true, processData: true,
success: function(data, status, xhr) { success: function(data, status, xhr) {
@ -585,9 +586,10 @@
var items = data var items = data
.result .result
.map(function(elem){ .map(function(elem) {
if(result = _.find(data.details, function(detailElem) { return detailElem.type == elem.type && detailElem.id == elem.id })) { if(result = _.find(data.details, function(detailElem) { return detailElem.type == elem.type && detailElem.id == elem.id })) {
return { return {
'category': result.subtitle,
'name': result.title, 'name': result.title,
'value': elem.id, 'value': elem.id,
'url': result.url 'url': result.url
@ -599,8 +601,11 @@
var element = $('<li>') var element = $('<li>')
.attr('data-id', elem.value) .attr('data-id', elem.value)
.attr('data-url', elem.url) .attr('data-url', elem.url)
.text(elem.name) .addClass('u-clickable u-textTruncate with-category')
.addClass('u-clickable u-textTruncate')
element.append($('<small>').text(elem.category))
element.append('<br>')
element.append($('<span>').text(elem.name))
if (index == array.length-1) { if (index == array.length-1) {
element.addClass('is-active') element.addClass('is-active')

View file

@ -38,6 +38,22 @@ class App.KnowledgeBaseCategory extends App.Model
memo.concat elem.categoriesForDropdown(nested: options.nested + 1, kb_locale: options.kb_locale) memo.concat elem.categoriesForDropdown(nested: options.nested + 1, kb_locale: options.kb_locale)
, initial , initial
categoriesForSearch: (options = {}) ->
result = [@guaranteedTitle(options.kb_locale.id)]
check = @
while check.parent()
result.push(check.parent().guaranteedTitle(options.kb_locale.id))
check = check.parent()
if options.full || result.length <= 2
result = result.reverse().join(' > ')
else
result = result.reverse()
result = "#{result[0]} > .. > #{result[result.length - 1]}"
result
configure_attributes: (kb_locale = undefined) -> configure_attributes: (kb_locale = undefined) ->
[ [
{ {

View file

@ -1,4 +1,5 @@
<li role="presentation" class="<%= @class %>" data-value="<%= @option.value %>" title="<%= @option.name %><% if @detail: %><%= @detail %><% end %>"> <li role="presentation" class="<%= @class %>" data-value="<%= @option.value %>" title="<%= @option.name %><% if @detail: %><%= @detail %><% end %>">
<% if @option.category: %><small><%= @option.category %></small><br><% end %>
<span class="searchableSelect-option-text"> <span class="searchableSelect-option-text">
<%= @option.name %><% if @detail: %><span class="dropdown-detail"><%= @detail %></span><% end %> <%= @option.name %><% if @detail: %><span class="dropdown-detail"><%= @detail %></span><% end %>
</span> </span>

View file

@ -27,6 +27,10 @@
- -
<% end %> <% end %>
</div> </div>
<div class="column">
<label><%- @T('Category') %></label>
<a href="<%= @object.parent().category().uiUrl(App.KnowledgeBaseLocale.find(@object.kb_locale_id)) %>" title="<%= @object.parent().category().categoriesForSearch({ full: true, kb_locale: App.KnowledgeBaseLocale.find(@object.kb_locale_id) }) %>"><%= @object.parent().category().guaranteedTitle(@object.kb_locale_id) %></a>
</div>
<div class="column"> <div class="column">
<label><%- @T('Language') %></label> <label><%- @T('Language') %></label>
<%= App.KnowledgeBaseLocale.localeFor(@object).systemLocale().name %> <%= App.KnowledgeBaseLocale.localeFor(@object).systemLocale().name %>

View file

@ -8371,6 +8371,16 @@ footer {
} }
} }
.dropdown li.with-category, .dropdown.dropdown--actions li.with-category {
line-height: 19.5px;
}
.dropdown.dropdown--actions li.with-category {
height: 39px;
padding: 0px 15px;
display: list-item;
}
.dropdown li:not(:first-child) { .dropdown li:not(:first-child) {
box-shadow: 0 1px rgba(255,255,255,.13) inset; box-shadow: 0 1px rgba(255,255,255,.13) inset;
} }
@ -13023,4 +13033,3 @@ span.is-disabled {
.text-modules-box { .text-modules-box {
max-height: 40vh; max-height: 40vh;
} }

View file

@ -76,6 +76,12 @@ class KnowledgeBase::SearchController < ApplicationController
def public_item_details_answer(meta, object) def public_item_details_answer(meta, object)
category_translation = object.answer.category.translation_preferred(object.kb_locale) category_translation = object.answer.category.translation_preferred(object.kb_locale)
path = help_answer_path(category_translation, object, locale: object.kb_locale.system_locale.locale) path = help_answer_path(category_translation, object, locale: object.kb_locale.system_locale.locale)
subtitle = object.answer.category.self_with_parents.map { |c| strip_tags(c.translation_preferred(object.kb_locale).title) }.reverse
subtitle = if subtitle.count <= 2
subtitle.join(' > ')
else
subtitle.values_at(0, -1).join(' > .. > ')
end
url = case url_type url = case url_type
when :public when :public
@ -91,7 +97,7 @@ class KnowledgeBase::SearchController < ApplicationController
date: object.updated_at, date: object.updated_at,
url: url, url: url,
title: meta.dig(:highlight, 'title')&.first || object.title, title: meta.dig(:highlight, 'title')&.first || object.title,
subtitle: strip_tags(category_translation.title), subtitle: subtitle,
body: meta.dig(:highlight, 'content.body')&.first || strip_tags(object.content.body).truncate(100) body: meta.dig(:highlight, 'content.body')&.first || strip_tags(object.content.body).truncate(100)
} }
end end

View file

@ -61,6 +61,18 @@ class KnowledgeBase::Category < ApplicationModel
[self] + children.map(&:self_with_children).flatten [self] + children.map(&:self_with_children).flatten
end end
def self_with_parents
result = [self]
check = self
while check.parent.present?
result << check.parent
check = check.parent
end
result
end
def self_with_children_answers def self_with_children_answers
KnowledgeBase::Answer.where(category_id: self_with_children_ids) KnowledgeBase::Answer.where(category_id: self_with_children_ids)
end end

View file

@ -40,6 +40,10 @@ RSpec.describe KnowledgeBase::Category, type: :model, current_user_id: 1 do
expect(kb_category_with_tree.self_with_children.count).to eq 7 expect(kb_category_with_tree.self_with_children.count).to eq 7
end end
it 'fetches all parents' do
expect(grandchild_category.self_with_parents.count).to eq 3
end
it 'root category has no parent' do it 'root category has no parent' do
expect(kb_category_with_tree.parent).to be_blank expect(kb_category_with_tree.parent).to be_blank
end end

View file

@ -61,4 +61,28 @@ RSpec.describe 'Knowledge Base search with details', type: :request, searchindex
expect(json_response['details'][0]['subtitle']).to eq category.translation_to(primary_locale).title expect(json_response['details'][0]['subtitle']).to eq category.translation_to(primary_locale).title
end end
end end
context 'when answer tree is long' do
let(:category1) { create('knowledge_base/category') }
let(:category2) { create('knowledge_base/category', parent: category1) }
let(:category3) { create('knowledge_base/category', parent: category2) }
let(:answer_cut_tree) { create(:knowledge_base_answer, :published, :with_attachment, category: category3) }
let(:category4) { create('knowledge_base/category') }
let(:category5) { create('knowledge_base/category', parent: category4) }
let(:answer_full_tree) { create(:knowledge_base_answer, :published, :with_attachment, category: category5) }
before do
answer_cut_tree && answer_full_tree && rebuild_searchindex
end
it 'returns category with cut tree', authenticated_as: -> { create(:admin) } do
post endpoint, params: { query: answer_cut_tree.translations.first.title }
expect(json_response['details'][0]['subtitle']).to eq("#{category1.translations.first.title} > .. > #{category3.translations.first.title}")
end
it 'returns category with full tree', authenticated_as: -> { create(:admin) } do
post endpoint, params: { query: answer_full_tree.translations.first.title }
expect(json_response['details'][0]['subtitle']).to eq("#{category4.translations.first.title} > #{category5.translations.first.title}")
end
end
end end