J3.x

J3.x:Developing an MVC Component/Adding a batch process

From Joomla! Documentation

Revision as of 21:24, 30 January 2019 by Robbiej (talk | contribs) (controller changes included)
Joomla! 
3.x
Tutorial
Developing an MVC Component



This is a multiple-article series of tutorials on how to develop a Model-View-Controller Component for Joomla! VersionJoomla 3.x.

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).



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.

In this step we add a helloworld batch process to the backend.

Under Construction!

Introduction

Joomla provides a number of facilities to enable administrators to update articles and other types of item in batch. These include:

  • moving or copying records into records with a selected category
  • setting the language and/or access for the selected records
  • adding a tag to the selected records

In this step we add these facilities into our helloworld component and also provide a batch process to enable the administrator to set the latitude and longitude.

Approach

To implement the batch processes we have to consider the following aspects.

Batch Button and Modal

  1. We show a Batch button in the toolbar on our helloworlds view. The administrator can click the checkboxes of the helloworld records to process in batch, and then click Batch to proceed.
  2. When the Batch button is clicked we want a modal to appear with the available options, so we must add the html for the Bootstrap modal. We'll do this in the helloworlds layout file, and utilise a couple of new subsidiary layouts for the modal body and footer.

The modal body will contain the html elements for the various options – eg set language, set access, set lat/long, etc. The elements for setting the language, access, category and tag are all standard Joomla library layouts within the /layouts directory tree. For setting the lat/long position we'll write our own layout file which we'll put in a new admin/layouts directory (and we have to remember to include this new folder in the main helloworld.xml manifest file).

The modal footer will contain the Cancel (for cancelling the operation) and Process (for starting the batch process) buttons.

Handling the HTTP POST in the Controller

When the Process button is pressed the javascript code will submit the form and so initiate an HTTP POST request to the server with parameters which include

  • the ids of the records which have been selected for batch processing
  • the batch options which have been chosen
  • the task variable set to task=helloworld.batch

Joomla will route this to a batch() method in admin/controllers/helloworld.php.

In our helloworld controller batch() method we do the following:

  • set up the model to be the helloworld model
  • set up the redirect to present the helloworlds view again, once the batch processing is complete
  • call the parent batch method, passing the model as parameter

The parent batch method (in JControllerForm) will obtain the POST params, perform some verification of them, and then call the batch() method within the passed model, passing the POST parameters.

Executing the batch processes in the Model

The way the Joomla batch model functionality works is that the JModelAdmin class has a property $batch_commands which is an associative array mapping the index of the batch parameters to the method which will perform that batch operation.

	protected $batch_commands = array(
		'assetgroup_id' => 'batchAccess',
		'language_id' => 'batchLanguage',
		'tag' => 'batchTag',
	);

So if in the batch modal an administrator sets the language selector to one of the available languages then batch[language_id] is set within the HTTP POST parameters. The model code looks up "language_id" in this $batch_commands property, finds "batchLanguage" and then proceeds to call the method batchLanguage().

In our case we will have a method called batchPosition() which will set the lat/long for the selected records, so we need to ensure that:

  • the html input elements are defined so that batch[position] parameters are sent in the HTTP POST
  • we add an extra record to the $batch_commands array, namely 'position' => 'batchPosition'.

The first aspect is done where we define the html of the input elements in our modal body.

The second aspect is done in a batch() method which we write in our helloworld model. This adds in the extra array element and then calls the parent::batch() to initiate all the batch operations.

Finally we must write our batchPosition() method to update the set of records with the passed-in latitude and longitude values.

Batch Button

