How to create a free Knowledge Base just with Joomla 4?

How to create a free Knowledge Base just with Joomla 4?

What are the benefits to create a knowledge base just with Joomla?

  • Your knowledge base's content is powered by Joomla
    You can manage it easlily, like any other content powered by Joomla. Adding and/or editing the content of your knowledge base is really simple.
  • No need to install and maintain an extension
    No extension to install and maintain. No learning curve to endure. Forget the heavy CCK and others page builders, you only need Joomla.
  • Improve your Joomla skills with the overrides
    All you'll have to do is to follow this tutorial to create your knowledge base in minutes. All the steps are fully explained and illustrated.
  • You keep a perfect control on your SEO
    Because your knowledge base is based on com_content, you'll be able to manage each title and description tags as per your SEO objectives.
  • Joomla is free for everyone and forever.
    Because of Joomla magic, creating this powerful knowledge base is free and you won't ever have to pay for a licence.

The structure of a knowledge base

Simply, a knowledge base is a list of articles displayed and grouped by their categories. To help the users to find quickly their answers, a search engine is oftenly used and displayed on the top of this kind of page.

Once we've defined this, it's easier to understand how we'll create this knowledge base just with Joomla. We'll have to create:

  • the categories (and sub-categories if needed)
  • the articles in each categories
  • and the search module

To manage your knowledge base easily, I suggest you to create first a parent category named "Knowledge base" and create as many child categories you need. Add a short description to your categories. The structure of your categories should looks like this:

List of categories - Knowledge Base with Joomla

Then, you add your content by creating all your articles in each relevant categories.

List of articles - Knowledge Base with Joomla

Now, let's display your knowledge base

In this tutorial, we'll display the search bar on the top of the page and the categories below.

To acheive this, we need to create a new Smart search module to display the search bar. Maybe you don't know but Joomla have a very powerful internal search engine and we'll use it.

Setup the component Smart Search

To be very efficient, your knowledge base must have a dedicaced search engine. All the results from this search engine must be related to the content of the knowledge base and not from your blog or any other part of your website. That's possible with Joomla and that's what you'll setup now.

  1. Open the Components Dashboard by clicking the icon in the left menu

    Knowledge Base with Joomla - Smart Search component
  2. In Smart Search, click Index.

    Knowledge Base with Joomla 4 - Smart Search component
  3. In this page, all the contents indexed by this component are displayed. Click on the Index button on the top bar to index the latest content added.

    Joomla 4 - Smart Search component
  4. In the left menu, click Filters.

  5. Now, we gonna create the specific filter limited to the content of the knowledge base. Click the New button in the top bar.

  6. First, indicate a name to your filter (Search KB in this example).

    Joomla 4 - Smart Search settings
  7. Click on Search by category and select only the categories of your knowledge base.

  8. Once you're done, click the Save and close button in the top bar.

Create the module Smart Search

  1. From the control panel, click on the + icon next to Module.

    Joomla - Module creation
  2. In the list, click the module Smart Search.

    Joomla - Module articles category creation
  3. Indicate a title for your module (f.ex: Search Knowledge Base).

  4. In the parameter Search filter, select the search filter you've created earlier (Search KB).

  5. Adjust the others parameters accordingly to your needs:

    Joomla - Module smart search options
  6. Finally, select a module position above the component position of your template and assign it to the right page.

  7. Once you're done, click Save and close button.

Create the menu item

  1. From the control panel, click on Menus in the left menu.

    Joomla - Creating a menu item
  2. Select your menu and click on the + icon to create a new menu item.

    Joomla - Creating a new menu item
  3. Indicate the title of your menu item (f.ex: Knowledge Base) and if needed, customize its alias.

  4. In Menu Item Type *, select Articles and then List All Categories.

  5. In Select a Top Level Category *, select the parent category Knowledge Base.

  6. Now, adjust the parameters of your module accordingly to the following setup:

  7. Categories tab

    Joomla - Module Smart Search options
  8. Category tab

    Joomla - Module Smart Search options
  9. Setup the options and the parameters in the others tabs.

  10. Once you're done, click Save and close button.

Let's play with the overrides

This is the technical point of this tutorial but don't panic, it's pretty simple :)

The module Smart Search

