This Joomla 4 override allows you to display a knwoledge base and its search bar simply using Joomla's com_content component and mod_finder module. At the bottom of this article, you can download the files of the override.

This override require Material Design Bootstrap as frontend framework.

Joomla 4 frontend rendering

The search bar

PhP markup mod_finder/override-kb.php

<?php
/**
 * @package     Joomla.Site
 * @subpackage  mod_finder
 * @author		web-eau.net
 * @copyright   (C) 2011 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Module\Finder\Site\Helper\FinderHelper;

// Load the smart search component language file.
$lang = $app->getLanguage();
$lang->load('com_finder', JPATH_SITE);

$input = '<input type="text" name="q" id="mod-finder-searchword' . $module->id . '" class="p-3 shadow-4 js-finder-search-query form-control" value="' . htmlspecialchars($app->input->get('q', '', 'string'), ENT_COMPAT, 'UTF-8') . '"'
	. ' placeholder="Search in the Joomla knowledge base..." size="150" aria-label="Search" />';

$showLabel  = $params->get('show_label', 1);
$labelClass = (!$showLabel ? 'visually-hidden ' : '') . 'finder';
$label      = '<label for="mod-finder-searchword' . $module->id . '" class="text-center text-whit pt-4 d-none d-sm-block h4 ' . $labelClass . '">' . $params->get('alt_label', Text::_('JSEARCH_FILTER_SUBMIT')) . '</label>';

$output = '';

if ($params->get('show_button', 0))
{
	$output .= $label;
	$output .= '<div class="mod-finder__search input-group">';
	$output .= $input;
	$output .= '<button class="btn btn-primary" type="submit"><span class="icon-search icon-black" aria-hidden="true"></span> ' . Text::_('JSEARCH_FILTER_SUBMIT') . '</button>';
	$output .= '</div>';
}
else
{
	$output .= $label;
	$output .= $input;
}

Text::script('MOD_FINDER_SEARCH_VALUE', true);

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $app->getDocument()->getWebAssetManager();
$wa->getRegistry()->addExtensionRegistryFile('com_finder');

/*
 * This segment of code sets up the autocompleter.
 */
if ($params->get('show_autosuggest', 1))
{
	$wa->usePreset('awesomplete');
	$app->getDocument()->addScriptOptions('finder-search', array('url' => Route::_('index.php?option=com_finder&task=suggestions.suggest&format=json&tmpl=component')));
}

$wa->useScript('com_finder.finder');

?>

<form class="mx-auto form-control-lg w-75 mod-finder js-finder-searchform form-search" action="<?php echo Route::_($route); ?>" method="get" role="search">
	<?php echo $output; ?>

	<?php $show_advanced = $params->get('show_advanced', 0); ?>
	<?php if ($show_advanced == 2) : ?>
		<br>
		<a href="<?php echo Route::_($route); ?>" class="mod-finder__advanced-link"><?php echo Text::_('COM_FINDER_ADVANCED_SEARCH'); ?></a>
	<?php elseif ($show_advanced == 1) : ?>
		<div class="mod-finder__advanced js-finder-advanced">
			<?php echo HTMLHelper::_('filter.select', $query, $params); ?>
		</div>
	<?php endif; ?>
	<?php echo FinderHelper::getGetFields($route, (int) $params->get('set_itemid', 0)); ?>
</form>

The knowledge base

PhP markup categories/default_items.php

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 * @author		web-eau.net
 * @copyright   (C) 2010 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;