We add the batch button in our helloworlds view. Because this is a custom button we need to get a handle on the toolbar instance, and then use appendButton(), as we can't use a ToolbarHelper function for this.

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();

                // Get data from the model
                $this->items                    = $this->get('Items');
                $this->pagination               = $this->get('Pagination');
                $this->state                    = $this->get('State');
                $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')))
                {
                        JError::raiseError(500, implode('<br />', $errors));

                        return false;
                }
        
                // Set the sidebar submenu and toolbar, but not on the modal window
                if ($this->getLayout() !== 'modal')
                {
                        HelloWorldHelper::addSubmenu('helloworlds');
                        $this->addToolBar();
                }
                else
                {
                        // If it's being displayed to select a record as an association, then forcedLanguage is set
                        if ($forcedLanguage = $app->input->get('forcedLanguage', '', 'CMD'))
                        {
                                // Transform the language selector filter into an hidden field, so it can't be set
                                $languageXml = new SimpleXMLElement('<field name="language" type="hidden" default="' . $forcedLanguage . '" />');
                                $this->filterForm->setField($languageXml, 'filter', true);

                                // Also, unset the active language filter so the search tools is not open by default with this filter.
                                unset($this->activeFilters['language']);
                        }
                }

                // Prepare a mapping from parent id to the ids of its children
                $this->ordering = array();
                foreach ($this->items as $item)
                {
                        $this->ordering[$item->parent_id][] = $item->id;
                }

                // 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');

                $bar = JToolbar::getInstance('toolbar');

                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.edit') || JFactory::getUser()->authorise('core.manage', 'com_checkin'))
                {
                        JToolBarHelper::checkin('helloworlds.checkin');
                }
                // Add a batch button
                if ($this->canDo->get('core.create') && $this->canDo->get('core.edit')
                        && $this->canDo->get('core.edit.state'))
                {
                        // we use a standard Joomla layout to get the html for the batch button
                        $layout = new JLayoutFile('joomla.toolbar.batch');
                        $batchButtonHtml = $layout->render(array('title' => JText::_('JTOOLBAR_BATCH')));
                        $bar->appendButton('Custom', $batchButtonHtml, 'batch');
                }
                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'));
        }
}

Batch Modal

We add the html for the batch modal into the helloworlds layout file

admin/views/helloworlds/tmpl/default.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');

use Joomla\Registry\Registry;

JHtml::_('formbehavior.chosen', 'select');