Let's start with the module Smart Search. We'll customize it with some CSS Bootstrap classes.

  1. From the control panel, click on System in the left menu.

  2. In the part dedicaced to the template, click on Site Templates.

  3. Click on your frontend template (Cassiopeia, in our example).

  4. Click on the tab Create Overrides.

  5. In the modules list, click on mod_finder.

  6. After the override creation processus, go back to the editor tab.

  7. In the left list, click on html and then on mod_finder.

    Here, you have one file:

    • default.php

  8. Because you may want to create more than one override of this module, let's copy it!

    • Copy the all code of the file default.php
    • Click on the button New File on the top bar
    • In the left of the popup window, click first on mod_finder to select the folder
    • In the right of the window, fill the field with the name of your new file (override-kb for example)
    • Select the type of the file: php and click on Create
      Joomla - Template folder structure
    • Now, your new file must be displayed below the Default.php file in the mod_finder folder
    • Your Default.php file is now your master file if you need to create another override with mod_finder
  9. Click on override-kb.php to open it in the editor. Your file must be empty.

  10. Copy all the code below:

  11. <?php
    /**
     * @package     Joomla.Site
     * @subpackage  mod_finder
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @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="py-4 z-depth-2 js-finder-search-query form-control form-control-lg" value="' . htmlspecialchars($app->input->get('q', '', 'string'), ENT_COMPAT, 'UTF-8') . '"'
    	. ' placeholder="Search in the knowledge base..." size="150" aria-label="Search" />';
    
    $showLabel  = $params->get('show_label', 0);
    $labelClass = (!$showLabel ? 'sr-only ' : '') . 'finder';
    $label      = '<label for="mod-finder-searchword' . $module->id . '" class="text-center text-white pb-4 d-none d-sm-block h3 ' . $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 .= '<span class="input-group-append">';
    	$output .= '<button class="btn btn-primary" type="submit"><span class="fas fa-search icon-black" aria-hidden="true"></span> ' . Text::_('JSEARCH_FILTER_SUBMIT') . '</button>';
    	$output .= '</span>';
    	$output .= '</div>';
    }
    else
    {
    	$output .= $label;
    	$output .= $input;
    }
    
    /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
    $wa = $app->getDocument()->getWebAssetManager();
    $wa->registerAndUseScript('com_finder', 'com_finder/finder.js', [], ['defer' => true]);
    
    Text::script('MOD_FINDER_SEARCH_VALUE', true);
    
    /*
     * 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')));
    }
    ?>
    <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>
  12. and paste it in your override-kb.php file.

  13. Joomla - Template files editor
  14. Click on the Save button.

Some explanations about this code
  • Line 21: py-4 z-depth-2
    I've added some CSS classes: some padding and a shadow all over the search bar.
  • Line 21: placeholder
    Don't hesitate to customize your placeholder (the text displayed inside the search bar) and its size (150 in this example).
  • Line 26: text-center text-white pb-4 d-none d-sm-block h3
    These CSS classes will be apply to the label text displayed above the search bar. In this example, the label is displayed in H3 with white and it will not be displayed on mobile. Adjust this accordingly to your needs.
  • Line 61: mx-auto form-control-lg w-75
    These CSS classes will be applied to the form: centered, displayed in large and with a width at 75%.

Looking for Joomla overrides?

If you need inspiration or tips on Joomla overrides, here are +50 free examples to help and inspire you.

Joomla Overrides

The content

Now take a deep breath, we'll override the category and categories views (hey, I'm kidding!).

As described earlier, create first the overrides of the com_content component with category and categories.

The categories

Just to remind you, categories is the view where all the categories of your knowledge base are displayed.

By default in the folder com_content / categories, you must have 2 files:

  • default.php and default_items.php
  • Open your default_items.php file

copy all the code below:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
 * @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__item">
				
            	<h3 class="page-header item-title">
					<i class="bleu fas fa-folder mr-1"></i><a class="bleu" href="<?php echo Route::_(RouteHelper::getCategoryRoute($item->id, $item->language)); ?>">
					<?php echo $this->escape($item->title); ?></a>
					<?php if ($this->params->get('show_cat_num_articles_cat') == 1) :?>
						<!--<span class="badge badge-info">
							<?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; ?>
					<?php if (count($item->getChildren()) > 0 && $this->maxLevelcat > 1) : ?>
						<button
							type="button"
							id="category-btn-<?php echo $item->id; ?>"
							data-target="#category-<?php echo $item->id; ?>"
							data-toggle="collapse"
							class="btn btn-secondary btn-sm float-right"
							aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"
						>
							<span class="fas fa-plus" aria-hidden="true"></span>
						</button>
					<?php endif; ?>
				</h3>
				<?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 collapse fade" id="category-<?php echo $item->id; ?>">
					<?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; ?>
  • and paste it in your default_items.php file.
  • Once it's done, click the Save and close button.

Explanations. In this example, I've added some Bootstrap CSS classes to display 3 categories per row (line 22), some padding and margin (line 25) and a blue icon right before the title of each category (line 28). Of course, you can keep these changes or adapt them to the design of your website.

The category

Just to remind you, category is the view where all the articles of a single category of your knowledge base are displayed.

By default in the folder com_content / category, you have 7 files:

Joomla - Structure folder
  • For your information, the Default files will present your articles in a table. So, we'll only modify the Blog files to present the knowledge base. Copy each code below and paste them into their respective files (com_content / category / ...).
  • The blog.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @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;
    
    $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));
    
    ?>
    <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) or $this->params->get('page_subheading')) : ?>
    		<h2 class="bleu"> <?php echo $this->escape($this->params->get('page_subheading')); ?>
    			<?php if ($this->params->get('show_category_title')) : ?>
    				<span class="subheading-category"><i class="fas fa-book-open mr-2"></i><?php echo $this->category->title; ?></span>
    			<?php endif; ?>
    		</h2>
    	<?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">
    			<?php if ($this->params->get('show_description_image') && $this->category->getParams()->get('image')) : ?>
    				<img src="<?php echo $this->category->getParams()->get('image'); ?>" alt="<?php echo htmlspecialchars($this->category->getParams()->get('image_alt'), ENT_COMPAT, 'UTF-8'); ?>">
    			<?php endif; ?>
    			<?php echo $beforeDisplayContent; ?>
    			<?php if ($this->params->get('show_description') && $this->category->description) : ?>
    				<?php echo HTMLHelper::_('content.prepare', $this->category->description, '', 'com_content.category'); ?>
    			<?php endif; ?>
    			<?php echo $afterDisplayContent; ?>
    		</div><hr>
    	<?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)) : ?>
    			<p><?php echo Text::_('COM_CONTENT_NO_ARTICLES'); ?></p>
    		<?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><br />
          
    				<?php $leadingcount++; ?>
    			<?php endforeach; ?>
    		</div>
    	<?php endif; ?>
    
    	<?php
    	$introcount = count($this->intro_items);
    	$counter = 0;
    	?>
    
    	<?php if (!empty($this->intro_items)) : ?>
    		<div class="list-group list-group-flush com-content-category-blog__items blog-items <?php echo $this->params->get('blog_class'); ?>">
    		<?php foreach ($this->intro_items as $key => &$item) : ?>
    			<div class="p-4 list-group-item 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="com-content-category-blog__items-more 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-right pt-3 pr-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>

Explanations. Here, I've added some padding (lines 33 and 77), a blue CSS Class (line 41) and an icon right before the title of each category (line 43). I've also added some Bootstrap CSS classes to the list of the aricles (lines 96 and 98). Of course, you can keep these changes or adapt them to the design of your website.

  • The blog_children.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @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\Router\Route;
    use Joomla\Component\Content\Site\Helper\RouteHelper;
    
    $lang   = Factory::getLanguage();
    $user   = Factory::getUser();
    $groups = $user->getAuthorisedViewLevels();
    
    if ($this->maxLevel != 0 && count($this->children[$this->category->id]) > 0) : ?>
    
    	<?php foreach ($this->children[$this->category->id] as $id => $child) : ?>
    		<?php // Check whether category access level allows access to subcategories. ?>
    		<?php if (in_array($child->access, $groups)) : ?>
    			<?php if ($this->params->get('show_empty_categories') || $child->numitems || count($child->getChildren())) : ?>
    			<div class="com-content-category-blog__child">
    				<?php if ($lang->isRtl()) : ?>
    				<h3 class="page-header item-title">
    					<?php if ( $this->params->get('show_cat_num_articles', 1)) : ?>
    						<span class="badge badge-info tip">
    							<?php echo $child->getNumItems(true); ?>
    						</span>
    					<?php endif; ?>
    					<a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
    					<?php echo $this->escape($child->title); ?></a>
    
    					<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
    						<a href="#category-<?php echo $child->id; ?>" data-toggle="collapse" data-toggle="button" class="btn btn-sm float-right" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="fas fa-plus" aria-hidden="true"></span></a>
    					<?php endif; ?>
    				</h3>
    				<?php else : ?>
    				<h3 class="page-header item-title"><a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
    					<?php echo $this->escape($child->title); ?></a>
    					<?php if ( $this->params->get('show_cat_num_articles', 1)) : ?>
    						<span class="badge badge-info">
    							<?php echo Text::_('COM_CONTENT_NUM_ITEMS'); ?>&nbsp;
    							<?php echo $child->getNumItems(true); ?>
    						</span>
    					<?php endif; ?>
    
    					<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
    						<a href="#category-<?php echo $child->id; ?>" data-toggle="collapse" data-toggle="button" class="btn btn-sm float-right" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="fas fa-plus" aria-hidden="true"></span></a>
    					<?php endif; ?>
    				</h3>
    				<?php endif; ?>
    
    				<?php if ($this->params->get('show_subcat_desc') == 1) : ?>
    				<?php if ($child->description) : ?>
    					<div class="com-content-category-blog__description category-desc">
    						<?php echo HTMLHelper::_('content.prepare', $child->description, '', 'com_content.category'); ?>
    					</div>
    				<?php endif; ?>
    				<?php endif; ?>
    
    				<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
    				<div class="com-content-category-blog__children collapse fade" id="category-<?php echo $child->id; ?>">
    					<?php
    					$this->children[$child->id] = $child->getChildren();
    					$this->category = $child;
    					$this->maxLevel--;
    					echo $this->loadTemplate('children');
    					$this->category = $child->getParent();
    					$this->maxLevel++;
    					?>
    				</div>
    				<?php endif; ?>
    			</div>
    			<?php endif; ?>
    		<?php endif; ?>
    	<?php endforeach; ?>
    <?php endif;
  • The blog_item.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @license     GNU General Public License version 2 or later; see LICENSE.txt
     */
    
    defined('_JEXEC') or die;
    
    use Joomla\CMS\Factory;
    use Joomla\CMS\Language\Associations;
    use Joomla\CMS\Layout\LayoutHelper;
    use Joomla\CMS\Router\Route;
    use Joomla\CMS\Uri\Uri;
    use Joomla\Component\Content\Administrator\Extension\ContentComponent;
    use Joomla\Component\Content\Site\Helper\RouteHelper;
    
    // Create a shortcut for params.
    $params = $this->item->params;
    $canEdit = $this->item->params->get('access-edit');
    $info    = $params->get('info_block_position', 0);
    
    // Check if associations are implemented. If they are, define the parameter.
    $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
    
    ?>
    
    <?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
    
    <div class="item-content">
    	<?php if ($this->item->stage_condition == ContentComponent::CONDITION_UNPUBLISHED || strtotime($this->item->publish_up) > strtotime(Factory::getDate())
    		|| (!is_null($this->item->publish_down) && strtotime($this->item->publish_down) < strtotime(Factory::getDate()))) : ?>
    		<div class="system-unpublished">
    	<?php endif; ?>
    
    	<?php echo LayoutHelper::render('joomla.content.blog_style_default_item_title', $this->item); ?>
    
    	<?php if ($canEdit) : ?>
    		<?php echo LayoutHelper::render('joomla.content.icons', array('params' => $params, 'item' => $this->item)); ?>
    	<?php endif; ?>
    
    	<?php // Todo Not that elegant would be nice to group the params ?>
    	<?php $useDefList = ($params->get('show_modify_date') || $params->get('show_publish_date') || $params->get('show_create_date')
    		|| $params->get('show_hits') || $params->get('show_category') || $params->get('show_parent_category') || $params->get('show_author') || $assocParam); ?>
    
    	<?php if ($useDefList && ($info == 0 || $info == 2)) : ?>
    		<?php echo LayoutHelper::render('joomla.content.info_block', array('item' => $this->item, 'params' => $params, 'position' => 'above')); ?>
    	<?php endif; ?>
    	<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
    		<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
    	<?php endif; ?>
    
    	<?php if (!$params->get('show_intro')) : ?>
    		<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
    		<?php echo $this->item->event->afterDisplayTitle; ?>
    	<?php endif; ?>
    
    	<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
    	<?php echo $this->item->event->beforeDisplayContent; ?>
    
    	<?php echo $this->item->introtext; ?>
    
    	<?php if ($info == 1 || $info == 2) : ?>
    		<?php if ($useDefList) : ?>
    			<?php echo LayoutHelper::render('joomla.content.info_block', array('item' => $this->item, 'params' => $params, 'position' => 'below')); ?>
    		<?php endif; ?>
    		<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
    			<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
    		<?php endif; ?>
    	<?php endif; ?>
    
    	<?php if ($params->get('show_readmore') && $this->item->readmore) :
    		if ($params->get('access-view')) :
    			$link = Route::_(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language));
    		else :
    			$menu = Factory::getApplication()->getMenu();
    			$active = $menu->getActive();
    			$itemId = $active->id;
    			$link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false));
    			$link->setVar('return', base64_encode(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language)));
    		endif; ?>
    
    		<?php echo LayoutHelper::render('joomla.content.readmore', array('item' => $this->item, 'params' => $params, 'link' => $link)); ?>
    
    	<?php endif; ?>
    
    	<?php if ($this->item->stage_condition == ContentComponent::CONDITION_UNPUBLISHED || strtotime($this->item->publish_up) > strtotime(Factory::getDate())
    		|| (!is_null($this->item->publish_down) && strtotime($this->item->publish_down) < strtotime(Factory::getDate()))) : ?>
    	</div>
    	<?php endif; ?>
    </div>
    
    <?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
    <?php echo $this->item->event->afterDisplayContent; ?>
  • The blog_links.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @license     GNU General Public License version 2 or later; see LICENSE.txt
     */
    
    defined('_JEXEC') or die;
    
    use Joomla\CMS\Router\Route;
    use Joomla\Component\Content\Site\Helper\RouteHelper;
    
    ?>
    
    <ol class="com-content-category-blog__links nav nav-tabs nav-stacked">
    	<?php foreach ($this->link_items as &$item) : ?>
    		<li class="com-content-category-blog__link">
    			<a href="<?php echo Route::_(RouteHelper::getArticleRoute($item->slug, $item->catid, $item->language)); ?>">
    				<?php echo $item->title; ?></a>
    		</li>
    	<?php endforeach; ?>
    </ol>

