In this chapter we will change the output of the extensions in the frontend. In Joomla this is done using
- overrides,
- alternative overrides,
- layouts and
- module chromes.
The standard output of each Joomla extension can be manipulated via files in the template's html
folder. Joomla offers different options for this purpose. Overrides, alternative overrides, layouts and module chromes. Each variant has its purpose.
Overrides are the first choice. If there is already an override for an extension, you create an alternative override. Layouts override a small area of a view and can be reused in different views. Last but not least, module chromes offer a variant to use an override in different places slightly modified.
For impatient people: Take a look at the changed programme code in the Diff-Ansicht[^codeberg.org/astrid/j4examplecode/compare/t36...t37] and copy these changes into your development version.
Step by step
In this section we change the frontend view of com_content/featured
, mod_articles_news
and mod_menu
. Thereby we use all variants possible in Joomla for overwriting. The template is not finished with this. There remain a lot of extensions whose view is not adapted.
My Goal: In the end, we will have discussed all override variations so that you can finish the template or edit your own template according to your needs. Finished will be the home page view of the Joomla 4 blog sample files.
Home page view of the Joomla 4 blog sample in Cassiopeia:
Home page view of the Joomla 4 blog sample in our new template Facile:
Overrides can be created comfortably with the help of the template manager. This offers a view that highlights the differences to Joomla's own code.
Tip: If you want to change a view only slightly, it is a good idea to take the original view as a template. Then you can change it. To do this, create a copy of the existing view in the
html
directory of the template and edit it. The copy is placed in the template directory, exactly as the filetemplates/TEMPLATE_NAME/html/EXTENSION_NAME/VIEW_NAME/FILE_NAME.php
. For example, if you want to change thefeature
view ofcom_content
, then copy the filecomponents/com_content/views/feature/tmpl/default.php
totemplates/TEMPLATE_NAME/html/com_content/feature/default.php
. Similarly, if you want to change the appearance of themod_article_latest
module. Copymodules/tmpl/mod_articles_news/default.php
totemplates/TEMPLATE_NAME/html/mod_articles_news/default.php
. Joomla 4 includes the standard frontend template Cassiopeia. Cassipeia uses template overrides to create the dropdown menu. You can use this as an example. Open the directory\template\cassiopeia
. In the template folder, you will find a subdirectory calledhtml
.
New files
Assets directory
I took the design from the HTML5 UP template TXT[^html5up.net/txt]. This tutorial is about Joomla. Explanations about HTML, SCSS and CSS would go beyond the scope of this post. Therefore I leave them out and concentrate on Joomla.
Override com_content/featured/
(including Layout Override)
The blog sample files use the components/com_content/tmpl/com_content/featured/
view as the start page. The code of this view is complex, at least in Joomla core. I don't need many of these complex features, so I'll limit myself to the essentials. Take a look at the code below. Basically, I go through all the items with the featured/
property and display them using the default_item.php
subtemplate. Joomla provides me with all the properties of an article in the variable $this->items
.
Joomla uses templates and subtemplates like
$this->loadTemplate('item')
or layouts likeLayoutHelper::render('joomla.content.intro_image', $this->item);
to structure the code clearly. I had already mentioned this with the frontend views of the categories in the tutorial part for the component. In the following, we will take a practical look at these functions again. Nachfolgend sehen wir uns diese Funktionen noch einmal praktisch an.
templates/facile/html/com_content/featured/default.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/com_content/featured/default.php
<?php
defined('_JEXEC') or die;
?>
<div>
<h1>
<?php echo $this->escape($this->params->get('page_heading')); ?>
</h1>
<?php if (!empty($this->items)) : ?>
<?php foreach ($this->items as $key => &$item) : ?>
<div>
<?php
$this->item = & $item;
echo $this->loadTemplate('item');
?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
The file /templates/facile/html/com_content/featured/default.php
is an override. It calls a subtemplate using echo $this->loadTemplate('item');
.
templates/facile/html/com_content/featured/default_item.php
The subtemplate templates/facile/html/com_content/featured/default_item.php
- displays an image using the
joomla.content.intro_image
layout, - then creates a linked headline and
- outputs the intro text below it.
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/com_content/featured/default_item.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Router\Route;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\CMS\Layout\LayoutHelper;
?>
<?php echo LayoutHelper::render('joomla.content.intro_image', $this->item); ?>
<div>
<h2>
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($this->item->slug, $this->item->catid, $this->item->language)); ?>">
<?php echo $this->escape($this->item->title); ?>
</a>
</h2>
<?php echo $this->item->introtext; ?>
</div>
Joomla first searches for files in the template directory. Therefore we create our own layout later. We will save it under templates/facile/html/layouts/joomla/content/intro_image.php
. Our own layout shows the image in the correct size. Since the file layouts/joomla/content/intro_image.php
exists directly in the Joomla root directory, it would otherwise be used for the display. If we had no special requirements, we could make it easy and use this Joomla own layout layouts/joomla/content/intro_image.php
.
If the file
templates/facile/html/layouts/joomla/content/intro_image.php
did not exist, next the directorylayouts/ joomla/content/
would be searched and the fileintro_image.php
there would be used for display.
templates/facile/html/layouts/joomla/content/intro_image.php
The layout joomla.content.intro_image
is used in many places in Joomla.
In addition to the override of entire views, Joomla supports the override of smaller code segments, so-called layouts. Layouts are used by Joomla in various places. For example, to generate the code that creates the search and sort filters in list views or when displaying post information (such as author, creation date...) above or below a post.
Because our template is built differently and expects different CSS elements, the display of the image via joomla.content.intro_image
is not optimal. Therefore we overwrite the layout in our template. Because we want to reuse this, we do it in a way that we can also access our layout in other places via echo LayoutHelper::render('joomla.content.intro_image', $this->item);
. For this we create the file templates/facile/html/layouts/joomla/content/intro_image.php
.
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/layouts/joomla/content/intro_image.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\Component\Content\Site\Helper\RouteHelper;
use Joomla\CMS\Router\Route;
$images = json_decode($displayData->images);
$img = HTMLHelper::cleanImageURL($images->image_intro);
$alt = empty($images->image_intro_alt) && empty($images->image_intro_alt_empty) ? '' : 'alt="'. htmlspecialchars($images->image_intro_alt, ENT_COMPAT, 'UTF-8') .'"';
?>
<a href="<?php echo Route::_(RouteHelper::getArticleRoute($displayData->slug, $displayData->catid, $displayData->language)); ?>" class="image featured">
<img src="<?php echo htmlspecialchars($img->url, ENT_COMPAT, 'UTF-8'); ?>" alt="<?php echo $alt; ?>" />
</a>
Again for comparison: The original Joomla-own file of the layout
joomla.content.intro_image
is located in the directorylayouts/ joomla/content/intro_image.php
. The special file for our template is saved undertemplates/facile/html/
+layouts/joomla/content/intro_image.php
.
Override via Module Chrome mod_articles_news
At the top of the home page, the Joomla Blog sample data displays the module mod_articles_news
. We create a standard override analogous to the view of the main articles in com_content/featured/
, in which we include the items in a subtemplate. The code of the two files mod_articles_news/_item.php
and mod_articles_news/default.php
can be found below. These only support the necessary functions and are therefore clearly compact for learning.
templates/facile/html/mod_articles_news/_item.php
also contains a layout.joomla.content.readmore
contains the code that creates a readmore link. This is a function that is used in many views and is therefore a good example of reusability.
templates/facile/html/mod_articles_news/_item.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/mod_articles_news/_item.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Layout\LayoutHelper;
?>
<div class="col-4 col-12-medium col-12-small">
<section class="box feature">
<a href="<?php echo $item->link; ?>" class="image featured"><img src="<?php echo $item->imageSrc; ?>" alt="<?php echo $item->imageAlt; ?>"/></a>
<h3><a href="<?php echo $item->link; ?>"><?php echo $item->title; ?></a></h3>
<p>
<?php echo $item->introtext; ?>
<?php echo LayoutHelper::render('joomla.content.readmore', ['item' => $item, 'params' => $item->params, 'link' => $item->link]); ?>
</p>
</section>
</div>
templates/facile/html/mod_articles_news/default.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/mod_articles_news/default.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Helper\ModuleHelper;
if (empty($list)) {
return;
}
?>
<div>
<div class="row">
<!-- Feature -->
<?php foreach ($list as $item) : ?>
<?php require ModuleHelper::getLayoutPath('mod_articles_news', '_item'); ?>
<?php endforeach; ?>
</div>
</div>
The override to the module 'mod_articles_news' should be displayed in the upper area with a large headline. On a subpage, it should appear with small headline in the sidebar. We could create a solution with an alternative override. This variant is the subject of the next section. However, a lot of program code would be written via alternativen Override redundantly. Actually, only the first line with the heading is different. And here Joomlas module Chromes comes into play. We create a file in the directory templates/facile/html/layouts/chromes/
which only contains the different code and otherwise embeds the module exactly as it is. The latter is taken care of by echo $module->content;
. We can name the modules chrome file anything we want. I have chosen hr.php
as name. In the index.php
at the end of this section you can see how to make sure that the hr.php
file is integrated in the header of the page but not in the sidebar.
templates/facile/html/layouts/chromes/hr.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/layouts/chromes/hr.php
<?php
defined('_JEXEC') or die;
$module = $displayData['module'];
?>
<section class="box features">
<h2 class="major"><span>News</span></h2>
<?php echo $module->content; ?>
</section>
The alternative override mod_menu
.
There are requirements where the design of a module varies greatly in different places. In this case it is necessary to create two different files. The file default.php
is actually the override. If we create another file in the directory next to default.php
, this is an alternative override. A use case is a menu. In the header, the main menu often looks quite different from the one in the footer. In our template the main menu is implemented in the file default.php
and the footer menu in the file bottom.php
.
Note: The two files differ slightly. In the
bottom.php
file, the<ul>
element must be given the classmenu
so that no list item symbols are displayed in the frontend view. This could also be handled via a Chrome module.
templates/facile/html/mod_menu/default.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/mod_menu/default.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Helper\ModuleHelper;
?>
<ul>
<?php foreach ($list as $i => &$item) {
$itemParams = $item->getParams();
$class = '';
if ($item->id == $active_id) {
$class .= ' current';
}
echo '<li class="' . $class . '">';
require ModuleHelper::getLayoutPath('mod_menu', 'default_url');
// The next item is deeper.
if ($item->deeper) {
echo '<ul>';
}
// The next item is shallower.
else if ($item->shallower) {
echo '</li>';
echo str_repeat('</ul></li>', $item->level_diff);
}
// The next item is on the same level.
else {
echo '</li>';
}
}
?></ul>
templates/facile/html/mod_menu/bottom.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t37/src/templates/facile/html/mod_menu/bottom.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Helper\ModuleHelper;
?>
<ul class="menu">
<?php foreach ($list as $i => &$item) {
$itemParams = $item->getParams();
$class = '';
if ($item->id == $active_id) {
$class .= ' current';
}
echo '<li class="' . $class . '">';
require ModuleHelper::getLayoutPath('mod_menu', 'default_url');
// The next item is deeper.
if ($item->deeper) {
echo '<ul>';
}
// The next item is shallower.
else if ($item->shallower) {
echo '</li>';
echo str_repeat('</ul></li>', $item->level_diff);
}
// The next item is on the same level.
else {
echo '</li>';
}
}
?></ul>
Modified files
templates/facile/index.php
The following index.php
is adapted to the newly added CSS styles and now outputs a more appealing design in the frontend.
The line <jdoc:include type="modules" name="top-a" style="hr" />
ensures that the Chrome hr
module is added at this point to display the module.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Titel</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="<?php echo $templatePath; ?>/assets/css/main.css" />
<title>Titel</title>
</head>
<body>
<header>
<div>
<nav>
<div>
<jdoc:include type="modules" name="menu" />
</div>
</nav>
<div>
<jdoc:include type="modules" name="search" />
</div>
</div>
</header>
<div>
<jdoc:include type="modules" name="banner" />
</div>
<div>
<jdoc:include type="modules" name="top-a" />
</div>
<div>
<jdoc:include type="modules" name="top-b" />
</div>
<div>
<jdoc:include type="modules" name="sidebar-left" />
</div>
<div>
<jdoc:include type="modules" name="breadcrumbs" />
<jdoc:include type="modules" name="main-top" />
<jdoc:include type="message" />
<main>
<jdoc:include type="component" />
</main>
<jdoc:include type="modules" name="main-bottom" />
</div>
<div>
<jdoc:include type="modules" name="sidebar-right" />
</div>
<div>
<jdoc:include type="modules" name="bottom-a" />
</div>
<div>
<jdoc:include type="modules" name="bottom-b" />
</div>
<footer>
<jdoc:include type="modules" name="footer" />
</footer>
<jdoc:include type="modules" name="debug" />
<body class="homepage is-preload">
<div id="page-wrapper">
<?php if ($this->countModules('menu', true)) : ?>
<nav id="nav">
<jdoc:include type="modules" name="menu" />
</nav>
<?php endif; ?>
<section id="main">
<div class="container">
<div class="row gtr-200">
<div class="row">
<?php if ($this->countModules('top-a', true)) : ?>
<jdoc:include type="modules" name="top-a" style="hr" />
<?php endif; ?>
<?php if ($this->countModules('sidebar-left', true)) : ?>
<div class="col-3 col-12-medium">
<div class="sidebar">
<jdoc:include type="modules" name="sidebar-left" style="none" />
</div>
</div>
<?php endif; ?>
<div class="col-6 col-12-medium imp-medium">
<div class="content">
<?php if ($this->countModules('search', true)) : ?>
<section id="search">
<jdoc:include type="modules" name="breadcrumbs" style="none" />
</section>
<?php endif; ?>
<?php if ($this->countModules('search', true)) : ?>
<section id="search">
<jdoc:include type="modules" name="search" style="none" />
</section>
<?php endif; ?>
<jdoc:include type="modules" name="main-top" style="none" />
<jdoc:include type="message" />
<main>
<jdoc:include type="component" />
</main>
<jdoc:include type="modules" name="main-bottom" style="none" />
</div>
</div>
<?php if ($this->countModules('sidebar-right', true)) : ?>
<div class="col-3 col-12-medium">
<div class="sidebar">
<jdoc:include type="modules" name="sidebar-right" style="none" />
</div>
</div>
<?php endif; ?>
<?php if ($this->countModules('bottom-a', true)) : ?>
<jdoc:include type="modules" name="bottom-a" style="none" />
<?php endif; ?>
</div>
</div>
</div>
</section>
<footer id="footer">
<?php if ($this->countModules('footer', true)) : ?>
<div id="copyright">
<jdoc:include type="modules" name="footer" />
</div>
<?php endif; ?>
</footer>
<jdoc:include type="modules" name="debug" />
<script src="<?php echo $templatePath; ?>/assets/js/jquery.min.js"></script>
<script src="<?php echo $templatePath; ?>/assets/js/jquery.dropotron.min.js"></script>
<script src="<?php echo $templatePath; ?>/assets/js/jquery.scrolly.min.js"></script>
<script src="<?php echo $templatePath; ?>/assets/js/browser.min.js"></script>
<script src="<?php echo $templatePath; ?>/assets/js/breakpoints.min.js"></script>
<script src="<?php echo $templatePath; ?>/assets/js/util.js"></script>
<script src="<?php echo $templatePath; ?>/assets/js/main.js"></script>
</div>
</body>
</html>
Tip: To avoid adding elements unnecessarily it is good practice to check if a module position is used in the Joomla installation. This is done with
$this->countModules('NAME_DER_POSITIONS', true)
.
I deleted the banner module, because I want to add a banner later using parameters.
Test your Joomla template
- install your template in Joomla version 4 to test it:
Copy the files in the templates
folder to the templates
folder of your Joomla 4 installation.
A new installation is not necessary. Continue using the ones from the previous part.
We have installed the sample data in the previous chapter. If you have not done so, please do it now so that the modules shown in the next image are available on the homepage of the Joomla installation.
- open the module
Bottom Menu
and choose as layoutbottom
. For the moduleMain Menu Blog
replace the layoutDropdown
with the default layoutDefault
from Module.
- open the module
mod_articles_news
(Articles - Newsflash
) with the nameLatest Posts
, which is shown in the header of the frontend. In the explanations ofindex.php
you have learned that a module Chrome is activated via the parameterstyle="hr"
in<jdoc:include type="modules" name="top-a" style="hr" />
. But you can also set this in the backend. The next picture shows you how to do this in theAdvanced
tab via theModule Style
parameter.
- Play with the different possibilities. Create different types of overrides and test the output in the frontend.
All override files in the directory templates/facile/html/com_content/article
are available for selection in the tab Options
in the selection field Layout
when creating an article.
When creating a menu item, you only have the overrides to choose from, for which you have created an XML file.
Attention: You will not get an error message if you create an XML file but the related PHP file is missing due to a typo. There is also no hint if you create two XML files with the same title. Joomla pretends in this case that there is only one of them. In the next screen, the file names are all correct. However, the title 'COM_CONTENT_ARTICLE_VIEW_DEFAULT_TITLE' already exists. When I created an article, the override was only offered for selection after I changed the language string to COM_CONTENT_ARTICLE_VIEW_DEFAULT_MEINSPRECHENDERNAME_TITLE
.
Webmentions