$listOrder     = $this->escape($this->state->get('list.ordering'));
$listDirn      = $this->escape($this->state->get('list.direction'));
$user = JFactory::getUser();
$userId = $user->get('id');
$saveOrder = ($listOrder == 'lft' && strtolower($listDirn) == 'asc');
if ($saveOrder)
{
        $saveOrderingUrl = 'index.php?option=com_helloworld&task=helloworlds.saveOrderAjax&tmpl=component';
        // pass true as parameter 7 to indicate that we have a nested set
        JHtml::_('sortablelist.sortable', 'helloworldList', 'adminForm', strtolower($listDirn), $saveOrderingUrl, false, true);
}
$assoc = JLanguageAssociations::isEnabled();
$authorFieldwidth = $assoc ? "10%" : "25%";
JLoader::register('JHtmlHelloworlds', JPATH_ADMINISTRATOR . '/components/com_helloworld/helpers/html/helloworlds.php');
?>
<form action="index.php?option=com_helloworld&view=helloworlds" method="post" id="adminForm" name="adminForm">
        <div id="j-sidebar-container" class="span2">
                <?php echo JHtmlSidebar::render(); ?>
        </div>
        <div id="j-main-container" class="span10">
        <div class="row-fluid">
            <div class="span6">
                <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_FILTER'); ?>
                <?php
                    echo JLayoutHelper::render(
                        'joomla.searchtools.default',
                        array('view' => $this)
                    );
                ?>
            </div>
        </div>
        <table class="table table-striped table-hover" id="helloworldList">
            <thead>
            <tr>
                <th width="1%">
                    <?php echo JHtml::_('searchtools.sort', '', 'lft', $listDirn, $listOrder, null, 'asc', 'JGRID_HEADING_ORDERING', 'icon-menu-2'); ?>
                </th>
                <th width="1%"><?php echo JText::_('COM_HELLOWORLD_NUM'); ?></th>
                <th width="1%">
                    <?php echo JHtml::_('grid.checkall'); ?>
                </th>
                <th width="10%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_HELLOWORLDS_NAME', 'greeting', $listDirn, $listOrder); ?>
                </th>
                <th width="10%">
                    <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_POSITION'); ?>
                </th>
                <th width="10%">
                    <?php echo JText::_('COM_HELLOWORLD_HELLOWORLDS_IMAGE'); ?>
                </th>
                <th width="20%">
                    <?php echo JHtml::_('searchtools.sort',  'JGRID_HEADING_ACCESS', 'access', $listDirn, $listOrder); ?>
                </th>
                <?php if ($assoc) : ?>
                    <th width="10%">
                        <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_HELLOWORLDS_ASSOCIATIONS', 'association', $listDirn, $listOrder); ?>
                    </th>
                <?php endif; ?>
                <th width="<?php echo $authorFieldwidth; ?>">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_AUTHOR', 'author', $listDirn, $listOrder); ?>
                </th>
                <th width="10%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_LANGUAGE', 'language', $listDirn, $listOrder); ?>
                </th>
                <th width="10%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_CREATED_DATE', 'created', $listDirn, $listOrder); ?>
                </th>
                <th width="5%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_PUBLISHED', 'published', $listDirn, $listOrder); ?>
                </th>
                <th width="2%">
                    <?php echo JHtml::_('searchtools.sort', 'COM_HELLOWORLD_ID', 'id', $listDirn, $listOrder); ?>
                </th>
            </tr>
            </thead>
            <tfoot>
                <tr>
                    <td colspan="5">
                        <?php echo $this->pagination->getListFooter(); ?>
                    </td>
                </tr>
            </tfoot>
            <tbody>
                <?php if (!empty($this->items)) : ?>
                    <?php foreach ($this->items as $i => $row) :
                        $link = JRoute::_('index.php?option=com_helloworld&task=helloworld.edit&id=' . $row->id);
                        $row->image = new Registry;
                        $row->image->loadString($row->imageInfo);
                        // create a list of the parents up the hierarchy to the root 
                        if ($row->level > 1)
                        {
                            $parentsStr = '';
                            $_currentParentId = $row->parent_id;
                            $parentsStr = ' ' . $_currentParentId;
                            for ($j = 0; $j < $row->level; $j++)
                            {
                                foreach ($this->ordering as $k => $v)
                                {
                                    $v = implode('-', $v);
                                    $v = '-' . $v . '-';
                                    if (strpos($v, '-' . $_currentParentId . '-') !== false)
                                    {
                                        $parentsStr .= ' ' . $k;
                                        $_currentParentId = $k;
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            $parentsStr = '';
                        }
                    ?>
                        <tr class="row<?php echo $i % 2; ?>" sortable-group-id="<?php echo $row->parent_id; ?>" item-id="<?php echo $row->id; ?>" parents="<?php echo $parentsStr; ?>" level="<?php echo $row->level; ?>">
                            <td><?php
                                $iconClass = '';
                                $canReorder  = $user->authorise('core.edit.state', 'com_helloworld.helloworld.' . $row->id);
                                if (!$canReorder)
                                {
                                    $iconClass = ' inactive';
                                }
                                elseif (!$saveOrder)
                                {
                                    $iconClass = ' inactive tip-top hasTooltip" title="' . JHtml::_('tooltipText', 'JORDERINGDISABLED');
                                }
                                ?>
                                <span class="sortable-handler<?php echo $iconClass ?>">
                                    <span class="icon-menu" aria-hidden="true"></span>
                                </span>
                                <?php if ($canReorder && $saveOrder) : ?>
                                    <input type="text" style="display:none" name="order[]" size="5" value="<?php echo $row->lft; ?>" class="width-20 text-area-order" />
                                <?php endif; ?>
                            </td>
                            <td><?php echo $this->pagination->getRowOffset($i); ?></td>
                            <td>
                                <?php echo JHtml::_('grid.id', $i, $row->id); ?>
                            </td>
                            <td>
                                <?php $prefix = JLayoutHelper::render('joomla.html.treeprefix', array('level' => $row->level)); ?>
                                <?php echo $prefix; ?>
                                <?php if ($row->checked_out) : ?>
                                    <?php $canCheckin = $user->authorise('core.manage', 'com_checkin') || $row->checked_out == $userId; ?>
                                    <?php echo JHtml::_('jgrid.checkedout', $i, $row->editor, $row->checked_out_time, 'helloworlds.', $canCheckin); ?>
                                <?php endif; ?>
                                <a href="<?php echo $link; ?>" title="<?php echo JText::_('COM_HELLOWORLD_EDIT_HELLOWORLD'); ?>">
                                    <?php echo $row->greeting; ?>
                                </a>
                                <span class="small break-word">
                                        <?php echo JText::sprintf('JGLOBAL_LIST_ALIAS', $this->escape($row->alias)); ?>
                                </span>
                                <div class="small">
                                    <?php echo JText::_('JCATEGORY') . ': ' . $this->escape($row->category_title); ?>
                                </div>
                                <div class="small">
                                    <?php echo 'Path: ' . $this->escape($row->path); ?>
                                </div>
                            </td>
                            <td align="center">
                                <?php echo "[" . $row->latitude . ", " . $row->longitude . "]"; ?>
                            </td>
                            <td align="center">
                                <?php
                                    $caption = $row->image->get('caption') ? : '' ;
                                    $src = JURI::root() . ($row->image->get('image') ? : '' );
                                    $html = '<p class="hasTooltip" style="display: inline-block" data-html="true" data-toggle="tooltip" data-placement="right" title="<img width=\'100px\' height=\'100px\' src=\'%s\'>">%s</p>';
                                    echo sprintf($html, $src, $caption);  ?>
                            </td>
                            <td align="center">
                                <?php echo $this->escape($row->access_level); ?>
                            </td>
                            <?php if ($assoc) : ?>
                                <td align="center">
                                    <?php if ($row->association) : ?>
                                        <?php echo JHtml::_('helloworlds.association', $row->id); ?>
                                    <?php endif; ?>
                                </td>
                            <?php endif; ?>
                            <td align="center">
                                <?php echo $row->author; ?>
                            </td>
                            <td align="center">
                                <?php echo JLayoutHelper::render('joomla.content.language', $row); ?>
                            </td>
                            <td align="center">
                                <?php echo substr($row->created, 0, 10); ?>
                            </td>
                            <td align="center">
                                <?php echo JHtml::_('jgrid.published', $row->published, $i, 'helloworlds.', true, 'cb'); ?>
                            </td>
                            <td align="center">
                                <?php echo $row->id; ?>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                <?php endif; ?>
            </tbody>
        </table>
        <?php // load the modal for displaying the batch options
            echo JHtml::_(
            'bootstrap.renderModal',
            'collapseModal',
            array(
                'title' => JText::_('COM_HELLOWORLD_BATCH_OPTIONS'),
                'footer' => $this->loadTemplate('batch_footer')
            ),
            $this->loadTemplate('batch_body')
        ); ?>
        <input type="hidden" name="task" value=""/>
        <input type="hidden" name="boxchecked" value="0"/>
        <?php echo JHtml::_('form.token'); ?>
    </div>
</form>

The loadTemplate('batch_xxx') method above will look for a file in the same directory with a filename which is a concatenation of the current filename 'default' and 'batch_xxx'.

We put the html for the modal body into a new default_batch_body.php file, using standard Joomla layouts for the standard options. For our Position option we render a layout which we define ourself below.

admin/views/helloworlds/tmpl/default_batch_body.php

<?php
/**
 * Layout file for the main body component of the modal showing the batch options
 * This layout displays the various html input elements relating to the batch processes
 */
defined('_JEXEC') or die;
$published = $this->state->get('filter.published');
?>

<div class="container-fluid">

	<div class="row-fluid">

		<div class="control-group span6">
			<div class="controls">
				<?php echo JLayoutHelper::render('joomla.html.batch.item', array('extension' => 'com_helloworld')); ?>
			</div>
			<div class="controls">
				<?php echo JLayoutHelper::render('position', array()); ?>
			</div>
		</div>

		<div class="control-group span6">
			<div class="controls">
				<?php echo JLayoutHelper::render('joomla.html.batch.language', array()); ?>
			</div>
			<div class="controls">
				<?php echo JLayoutHelper::render('joomla.html.batch.access', array()); ?>
			</div>
			<div class="controls">
				<?php echo JLayoutHelper::render('joomla.html.batch.tag', array()); ?>
			</div>
		</div>

	</div>
	
</div>

And we define our layout for the Position elements in a new file which we put in a new admin/layouts directory

admin/layouts/position.php

<?php

defined('_JEXEC') or die;

$html   = array();
$html[] = '<fieldset>';
$html[] = '<label id="batch_setposition-lbl" for="batch_setposition">' .
			JText::_('COM_HELLOWORLD_BATCH_SETPOSITION_LABEL') . '</label>';
$html[] = '<select name="batch[position][setposition]">' .
			'<option value="keepPosition" selected>' . JText::_('COM_HELLOWORLD_BATCH_KEEP_POSITION') . '</OPTION>' .
			'<option value="changePosition">' . JText::_('COM_HELLOWORLD_BATCH_CHANGE_POSITION') . '</OPTION>' .
		  '</select>';
$html[] = '<label id="batch_latitude-lbl" for="batch_latitude">' . 
				JText::_('COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_LABEL') . '</label>';
$html[] = '<input id="batch_latitude" class="inputbox" type="number" step=any min=-90.0 max=90.0 name="batch[position][latitude]">';
$html[] = '<label id="batch_longitude-lbl" for="batch_longitude">' . 
				JText::_('COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_LABEL') . '</label>';
$html[] = '<input id="batch_longitude" class="inputbox" type="number" step=any min=-180.0 max=180.0 name="batch[position][longitude]">';
$html[] = '</fieldset>';

echo implode('', $html);

The html for the modal Cancel and Process buttons we put into the batch footer file.

admin/views/helloworlds/tmpl/default_batch_footer.php

<?php
/**
 * Layout file for the footer component of the modal showing the batch options
 */
defined('_JEXEC') or die;
?>
<a class="btn" type="button" onclick="document.getElementById('batch-category-id').value='';document.getElementById('batch-access').value='';document.getElementById('batch-language-id').value='';document.getElementById('batch-user-id').value='';document.getElementById('batch-tag-id').value=''" data-dismiss="modal">
	<?php echo JText::_('JCANCEL'); ?>
</a>
<button class="btn btn-success" type="submit" onclick="Joomla.submitbutton('helloworld.batch');">
	<?php echo JText::_('JGLOBAL_BATCH_PROCESS'); ?>
</button>

Handling the HTTP POST in the Controller

We override the batch() method in JControllerForm in order to set the model and the redirect back to the same URL, before continuing with the normal batch processing.

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 );
		}
	}

	public function batch($model = null)
	{
		$model = $this->getModel('helloworld');
		$this->setRedirect((string)JUri::getInstance());
		return parent::batch($model);
	}
}


Packaging the Component

Contents 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

<?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.30</version>
	<!-- The description is optional and defaults to the name -->
	<description>COM_HELLOWORLD_DESCRIPTION</description>

	<!-- Runs on install/uninstall/update; New in 2.5 -->
	<scriptfile>script.php</scriptfile>

	<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>
		<filename>router.php</filename>
		<folder>controllers</folder>
		<folder>views</folder>
		<folder>models</folder>
		<folder>helpers</folder>
	</files>

		<languages folder="site/language">
			<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
		</languages>

	<media destination="com_helloworld" folder="media">
		<filename>index.html</filename>
		<folder>images</folder>
		<folder>js</folder>
		<folder>css</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>
			<!-- layout files section -->
			<folder>layouts</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>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
			<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.sys.ini</language>
		</languages>
	</administration>

</extension>


Contributors