A last step...

Now, go back to the module Smart Search you've created earlier.

Open it and in the tab Advanced select override-kb (or the name given to your override) in the layout parameter.

Joomla - Module parameter

Once it's done, click the Save and close button.

The frontend

I've added some additional CSS to my code but you have followed carefully all the steps of this tutorial, your knowledge base must looks like something like this:

Joomla Knowledge Base Override

The categories frontend view

Joomla Knowledge Base Override

The category frontend view

The Joomla Override Challenge

In association with Viviana Menzel, we've created the unofficial Joomla Override Challenge. The goal is to create each month an override based on an extension or on an project. If you want to participate, feel free to contact Viviana or me :)

What are the benefits to create a knowledge base just with Joomla?

  • Your knowledge base's content is powered by Joomla
    You can manage it easlily, like any other content powered by Joomla. Adding and/or editing the content of your knowledge base is really simple.
  • No need to install and maintain an extension
    No extension to install and maintain. No learning curve to endure. Forget the heavy CCK and others page builders, you only need Joomla.
  • Improve your Joomla skills with the overrides
    All you'll have to do is to follow this tutorial to create your knowledge base in minutes. All the steps are fully explained and illustrated.
  • You keep a perfect control on your SEO
    Because your knowledge base is based on com_content, you'll be able to manage each title and description tags as per your SEO objectives.
  • Joomla is free for everyone and forever.
    Because of Joomla magic, creating this powerful knowledge base is free and you won't ever have to pay for a licence.

