Not everyone has the right to edit all content. For this purpose Joomla offers an access control list, the ACL. With this you manage user rights in your component.
For impatient people: View the changed program code in the Diff View[^codeberg.org/astrid/j4examplecode/compare/t9...t10] and copy these changes into your development version.
Step by step
New files
administrator/components/com_foos/access.xml
First, we set all possible permissions in an XML file. Each component can define individual permissions. I orientate myself here on the usual actions in Joomla. core.admin
thereby determines which groups are allowed to configure the permissions at component level via the options
button in the toolbar. core.manage
determines which groups are allowed to access the backend of the component.
The Access Control List Tutorial[^docs.joomla.org/j3.x:access_control_list_tutorial] is a helpful read, even if it is related to Joomla 3x.
administrator/components/com_foos/access.xml
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t10/src/administrator/components/com_foos/access.xml -->
<?xml version="1.0" encoding="utf-8"?>
<access component="com_foos">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" />
<action name="core.options" title="JACTION_OPTIONS" />
<action name="core.manage" title="JACTION_MANAGE" />
<action name="core.create" title="JACTION_CREATE" />
<action name="core.delete" title="JACTION_DELETE" />
<action name="core.edit" title="JACTION_EDIT" />
<action name="core.edit.state" title="JACTION_EDITSTATE" />
<action name="core.edit.own" title="JACTION_EDITOWN" />
</section>
</access>
administrator/components/com_foos/sql/updates/mysql/10.0.0.sql
Joomla stores the permissions in the database. Regarding the database, changes are relevant during a Joomla update. We enter these in the file administrator/components/com_foos/sql/updates/mysql/VERSIONSNUMMER.sql
, here this is specifically administrator/components/com_foos/sql/updates/mysql/10.0.0.sql
. This file is only called during an update. In case of a new installation the database will be set up via the main file administrator/components/com_foos/sql/install.mysql.utf8.sql
.
administrator/components/com_foos/sql/updates/mysql/10.0.0.sql
<!-- https://codeberg.org/astrid/j4examplecode/raw/branch/t10/src/administrator/components/com_foos/sql/updates/mysql/10.0.0.sql -->
ALTER TABLE `#__foos_details` ADD COLUMN `access` int(10) unsigned NOT NULL DEFAULT 0 AFTER `alias`;
ALTER TABLE `#__foos_details` ADD KEY `idx_access` (`access`);
Modified files
administrator/components/com_foos/config.xml
We set the permissions for the entire component in the configuration. For this we integrate a special form field. Joomla offers the field type rules
for this.
administrator/components/com_foos/config.xml
<option value="1">JYES</option>
</field>
</fieldset>
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
description="JCONFIG_PERMISSIONS_DESC"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
validate="rules"
filter="rules"
component="com_foos"
section="component"
/>
</fieldset>
</config>
administrator/components/com_foos/foos.xml
To make sure that everything runs smoothly during the installation, we add the new file and folder sql/updates/mysql
and access.xml
in the XML-manifest.
administrator/components/com_foos/foos.xml
<file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file>
</sql>
</uninstall>
<update> <!-- Runs on update -->
<schemas>
<schemapath type="mysql">sql/updates/mysql</schemapath>
</schemas>
</update>
<!-- Frond-end files -->
<files folder="components/com_foos">
<folder>language</folder>
<menu link="option=com_foos">COM_FOOS</menu>
</submenu>
<files folder="administrator/components/com_foos">
<filename>access.xml</filename>
<filename>foos.xml</filename>
<filename>config.xml</filename>
<folder>forms</folder>
administrator/components/com_foos/forms/foo.xml
We extend the form for creating a new Foo item with the possibility to set permissions for a single item. We add the field name="access"
.
administrator/components/com_foos/forms/foo.xml
size="45"
hint="JFIELD_ALIAS_PLACEHOLDER"
/>
<field
name="access"
type="accesslevel"
label="JFIELD_ACCESS_LABEL"
size="1"
/>
</fieldset>
</form>
administrator/components/com_foos/sql/install.mysql.utf8.sql
The SQL script for a new installation of the component is also extended with the necessary fields. In this way we ensure that the database is also completely set up for a new installation.
administrator/components/com_foos/sql/install.mysql.utf8.sql
('Nina'),
('Astrid'),
('Elmar');
ALTER TABLE `#__foos_details` ADD COLUMN `access` int(10) unsigned NOT NULL DEFAULT 0 AFTER `alias`;
ALTER TABLE `#__foos_details` ADD KEY `idx_access` (`access`);
administrator/components/com_foos/src/Model/FoosModel.php
If you are not familiar with SQL, the database query in the model will now seem complex. It is now necessary to combine data from two database tables. One table is #__viewlevels
which manages the permissions of com_user
. The other table is that of our example component which is named #__foos_details
. Don't feel discouraged by this. Joomla supports you in creating the queries.
administrator/components/com_foos/src/Model/FoosModel.php
// Select the required fields from the table.
$query->select(
$db->quoteName(array('id', 'name', 'alias'))
$db->quoteName(array('a.id', 'a.name', 'a.alias', 'a.access'))
);
$query->from($db->quoteName('#__foos_details'));
$query->from($db->quoteName('#__foos_details', 'a'));
// Join over the asset groups.
$query->select($db->quoteName('ag.title', 'access_level'))
->join(
'LEFT',
$db->quoteName('#__viewlevels', 'ag') . ' ON ' . $db->quoteName('ag.id') . ' = ' . $db->quoteName('a.access')
);
return $query;
}
As a reminder, Joomla supports you in creating the database queries. If you use the available statements[^docs.joomla.org/accessing_the_database_using_jdatabase], Joomla will take care of security or different syntax in PostgreSQL and MySQL for you.
administrator/components/com_foos/src/View/Foos/HtmlView.php
A button to create an element is only useful if this is allowed. Therefore we change the view - $canDo
is added. The call $canDo = ContentHelper::getActions('com_foos');
gets the actions you defined in the file administrator/components/com_foos/access.xml
at the beginning of this chapter.
administrator/components/com_foos/src/View/Foos/HtmlView.php
\defined('_JEXEC') or die;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Toolbar;
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_foos');
// Get the toolbar object instance
$toolbar = Toolbar::getInstance('toolbar');
ToolbarHelper::title(Text::_('COM_FOOS_MANAGER_FOOS'), 'address foo');
$toolbar->addNew('foo.add');
if ($canDo->get('core.create'))
{
$toolbar->addNew('foo.add');
}
$toolbar->preferences('com_foos');
if ($canDo->get('core.options'))
{
$toolbar->preferences('com_foos');
}
}
}
administrator/components/com_foos/tmpl/foo/edit.php
The entry <?php echo $this->getForm()->renderField(access);
is necessary to include the field in the form, which we have already configured in the XML file. Only this way it is possible to change the permissions per element.
administrator/components/com_foos/tmpl/foo/edit.php
<form action="<?php echo Route::_('index.php?option=com_foos&layout=' . $layout . $tmpl . '&id=' . (int) $this->item->id); ?>" method="post" name="adminForm" id="foo-form" class="form-validate">
<?php echo $this->getForm()->renderField('name'); ?>
<?php echo $this->getForm()->renderField('alias'); ?>
<?php echo $this->getForm()->renderField('access'); ?>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</form>
administrator/components/com_foos/tmpl/foos/default.php
Last but not least, we include a column in the overview for the authorization display.
administrator/components/com_foos/tmpl/foos/default.php
<th scope="col" style="width:1%" class="text-center d-none d-md-table-cell">
<?php echo Text::_('COM_FOOS_TABLE_TABLEHEAD_NAME'); ?>
</th>
<th scope="col" style="width:10%" class="d-none d-md-table-cell">
<?php echo TEXT::_('JGRID_HEADING_ACCESS') ?>
</th>
<th scope="col">
<?php echo Text::_('COM_FOOS_TABLE_TABLEHEAD_ID'); ?>
</th>
<?php echo $editIcon; ?><?php echo $this->escape($item->name); ?></a>
</th>
<td class="small d-none d-md-table-cell">
<?php echo $item->access_level; ?>
</td>
<td class="d-none d-md-table-cell">
<?php echo $item->id; ?>
</td>
Note that I have not covered all cases here where permissions need to be handled. This description is intended as a best practice.
Test your Joomla component
-
install your component in Joomla version 4 to test it: Copy the files in the
administrator
folder to theadministrator
folder of your Joomla 4 installation. Install your component as described in part one, after copying all files. Joomla will update the database for you during the installation. -
create a new item in your component. Make sure that you are offered a checkbox for saving a permission. The value you enter here will be saved with the item and can be queried when it is displayed in a list.
-
consider how you use the permission set in point 2 for your purposes? So far, the value is only stored, we do not use it.
-
for an overview, the value is additionally displayed in the main view.
- Open the options of the global configuration. Here you have the possibility to set the permissions for the use of the component globally.
- Play with the settings. Allow the administrator to use the component in the backend. But remove his right to create new elements in the extension. After that, log in as a simple administrator and make sure that the 'New' button has disappeared.
Links
Access Control List Tutorial[^docs.joomla.org/j3.x:access_control_list_tutorial]
Webmentions