if ($this->maxLevelcat != 0 && count($this->items[$this->parent->id]) > 0) :
?>
	<div class="row com-content-categories__items">
		<?php foreach ($this->items[$this->parent->id] as $id => $item) : ?>
      	<div class="col-lg-4 col-sm-12">
			<?php if ($this->params->get('show_empty_categories_cat') || $item->numitems || count($item->getChildren())) : ?>
			<div class="p-3 m-3 com-content-categories__ite">
				<div class="com-content-categories__item-title-wrapper">
					<div class="com-content-categories__item-title">
                      <h3 class="page-header item-title">
						<i class="bleu fas fa-folder me-2"></i><a class="bleu" href="<?php echo Route::_(RouteHelper::getCategoryRoute($item->id, $item->language)); ?>">
						<?php echo $this->escape($item->title); ?></a>
                      </h3>
						<?php if ($this->params->get('show_cat_num_articles_cat') == 1) :?>
							<span class="small">
								<?php echo Text::_('COM_CONTENT_NUM_ITEMS'); ?>&nbsp;
								<?php echo $item->numitems; ?>
							</span>
                      	 	<!--<br />
                      		<span class="nombre-articles">
                      		<?php echo $item->numitems; ?>
                      	 	<?php echo JText::_('COM_CONTENT_NUM_ITEMS'); ?>&nbsp;
							</span>-->
						<?php endif; ?>     
					</div>
					<?php if (count($item->getChildren()) > 0 && $this->maxLevelcat > 1) : ?>
						<button
							type="button"
							id="category-btn-<?php echo $item->id; ?>"
							data-category-id="<?php echo $item->id; ?>"
							class="btn btn-secondary btn-sm"
							aria-expanded="false"
							aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"
						>
							<span class="icon-plus" aria-hidden="true"></span>
						</button>
					<?php endif; ?>
				</div>
				<?php if ($this->params->get('show_description_image') && $item->getParams()->get('image')) : ?>
					<img src="<?php echo $item->getParams()->get('image'); ?>" alt="<?php echo htmlspecialchars($item->getParams()->get('image_alt'), ENT_COMPAT, 'UTF-8'); ?>">
				<?php endif; ?>
				<?php if ($this->params->get('show_subcat_desc_cat') == 1) : ?>
					<?php if ($item->description) : ?>
						<div class="com-content-categories__description category-desc">
							<?php echo HTMLHelper::_('content.prepare', $item->description, '', 'com_content.categories'); ?>
						</div>
					<?php endif; ?>
				<?php endif; ?>

				<?php if (count($item->getChildren()) > 0 && $this->maxLevelcat > 1) : ?>
					<div class="com-content-categories__children" id="category-<?php echo $item->id; ?>" hidden="">
					<?php
					$this->items[$item->id] = $item->getChildren();
					$this->parent = $item;
					$this->maxLevelcat--;
					echo $this->loadTemplate('items');
					$this->parent = $item->getParent();
					$this->maxLevelcat++;
					?>
					</div>
				<?php endif; ?>
			</div>
			<?php endif; ?>
            </div>
		<?php endforeach; ?>
	</div>
<?php endif; ?>