The structure of a knowledge base

Simply, a knowledge base is a list of articles displayed and grouped by their categories. To help the users to find quickly their answers, a search engine is oftenly used and displayed on the top of this kind of page.

Once we've defined this, it's easier to understand how we'll create this knowledge base just with Joomla. We'll have to create:

  • the categories (and sub-categories if needed)
  • the articles in each categories
  • and the search module

To manage your knowledge base easily, I suggest you to create first a parent category named "Knowledge base" and create as many child categories you need. Add a short description to your categories. The structure of your categories should looks like this:

List of categories - Knowledge Base with Joomla

Then, you add your content by creating all your articles in each relevant categories.

List of articles - Knowledge Base with Joomla

Now, let's display your knowledge base

In this tutorial, we'll display the search bar on the top of the page and the categories below.

To acheive this, we need to create a new Smart search module to display the search bar. Maybe you don't know but Joomla have a very powerful internal search engine and we'll use it.

Setup the component Smart Search

To be very efficient, your knowledge base must have a dedicaced search engine. All the results from this search engine must be related to the content of the knowledge base and not from your blog or any other part of your website. That's possible with Joomla and that's what you'll setup now.

  1. Open the Components Dashboard by clicking the icon in the left menu

    Knowledge Base with Joomla - Smart Search component
  2. In Smart Search, click Index.

    Knowledge Base with Joomla 4 - Smart Search component
  3. In this page, all the contents indexed by this component are displayed. Click on the Index button on the top bar to index the latest content added.

    Joomla 4 - Smart Search component
  4. In the left menu, click Filters.

  5. Now, we gonna create the specific filter limited to the content of the knowledge base. Click the New button in the top bar.

  6. First, indicate a name to your filter (Search KB in this example).

    Joomla 4 - Smart Search settings
  7. Click on Search by category and select only the categories of your knowledge base.

  8. Once you're done, click the Save and close button in the top bar.

