We'll start with the basics. For this we create the View in the administration area rudimentary. At the end of this text you know how to insert a menu item in the menu of the administration area. Via the menu item you open the view to your component. Don't be disappointed: This view contains nothing more than a short text. You have a good basis for the next steps.
For impatient people: View the changed program code in the Diff View[^codeberg.org/astrid/j4examplecode/compare/t0...t1] and copy these changes into your development version.
Step by step
New files
administrator/components/com_foos/foos.xml
foos.xml
tells Joomla how to install our component. Just like modules and plugins, components have an XML installation file that informs Joomla about the extension to be installed. This file is called a manifest and contains details such as
- the version number,
- all files and folders used by the component,
- details of the database,
- and component parameters.
Create a new file and name it foos.xml
. This is the name of the extension without the prefix com_
. We will then go through each line and see what it does.
The first line is not specific to Joomla. It tells us that this is an XML file
<?xml version="1.0" encoding="utf-8" ?>
Then we tell Joomla that this is a component. And we want the upgrade installation method to be used. So it is possible to use this package not only for installation but also for an upgrade
<extension type="component" method="upgrade">
Sometimes you will find a parameter with a version number in the
<extension>
tag of the manifest. This is not used anywhere, so it is unnecessary. For more information, see github.com/joomla/joomla-cms/pull/25820.
Then we define the name of the component. In this case COM_FOOS
.
<name>COM_FOOS</name>
Read more about the name at github.com/joomla/joomla-cms/issues/26221.
The next lines are self-explanatory. Add your data.
<creationDate>[DATE]</creationDate>
<author>[AUTHOR]</author>
<authorEmail>[AUTHOR_EMAIL]</authorEmail>
<authorUrl>[AUTHOR_URL]</authorUrl>
<copyright>[COPYRIGHT]</copyright>
<license>GNU General Public License version 2 or later;</license>
This is the first version of the component. We will give it the version number 1.0.0
: <version>1.0.0</version>
. If we fix a small bug, the next number would be 1.0.1
. If we introduce a new feature, we choose 1.1.0
. If we made major changes that alter implementations in earlier versions, the next version would be 2.0.0
. It is important that you use the three-part version numbering, as this makes it easier to create updates later using semantic versioning.
Joomla follows semantic versioning[^developer.joomla.org/news/586-joomla-development-strategy.html#version_numbering]. I recommend this as well.
In the description field we use a language string.
<description>COM_FOOS_ XML_DESCRIPTION</description>
At the moment, this has no effect. Later, this text will change based on the language files we introduce in one of the next chapters.
The description of the component will be shown during installation and when you click the menu System
and open the submenu Manage | Extensions
.
Next we set the HTML tag for the namespace. In the preface I explained why we use namespaces. Now we create it practically. How do you name your namespace?
-
The first element of the namespace is your CompanyName. For this tutorial I have used
FooNamespace
. It is used to distinguish the code from the code in other extensions. This makes it possible to use identical class names without conflicts. The namespace is also used to register a service provider. A service provider is a PHP class that provides services. -
The second element is the type of extension: component, module, plugin, language or template.
-
The third element is the name of the extension without the preceding
com_
,mod_
,plg_
ortpl_
, in our caseFoos
.
<namespace>FooNamespace\Component\Foos</namespace>
With the script
file you call code when your component is installed, uninstalled or updated.
<scriptfile>script.php </scriptfile>
Like Joomla itself, components have a frontend and an administration area. The folder administrator/components/ com_foos
contains all files used by the backend. You add individual files with the tag 'filename'. For a complete directory it is better to use the tag folder
. The files for the administration area of your component are all inside the tag administration
. Here is also a menu
tag. This is the menu item that is displayed in the sidebar in the backend. We use the language string COM_FOOS
, which we will replace later with text from a language file.
<administration>
<!-- Menu entries -->
<menu view="foos">COM_FOOS</menu>
<submenu>
<menu link="option=com_foos">COM_FOOS</menu>
</submenu>
<files folder="administrator/components/com_foos">
<filename>foos.xml</filename>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
</administration>
We will look at the changelogurl
and updateservers
tags in more detail in the next chapter.
<changelogurl>https://codeberg.org/astrid/j4examplecode/raw/branch/tutorial/changelog.xml</changelogurl>
<updateservers>
<server type="extension" name="Foo Updates">https://codeberg.org/astrid/j4examplecode/raw/branch/tutorial/foo_update.xml</server>
</updateservers>
Let's move on to the dlid-tag.
<dlid prefix="dlid=" suffix="" />
You need this if you use the Download Key Manager. In general, this is only the case for commercial extensions. You can find more information on Github at github.com/joomla/joomla-cms/pull/25553.
Finally, we close the </extension>
tag. Here is the complete code:
administrator/components/com_foos/foos.xml
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/foos.xml -->
<?xml version="1.0" encoding="utf-8" ?>
<extension type="component" method="upgrade">
<name>COM_FOOS</name>
<creationDate>[DATE]</creationDate>
<author>[AUTHOR]</author>
<authorEmail>[AUTHOR_EMAIL]</authorEmail>
<authorUrl>[AUTHOR_URL]</authorUrl>
<copyright>[COPYRIGHT]</copyright>
<license>GNU General Public License version 2 or later;</license>
<version>__BUMP_VERSION__</version>
<description>COM_FOOS_XML_DESCRIPTION</description>
<namespace path="src">FooNamespace\Component\Foos</namespace>
<scriptfile>script.php</scriptfile>
<!-- Back-end files -->
<administration>
<!-- Menu entries -->
<menu view="foos">COM_FOOS</menu>
<submenu>
<menu link="option=com_foos">COM_FOOS</menu>
</submenu>
<files folder="administrator/components/com_foos">
<filename>foos.xml</filename>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
</administration>
<changelogurl>https://codeberg.org/astrid/j4examplecode/raw/branch/tutorial/changelog.xml</changelogurl>
<updateservers>
<server type="extension" name="Foo Updates">https://codeberg.org/astrid/j4examplecode/raw/branch/tutorial/foo_update.xml</server>
</updateservers>
<dlid prefix="dlid=" suffix="" />
</extension>
administrator/components/com_foos/script.php
With the installation script file you call code
- when your component is installed,
- before your component is installed,
- when your component is uninstalled,
- before your component is uninstalled,
- or when your component is updated.
Create the file script.php
with the following content:
administrator/components/com_foos/script.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/script.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 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\Installer\InstallerAdapter;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
/**
* Script file of Foo Component
*
* @since __BUMP_VERSION__
*/
class Com_FoosInstallerScript
{
/**
* Minimum Joomla version to check
*
* @var string
* @since __BUMP_VERSION__
*/
private $minimumJoomlaVersion = '4.0';
/**
* Minimum PHP version to check
*
* @var string
* @since __BUMP_VERSION__
*/
private $minimumPHPVersion = JOOMLA_MINIMUM_PHP;
/**
* Method to install the extension
*
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*
* @since __BUMP_VERSION__
*/
public function install($parent): bool
{
echo Text::_('COM_FOOS_INSTALLERSCRIPT_INSTALL');
return true;
}
/**
* Method to uninstall the extension
*
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*
* @since __BUMP_VERSION__
*/
public function uninstall($parent): bool
{
echo Text::_('COM_FOOS_INSTALLERSCRIPT_UNINSTALL');
return true;
}
/**
* Method to update the extension
*
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*
* @since __BUMP_VERSION__
*
*/
public function update($parent): bool
{
echo Text::_('COM_FOOS_INSTALLERSCRIPT_UPDATE');
return true;
}
/**
* Function called before extension installation/update/removal procedure commences
*
* @param string $type The type of change (install, update or discover_install, not uninstall)
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*
* @since __BUMP_VERSION__
*
* @throws Exception
*/
public function preflight($type, $parent): bool
{
if ($type !== 'uninstall') {
// Check for the minimum PHP version before continuing
if (!empty($this->minimumPHPVersion) && version_compare(PHP_VERSION, $this->minimumPHPVersion, '<')) {
Log::add(
Text::sprintf('JLIB_INSTALLER_MINIMUM_PHP', $this->minimumPHPVersion),
Log::WARNING,
'jerror'
);
return false;
}
// Check for the minimum Joomla version before continuing
if (!empty($this->minimumJoomlaVersion) && version_compare(JVERSION, $this->minimumJoomlaVersion, '<')) {
Log::add(
Text::sprintf('JLIB_INSTALLER_MINIMUM_JOOMLA', $this->minimumJoomlaVersion),
Log::WARNING,
'jerror'
);
return false;
}
}
echo Text::_('COM_FOOS_INSTALLERSCRIPT_PREFLIGHT');
return true;
}
/**
* Function called after extension installation/update/removal procedure commences
*
* @param string $type The type of change (install, update or discover_install, not uninstall)
* @param InstallerAdapter $parent The class calling this method
*
* @return boolean True on success
*
* @since __BUMP_VERSION__
*
*/
public function postflight($type, $parent)
{
echo Text::_('COM_FOOS_INSTALLERSCRIPT_POSTFLIGHT');
return true;
}
}
The install
function, as the name suggests, is called when the component is installed. At the moment, text is output. It is possible to install sample data.
uninstall
is called when someone uninstalls the component. At the moment, only text is displayed.
The update function update
is called whenever you update the component. Have there been changes to save locations in the extension? In that case, you might want to delete files? Then the update
method could look like this:
public function update($parent)
{
$this->deleteFiles[] = '/administrator/language/en-GB/?.ini';
$this->deleteFiles[] = '/administrator/language/en-GB/?.sys.ini';
$this->removeFiles();
return true;
}
The preflight
function is called before the component is installed, discover_installed, updated or uninstalled. You can add code here to check the prerequisites like the PHP version or to check if another extension is installed or not.
The postflight
function is called after the component has been installed, discover_installed, updated or uninstalled. This function is used to set default values for component parameters.
Note: In Joomla 3, only plugins called the Preflight method during the uninstall process and Postflight was never used during uninstall. As of version 4.0, these two hooks are available during the uninstall for all extension types.
Do you want to know exactly when which method is called? Then have a look at the file
/libraries/src/Installer/InstallerAdapter.php
. The commands$this->triggerManifestScript('');
will start the execution of the related method. For example, thepostflight
function is triggered via$this->triggerManifestScript('postflight');
. See Potential backward compatibility issues in Joomla 4[^docs.joomla.org/Potential_backward_compatibility_issues_in_Joomla_4#CMS_Libraries].
administrator/components/com_foos/services/provider.php
provider.php
is used to implement the component services. Via an interface, the component class defines which services it provides. A dependency injection container or DI container is used for this. To register, ComponentDispatcherFactory
and MVCFactory
are mandatory for each component. Registering CategoryFactory
is at this place optional, we need CategoryFactory
when we integrate categories later. Using provider.php
it is possible to introduce new services without breaking backwards compatibility (BC). If you are not familiar with the concept of DI Container but would like to learn more, you can find explanations and some examples in the following links:
- joomla-framework/di[^github.com/joomla-framework/di].
- docs/why-dependency-injection.md[^github.com/joomla-framework/di/blob/master/docs/why-dependency-injection.md].
More information about the implementation can be found on Github[^github.com/joomla/joomla-cms/pull/20217].
You often see the word 'factory' in Joomla. This is because Joomla uses the factory design pattern[^en.wikipedia.org/wiki/factory_method_pattern]. The factory method is a pattern where the interface to create an object is an abstract method of an inheriting class. However, the concrete implementation of the creation of new objects does not take place in the superclass, but in subclasses derived from it. The latter implement the said abstract method. To program extensions for Joomla it is not mandatory that you know the design patterns. However, it can be worthwhile to think outside the box. In software engineering, a design pattern[^en.wikipedia.org/wiki/software_design_pattern] is a general, reusable solution to a common problem. Someone else had the same problem and found a solution. We don't have to solve the same problem, but can build on it.
administrator/components/com_foos/services/provider.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/services/provider.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 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\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\CategoryFactory;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\HTML\Registry;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use FooNamespace\Component\Foos\Administrator\Extension\FoosComponent;
/**
* The foos service provider.
* https://github.com/joomla/joomla-cms/pull/20217
*
* @since __BUMP_VERSION__
*/
return new class implements ServiceProviderInterface
{
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since __BUMP_VERSION__
*/
public function register(Container $container)
{
$container->registerServiceProvider(new CategoryFactory('\\FooNamespace\\Component\\Foos'));
$container->registerServiceProvider(new MVCFactory('\\FooNamespace\\Component\\Foos'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\FooNamespace\\Component\\Foos'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new FoosComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setRegistry($container->get(Registry::class));
return $component;
}
);
}
};
administrator/components/com_foos/src/Controller/DisplayController.php
The file DisplayController.php
is the entry point for the Model View Controller part in the administration area of the Foo component. Name the class DisplayController. Joomla expects it like this. Extend BaseController to use many things out-of-the-box.
The main task of this controller is to prepare the display. Therefore the default controller is called DisplayController. It calls the method display()
of the parent class BaseController
in the namespace Joomla\CMS\MVC\Controller
- exactly this is the file libraries/src/MVC/Controller/ BaseController.php
. In the Model-View-Controller model, controllers are often used to set up the start environment.
Let's create the DisplayController. As always, we first create the DocBlock. Here is an example of a typical document block.
What does
__BUMP_VERSION__
or__DEPLOY_VERSION__
stand for? Sometimes you see strange text like this in a DocBlock. For example in PR 27712[^github.com/joomla/joomla-cms/pull/27712/files]. In Joomla, we put__DEPLOY_VERSION__
in place of the version number of a new method we create. Since we don't know in which version this new code will be accepted in Joomla, we can't use a real version number. When the new code is added to the core, this strange string is automatically replaced with the current version number. In other systems__BUMP_VERSION__
is common. I use__BUMP_VERSION__
here as well.
How to create DocBlocks for Joomla is explained in the Joomla coding standards at developer.joomla.org/ coding-standards/docblocks.html.
A DocBlock is displayed before each class and before each function. All code contains DocBlock comments, which make it easier for automated tools to generate documentation. In addition it helps some IDEs to provide code completion. And sometimes the comment is helpful for programmers. I don't print the documentary blocks further here. In the code examples on Github, the DocBlocks are still present.
After the DocBlock you add the namespace.
namespace FooNamespace\Component\Foos\Administrator\Controller;
You declare this with the corresponding keyword. Namespaces were introduced in Joomla 4. If this concept is new to you, read the overview of namespaces at php.net[^php.net/manual/en/language.namespaces.php]. It is important that it is in the file before any other code.
After the namespace we add
\defined('_JEXEC') or die;`
so that this PHP file is not directly callable.
Next, we import the namespace of the parent class BaseController
with the keyword use
to be able to use this class.
use Joomla\CMS\MVC\Controller\BaseController;
Then we create the class for the controller. I already wrote that you should call this DisplayController
and extend the class BaseController
. Then define the variable $default_view
in which you set the default view with foos
. You choose foos
as the view because the name of the component is foos
and for this reason you will also created the directory /administrator/components/ com_foos/src/View/Foos
. If nothing is defined, the Foos view with the default layout is used by default. Setting this variable is not necessary. But I think it is always better to insert this line.
If you take a closer look at the URL while using a component in the administration area, you may notice the view and layout variables. For example, the URL index.php ?option=com_foos &view=foos &layout=default
loads the foos
view with the default layout default
. Thus, the file components/
+ com_foos/tmpl/foos/
+ default.php
is called when you are in the frontend. If you are working in the backend, administrator/components/
+ com_foos/tmpl/foos/
+ default.php
is used.
The visibility is defined in PHP with
public
,private
orprotected
. When to use which is explained in the PHP manual[^php.net/manual/en/language.oop5.visibility.php].
Create everything as it is intended in Joomla. This will bring you advantages. For many frequently used functions, you do not reinvent the wheel. You can see this in practice with the display
method. You do not implement any action in your code. All the work is done by parent::display()
.
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/src/Controller/DisplayController.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Administrator\Controller;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\Controller\BaseController;
/**
* Foos master display controller.
*
* @since __BUMP_VERSION__
*/
class DisplayController extends BaseController
{
/**
* The default view.
*
* @var string
* @since __BUMP_VERSION__
*/
protected $default_view = 'foos';
/**
* Method to display a view.
*
* @param boolean $cachable If true, the view output will be cached
* @param array $urlparams An array of safe URL parameters and their variable types, for valid values see {@link \JFilterInput::clean()}.
*
* @return BaseController|bool This object to support chaining.
*
* @since __BUMP_VERSION__
*
* @throws \Exception
*/
public function display($cachable = false, $urlparams = [])
{
return parent::display();
}
}
administrator/components/com_foos/src/Extension/FoosComponent.php
FoosComponent.php
is the code for booting the extension. It is the first file that is called when Joomla loads the component. Boot' is the function to set up the environment of the extension, such as registering new classes. For more information, see the pull request github.com/joomla/joomla-cms/pull/20217. In the following we will expand the file FoosComponent.php
.
administrator/components/com_foos/src/Extension/FoosComponent.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/src/Extension/FoosComponent.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Administrator\Extension;
defined('JPATH_PLATFORM') or die;
use Joomla\CMS\Categories\CategoryServiceInterface;
use Joomla\CMS\Categories\CategoryServiceTrait;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use FooNamespace\Component\Foos\Administrator\Service\HTML\AdministratorService;
use Psr\Container\ContainerInterface;
/**
* Component class for com_foos
*
* @since __BUMP_VERSION__
*/
class FoosComponent extends MVCComponent implements BootableExtensionInterface, CategoryServiceInterface
{
use CategoryServiceTrait;
use HTMLRegistryAwareTrait;
/**
* Booting the extension. This is the function to set up the environment of the extension like
* registering new class loaders, etc.
*
* If required, some initial set up can be done from services of the container, eg.
* registering HTML services.
*
* @param ContainerInterface $container The container
*
* @return void
*
* @since __BUMP_VERSION__
*/
public function boot(ContainerInterface $container)
{
$this->getRegistry()->register('foosadministrator', new AdministratorService);
}
}
administrator/components/com_foos/src/Service/HTML/AdministratorService.php
Although we are developing the code for a minimal component, some administrator files are needed. The file AdministratorService.php
will be used later to add functions like multilingualism or main entries/featured. At the moment we do not need these functions. But we are already preparing everything here.
administrator/components/com_foos/src/Service/HTML/AdministratorService.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/src/Service/HTML/AdministratorService.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Administrator\Service\HTML;
defined('JPATH_BASE') or die;
/**
* Foo HTML class.
*
* @since __BUMP_VERSION__
*/
class AdministratorService
{
}
administrator/components/com_foos/src/View/Foos/HtmlView.php
The view administrator/components/com_foos/src/View/Foos/HtmlView.php
defines objects (toolbar, title) and calls the model (data). At the moment our component has a primitive view. Only a static text is shown. This will change soon! There are several files that work together to generate the view in the frontend. For example, the controller that calls the view is involved. We created the controller earlier in the current chapter. Later, we will add the model, which prepares the data.
In the file HtmlView.php
all buttons and titles of the toolbar are defined. The model is called to prepare the data for the view. At the moment we only call the function of the parent class to display the default template: parent::display($tpl);
. Why do it yourself when there are functions in Joomla to do it?
When naming a view, it is best to use only a capital letter as the initial letter. I had a problem with the name of an additional
View
. I usedFOOPlaces
. The view was not found under this name. After I renamed the view folder and namespace toFooplaces
, everything works fine. I found an explanation of naming conventions on Github[^docs.joomla.org/j4.x:file_structure_and_naming_conventions]. According to this page, the folder name for the template should be written in lower case. It does not say that in addition the view is allowed to use an uppercase letter only for initial letters. According to a discussion[^github.com/joomla/joomla-cms/discussions/36679] this is nevertheless the case.
administrator/components/com_foos/src/View/Foos/HtmlView.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/src/View/Foos/HtmlView.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace FooNamespace\Component\Foos\Administrator\View\Foos;
\defined('_JEXEC') or die;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
/**
* View class for a list of foos.
*
* @since __BUMP_VERSION__
*/
class HtmlView extends BaseHtmlView
{
/**
* Method to display the view.
*
* @param string $tpl A template file to load. [optional]
*
* @return void
*
* @since __BUMP_VERSION__
*/
public function display($tpl = null): void
{
parent::display($tpl);
}
}
administrator/components/com_foos/tmpl/foos/default.php
The file default.php
is the template for rendering the view. You can further identify them by the directory name tmpl
. In it is the text that we display. At the moment we are putting all the effort into the output of the text "Hello Foos".
administrator/components/com_foos/tmpl/foos/default.php
// https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/administrator/components/com_foos/tmpl/foos/default.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_foos
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
?>
Hello Foos
components/com_foos/index.html and api/com_foos/index.html
I wrote in the preface that the file index.html
is not needed. That is correct! Here I only added it because I am putting together an installation package, but Joomla reports an error during the installation if there is no folder for the frontend or if an empty directory is passed in the installation package. At the moment we have no content for the frontend. The temporary insertion of the file is therefore only a help at this point to avoid error messages during the installation. I create the folder api
for the sake of completeness.
components/com_foos/index.html
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t1/src/components/com_foos/index.html -- >
<!DOCTYPE html><title></title>
Changed files
Everything is new. There are no changed files yet.
Test your Joomla component
-
install your component in Joomla version 4 to test it: In the beginning, the easiest thing to do is to copy the files manually in place. Copy the files in the
administrator
folder into theadministrator
folder of your Joomla 4 installation. Copy the files in thecomponents
folder into thecomponents
folder of your Joomla 4 installation. -
open the menu
System | Install | Discover
. Here you will see an entry for the component you just copied. Select it and click on the button 'Install
.
- if everything works, you will see these displays in front of you after the installation.
- next test if you get the view for your component without errors.
Up to this point, it wasn't rocket science. We have a solid basis for the next steps.
Webmentions