Saturday, 24 October 2009 14:47

Lately in my spare time, I've been working on a new Extension collection for my new site, I Hate My Neighbors. I'm planning on releasing this one as a commercial component soon, and as such I wanted to make sure the install process was as simple as possible. Since the component has some plugins and modules that go along with it, I decided it should go ahead and install everything from a single package. Here's how I did it.

First, I created a directory in the administrator section of the component package named "extensions" and copied the installers for the modules and plugins into it. I also created an install file for my component in component.install.php. I then made sure to add these files to the component's xml install file under the administrator files section.

<administration>
  ...
  <files folder="admin">
    ...
    <filename>component.install.php</filename>
    ...
      <filename>extensions/index.html</filename>
      <filename>extensions/mod_module.zip</filename>
      <filename>extensions/plg_plugin.zip</filename>
    ...
  </files>
  ...
</administration>

What this will do is copy the install packages for the helper extensions to the server, but it won't actually install the extensions. To handle the install, I added code to component.install.php to handle this after the main component was finished installing. The first thing I did was import Joomla's install helper and create a new instance of JInstaller:

jimport('joomla.installer.helper');
$installer = new JInstaller();
$installer->_overwrite = true;

Next, I set up an array of each extension I wanted installed.

$pkg_path = JPATH_ADMINISTRATOR.DS.'components'.DS.'com_component'.DS.'extensions'.DS;
$pkgs = array( 'mod_module.zip'=>'Component Extra Module',
               'plg_plugin.zip'=>'Component Extra Plugin'
             );

Finally, I ran this array through a loop to install each item, as well as output status to the user:

foreach( $pkgs as $pkg => $pkgname ):
  $package = JInstallerHelper::unpack( $pkg_path.$pkg );
  if( $installer->install( $package['dir'] ) )
  {
    $msgcolor = "#E0FFE0";
    $msgtext  = "$pkgname successfully installed.";
  }
  else
  {
    $msgcolor = "#FFD0D0";
    $msgtext  = "ERROR: Could not install the $pkgname. Please install manually.";
  }
  ?>
  <table bgcolor="<?php echo $msgcolor; ?>" width ="100%">
    <tr style="height:30px">
      <td width="50px"><img src="/administrator/images/tick.png" height="20px" width="20px"></td>
      <td><font size="2"><b><?php echo $msgtext; ?></b></font></td>
    </tr>
  </table>
<?php
JInstallerHelper::cleanupInstall( $pkg_path.$pkg, $package['dir'] ); 
endforeach;
Last Updated on Wednesday, 24 March 2010 19:07
 
Comments (8)
1 Wednesday, 24 March 2010 19:04
Pablo
Hi there!
I tried using your code above and I'm having some problems.
It works just fine when I try installing a module. However, when I try installing a component, when calling install() it starts creating an infinite amount of copies of the folder created by unpack()
Do you happen to know how to fix this?? Thanks in advance!
2 Wednesday, 24 March 2010 19:06
Jeff Channell
JInstallerHelper::cleanupInstall( $pkg_path.$pkg, $package['dir'] );

code above changed. ;)
3 Wednesday, 24 March 2010 19:52
Pablo Romanowski
That was a fast reply! Unfortunately, it's still happening... when i try to install a component, it starts filling up the folder where the .zip file is located with install_4bxxxxxx folders. Anything else I might be able to try?
4 Thursday, 25 March 2010 12:35
Jeff Channell
You'll have to adapt this a bit - it's written as if these are class functions (notice the $this->recursiveDelete)...
function cleanPackageRoot( $subdir='' )

{
if( strlen( $subdir ) ) $subdir .= DS;
$dh = opendir( $this->packageroot.$subdir );
while( ( $file = readdir( $dh ) ) !== false )
{
if( preg_match( '`^install`', $file ) )
{
$this->recursiveDelete( $this->packageroot.$subdir.$file );
}
}
closedir($dh);
}
// http://www.php.net/manual/en/function.unlink.php#94766
function recursiveDelete( $str )
{
if( is_file( $str ) )
{
return @unlink( $str );
}
elseif( is_dir( $str ) )
{
$scan = glob( rtrim( $str, '/' ).'/*' );
if( $scan )
{
foreach( $scan as $index => $path )
{
$this->recursiveDelete( $path );
}
}
return @rmdir($str);
}
}
5 Monday, 06 September 2010 08:45
forgetso
Thanks very much. This helped me to get my head round the process.

I was, however, creating a new JInstaller from within the com_install function of the first component. This caused a "cannot redeclare class" error.

The solution I used was to jump into a controller file from the first component and run the secondary installs from in there.

Thanks again.
6 Sunday, 26 September 2010 13:31
Sam Mittelstaedt
Is there a complete install script available for download somewhere?
7 Sunday, 26 September 2010 14:05
Jeff Channell
JMyLife Pro uses this technique. If you want to take a look at the installer script, email me privately.
8 Saturday, 23 October 2010 22:22
webconstructor
Thank you very much for this code. I was looking for exactly something like this. I'm going to use it for my extensions if that's Ok with you.

Add your comment

Your name:
Comment:
  The word for verification. Lowercase letters only with no spaces.
Word verification:

The Joomla!® name is used under a limited license from Open Source Matters in the United States and other countries. Jeff Channell is not affiliated with or endorsed by Open Source Matters or the Joomla!® Project.

Santorum
Joomla Extensions