PhP markup category/blog.php

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 * @author	    web-eau.net
 * @copyright   (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;

$app = Factory::getApplication();

$this->category->text = $this->category->description;
$app->triggerEvent('onContentPrepare', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$this->category->description = $this->category->text;

$results = $app->triggerEvent('onContentAfterTitle', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$afterDisplayTitle = trim(implode("\n", $results));

$results = $app->triggerEvent('onContentBeforeDisplay', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$beforeDisplayContent = trim(implode("\n", $results));

$results = $app->triggerEvent('onContentAfterDisplay', array($this->category->extension . '.categories', &$this->category, &$this->params, 0));
$afterDisplayContent = trim(implode("\n", $results));

$htag    = $this->params->get('show_page_heading') ? 'h2' : 'h1';

?>
<div class="p-4 com-content-category-blog blog" itemscope itemtype="https://schema.org/Blog">
	<?php if ($this->params->get('show_page_heading')) : ?>
		<div class="page-header">
			<h1> <?php echo $this->escape($this->params->get('page_heading')); ?> </h1>
		</div>
	<?php endif; ?>

	<?php if ($this->params->get('show_category_title', 1)) : ?>
	<<?php echo $htag; ?>>
		 <h1 class="bleu">
  			<span class="subheading-category"><i class="fas fa-book-open mr-2"></i>
 		 	<?php echo $this->category->title; ?></span>
  		</h1>
	</<?php echo $htag; ?>>
	<?php endif; ?>
	<?php echo $afterDisplayTitle; ?>

	<?php if ($this->params->get('show_cat_tags', 1) && !empty($this->category->tags->itemTags)) : ?>
		<?php $this->category->tagLayout = new FileLayout('joomla.content.tags'); ?>
		<?php echo $this->category->tagLayout->render($this->category->tags->itemTags); ?>
	<?php endif; ?>

	<?php if ($beforeDisplayContent || $afterDisplayContent || $this->params->get('show_description', 1) || $this->params->def('show_description_image', 1)) : ?>
		<div class="category-desc clearfix mb-4">
			<?php if ($this->params->get('show_description_image') && $this->category->getParams()->get('image')) : ?>
				<?php echo LayoutHelper::render(
					'joomla.html.image',
					[
						'src' => $this->category->getParams()->get('image'),
						'alt' => empty($this->category->getParams()->get('image_alt')) && empty($this->category->getParams()->get('image_alt_empty')) ? false : $this->category->getParams()->get('image_alt'),
					]
				); ?>
			<?php endif; ?>
			<?php echo $beforeDisplayContent; ?>
			<?php if ($this->params->get('show_description') && $this->category->description) : ?>
				<span class="fs-5"><?php echo HTMLHelper::_('content.prepare', $this->category->description, '', 'com_content.category'); ?></span>
			<?php endif; ?>
			<?php echo $afterDisplayContent; ?>
		</div>
	<?php endif; ?>

	<?php if (empty($this->lead_items) && empty($this->link_items) && empty($this->intro_items)) : ?>
		<?php if ($this->params->get('show_no_articles', 1)) : ?>
			<div class="alert alert-info">
				<span class="icon-info-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
					<?php echo Text::_('COM_CONTENT_NO_ARTICLES'); ?>
			</div>
		<?php endif; ?>
	<?php endif; ?>

	<?php $leadingcount = 0; ?>
	<?php if (!empty($this->lead_items)) : ?>
		<div class="com-content-category-blog__items blog-items items-leading <?php echo $this->params->get('blog_class_leading'); ?>">
			<?php foreach ($this->lead_items as &$item) : ?>
				<div class="pl-2 py-4 com-content-category-blog__item blog-item" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
					<?php
					$this->item = &$item;
					echo $this->loadTemplate('item');
					?>
				</div>
				<?php $leadingcount++; ?>
			<?php endforeach; ?>
		</div><br />
	<?php endif; ?>

	<?php
	$introcount = count($this->intro_items);
	$counter = 0;
	?>

	<?php if (!empty($this->intro_items)) : ?>
		<?php $blogClass = $this->params->get('blog_class', ''); ?>
		<?php if ((int) $this->params->get('num_columns') > 1) : ?>
			<?php $blogClass .= (int) $this->params->get('multi_column_order', 0) === 0 ? ' masonry-' : ' columns-'; ?>
			<?php $blogClass .= (int) $this->params->get('num_columns'); ?>
		<?php endif; ?>
		<div class="col-container com-content-category-blog__items blog-items <?php echo $blogClass; ?>">
		<?php foreach ($this->intro_items as $key => &$item) : ?>
			<div class="p-4 item-kb com-content-category-blog__item blog-item"
				itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
					<?php
					$this->item = & $item;
					echo $this->loadTemplate('item');
					?>
			</div>
		<?php endforeach; ?>
		</div>
	<?php endif; ?>

	<?php if (!empty($this->link_items)) : ?>
		<div class="items-more">
			<?php echo $this->loadTemplate('links'); ?>
		</div>
	<?php endif; ?>

	<?php if ($this->maxLevel != 0 && !empty($this->children[$this->category->id])) : ?>
		<div class="com-content-category-blog__children cat-children">
			<?php if ($this->params->get('show_category_heading_title_text', 1) == 1) : ?>
				<h3> <?php echo Text::_('JGLOBAL_SUBCATEGORIES'); ?> </h3>
			<?php endif; ?>
			<?php echo $this->loadTemplate('children'); ?> </div>
	<?php endif; ?>
	<?php if (($this->params->def('show_pagination', 1) == 1 || ($this->params->get('show_pagination') == 2)) && ($this->pagination->pagesTotal > 1)) : ?>
		<div class="com-content-category-blog__navigation w-100">
			<?php if ($this->params->def('show_pagination_results', 1)) : ?>
				<p class="com-content-category-blog__counter counter float-end pt-3 pe-2">
					<?php echo $this->pagination->getPagesCounter(); ?>
				</p>
			<?php endif; ?>
			<div class="com-content-category-blog__pagination">
				<?php echo $this->pagination->getPagesLinks(); ?>
			</div>
		</div>
	<?php endif; ?>
</div>

CSS

.small {
	font-size:0.75em;
	color:#C0C0C0;
}
.bleu {
	color:#145889;
}
.col-container {
	display: table;width: 100%;
}
.item-kb {
	display: table-cell;
}
 

web-eau.net

France - 29800 Landerneau

+33 674 502 799

daniel@web-eau.net