Create the module Smart Search

  1. From the control panel, click on the + icon next to Module.

    Joomla - Module creation
  2. In the list, click the module Smart Search.

    Joomla - Module articles category creation
  3. Indicate a title for your module (f.ex: Search Knowledge Base).

  4. In the parameter Search filter, select the search filter you've created earlier (Search KB).

  5. Adjust the others parameters accordingly to your needs:

    Joomla - Module smart search options
  6. Finally, select a module position above the component position of your template and assign it to the right page.

  7. Once you're done, click Save and close button.

Create the menu item

  1. From the control panel, click on Menus in the left menu.

    Joomla - Creating a menu item
  2. Select your menu and click on the + icon to create a new menu item.

    Joomla - Creating a new menu item
  3. Indicate the title of your menu item (f.ex: Knowledge Base) and if needed, customize its alias.

  4. In Menu Item Type *, select Articles and then List All Categories.

  5. In Select a Top Level Category *, select the parent category Knowledge Base.

  6. Now, adjust the parameters of your module accordingly to the following setup:

  7. Categories tab

    Joomla - Module Smart Search options
  8. Category tab

    Joomla - Module Smart Search options
  9. Setup the options and the parameters in the others tabs.

  10. Once you're done, click Save and close button.

Let's play with the overrides

This is the technical point of this tutorial but don't panic, it's pretty simple :)

The module Smart Search

