Developing an MVC Component/Adding ACL
From Joomla! Documentation
< J3.x:Developing an MVC Component
Articles in This Series
- Introduction
- Developing a Basic Component
- Adding a View to the Site Part
- Adding a Menu Type to the Site Part
- Adding a Model to the Site Part
- Adding a Variable Request in the Menu Type
- Using the Database
- Basic Backend
- Adding Language Management
- Adding Backend Actions
- Adding Decorations to the Backend
- Adding Verifications
- Adding Categories
- Adding Configuration
- Adding ACL
- Adding an Install/Uninstall/Update Script File
- Adding a Frontend Form
- Adding an Image
- Adding a Map
- Adding AJAX
- Adding an Alias
- Using the Language Filter Facility
- Adding a Modal
- Adding Associations
- Adding Checkout
- Adding Ordering
- Adding Levels
- Adding Versioning
- Adding Tags
- Adding Access
- Adding a Batch Process
- Adding Cache
- Adding a Feed
- Adding an Update Server
- Adding Custom Fields
- Upgrading to Joomla4
This is a multiple-article series of tutorials on how to develop a Model-View-Controller Component for Joomla! Version.
Begin with the Introduction, and navigate the articles in this series by using the navigation button at the bottom or the box to the right (the Articles in this series).
Introduction[edit]
This tutorial is part of the Developing an MVC Component for Joomla! 3.2 tutorial. You are encouraged to read the previous parts of the tutorial before reading this.
Adding Access Control[edit]
With Joomla!'s Access Control we can define which user groups are allowed or denied to do which actions in your component. In this example we use actions that are defined in the core. For the component as a whole: core.admin (access to the configuration) and core.manage (access to the backend). And at various levels actions like create, delete and edit. Besides those core actions you can define your own actions, but that is often not necessary and is not shown in this example. View/Read Access is not managed via those actions but with View Access Levels; see general documentation about Joomla!'s ACL for that.
In the #__assets table the actual list is stored: which user groups are allowed or denied to do which actions on which resources (assets). This is the implementation of the Access Control List (ACL).
In this article we will show how to add and use access permissions at several levels of granularity: for your component as a whole, for the categories and for the individual items.
To test this functionality create users and associate them with the user groups which have access to the back end admin functionality (ie those user groups which have Administrator Login permission in the Global Configuration Permission Settings). By default this is Manager and Administrator, but you can also define your own user groups, and optionally set them as children of Manager or Administrator.
If you're not already very familiar with Joomla Access Control then it's recommended to watch this video ACL Explained (between 2 minutes and 32 minutes is the section to watch) and read this Access Control List Tutorial.
Three videos associated with this tutorial step cover Joomla's Access Control Infrastructure, an explanation of this step's tutorial code and a supplementary video on Access View Levels.
Minimal ACL requirements at the component level[edit]
There are 2 actions that need to be defined at the component level for a Joomla! 2.5+ component to offer basic ACL support:
- Configure (core.admin): which groups are allowed to configure the component level permissions via the 'Options' toolbar button?
- Access Component (core.manage): which groups are allowed to access the component's backend?
This basic ACL support is done in 4 simple steps:
- Add the 2 minimal component level actions to access.xml
- Add the permissions fieldset to config.xml
- Add the 'Options' toolbar button
- Restrict the access to the component's backend
Add the 2 minimal component level actions to access.xml[edit]
A minimal ACL access.xml would consist of only those 2 basic actions:
Basic admin/access.xml
<?xml version="1.0" encoding="utf-8" ?>
<access component="com_helloworld">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" description="JACTION_ADMIN_COMPONENT_DESC" />
<action name="core.manage" title="JACTION_MANAGE" description="JACTION_MANAGE_COMPONENT_DESC" />
</section>
</access>
See admin/access.xml for the current and more elaborate version.
Add the permissions fieldset to config.xml[edit]
Add the following permissions fieldset to admin/config.xml in order to be able to set our component level permissions.
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
description="JCONFIG_PERMISSIONS_DESC"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
class="inputbox"
validate="rules"
filter="rules"
component="com_helloworld"
section="component"
/>
</fieldset>
See the more elaborate config.xml example further downwards for the exact place where to insert this code.
Add the 'Options' toolbar button when user is authorised for it[edit]
In the admin/views/helloworlds/view.html.php file you can add the following code to check if the user can edit the preferences:
// Options button.
if (JFactory::getUser()->authorise('core.admin', 'com_helloworld'))
{
JToolBarHelper::preferences('com_helloworld');
}
See further downwards for a more elaborated example of admin/views/helloworlds/view.html.php where this JToolBarHelper::preferences('com_helloworld') is done in an addToolBar()-method together with the other toolbar buttons and the JUser->authorise()-check is done with JHelperContent, resulting in the $canDo-property.
Restrict the access to the component's backend to authorised usergroups[edit]
To control the access to the backend of the component add the following lines to the admin/helloworld.php entry-file:
// Access check: is this user allowed to access the backend of this component?
if (!JFactory::getUser()->authorise('core.manage', 'com_helloworld'))
{
throw new Exception(JText::_('JERROR_ALERTNOAUTHOR'));
}
See further downwards for the whole code of the admin/helloworld.php file.
Adding more actions, also at category level and item level[edit]
When adding more actions and more levels, the above described 4 steps are done too:
- Add the actions to access.xml; here we can add more actions and levels
- Add the permissions-fieldset to config.xml
- Add the 'Options' toolbar button
- Restrict the access to the component's backend
In addition we also have to do the following steps:
- Add an asset_id to the item's database table for item level access control
- Store the permissions in the assets table. Especially take care of setting the asset_id of the parent-asset
- Make the settings of the permissions at the item level editable
- Add some language strings
Describing the actions you want to control the access to[edit]
Each component (or part of it) has its own set of permissions that can be controlled. They are described in an access.xml file located at the root of the admin folder. In this helloworld-example the actions to which access is controlled are divided in three sections: at the component level, the category level and the item level. An 'item' is called a 'message' in our example component, hence the name of the third section.
admin/access.xml
<?xml version="1.0" encoding="utf-8" ?>
<access component="com_helloworld">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" description="JACTION_ADMIN_COMPONENT_DESC" />
<action name="core.manage" title="JACTION_MANAGE" description="JACTION_MANAGE_COMPONENT_DESC" />
<action name="core.create" title="JACTION_CREATE" description="JACTION_CREATE_COMPONENT_DESC" />
<action name="core.delete" title="JACTION_DELETE" description="JACTION_DELETE_COMPONENT_DESC" />
<action name="core.edit" title="JACTION_EDIT" description="JACTION_EDIT_COMPONENT_DESC" />
<action name="core.edit.state" title="JACTION_EDITSTATE" description="JACTION_EDITSTATE_COMPONENT_DESC" />
<action name="core.edit.own" title="JACTION_EDITOWN" description="JACTION_EDITOWN_COMPONENT_DESC" />
</section>
<section name="category">
<action name="core.create" title="JACTION_CREATE" description="COM_CATEGORIES_ACCESS_CREATE_DESC" />
<action name="core.delete" title="JACTION_DELETE" description="COM_CATEGORIES_ACCESS_DELETE_DESC" />
<action name="core.edit" title="JACTION_EDIT" description="COM_CATEGORIES_ACCESS_EDIT_DESC" />
<action name="core.edit.state" title="JACTION_EDITSTATE" description="COM_CATEGORIES_ACCESS_EDITSTATE_DESC" />
<action name="core.edit.own" title="JACTION_EDITOWN" description="COM_CATEGORIES_ACCESS_EDITOWN_DESC" />
</section>
<section name="message">
<action name="core.delete" title="JACTION_DELETE" description="COM_HELLOWORLD_ACCESS_DELETE_DESC" />
<action name="core.edit" title="JACTION_EDIT" description="COM_HELLOWORLD_ACCESS_EDIT_DESC" />
</section>
</access>
Adding the setting of permissions in the component's Preferences[edit]
Since we now use Access Control permissions in our component, we need to be able to set them at the component level. That is done in the Preferences of this component: the screen you see after clicking the 'Options' button. The config.xml-file is a form-definition for those Preferences. We could define the component level actions here too, as a child of the "rules" field-tag, but it is now preferred to also put those actions in access.xml in that way all access rules for this component are on one spot.
admin/config.xml
<?xml version="1.0" encoding="utf-8"?>
<config>
<fieldset
name="greetings"
label="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL"
description="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC"
>
<field
name="show_category"
type="radio"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
default="0"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
</fieldset>
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
description="JCONFIG_PERMISSIONS_DESC"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
class="inputbox"
validate="rules"
filter="rules"
component="com_helloworld"
section="component"
/>
</fieldset>
</config>
Displaying only the right toolbar buttons[edit]
Which toolbar buttons to display depends on the Access Control permissions for the user.
In common with Joomla core components, we use JHelperContent::getActions() to find the permissions. We pass to this function the identity of the asset record, and it returns an object containing all the Actions which are associated with our helloworld component, together with (for each Action) whether the currently logged-in user has that permission or not. If we then call get('some.action') on this object, it will return true or false, depending on whether the user has this permission or not.
In the admin/views/helloworlds/view.html.php, put this code:
admin/views/helloworlds/view.html.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
/**
* HelloWorlds View
*
* @since 0.0.1
*/
class HelloWorldViewHelloWorlds extends JViewLegacy
{
/**
* Display the Hello World view
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
function display($tpl = null)
{
// Get application
$app = JFactory::getApplication();
$context = "helloworld.list.admin.helloworld";
// Get data from the model
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filter_order = $app->getUserStateFromRequest($context.'filter_order', 'filter_order', 'greeting', 'cmd');
$this->filter_order_Dir = $app->getUserStateFromRequest($context.'filter_order_Dir', 'filter_order_Dir', 'asc', 'cmd');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
// What Access Permissions does this user have? What can (s)he do?
$this->canDo = JHelperContent::getActions('com_helloworld');
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
// Set the submenu
HelloWorldHelper::addSubmenu('helloworlds');
// Set the toolbar and number of found items
$this->addToolBar();
// Display the template
parent::display($tpl);
// Set the document
$this->setDocument();
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 1.6
*/
protected function addToolBar()
{
$title = JText::_('COM_HELLOWORLD_MANAGER_HELLOWORLDS');
if ($this->pagination->total)
{
$title .= "<span style='font-size: 0.5em; vertical-align: middle;'>(" . $this->pagination->total . ")</span>";
}
JToolBarHelper::title($title, 'helloworld');
if ($this->canDo->get('core.create'))
{
JToolBarHelper::addNew('helloworld.add', 'JTOOLBAR_NEW');
}
if ($this->canDo->get('core.edit'))
{
JToolBarHelper::editList('helloworld.edit', 'JTOOLBAR_EDIT');
}
if ($this->canDo->get('core.delete'))
{
JToolBarHelper::deleteList('', 'helloworlds.delete', 'JTOOLBAR_DELETE');
}
if ($this->canDo->get('core.admin'))
{
JToolBarHelper::divider();
JToolBarHelper::preferences('com_helloworld');
}
}
/**
* Method to set up the document properties
*
* @return void
*/
protected function setDocument()
{
$document = JFactory::getDocument();
$document->setTitle(JText::_('COM_HELLOWORLD_ADMINISTRATION'));
}
}
In the admin/views/helloworld/view.html.php, put this code:
admin/views/helloworld/view.html.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
/**
* HelloWorld View
*
* @since 0.0.1
*/
class HelloWorldViewHelloWorld extends JViewLegacy
{
protected $form;
protected $item;
protected $script;
protected $canDo;
/**
* Display the Hello World view
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
public function display($tpl = null)
{
// Get the Data
$this->form = $this->get('Form');
$this->item = $this->get('Item');
$this->script = $this->get('Script');
// What Access Permissions does this user have? What can (s)he do?
$this->canDo = JHelperContent::getActions('com_helloworld', 'helloworld', $this->item->id);
// Check for errors.
if (count($errors = $this->get('Errors')))
{
throw new Exception(implode("\n", $errors), 500);
}
// Set the toolbar
$this->addToolBar();
// Display the template
parent::display($tpl);
// Set the document
$this->setDocument();
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 1.6
*/
protected function addToolBar()
{
$input = JFactory::getApplication()->input;
// Hide Joomla Administrator Main menu
$input->set('hidemainmenu', true);
$isNew = ($this->item->id == 0);
JToolBarHelper::title($isNew ? JText::_('COM_HELLOWORLD_MANAGER_HELLOWORLD_NEW')
: JText::_('COM_HELLOWORLD_MANAGER_HELLOWORLD_EDIT'), 'helloworld');
// Build the actions for new and existing records.
if ($isNew)
{
// For new records, check the create permission.
if ($this->canDo->get('core.create'))
{
JToolBarHelper::apply('helloworld.apply', 'JTOOLBAR_APPLY');
JToolBarHelper::save('helloworld.save', 'JTOOLBAR_SAVE');
JToolBarHelper::custom('helloworld.save2new', 'save-new.png', 'save-new_f2.png',
'JTOOLBAR_SAVE_AND_NEW', false);
}
JToolBarHelper::cancel('helloworld.cancel', 'JTOOLBAR_CANCEL');
}
else
{
if ($this->canDo->get('core.edit'))
{
// We can save the new record
JToolBarHelper::apply('helloworld.apply', 'JTOOLBAR_APPLY');
JToolBarHelper::save('helloworld.save', 'JTOOLBAR_SAVE');
// We can save this record, but check the create permission to see
// if we can return to make a new one.
if ($this->canDo->get('core.create'))
{
JToolBarHelper::custom('helloworld.save2new', 'save-new.png', 'save-new_f2.png',
'JTOOLBAR_SAVE_AND_NEW', false);
}
}
if ($this->canDo->get('core.create'))
{
JToolBarHelper::custom('helloworld.save2copy', 'save-copy.png', 'save-copy_f2.png',
'JTOOLBAR_SAVE_AS_COPY', false);
}
JToolBarHelper::cancel('helloworld.cancel', 'JTOOLBAR_CLOSE');
}
}
/**
* Method to set up the document properties
*
* @return void
*/
protected function setDocument()
{
$isNew = ($this->item->id == 0);
$document = JFactory::getDocument();
$document->setTitle($isNew ? JText::_('COM_HELLOWORLD_HELLOWORLD_CREATING')
: JText::_('COM_HELLOWORLD_HELLOWORLD_EDITING'));
$document->addScript(JURI::root() . $this->script);
$document->addScript(JURI::root() . "/administrator/components/com_helloworld"
. "/views/helloworld/submitbutton.js");
JText::script('COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE');
}
}
Restricting access to the component[edit]
The main idea in ACL is to restrict actions to groups of users. The first action to be restricted is access to the administrative backend of the component itself. With your favourite file editor, edit the admin/helloworld.php file and add the lines with the access check.
admin/helloworld.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// Set some global property
$document = JFactory::getDocument();
$document->addStyleDeclaration('.icon-helloworld {background-image: url(../media/com_helloworld/images/tux-16x16.png);}');
// Access check: is this user allowed to access the backend of this component?
if (!JFactory::getUser()->authorise('core.manage', 'com_helloworld'))
{
throw new Exception(JText::_('JERROR_ALERTNOAUTHOR'));
}
// Require helper file
JLoader::register('HelloWorldHelper', JPATH_COMPONENT . '/helpers/helloworld.php');
// Get an instance of the controller prefixed by HelloWorld
$controller = JControllerLegacy::getInstance('HelloWorld');
// Perform the Request task
$controller->execute(JFactory::getApplication()->input->get('task'));;
// Redirect if set by the controller
$controller->redirect();
Add the asset_id column to the database table[edit]
In order to be able to work with JTable an asset_id column has to be added to the database #__helloworld table.
So, admin/sql/install.mysql.utf8.sql becomes:
admin/sql/install.mysql.utf8.sql
DROP TABLE IF EXISTS `#__helloworld`;
CREATE TABLE `#__helloworld` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`asset_id` INT(10) NOT NULL DEFAULT '0',
`greeting` VARCHAR(25) NOT NULL,
`published` tinyint(4) NOT NULL,
`catid` int(11) NOT NULL DEFAULT '0',
`params` VARCHAR(1024) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
)
ENGINE =MyISAM
AUTO_INCREMENT =0
DEFAULT CHARSET =utf8;
INSERT INTO `#__helloworld` (`greeting`) VALUES
('Hello World!'),
('Good bye World!');
For updates we add a sql-update-file:
admin/sql/updates/mysql/0.0.14.sql
ALTER TABLE `#__helloworld` ADD COLUMN `asset_id` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`;
Restricting access to the messages[edit]
So far we have restricted access to the component itself, but we also need to do that at message level.
To check the "core.delete" permission you need to modify the model class: admin/models/helloworld.php by adding these lines:
admin/models/helloworld.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
/**
* HelloWorld Model
*
* @since 0.0.1
*/
class HelloWorldModelHelloWorld extends JModelAdmin
{
/**
* Method to get a table object, load it if necessary.
*
* @param string $type The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return JTable A JTable object
*
* @since 1.6
*/
public function getTable($type = 'HelloWorld', $prefix = 'HelloWorldTable', $config = array())
{
return JTable::getInstance($type, $prefix, $config);
}
/**
* Method to get the record form.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return mixed A JForm object on success, false on failure
*
* @since 1.6
*/
public function getForm($data = array(), $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_helloworld.helloworld',
'helloworld',
array(
'control' => 'jform',
'load_data' => $loadData
)
);
if (empty($form))
{
return false;
}
return $form;
}
/**
* Method to get the script that have to be included on the form
*
* @return string Script files
*/
public function getScript()
{
return 'administrator/components/com_helloworld/models/forms/helloworld.js';
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 1.6
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = JFactory::getApplication()->getUserState(
'com_helloworld.edit.helloworld.data',
array()
);
if (empty($data))
{
$data = $this->getItem();
}
return $data;
}
/**
* Method to check if it's OK to delete a message. Overrides JModelAdmin::canDelete
*/
protected function canDelete($record)
{
if( !empty( $record->id ) )
{
return JFactory::getUser()->authorise( "core.delete", "com_helloworld.helloworld." . $record->id );
}
}
}
To check "core.edit" (and core.add if you wish) you need to update the sub-controller (not the model). I am not sure why this is so, but that's how other standard Joomla components do it. You need to add the following lines in the file: admin/controllers/helloworld.php
admin/controllers/helloworld.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
/**
* HelloWorld Controller
*
* @package Joomla.Administrator
* @subpackage com_helloworld
* @since 0.0.9
*/
class HelloWorldControllerHelloWorld extends JControllerForm
{
/**
* Implement to allowAdd or not
*
* Not used at this time (but you can look at how other components use it....)
* Overwrites: JControllerForm::allowAdd
*
* @param array $data
* @return bool
*/
protected function allowAdd($data = array())
{
return parent::allowAdd($data);
}
/**
* Implement to allow edit or not
* Overwrites: JControllerForm::allowEdit
*
* @param array $data
* @param string $key
* @return bool
*/
protected function allowEdit($data = array(), $key = 'id')
{
$id = isset( $data[ $key ] ) ? $data[ $key ] : 0;
if( !empty( $id ) )
{
return JFactory::getUser()->authorise( "core.edit", "com_helloworld.helloworld." . $id );
}
}
}
Please note that allowAdd simply calls its parent. I've put it here in case you want to actually use it in your component. If you look at your admin/access.xml file, you will notice there is no core.add action defined for "messages", so you will need to add it there as well if you want to be able to configure it in the interface.
Setting the permission values in the assets table[edit]
In order to store permissions for each message in the assets database table, we have to instruct the table class associated with the model to save those permissions in the assets table.
JTable not only provides an interface for storing the data of the record itself in the item's database table, but also for storing the permissions for that record in the assets database table. Therefore we must add information to the bind()-method about the permission-values. We also have to provide the asset name, asset title and the id of the asset parent via the helloworld JTable. Therefore we override 3 methods:
- _getAssetName(): a unique name for this asset, by which it can be retrieved
- _getAssetTitle(): a more human-friendly way to identify the asset (not necessary unique)
- _getAssetParentId(): the asset_id of the parent in the asset database table (from whom permissions are inherited)
admin/tables/helloworld.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access
defined('_JEXEC') or die('Restricted access');
/**
* Hello Table class
*
* @since 0.0.1
*/
class HelloWorldTableHelloWorld extends JTable
{
/**
* Constructor
*
* @param JDatabaseDriver &$db A database connector object
*/
function __construct(&$db)
{
parent::__construct('#__helloworld', 'id', $db);
}
/**
* Overloaded bind function
*
* @param array named array
* @return null|string null is operation was satisfactory, otherwise returns an error
* @see JTable:bind
* @since 1.5
*/
public function bind($array, $ignore = '')
{
if (isset($array['params']) && is_array($array['params']))
{
// Convert the params field to a string.
$parameter = new JRegistry;
$parameter->loadArray($array['params']);
$array['params'] = (string)$parameter;
}
// Bind the rules.
if (isset($array['rules']) && is_array($array['rules']))
{
$rules = new JAccessRules($array['rules']);
$this->setRules($rules);
}
return parent::bind($array, $ignore);
}
/**
* Method to compute the default name of the asset.
* The default name is in the form `table_name.id`
* where id is the value of the primary key of the table.
*
* @return string
* @since 2.5
*/
protected function _getAssetName()
{
$k = $this->_tbl_key;
return 'com_helloworld.helloworld.'.(int) $this->$k;
}
/**
* Method to return the title to use for the asset table.
*
* @return string
* @since 2.5
*/
protected function _getAssetTitle()
{
return $this->greeting;
}
/**
* Method to get the asset-parent-id of the item
*
* @return int
*/
protected function _getAssetParentId(JTable $table = NULL, $id = NULL)
{
// We will retrieve the parent-asset from the Asset-table
$assetParent = JTable::getInstance('Asset');
// Default: if no asset-parent can be found we take the global asset
$assetParentId = $assetParent->getRootId();
// Find the parent-asset
if (($this->catid)&& !empty($this->catid))
{
// The item has a category as asset-parent
$assetParent->loadByName('com_helloworld.category.' . (int) $this->catid);
}
else
{
// The item has the component as asset-parent
$assetParent->loadByName('com_helloworld');
}
// Return the found asset-parent-id
if ($assetParent->id)
{
$assetParentId=$assetParent->id;
}
return $assetParentId;
}
}
This code for _getAssetParentId() above uses JTableAsset to retrieve the asset_id of the asset-parent. This is different from the code in the current version of com_content, where the asset_id of the category is retrieved from the #__categories database table. That is another possibility; many ways leading to Rome. In com_content however, if an item would not be under a category, then the asset_id of the global asset is returned. That would of course not be right, but is fixed there by providing a default category "uncategorised", so that an article is always under a category.
Showing the setting of permissions on the item level[edit]
Adding the rules field to the form-definition of the edit-form.
admin/models/forms/helloworld.xml
<?xml version="1.0" encoding="utf-8"?>
<form
addrulepath="/administrator/components/com_helloworld/models/rules"
>
<fieldset
name="details"
label="COM_HELLOWORLD_HELLOWORLD_DETAILS"
>
<field
name="id"
type="hidden"
/>
<field
name="greeting"
type="text"
label="COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_GREETING_DESC"
size="40"
class="inputbox validate-greeting"
validate="greeting"
required="true"
default=""
/>
<field
name="catid"
type="category"
extension="com_helloworld"
class="inputbox"
default=""
label="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC"
required="true"
>
<option value="0">JOPTION_SELECT_CATEGORY</option>
</field>
</fieldset>
<fields name="params">
<fieldset
name="params"
label="JGLOBAL_FIELDSET_DISPLAY_OPTIONS"
>
<field
name="show_category"
type="list"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
default=""
>
<option value="">JGLOBAL_USE_GLOBAL</option>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
</fieldset>
</fields>
<fieldset
name="accesscontrol"
label="COM_HELLOWORLD_FIELDSET_RULES"
>
<field
name="asset_id"
type="hidden"
filter="unset"
/>
<field
name="rules"
type="rules"
label="COM_HELLOWORLD_FIELD_RULES_LABEL"
filter="rules"
validate="rules"
class="inputbox"
component="com_helloworld"
section="message"
/>
</fieldset>
</form>
The previous edit.php layout file would display the permissions field automatically, but we've changed the layout to display in a tabbed format, similar to other Joomla components. When a user changes a permission there's an Ajax call to recalculate the Calculated Setting, and we need a little bit of javascript to enable that to work.
admin/views/helloworld/tmpl/edit.php
<?php
/**
* @package Joomla.Administrator
* @subpackage com_helloworld
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
// No direct access
defined('_JEXEC') or die('Restricted access');
JHtml::_('behavior.formvalidator');
// The following is to enable setting the permission's Calculated Setting
// when you change the permission's Setting.
// The core javascript code for initiating the Ajax request looks for a field
// with id="jform_title" and sets its value as the 'title' parameter to send in the Ajax request
JFactory::getDocument()->addScriptDeclaration('
jQuery(document).ready(function() {
greeting = jQuery("#jform_greeting").val();
jQuery("#jform_title").val(greeting);
});
');
?>
<form action="<?php echo JRoute::_('index.php?option=com_helloworld&layout=edit&id=' . (int) $this->item->id); ?>"
method="post" name="adminForm" id="adminForm" class="form-validate">
<input id="jform_title" type="hidden" name="helloworld-message-title"/>
<div class="form-horizontal">
<?php echo JHtml::_('bootstrap.startTabSet', 'myTab', array('active' => 'details')); ?>
<?php echo JHtml::_('bootstrap.addTab', 'myTab', 'details',
empty($this->item->id) ? JText::_('COM_HELLOWORLD_TAB_NEW_MESSAGE') : JText::_('COM_HELLOWORLD_TAB_EDIT_MESSAGE')); ?>
<fieldset class="adminform">
<legend><?php echo JText::_('COM_HELLOWORLD_LEGEND_DETAILS') ?></legend>
<div class="row-fluid">
<div class="span6">
<?php echo $this->form->renderFieldset('details'); ?>
</div>
</div>
</fieldset>
<?php echo JHtml::_('bootstrap.endTab'); ?>
<?php echo JHtml::_('bootstrap.addTab', 'myTab', 'params', JText::_('COM_HELLOWORLD_TAB_PARAMS')); ?>
<fieldset class="adminform">
<legend><?php echo JText::_('COM_HELLOWORLD_LEGEND_PARAMS') ?></legend>
<div class="row-fluid">
<div class="span6">
<?php echo $this->form->renderFieldset('params'); ?>
</div>
</div>
</fieldset>
<?php echo JHtml::_('bootstrap.endTab'); ?>
<?php echo JHtml::_('bootstrap.addTab', 'myTab', 'permissions', JText::_('COM_HELLOWORLD_TAB_PERMISSIONS')); ?>
<fieldset class="adminform">
<legend><?php echo JText::_('COM_HELLOWORLD_LEGEND_PERMISSIONS') ?></legend>
<div class="row-fluid">
<div class="span12">
<?php echo $this->form->renderFieldset('accesscontrol'); ?>
</div>
</div>
</fieldset>
<?php echo JHtml::_('bootstrap.endTab'); ?>
<?php echo JHtml::_('bootstrap.endTabSet'); ?>
</div>
<input type="hidden" name="task" value="helloworld.edit" />
<?php echo JHtml::_('form.token'); ?>
</form>
Adding language strings[edit]
We used several language strings that have to be added to the backend language-file.
admin/language/en-GB/en-GB.com_helloworld.ini
; Joomla! Project
; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
; Note : All ini files need to be saved as UTF-8
COM_HELLOWORLD_ADMINISTRATION="HelloWorld - Administration"
COM_HELLOWORLD_ADMINISTRATION_CATEGORIES="HelloWorld - Categories"
COM_HELLOWORLD_NUM="#"
COM_HELLOWORLD_HELLOWORLDS_FILTER="Filters"
COM_HELLOWORLD_PUBLISHED="Published"
COM_HELLOWORLD_HELLOWORLDS_NAME="Name"
COM_HELLOWORLD_ID="Id"
COM_HELLOWORLD_HELLOWORLD_CREATING="HelloWorld - Creating"
COM_HELLOWORLD_HELLOWORLD_DETAILS="Details"
COM_HELLOWORLD_HELLOWORLD_EDITING="HelloWorld - Editing"
COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE="Some values are unacceptable"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC="The category the messages belongs to"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL="Category"
COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_DESC="This message will be displayed"
COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_LABEL="Message"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL="Show category"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC="If set to Show, the title of the message’s category will show."
COM_HELLOWORLD_HELLOWORLD_HEADING_GREETING="Greeting"
COM_HELLOWORLD_HELLOWORLD_HEADING_ID="Id"
COM_HELLOWORLD_MANAGER_HELLOWORLD_EDIT="HelloWorld manager: Edit Message"
COM_HELLOWORLD_MANAGER_HELLOWORLD_NEW="HelloWorld manager: New Message"
COM_HELLOWORLD_MANAGER_HELLOWORLDS="HelloWorld manager"
COM_HELLOWORLD_EDIT_HELLOWORLD="Edit message"
COM_HELLOWORLD_N_ITEMS_DELETED_1="One message deleted"
COM_HELLOWORLD_N_ITEMS_DELETED_MORE="%d messages deleted"
COM_HELLOWORLD_N_ITEMS_PUBLISHED="%d message(s) published"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED="%d message(s) unpublished"
COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL="Greeting"
COM_HELLOWORLD_HELLOWORLD_GREETING_DESC="Add Hello World Greeting"
COM_HELLOWORLD_SUBMENU_MESSAGES="Messages"
COM_HELLOWORLD_SUBMENU_CATEGORIES="Categories"
COM_HELLOWORLD_CONFIGURATION="HelloWorld Configuration"
COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL="Messages settings"
COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC="Settings that will be applied to all messages by default"
COM_HELLOWORLD_FIELDSET_RULES="Message Permissions"
COM_HELLOWORLD_FIELD_RULES_LABEL="Permissions"
COM_HELLOWORLD_ACCESS_EDIT_DESC="Is this group allowed to edit this message?"
COM_HELLOWORLD_ACCESS_DELETE_DESC="Is this group allowed to delete this message?"
COM_HELLOWORLD_TAB_NEW_MESSAGE="New Message"
COM_HELLOWORLD_TAB_EDIT_MESSAGE="Edit Message"
COM_HELLOWORLD_TAB_PARAMS="Parameters"
COM_HELLOWORLD_TAB_PERMISSIONS="Permissions"
COM_HELLOWORLD_LEGEND_DETAILS="Message Details"
COM_HELLOWORLD_LEGEND_PARAMS="Message Parameters"
COM_HELLOWORLD_LEGEND_PERMISSIONS="Message Permissions"
Further reading[edit]
More information on actions, assets and ACL can be found on the following pages:
- General information and use: Access Control List/Tutorial
- How to implement actions in your code
- Adding ACL rules to your component
Packaging the component[edit]
Content of your code directory. Each file link below takes you to the step in the tutorial which has the latest version of that source code file.
- helloworld.xml
- site/helloworld.php
- site/index.html
- site/controller.php
- site/views/index.html
- site/views/helloworld/index.html
- site/views/helloworld/view.html.php
- site/views/helloworld/tmpl/index.html
- site/views/helloworld/tmpl/default.xml
- site/views/helloworld/tmpl/default.php
- site/models/index.html
- site/models/helloworld.php
- site/language/index.html
- site/language/en-GB/index.html
- site/language/en-GB/en-GB.com_helloworld.ini
- admin/index.html
- admin/helloworld.php
- admin/config.xml
- admin/controller.php
- admin/access.xml
- admin/helpers/helloworld.php
- admin/helpers/index.html
- admin/sql/index.html
- admin/sql/install.mysql.utf8.sql
- admin/sql/uninstall.mysql.utf8.sql
- admin/sql/updates/index.html
- admin/sql/updates/mysql/index.html
- admin/sql/updates/mysql/0.0.1.sql
- admin/sql/updates/mysql/0.0.6.sql
- admin/sql/updates/mysql/0.0.12.sql
- admin/sql/updates/mysql/0.0.13.sql
- admin/sql/updates/mysql/0.0.14.sql
- admin/models/index.html
- admin/models/fields/index.html
- admin/models/fields/helloworld.php
- admin/models/helloworlds.php
- admin/models/helloworld.php
- admin/models/forms/filter_helloworlds.xml
- admin/models/forms/index.html
- admin/models/forms/helloworld.js
- admin/models/forms/helloworld.xml
- admin/models/rules/greeting.php
- admin/models/rules/index.html
- admin/controllers/helloworld.php
- admin/controllers/helloworlds.php
- admin/controllers/index.html
- admin/views/index.html
- admin/views/helloworld/index.html
- admin/views/helloworld/view.html.php
- admin/views/helloworld/tmpl/index.html
- admin/views/helloworld/tmpl/edit.php
- admin/views/helloworld/submitbutton.js
- admin/views/helloworlds/index.html
- admin/views/helloworlds/view.html.php
- admin/views/helloworlds/tmpl/index.html
- admin/views/helloworlds/tmpl/default.php
- admin/tables/index.html
- admin/tables/helloworld.php
- admin/language/index.html
- admin/language/en-GB/index.html
- admin/language/en-GB/en-GB.com_helloworld.ini
- admin/language/en-GB/en-GB.com_helloworld.sys.ini
- media/index.html
- media/images/index.html
- media/images/tux-16x16.png
- media/images/tux-48x48.png
Create a compressed file of this directory or directly download the archive and install it using the extension manager of Joomla. You can add a menu item of this component using the menu manager in the backend.
helloworld.xml
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.0" method="upgrade">
<name>COM_HELLOWORLD</name>
<!-- The following elements are optional and free of formatting constraints -->
<creationDate>January 2018</creationDate>
<author>John Doe</author>
<authorEmail>john.doe@example.org</authorEmail>
<authorUrl>http://www.example.org</authorUrl>
<copyright>Copyright Info</copyright>
<license>License Info</license>
<!-- The version string is recorded in the components table -->
<version>0.0.14</version>
<!-- The description is optional and defaults to the name -->
<description>COM_HELLOWORLD_DESCRIPTION</description>
<install> <!-- Runs on install -->
<sql>
<file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file>
</sql>
</install>
<uninstall> <!-- Runs on uninstall -->
<sql>
<file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file>
</sql>
</uninstall>
<update> <!-- Runs on update; New since J2.5 -->
<schemas>
<schemapath type="mysql">sql/updates/mysql</schemapath>
</schemas>
</update>
<!-- Site Main File Copy Section -->
<!-- Note the folder attribute: This attribute describes the folder
to copy FROM in the package to install therefore files copied
in this section are copied from /site/ in the package -->
<files folder="site">
<filename>index.html</filename>
<filename>helloworld.php</filename>
<filename>controller.php</filename>
<folder>views</folder>
<folder>models</folder>
</files>
<languages folder="site/language">
<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
</languages>
<media destination="com_helloworld" folder="media">
<filename>index.html</filename>
<folder>images</folder>
</media>
<administration>
<!-- Administration Menu Section -->
<menu link='index.php?option=com_helloworld' img="../media/com_helloworld/images/tux-16x16.png">COM_HELLOWORLD_MENU</menu>
<!-- Administration Main File Copy Section -->
<!-- Note the folder attribute: This attribute describes the folder
to copy FROM in the package to install therefore files copied
in this section are copied from /admin/ in the package -->
<files folder="admin">
<!-- Admin Main File Copy Section -->
<filename>index.html</filename>
<filename>config.xml</filename>
<filename>helloworld.php</filename>
<filename>controller.php</filename>
<filename>access.xml</filename>
<!-- SQL files section -->
<folder>sql</folder>
<!-- tables files section -->
<folder>tables</folder>
<!-- models files section -->
<folder>models</folder>
<!-- views files section -->
<folder>views</folder>
<!-- controllers files section -->
<folder>controllers</folder>
<!-- helpers files section -->
<folder>helpers</folder>
</files>
<languages folder="admin/language">
<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
<language tag="en-GB">en-GB/en-GB.com_helloworld.sys.ini</language>
</languages>
</administration>
</extension>
Contributors[edit]
- Dwarkesh Soni
- Christophe Demko
- Ozgur Aksu
- Florian Denizot
- Herman Peeren
- TDZweb (Ståle Rolf Sæbøe), Elin Waring, Neil via https://groups.google.com/forum/#!topic/joomla-dev-general/aCH3GNA91m4
- Scionescire
- Robbie Jackson