Let's start with the module Smart Search. We'll customize it with some CSS Bootstrap classes.

  1. From the control panel, click on System in the left menu.

  2. In the part dedicaced to the template, click on Site Templates.

  3. Click on your frontend template (Cassiopeia, in our example).

  4. Click on the tab Create Overrides.

  5. In the modules list, click on mod_finder.

  6. After the override creation processus, go back to the editor tab.

  7. In the left list, click on html and then on mod_finder.

    Here, you have one file:

    • default.php

  8. Because you may want to create more than one override of this module, let's copy it!

    • Copy the all code of the file default.php
    • Click on the button New File on the top bar
    • In the left of the popup window, click first on mod_finder to select the folder
    • In the right of the window, fill the field with the name of your new file (override-kb for example)
    • Select the type of the file: php and click on Create
      Joomla - Template folder structure
    • Now, your new file must be displayed below the Default.php file in the mod_finder folder
    • Your Default.php file is now your master file if you need to create another override with mod_finder
  9. Click on override-kb.php to open it in the editor. Your file must be empty.

  10. Copy all the code below:

  11. <?php
    /**
     * @package     Joomla.Site
     * @subpackage  mod_finder
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @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="py-4 z-depth-2 js-finder-search-query form-control form-control-lg" value="' . htmlspecialchars($app->input->get('q', '', 'string'), ENT_COMPAT, 'UTF-8') . '"'
    	. ' placeholder="Search in the knowledge base..." size="150" aria-label="Search" />';
    
    $showLabel  = $params->get('show_label', 0);
    $labelClass = (!$showLabel ? 'sr-only ' : '') . 'finder';
    $label      = '<label for="mod-finder-searchword' . $module->id . '" class="text-center text-white pb-4 d-none d-sm-block h3 ' . $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 .= '<span class="input-group-append">';
    	$output .= '<button class="btn btn-primary" type="submit"><span class="fas fa-search icon-black" aria-hidden="true"></span> ' . Text::_('JSEARCH_FILTER_SUBMIT') . '</button>';
    	$output .= '</span>';
    	$output .= '</div>';
    }
    else
    {
    	$output .= $label;
    	$output .= $input;
    }
    
    /** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
    $wa = $app->getDocument()->getWebAssetManager();
    $wa->registerAndUseScript('com_finder', 'com_finder/finder.js', [], ['defer' => true]);
    
    Text::script('MOD_FINDER_SEARCH_VALUE', true);
    
    /*
     * 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')));
    }
    ?>
    <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>
  12. and paste it in your override-kb.php file.

  13. Joomla - Template files editor
  14. Click on the Save button.

Some explanations about this code
  • Line 21: py-4 z-depth-2
    I've added some CSS classes: some padding and a shadow all over the search bar.
  • Line 21: placeholder
    Don't hesitate to customize your placeholder (the text displayed inside the search bar) and its size (150 in this example).
  • Line 26: text-center text-white pb-4 d-none d-sm-block h3
    These CSS classes will be apply to the label text displayed above the search bar. In this example, the label is displayed in H3 with white and it will not be displayed on mobile. Adjust this accordingly to your needs.
  • Line 61: mx-auto form-control-lg w-75
    These CSS classes will be applied to the form: centered, displayed in large and with a width at 75%.

Looking for Joomla overrides?

If you need inspiration or tips on Joomla overrides, here are +50 free examples to help and inspire you.

Joomla Overrides

The content

Now take a deep breath, we'll override the category and categories views (hey, I'm kidding!).

As described earlier, create first the overrides of the com_content component with category and categories.

The categories

Just to remind you, categories is the view where all the categories of your knowledge base are displayed.

By default in the folder com_content / categories, you must have 2 files:

  • default.php and default_items.php
  • Open your default_items.php file

copy all the code below:

<?php
/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
 * @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__item">
				
            	<h3 class="page-header item-title">
					<i class="bleu fas fa-folder mr-1"></i><a class="bleu" href="<?php echo Route::_(RouteHelper::getCategoryRoute($item->id, $item->language)); ?>">
					<?php echo $this->escape($item->title); ?></a>
					<?php if ($this->params->get('show_cat_num_articles_cat') == 1) :?>
						<!--<span class="badge badge-info">
							<?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; ?>
					<?php if (count($item->getChildren()) > 0 && $this->maxLevelcat > 1) : ?>
						<button
							type="button"
							id="category-btn-<?php echo $item->id; ?>"
							data-target="#category-<?php echo $item->id; ?>"
							data-toggle="collapse"
							class="btn btn-secondary btn-sm float-right"
							aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"
						>
							<span class="fas fa-plus" aria-hidden="true"></span>
						</button>
					<?php endif; ?>
				</h3>
				<?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 collapse fade" id="category-<?php echo $item->id; ?>">
					<?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; ?>
  • and paste it in your default_items.php file.
  • Once it's done, click the Save and close button.

Explanations. In this example, I've added some Bootstrap CSS classes to display 3 categories per row (line 22), some padding and margin (line 25) and a blue icon right before the title of each category (line 28). Of course, you can keep these changes or adapt them to the design of your website.

The category

Just to remind you, category is the view where all the articles of a single category of your knowledge base are displayed.

By default in the folder com_content / category, you have 7 files:

Joomla - Structure folder
  • For your information, the Default files will present your articles in a table. So, we'll only modify the Blog files to present the knowledge base. Copy each code below and paste them into their respective files (com_content / category / ...).
  • The blog.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @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;
    
    $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));
    
    ?>
    <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) or $this->params->get('page_subheading')) : ?>
    		<h2 class="bleu"> <?php echo $this->escape($this->params->get('page_subheading')); ?>
    			<?php if ($this->params->get('show_category_title')) : ?>
    				<span class="subheading-category"><i class="fas fa-book-open mr-2"></i><?php echo $this->category->title; ?></span>
    			<?php endif; ?>
    		</h2>
    	<?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">
    			<?php if ($this->params->get('show_description_image') && $this->category->getParams()->get('image')) : ?>
    				<img src="<?php echo $this->category->getParams()->get('image'); ?>" alt="<?php echo htmlspecialchars($this->category->getParams()->get('image_alt'), ENT_COMPAT, 'UTF-8'); ?>">
    			<?php endif; ?>
    			<?php echo $beforeDisplayContent; ?>
    			<?php if ($this->params->get('show_description') && $this->category->description) : ?>
    				<?php echo HTMLHelper::_('content.prepare', $this->category->description, '', 'com_content.category'); ?>
    			<?php endif; ?>
    			<?php echo $afterDisplayContent; ?>
    		</div><hr>
    	<?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)) : ?>
    			<p><?php echo Text::_('COM_CONTENT_NO_ARTICLES'); ?></p>
    		<?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><br />
          
    				<?php $leadingcount++; ?>
    			<?php endforeach; ?>
    		</div>
    	<?php endif; ?>
    
    	<?php
    	$introcount = count($this->intro_items);
    	$counter = 0;
    	?>
    
    	<?php if (!empty($this->intro_items)) : ?>
    		<div class="list-group list-group-flush com-content-category-blog__items blog-items <?php echo $this->params->get('blog_class'); ?>">
    		<?php foreach ($this->intro_items as $key => &$item) : ?>
    			<div class="p-4 list-group-item 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="com-content-category-blog__items-more 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-right pt-3 pr-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>

Explanations. Here, I've added some padding (lines 33 and 77), a blue CSS Class (line 41) and an icon right before the title of each category (line 43). I've also added some Bootstrap CSS classes to the list of the aricles (lines 96 and 98). Of course, you can keep these changes or adapt them to the design of your website.

  • The blog_children.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @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\Router\Route;
    use Joomla\Component\Content\Site\Helper\RouteHelper;
    
    $lang   = Factory::getLanguage();
    $user   = Factory::getUser();
    $groups = $user->getAuthorisedViewLevels();
    
    if ($this->maxLevel != 0 && count($this->children[$this->category->id]) > 0) : ?>
    
    	<?php foreach ($this->children[$this->category->id] as $id => $child) : ?>
    		<?php // Check whether category access level allows access to subcategories. ?>
    		<?php if (in_array($child->access, $groups)) : ?>
    			<?php if ($this->params->get('show_empty_categories') || $child->numitems || count($child->getChildren())) : ?>
    			<div class="com-content-category-blog__child">
    				<?php if ($lang->isRtl()) : ?>
    				<h3 class="page-header item-title">
    					<?php if ( $this->params->get('show_cat_num_articles', 1)) : ?>
    						<span class="badge badge-info tip">
    							<?php echo $child->getNumItems(true); ?>
    						</span>
    					<?php endif; ?>
    					<a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
    					<?php echo $this->escape($child->title); ?></a>
    
    					<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
    						<a href="#category-<?php echo $child->id; ?>" data-toggle="collapse" data-toggle="button" class="btn btn-sm float-right" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="fas fa-plus" aria-hidden="true"></span></a>
    					<?php endif; ?>
    				</h3>
    				<?php else : ?>
    				<h3 class="page-header item-title"><a href="<?php echo Route::_(RouteHelper::getCategoryRoute($child->id, $child->language)); ?>">
    					<?php echo $this->escape($child->title); ?></a>
    					<?php if ( $this->params->get('show_cat_num_articles', 1)) : ?>
    						<span class="badge badge-info">
    							<?php echo Text::_('COM_CONTENT_NUM_ITEMS'); ?>&nbsp;
    							<?php echo $child->getNumItems(true); ?>
    						</span>
    					<?php endif; ?>
    
    					<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
    						<a href="#category-<?php echo $child->id; ?>" data-toggle="collapse" data-toggle="button" class="btn btn-sm float-right" aria-label="<?php echo Text::_('JGLOBAL_EXPAND_CATEGORIES'); ?>"><span class="fas fa-plus" aria-hidden="true"></span></a>
    					<?php endif; ?>
    				</h3>
    				<?php endif; ?>
    
    				<?php if ($this->params->get('show_subcat_desc') == 1) : ?>
    				<?php if ($child->description) : ?>
    					<div class="com-content-category-blog__description category-desc">
    						<?php echo HTMLHelper::_('content.prepare', $child->description, '', 'com_content.category'); ?>
    					</div>
    				<?php endif; ?>
    				<?php endif; ?>
    
    				<?php if ($this->maxLevel > 1 && count($child->getChildren()) > 0) : ?>
    				<div class="com-content-category-blog__children collapse fade" id="category-<?php echo $child->id; ?>">
    					<?php
    					$this->children[$child->id] = $child->getChildren();
    					$this->category = $child;
    					$this->maxLevel--;
    					echo $this->loadTemplate('children');
    					$this->category = $child->getParent();
    					$this->maxLevel++;
    					?>
    				</div>
    				<?php endif; ?>
    			</div>
    			<?php endif; ?>
    		<?php endif; ?>
    	<?php endforeach; ?>
    <?php endif;
  • The blog_item.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @license     GNU General Public License version 2 or later; see LICENSE.txt
     */
    
    defined('_JEXEC') or die;
    
    use Joomla\CMS\Factory;
    use Joomla\CMS\Language\Associations;
    use Joomla\CMS\Layout\LayoutHelper;
    use Joomla\CMS\Router\Route;
    use Joomla\CMS\Uri\Uri;
    use Joomla\Component\Content\Administrator\Extension\ContentComponent;
    use Joomla\Component\Content\Site\Helper\RouteHelper;
    
    // Create a shortcut for params.
    $params = $this->item->params;
    $canEdit = $this->item->params->get('access-edit');
    $info    = $params->get('info_block_position', 0);
    
    // Check if associations are implemented. If they are, define the parameter.
    $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
    
    ?>
    
    <?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
    
    <div class="item-content">
    	<?php if ($this->item->stage_condition == ContentComponent::CONDITION_UNPUBLISHED || strtotime($this->item->publish_up) > strtotime(Factory::getDate())
    		|| (!is_null($this->item->publish_down) && strtotime($this->item->publish_down) < strtotime(Factory::getDate()))) : ?>
    		<div class="system-unpublished">
    	<?php endif; ?>
    
    	<?php echo LayoutHelper::render('joomla.content.blog_style_default_item_title', $this->item); ?>
    
    	<?php if ($canEdit) : ?>
    		<?php echo LayoutHelper::render('joomla.content.icons', array('params' => $params, 'item' => $this->item)); ?>
    	<?php endif; ?>
    
    	<?php // Todo Not that elegant would be nice to group the params ?>
    	<?php $useDefList = ($params->get('show_modify_date') || $params->get('show_publish_date') || $params->get('show_create_date')
    		|| $params->get('show_hits') || $params->get('show_category') || $params->get('show_parent_category') || $params->get('show_author') || $assocParam); ?>
    
    	<?php if ($useDefList && ($info == 0 || $info == 2)) : ?>
    		<?php echo LayoutHelper::render('joomla.content.info_block', array('item' => $this->item, 'params' => $params, 'position' => 'above')); ?>
    	<?php endif; ?>
    	<?php if ($info == 0 && $params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
    		<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
    	<?php endif; ?>
    
    	<?php if (!$params->get('show_intro')) : ?>
    		<?php // Content is generated by content plugin event "onContentAfterTitle" ?>
    		<?php echo $this->item->event->afterDisplayTitle; ?>
    	<?php endif; ?>
    
    	<?php // Content is generated by content plugin event "onContentBeforeDisplay" ?>
    	<?php echo $this->item->event->beforeDisplayContent; ?>
    
    	<?php echo $this->item->introtext; ?>
    
    	<?php if ($info == 1 || $info == 2) : ?>
    		<?php if ($useDefList) : ?>
    			<?php echo LayoutHelper::render('joomla.content.info_block', array('item' => $this->item, 'params' => $params, 'position' => 'below')); ?>
    		<?php endif; ?>
    		<?php if ($params->get('show_tags', 1) && !empty($this->item->tags->itemTags)) : ?>
    			<?php echo LayoutHelper::render('joomla.content.tags', $this->item->tags->itemTags); ?>
    		<?php endif; ?>
    	<?php endif; ?>
    
    	<?php if ($params->get('show_readmore') && $this->item->readmore) :
    		if ($params->get('access-view')) :
    			$link = Route::_(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language));
    		else :
    			$menu = Factory::getApplication()->getMenu();
    			$active = $menu->getActive();
    			$itemId = $active->id;
    			$link = new Uri(Route::_('index.php?option=com_users&view=login&Itemid=' . $itemId, false));
    			$link->setVar('return', base64_encode(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language)));
    		endif; ?>
    
    		<?php echo LayoutHelper::render('joomla.content.readmore', array('item' => $this->item, 'params' => $params, 'link' => $link)); ?>
    
    	<?php endif; ?>
    
    	<?php if ($this->item->stage_condition == ContentComponent::CONDITION_UNPUBLISHED || strtotime($this->item->publish_up) > strtotime(Factory::getDate())
    		|| (!is_null($this->item->publish_down) && strtotime($this->item->publish_down) < strtotime(Factory::getDate()))) : ?>
    	</div>
    	<?php endif; ?>
    </div>
    
    <?php // Content is generated by content plugin event "onContentAfterDisplay" ?>
    <?php echo $this->item->event->afterDisplayContent; ?>
  • The blog_links.php file
    <?php
    /**
     * @package     Joomla.Site
     * @subpackage  com_content
     *
     * @copyright   Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
     * @license     GNU General Public License version 2 or later; see LICENSE.txt
     */
    
    defined('_JEXEC') or die;
    
    use Joomla\CMS\Router\Route;
    use Joomla\Component\Content\Site\Helper\RouteHelper;
    
    ?>
    
    <ol class="com-content-category-blog__links nav nav-tabs nav-stacked">
    	<?php foreach ($this->link_items as &$item) : ?>
    		<li class="com-content-category-blog__link">
    			<a href="<?php echo Route::_(RouteHelper::getArticleRoute($item->slug, $item->catid, $item->language)); ?>">
    				<?php echo $item->title; ?></a>
    		</li>
    	<?php endforeach; ?>
    </ol>

A last step...

Now, go back to the module Smart Search you've created earlier.

Open it and in the tab Advanced select override-kb (or the name given to your override) in the layout parameter.

Joomla - Module parameter

Once it's done, click the Save and close button.

The frontend

I've added some additional CSS to my code but you have followed carefully all the steps of this tutorial, your knowledge base must looks like something like this:

Joomla Knowledge Base Override

The categories frontend view

Joomla Knowledge Base Override

The category frontend view

The Joomla Override Challenge

In association with Viviana Menzel, we've created the unofficial Joomla Override Challenge. The goal is to create each month an override based on an extension or on an project. If you want to participate, feel free to contact Viviana or me :)

Daniel Dubois - auteur à web-eau.net

About Daniel

Passionate about the Web since 2007, Daniel defends the widow and the orphan of the Web by creating W3C-compliant sites. With his experience, he shares his knowledge in an open source mindset. Very involved in favor of the Joomla CMS since 2014, he is the founder of the Joomla User Group Breizh and a speaker in Joomla events.

Website Facebook Twitter Joomla E-mail

Related Articles

web-eau.net

France - 29800 Landerneau

+33 674 502 799

daniel@web-eau.net