Source for file installer.php

Documentation is available at installer.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Libraries
  4.  * @subpackage  Installer
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('JPATH_PLATFORM'or die;
  11.  
  12. jimport('joomla.filesystem.file');
  13. jimport('joomla.filesystem.folder');
  14. jimport('joomla.filesystem.path');
  15. jimport('joomla.base.adapter');
  16.  
  17. /**
  18.  * Joomla base installer class
  19.  *
  20.  * @package     Joomla.Libraries
  21.  * @subpackage  Installer
  22.  * @since       3.1
  23.  */
  24. class JInstaller extends JAdapter
  25. {
  26.     /**
  27.      * Array of paths needed by the installer
  28.      *
  29.      * @var    array 
  30.      * @since  3.1
  31.      */
  32.     protected $paths = array();
  33.  
  34.     /**
  35.      * True if package is an upgrade
  36.      *
  37.      * @var    boolean 
  38.      * @since  3.1
  39.      */
  40.     protected $upgrade = null;
  41.  
  42.     /**
  43.      * The manifest trigger class
  44.      *
  45.      * @var    object 
  46.      * @since  3.1
  47.      */
  48.     public $manifestClass = null;
  49.  
  50.     /**
  51.      * True if existing files can be overwritten
  52.      *
  53.      * @var    boolean 
  54.      * @since  12.1
  55.      */
  56.     protected $overwrite = false;
  57.  
  58.     /**
  59.      * Stack of installation steps
  60.      * - Used for installation rollback
  61.      *
  62.      * @var    array 
  63.      * @since  3.1
  64.      */
  65.     protected $stepStack = array();
  66.  
  67.     /**
  68.      * Extension Table Entry
  69.      *
  70.      * @var    JTableExtension 
  71.      * @since  3.1
  72.      */
  73.     public $extension = null;
  74.  
  75.     /**
  76.      * The output from the install/uninstall scripts
  77.      *
  78.      * @var    string 
  79.      * @since  3.1
  80.      *  */
  81.     public $message = null;
  82.  
  83.     /**
  84.      * The installation manifest XML object
  85.      *
  86.      * @var    object 
  87.      * @since  3.1
  88.      */
  89.     public $manifest = null;
  90.  
  91.     /**
  92.      * The extension message that appears
  93.      *
  94.      * @var    string 
  95.      * @since  3.1
  96.      */
  97.     protected $extension_message = null;
  98.  
  99.     /**
  100.      * The redirect URL if this extension (can be null if no redirect)
  101.      *
  102.      * @var    string 
  103.      * @since  3.1
  104.      */
  105.     protected $redirect_url = null;
  106.  
  107.     /**
  108.      * JInstaller instance container.
  109.      *
  110.      * @var    JInstaller 
  111.      * @since  3.1
  112.      */
  113.     protected static $instance;
  114.  
  115.     /**
  116.      * Constructor
  117.      *
  118.      * @since   3.1
  119.      */
  120.     public function __construct()
  121.     {
  122.         parent::__construct(__DIR__'JInstallerAdapter'__DIR__ . '/adapter');
  123.  
  124.         // Override the default adapter folder
  125.         $this->_adapterfolder = 'adapter';
  126.     }
  127.  
  128.     /**
  129.      * Returns the global Installer object, only creating it
  130.      * if it doesn't already exist.
  131.      *
  132.      * @return  JInstaller  An installer object
  133.      *
  134.      * @since   3.1
  135.      */
  136.     public static function getInstance()
  137.     {
  138.         if (!isset(self::$instance))
  139.         {
  140.             self::$instance new JInstaller;
  141.         }
  142.  
  143.         return self::$instance;
  144.     }
  145.  
  146.     /**
  147.      * Get the allow overwrite switch
  148.      *
  149.      * @return  boolean  Allow overwrite switch
  150.      *
  151.      * @since   3.1
  152.      */
  153.     public function isOverwrite()
  154.     {
  155.         return $this->overwrite;
  156.     }
  157.  
  158.     /**
  159.      * Set the allow overwrite switch
  160.      *
  161.      * @param   boolean  $state  Overwrite switch state
  162.      *
  163.      * @return  boolean  True it state is set, false if it is not
  164.      *
  165.      * @since   3.1
  166.      */
  167.     public function setOverwrite($state false)
  168.     {
  169.         $tmp $this->overwrite;
  170.  
  171.         if ($state)
  172.         {
  173.             $this->overwrite = true;
  174.         }
  175.         else
  176.         {
  177.             $this->overwrite = false;
  178.         }
  179.  
  180.         return $tmp;
  181.     }
  182.  
  183.     /**
  184.      * Get the redirect location
  185.      *
  186.      * @return  string  Redirect location (or null)
  187.      *
  188.      * @since   3.1
  189.      */
  190.     public function getRedirectURL()
  191.     {
  192.         return $this->redirect_url;
  193.     }
  194.  
  195.     /**
  196.      * Set the redirect location
  197.      *
  198.      * @param   string  $newurl  New redirect location
  199.      *
  200.      * @return  void 
  201.      *
  202.      * @since   3.1
  203.      */
  204.     public function setRedirectURL($newurl)
  205.     {
  206.         $this->redirect_url = $newurl;
  207.     }
  208.  
  209.     /**
  210.      * Get the upgrade switch
  211.      *
  212.      * @return  boolean 
  213.      *
  214.      * @since   3.1
  215.      */
  216.     public function isUpgrade()
  217.     {
  218.         return $this->upgrade;
  219.     }
  220.  
  221.     /**
  222.      * Set the upgrade switch
  223.      *
  224.      * @param   boolean  $state  Upgrade switch state
  225.      *
  226.      * @return  boolean  True if upgrade, false otherwise
  227.      *
  228.      * @since   3.1
  229.      */
  230.     public function setUpgrade($state false)
  231.     {
  232.         $tmp $this->upgrade;
  233.  
  234.         if ($state)
  235.         {
  236.             $this->upgrade = true;
  237.         }
  238.         else
  239.         {
  240.             $this->upgrade = false;
  241.         }
  242.  
  243.         return $tmp;
  244.     }
  245.  
  246.     /**
  247.      * Get the installation manifest object
  248.      *
  249.      * @return  object  Manifest object
  250.      *
  251.      * @since   3.1
  252.      */
  253.     public function getManifest()
  254.     {
  255.         if (!is_object($this->manifest))
  256.         {
  257.             $this->findManifest();
  258.         }
  259.  
  260.         return $this->manifest;
  261.     }
  262.  
  263.     /**
  264.      * Get an installer path by name
  265.      *
  266.      * @param   string  $name     Path name
  267.      * @param   string  $default  Default value
  268.      *
  269.      * @return  string  Path
  270.      *
  271.      * @since   3.1
  272.      */
  273.     public function getPath($name$default null)
  274.     {
  275.         return (!empty($this->paths[$name])) $this->paths[$name$default;
  276.     }
  277.  
  278.     /**
  279.      * Sets an installer path by name
  280.      *
  281.      * @param   string  $name   Path name
  282.      * @param   string  $value  Path
  283.      *
  284.      * @return  void 
  285.      *
  286.      * @since   3.1
  287.      */
  288.     public function setPath($name$value)
  289.     {
  290.         $this->paths[$name$value;
  291.     }
  292.  
  293.     /**
  294.      * Pushes a step onto the installer stack for rolling back steps
  295.      *
  296.      * @param   array  $step  Installer step
  297.      *
  298.      * @return  void 
  299.      *
  300.      * @since   3.1
  301.      */
  302.     public function pushStep($step)
  303.     {
  304.         $this->stepStack[$step;
  305.     }
  306.  
  307.     /**
  308.      * Installation abort method
  309.      *
  310.      * @param   string  $msg   Abort message from the installer
  311.      * @param   string  $type  Package type if defined
  312.      *
  313.      * @return  boolean  True if successful
  314.      *
  315.      * @since   3.1
  316.      * @throws  RuntimeException
  317.      */
  318.     public function abort($msg null$type null)
  319.     {
  320.         $retval true;
  321.         $step array_pop($this->stepStack);
  322.  
  323.         // Raise abort warning
  324.         if ($msg)
  325.         {
  326.             JLog::add($msgJLog::WARNING'jerror');
  327.         }
  328.  
  329.         while ($step != null)
  330.         {
  331.             switch ($step['type'])
  332.             {
  333.                 case 'file':
  334.                     // Remove the file
  335.                     $stepval JFile::delete($step['path']);
  336.                     break;
  337.  
  338.                 case 'folder':
  339.                     // Remove the folder
  340.                     $stepval JFolder::delete($step['path']);
  341.                     break;
  342.  
  343.                 case 'query':
  344.                     // Placeholder in case this is necessary in the future
  345.                     // $stepval is always false because if this step was called it invariably failed
  346.                     $stepval false;
  347.                     break;
  348.  
  349.                 case 'extension':
  350.                     // Get database connector object
  351.                     $db $this->getDBO();
  352.                     $query $db->getQuery(true);
  353.  
  354.                     // Remove the entry from the #__extensions table
  355.                     $query->delete($db->quoteName('#__extensions'))
  356.                         ->where($db->quoteName('extension_id'' = ' . (int) $step['id']);
  357.                     $db->setQuery($query);
  358.                     $stepval $db->execute();
  359.  
  360.                     break;
  361.  
  362.                 default:
  363.                     if ($type && is_object($this->_adapters[$type]))
  364.                     {
  365.                         // Build the name of the custom rollback method for the type
  366.                         $method '_rollback_' $step['type'];
  367.  
  368.                         // Custom rollback method handler
  369.                         if (method_exists($this->_adapters[$type]$method))
  370.                         {
  371.                             $stepval $this->_adapters[$type]->$method($step);
  372.                         }
  373.                     }
  374.                     else
  375.                     {
  376.                         // Set it to false
  377.                         $stepval false;
  378.                     }
  379.                     break;
  380.             }
  381.  
  382.             // Only set the return value if it is false
  383.             if ($stepval === false)
  384.             {
  385.                 $retval false;
  386.             }
  387.  
  388.             // Get the next step and continue
  389.             $step array_pop($this->stepStack);
  390.         }
  391.  
  392.         $conf JFactory::getConfig();
  393.         $debug $conf->get('debug');
  394.  
  395.         if ($debug)
  396.         {
  397.             throw new RuntimeException('Installation unexpectedly terminated: ' $msg500);
  398.         }
  399.  
  400.         return $retval;
  401.     }
  402.  
  403.     // Adapter functions
  404.  
  405.     /**
  406.      * Package installation method
  407.      *
  408.      * @param   string  $path  Path to package source folder
  409.      *
  410.      * @return  boolean  True if successful
  411.      *
  412.      * @since   3.1
  413.      */
  414.     public function install($path null)
  415.     {
  416.         if ($path && JFolder::exists($path))
  417.         {
  418.             $this->setPath('source'$path);
  419.         }
  420.         else
  421.         {
  422.             $this->abort(JText::_('JLIB_INSTALLER_ABORT_NOINSTALLPATH'));
  423.  
  424.             return false;
  425.         }
  426.  
  427.         if (!$this->setupInstall())
  428.         {
  429.             $this->abort(JText::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST'));
  430.  
  431.             return false;
  432.         }
  433.  
  434.         $type = (string) $this->manifest->attributes()->type;
  435.  
  436.         if (is_object($this->_adapters[$type]))
  437.         {
  438.             // Add the languages from the package itself
  439.             if (method_exists($this->_adapters[$type]'loadLanguage'))
  440.             {
  441.                 $this->_adapters[$type]->loadLanguage($path);
  442.             }
  443.  
  444.             // Fire the onExtensionBeforeInstall event.
  445.             JPluginHelper::importPlugin('extension');
  446.             $dispatcher JEventDispatcher::getInstance();
  447.             $dispatcher->trigger(
  448.                 'onExtensionBeforeInstall',
  449.                 array('method' => 'install''type' => $type'manifest' => $this->manifest'extension' => 0)
  450.             );
  451.  
  452.             // Run the install
  453.             $result $this->_adapters[$type]->install();
  454.  
  455.             // Fire the onExtensionAfterInstall
  456.             $dispatcher->trigger(
  457.                 'onExtensionAfterInstall',
  458.                 array('installer' => clone $this'eid' => $result)
  459.             );
  460.  
  461.             if ($result !== false)
  462.             {
  463.                 // Refresh versionable assets cache
  464.                 JFactory::getApplication()->flushAssets();
  465.  
  466.                 return true;
  467.             }
  468.             else
  469.             {
  470.                 return false;
  471.             }
  472.         }
  473.  
  474.         return false;
  475.     }
  476.  
  477.     /**
  478.      * Discovered package installation method
  479.      *
  480.      * @param   integer  $eid  Extension ID
  481.      *
  482.      * @return  boolean  True if successful
  483.      *
  484.      * @since   3.1
  485.      */
  486.     public function discover_install($eid null)
  487.     {
  488.         if ($eid)
  489.         {
  490.             $this->extension = JTable::getInstance('extension');
  491.  
  492.             if (!$this->extension->load($eid))
  493.             {
  494.                 $this->abort(JText::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS'));
  495.  
  496.                 return false;
  497.             }
  498.  
  499.             if ($this->extension->state != -1)
  500.             {
  501.                 $this->abort(JText::_('JLIB_INSTALLER_ABORT_ALREADYINSTALLED'));
  502.  
  503.                 return false;
  504.             }
  505.  
  506.             // Lazy load the adapter
  507.             if (!isset($this->_adapters[$this->extension->type]|| !is_object($this->_adapters[$this->extension->type]))
  508.             {
  509.                 if (!$this->setAdapter($this->extension->type))
  510.                 {
  511.                     return false;
  512.                 }
  513.             }
  514.  
  515.             if (is_object($this->_adapters[$this->extension->type]))
  516.             {
  517.                 if (method_exists($this->_adapters[$this->extension->type]'discover_install'))
  518.                 {
  519.                     // Add the languages from the package itself
  520.                     if (method_exists($this->_adapters[$this->extension->type]'loadLanguage'))
  521.                     {
  522.                         $this->_adapters[$this->extension->type]->loadLanguage();
  523.                     }
  524.  
  525.                     // Fire the onExtensionBeforeInstall event.
  526.                     JPluginHelper::importPlugin('extension');
  527.                     $dispatcher JEventDispatcher::getInstance();
  528.                     $dispatcher->trigger(
  529.                         'onExtensionBeforeInstall',
  530.                         array(
  531.                             'method' => 'discover_install',
  532.                             'type' => $this->extension->get('type'),
  533.                             'manifest' => null,
  534.                             'extension' => $this->extension->get('extension_id')
  535.                         )
  536.                     );
  537.  
  538.                     // Run the install
  539.                     $result $this->_adapters[$this->extension->type]->discover_install();
  540.  
  541.                     // Fire the onExtensionAfterInstall
  542.                     $dispatcher->trigger(
  543.                         'onExtensionAfterInstall',
  544.                         array('installer' => clone $this'eid' => $result)
  545.                     );
  546.  
  547.                     if ($result !== false)
  548.                     {
  549.                         // Refresh versionable assets cache
  550.                         JFactory::getApplication()->flushAssets();
  551.  
  552.                         return true;
  553.                     }
  554.                     else
  555.                     {
  556.                         return false;
  557.                     }
  558.                 }
  559.                 else
  560.                 {
  561.                     $this->abort(JText::_('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED'));
  562.  
  563.                     return false;
  564.                 }
  565.             }
  566.  
  567.             return false;
  568.         }
  569.         else
  570.         {
  571.             $this->abort(JText::_('JLIB_INSTALLER_ABORT_EXTENSIONNOTVALID'));
  572.  
  573.             return false;
  574.         }
  575.     }
  576.  
  577.     /**
  578.      * Extension discover method
  579.      * Asks each adapter to find extensions
  580.      *
  581.      * @return  array  JExtension
  582.      *
  583.      * @since   3.1
  584.      */
  585.     public function discover()
  586.     {
  587.         $this->loadAllAdapters();
  588.         $results array();
  589.  
  590.         foreach ($this->_adapters as $adapter)
  591.         {
  592.             // Joomla! 1.5 installation adapter legacy support
  593.             if (method_exists($adapter'discover'))
  594.             {
  595.                 $tmp $adapter->discover();
  596.  
  597.                 // If its an array and has entries
  598.                 if (is_array($tmp&& count($tmp))
  599.                 {
  600.                     // Merge it into the system
  601.                     $results array_merge($results$tmp);
  602.                 }
  603.             }
  604.         }
  605.  
  606.         return $results;
  607.     }
  608.  
  609.     /**
  610.      * Package update method
  611.      *
  612.      * @param   string  $path  Path to package source folder
  613.      *
  614.      * @return  boolean  True if successful
  615.      *
  616.      * @since   3.1
  617.      */
  618.     public function update($path null)
  619.     {
  620.         if ($path && JFolder::exists($path))
  621.         {
  622.             $this->setPath('source'$path);
  623.         }
  624.         else
  625.         {
  626.             $this->abort(JText::_('JLIB_INSTALLER_ABORT_NOUPDATEPATH'));
  627.  
  628.             return false;
  629.         }
  630.  
  631.         if (!$this->setupInstall())
  632.         {
  633.             $this->abort(JText::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST'));
  634.  
  635.             return false;
  636.         }
  637.  
  638.         $type = (string) $this->manifest->attributes()->type;
  639.  
  640.         if (is_object($this->_adapters[$type]))
  641.         {
  642.             // Add the languages from the package itself
  643.             if (method_exists($this->_adapters[$type]'loadLanguage'))
  644.             {
  645.                 $this->_adapters[$type]->loadLanguage($path);
  646.             }
  647.  
  648.             // Fire the onExtensionBeforeUpdate event.
  649.             JPluginHelper::importPlugin('extension');
  650.             $dispatcher JEventDispatcher::getInstance();
  651.             $dispatcher->trigger('onExtensionBeforeUpdate'array('type' => $type'manifest' => $this->manifest));
  652.  
  653.             // Run the update
  654.             $result $this->_adapters[$type]->update();
  655.  
  656.             // Fire the onExtensionAfterUpdate
  657.             $dispatcher->trigger(
  658.                 'onExtensionAfterUpdate',
  659.                 array('installer' => clone $this'eid' => $result)
  660.             );
  661.  
  662.             if ($result !== false)
  663.             {
  664.                 return true;
  665.             }
  666.             else
  667.             {
  668.                 return false;
  669.             }
  670.         }
  671.  
  672.         return false;
  673.     }
  674.  
  675.     /**
  676.      * Package uninstallation method
  677.      *
  678.      * @param   string   $type        Package type
  679.      * @param   mixed    $identifier  Package identifier for adapter
  680.      * @param   integer  $cid         Application ID; deprecated in 1.6
  681.      *
  682.      * @return  boolean  True if successful
  683.      *
  684.      * @since   3.1
  685.      */
  686.     public function uninstall($type$identifier$cid 0)
  687.     {
  688.         if (!isset($this->_adapters[$type]|| !is_object($this->_adapters[$type]))
  689.         {
  690.             if (!$this->setAdapter($type))
  691.             {
  692.                 // We failed to get the right adapter
  693.                 return false;
  694.             }
  695.         }
  696.  
  697.         if (is_object($this->_adapters[$type]))
  698.         {
  699.             // We don't load languages here, we get the extension adapter to work it out
  700.             // Fire the onExtensionBeforeUninstall event.
  701.             JPluginHelper::importPlugin('extension');
  702.             $dispatcher JEventDispatcher::getInstance();
  703.             $dispatcher->trigger('onExtensionBeforeUninstall'array('eid' => $identifier));
  704.  
  705.             // Run the uninstall
  706.             $result $this->_adapters[$type]->uninstall($identifier);
  707.  
  708.             // Fire the onExtensionAfterInstall
  709.             $dispatcher->trigger(
  710.                 'onExtensionAfterUninstall',
  711.                 array('installer' => clone $this'eid' => $identifier'result' => $result)
  712.             );
  713.  
  714.             // Refresh versionable assets cache
  715.             JFactory::getApplication()->flushAssets();
  716.  
  717.             return $result;
  718.         }
  719.  
  720.         return false;
  721.     }
  722.  
  723.     /**
  724.      * Refreshes the manifest cache stored in #__extensions
  725.      *
  726.      * @param   integer  $eid  Extension ID
  727.      *
  728.      * @return  mixed  void on success, false on error @todo missing return value ?
  729.      *
  730.      * @since   3.1
  731.      */
  732.     public function refreshManifestCache($eid)
  733.     {
  734.         if ($eid)
  735.         {
  736.             $this->extension = JTable::getInstance('extension');
  737.  
  738.             if (!$this->extension->load($eid))
  739.             {
  740.                 $this->abort(JText::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS'));
  741.  
  742.                 return false;
  743.             }
  744.  
  745.             if ($this->extension->state == -1)
  746.             {
  747.                 $this->abort(JText::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE'));
  748.  
  749.                 return false;
  750.             }
  751.  
  752.             // Lazy load the adapter
  753.             if (!isset($this->_adapters[$this->extension->type]|| !is_object($this->_adapters[$this->extension->type]))
  754.             {
  755.                 if (!$this->setAdapter($this->extension->type))
  756.                 {
  757.                     return false;
  758.                 }
  759.             }
  760.  
  761.             if (is_object($this->_adapters[$this->extension->type]))
  762.             {
  763.                 if (method_exists($this->_adapters[$this->extension->type]'refreshManifestCache'))
  764.                 {
  765.                     $result $this->_adapters[$this->extension->type]->refreshManifestCache();
  766.  
  767.                     if ($result !== false)
  768.                     {
  769.                         return true;
  770.                     }
  771.                     else
  772.                     {
  773.                         return false;
  774.                     }
  775.                 }
  776.                 else
  777.                 {
  778.                     $this->abort(JText::sprintf('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED_TYPE'$this->extension->type));
  779.  
  780.                     return false;
  781.                 }
  782.             }
  783.  
  784.             return false;
  785.         }
  786.         else
  787.         {
  788.             $this->abort(JText::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE_VALID'));
  789.  
  790.             return false;
  791.         }
  792.     }
  793.  
  794.     // Utility functions
  795.  
  796.     /**
  797.      * Prepare for installation: this method sets the installation directory, finds
  798.      * and checks the installation file and verifies the installation type.
  799.      *
  800.      * @return  boolean  True on success
  801.      *
  802.      * @since   3.1
  803.      */
  804.     public function setupInstall()
  805.     {
  806.         // We need to find the installation manifest file
  807.         if (!$this->findManifest())
  808.         {
  809.             return false;
  810.         }
  811.  
  812.         // Load the adapter(s) for the install manifest
  813.         $type = (string) $this->manifest->attributes()->type;
  814.  
  815.         // Lazy load the adapter
  816.         if (!isset($this->_adapters[$type]|| !is_object($this->_adapters[$type]))
  817.         {
  818.             if (!$this->setAdapter($type))
  819.             {
  820.                 return false;
  821.             }
  822.         }
  823.  
  824.         return true;
  825.     }
  826.  
  827.     /**
  828.      * Backward compatible method to parse through a queries element of the
  829.      * installation manifest file and take appropriate action.
  830.      *
  831.      * @param   SimpleXMLElement  $element  The XML node to process
  832.      *
  833.      * @return  mixed  Number of queries processed or False on error
  834.      *
  835.      * @since   3.1
  836.      */
  837.     public function parseQueries(SimpleXMLElement $element)
  838.     {
  839.         // Get the database connector object
  840.         $db $this->_db;
  841.  
  842.         if (!$element || !count($element->children()))
  843.         {
  844.             // Either the tag does not exist or has no children therefore we return zero files processed.
  845.             return 0;
  846.         }
  847.  
  848.         // Get the array of query nodes to process
  849.         $queries $element->children();
  850.  
  851.         if (count($queries== 0)
  852.         {
  853.             // No queries to process
  854.             return 0;
  855.         }
  856.  
  857.         // Process each query in the $queries array (children of $tagName).
  858.         foreach ($queries as $query)
  859.         {
  860.             $db->setQuery($query->data());
  861.  
  862.             if (!$db->execute())
  863.             {
  864.                 JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR'$db->stderr(true))JLog::WARNING'jerror');
  865.  
  866.                 return false;
  867.             }
  868.         }
  869.  
  870.         return (int) count($queries);
  871.     }
  872.  
  873.     /**
  874.      * Method to extract the name of a discreet installation sql file from the installation manifest file.
  875.      *
  876.      * @param   object  $element  The XML node to process
  877.      *
  878.      * @return  mixed  Number of queries processed or False on error
  879.      *
  880.      * @since   3.1
  881.      */
  882.     public function parseSQLFiles($element)
  883.     {
  884.         if (!$element || !count($element->children()))
  885.         {
  886.             // The tag does not exist.
  887.             return 0;
  888.         }
  889.  
  890.         $queries array();
  891.         $db $this->_db;
  892.         $dbDriver strtolower($db->name);
  893.  
  894.         if ($dbDriver == 'mysqli')
  895.         {
  896.             $dbDriver 'mysql';
  897.         }
  898.  
  899.         // Get the name of the sql file to process
  900.         foreach ($element->children(as $file)
  901.         {
  902.             $fCharset (strtolower($file->attributes()->charset== 'utf8''utf8' '';
  903.             $fDriver strtolower($file->attributes()->driver);
  904.  
  905.             if ($fDriver == 'mysqli')
  906.             {
  907.                 $fDriver 'mysql';
  908.             }
  909.  
  910.             if ($fCharset == 'utf8' && $fDriver == $dbDriver)
  911.             {
  912.                 $sqlfile $this->getPath('extension_root''/' $file;
  913.  
  914.                 // Check that sql files exists before reading. Otherwise raise error for rollback
  915.                 if (!file_exists($sqlfile))
  916.                 {
  917.                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR'$db->stderr(true))JLog::WARNING'jerror');
  918.  
  919.                     return false;
  920.                 }
  921.  
  922.                 $buffer file_get_contents($sqlfile);
  923.  
  924.                 // Graceful exit and rollback if read not successful
  925.                 if ($buffer === false)
  926.                 {
  927.                     JLog::add(JText::_('JLIB_INSTALLER_ERROR_SQL_READBUFFER')JLog::WARNING'jerror');
  928.  
  929.                     return false;
  930.                 }
  931.  
  932.                 // Create an array of queries from the sql file
  933.                 $queries JDatabaseDriver::splitSql($buffer);
  934.  
  935.                 if (count($queries== 0)
  936.                 {
  937.                     // No queries to process
  938.                     return 0;
  939.                 }
  940.  
  941.                 // Process each query in the $queries array (split out of sql file).
  942.                 foreach ($queries as $query)
  943.                 {
  944.                     $query trim($query);
  945.  
  946.                     if ($query != '' && $query{0!= '#')
  947.                     {
  948.                         $db->setQuery($query);
  949.  
  950.                         if (!$db->execute())
  951.                         {
  952.                             JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR'$db->stderr(true))JLog::WARNING'jerror');
  953.  
  954.                             return false;
  955.                         }
  956.                     }
  957.                 }
  958.             }
  959.         }
  960.  
  961.         return (int) count($queries);
  962.     }
  963.  
  964.     /**
  965.      * Set the schema version for an extension by looking at its latest update
  966.      *
  967.      * @param   SimpleXMLElement  $schema  Schema Tag
  968.      * @param   integer           $eid     Extension ID
  969.      *
  970.      * @return  void 
  971.      *
  972.      * @since   3.1
  973.      */
  974.     public function setSchemaVersion(SimpleXMLElement $schema$eid)
  975.     {
  976.         if ($eid && $schema)
  977.         {
  978.             $db JFactory::getDbo();
  979.             $schemapaths $schema->children();
  980.  
  981.             if (!$schemapaths)
  982.             {
  983.                 return;
  984.             }
  985.  
  986.             if (count($schemapaths))
  987.             {
  988.                 $dbDriver strtolower($db->name);
  989.  
  990.                 if ($dbDriver == 'mysqli')
  991.                 {
  992.                     $dbDriver 'mysql';
  993.                 }
  994.  
  995.                 $schemapath '';
  996.  
  997.                 foreach ($schemapaths as $entry)
  998.                 {
  999.                     $attrs $entry->attributes();
  1000.  
  1001.                     if ($attrs['type'== $dbDriver)
  1002.                     {
  1003.                         $schemapath $entry;
  1004.                         break;
  1005.                     }
  1006.                 }
  1007.  
  1008.                 if (strlen($schemapath))
  1009.                 {
  1010.                     $files str_replace('.sql'''JFolder::files($this->getPath('extension_root''/' $schemapath'\.sql$'));
  1011.                     usort($files'version_compare');
  1012.  
  1013.                     // Update the database
  1014.                     $query $db->getQuery(true)
  1015.                         ->delete('#__schemas')
  1016.                         ->where('extension_id = ' $eid);
  1017.                     $db->setQuery($query);
  1018.  
  1019.                     if ($db->execute())
  1020.                     {
  1021.                         $query->clear()
  1022.                             ->insert($db->quoteName('#__schemas'))
  1023.                             ->columns(array($db->quoteName('extension_id')$db->quoteName('version_id')))
  1024.                             ->values($eid ', ' $db->quote(end($files)));
  1025.                         $db->setQuery($query);
  1026.                         $db->execute();
  1027.                     }
  1028.                 }
  1029.             }
  1030.         }
  1031.     }
  1032.  
  1033.     /**
  1034.      * Method to process the updates for an item
  1035.      *
  1036.      * @param   SimpleXMLElement  $schema  The XML node to process
  1037.      * @param   integer           $eid     Extension Identifier
  1038.      *
  1039.      * @return  boolean           Result of the operations
  1040.      *
  1041.      * @since   3.1
  1042.      */
  1043.     public function parseSchemaUpdates(SimpleXMLElement $schema$eid)
  1044.     {
  1045.         $update_count 0;
  1046.  
  1047.         // Ensure we have an XML element and a valid extension id
  1048.         if ($eid && $schema)
  1049.         {
  1050.             $db JFactory::getDbo();
  1051.             $schemapaths $schema->children();
  1052.  
  1053.             if (count($schemapaths))
  1054.             {
  1055.                 $dbDriver strtolower($db->name);
  1056.  
  1057.                 if ($dbDriver == 'mysqli')
  1058.                 {
  1059.                     $dbDriver 'mysql';
  1060.                 }
  1061.  
  1062.                 $schemapath '';
  1063.  
  1064.                 foreach ($schemapaths as $entry)
  1065.                 {
  1066.                     $attrs $entry->attributes();
  1067.  
  1068.                     if ($attrs['type'== $dbDriver)
  1069.                     {
  1070.                         $schemapath $entry;
  1071.                         break;
  1072.                     }
  1073.                 }
  1074.  
  1075.                 if (strlen($schemapath))
  1076.                 {
  1077.                     $files str_replace('.sql'''JFolder::files($this->getPath('extension_root''/' $schemapath'\.sql$'));
  1078.                     usort($files'version_compare');
  1079.  
  1080.                     if (!count($files))
  1081.                     {
  1082.                         return false;
  1083.                     }
  1084.  
  1085.                     $query $db->getQuery(true)
  1086.                         ->select('version_id')
  1087.                         ->from('#__schemas')
  1088.                         ->where('extension_id = ' $eid);
  1089.                     $db->setQuery($query);
  1090.                     $version $db->loadResult();
  1091.  
  1092.                     if ($version)
  1093.                     {
  1094.                         // We have a version!
  1095.                         foreach ($files as $file)
  1096.                         {
  1097.                             if (version_compare($file$version0)
  1098.                             {
  1099.                                 $buffer file_get_contents($this->getPath('extension_root''/' $schemapath '/' $file '.sql');
  1100.  
  1101.                                 // Graceful exit and rollback if read not successful
  1102.                                 if ($buffer === false)
  1103.                                 {
  1104.                                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_SQL_READBUFFER')JLog::WARNING'jerror');
  1105.  
  1106.                                     return false;
  1107.                                 }
  1108.  
  1109.                                 // Create an array of queries from the sql file
  1110.                                 $queries JDatabaseDriver::splitSql($buffer);
  1111.  
  1112.                                 if (count($queries== 0)
  1113.                                 {
  1114.                                     // No queries to process
  1115.                                     continue;
  1116.                                 }
  1117.  
  1118.                                 // Process each query in the $queries array (split out of sql file).
  1119.                                 foreach ($queries as $query)
  1120.                                 {
  1121.                                     $query trim($query);
  1122.  
  1123.                                     if ($query != '' && $query{0!= '#')
  1124.                                     {
  1125.                                         $db->setQuery($query);
  1126.  
  1127.                                         if (!$db->execute())
  1128.                                         {
  1129.                                             JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR'$db->stderr(true))JLog::WARNING'jerror');
  1130.  
  1131.                                             return false;
  1132.                                         }
  1133.                                         else
  1134.                                         {
  1135.                                             $queryString = (string) $query;
  1136.                                             $queryString str_replace(array("\r""\n")array(''' ')substr($queryString080));
  1137.                                             JLog::add(JText::sprintf('JLIB_INSTALLER_UPDATE_LOG_QUERY'$file$queryString)JLog::INFO'Update');
  1138.                                         }
  1139.  
  1140.                                         $update_count++;
  1141.                                     }
  1142.                                 }
  1143.                             }
  1144.                         }
  1145.                     }
  1146.  
  1147.                     // Update the database
  1148.                     $query $db->getQuery(true)
  1149.                         ->delete('#__schemas')
  1150.                         ->where('extension_id = ' $eid);
  1151.                     $db->setQuery($query);
  1152.  
  1153.                     if ($db->execute())
  1154.                     {
  1155.                         $query->clear()
  1156.                             ->insert($db->quoteName('#__schemas'))
  1157.                             ->columns(array($db->quoteName('extension_id')$db->quoteName('version_id')))
  1158.                             ->values($eid ', ' $db->quote(end($files)));
  1159.                         $db->setQuery($query);
  1160.                         $db->execute();
  1161.                     }
  1162.                 }
  1163.             }
  1164.         }
  1165.  
  1166.         return $update_count;
  1167.     }
  1168.  
  1169.     /**
  1170.      * Method to parse through a files element of the installation manifest and take appropriate
  1171.      * action.
  1172.      *
  1173.      * @param   SimpleXMLElement  $element   The XML node to process
  1174.      * @param   integer           $cid       Application ID of application to install to
  1175.      * @param   array             $oldFiles  List of old files (SimpleXMLElement's)
  1176.      * @param   array             $oldMD5    List of old MD5 sums (indexed by filename with value as MD5)
  1177.      *
  1178.      * @return  boolean      True on success
  1179.      *
  1180.      * @since   3.1
  1181.      */
  1182.     public function parseFiles(SimpleXMLElement $element$cid 0$oldFiles null$oldMD5 null)
  1183.     {
  1184.         // Get the array of file nodes to process; we checked whether this had children above.
  1185.         if (!$element || !count($element->children()))
  1186.         {
  1187.             // Either the tag does not exist or has no children (hence no files to process) therefore we return zero files processed.
  1188.             return 0;
  1189.         }
  1190.  
  1191.         $copyfiles array();
  1192.  
  1193.         // Get the client info
  1194.         $client JApplicationHelper::getClientInfo($cid);
  1195.  
  1196.         /*
  1197.          * Here we set the folder we are going to remove the files from.
  1198.          */
  1199.         if ($client)
  1200.         {
  1201.             $pathname 'extension_' $client->name;
  1202.             $destination $this->getPath($pathname);
  1203.         }
  1204.         else
  1205.         {
  1206.             $pathname 'extension_root';
  1207.             $destination $this->getPath($pathname);
  1208.         }
  1209.  
  1210.         /*
  1211.          * Here we set the folder we are going to copy the files from.
  1212.          *
  1213.          * Does the element have a folder attribute?
  1214.          *
  1215.          * If so this indicates that the files are in a subdirectory of the source
  1216.          * folder and we should append the folder attribute to the source path when
  1217.          * copying files.
  1218.          */
  1219.  
  1220.         $folder = (string) $element->attributes()->folder;
  1221.  
  1222.         if ($folder && file_exists($this->getPath('source''/' $folder))
  1223.         {
  1224.             $source $this->getPath('source''/' $folder;
  1225.         }
  1226.         else
  1227.         {
  1228.             $source $this->getPath('source');
  1229.         }
  1230.  
  1231.         // Work out what files have been deleted
  1232.         if ($oldFiles && ($oldFiles instanceof SimpleXMLElement))
  1233.         {
  1234.             $oldEntries $oldFiles->children();
  1235.  
  1236.             if (count($oldEntries))
  1237.             {
  1238.                 $deletions $this->findDeletedFiles($oldEntries$element->children());
  1239.  
  1240.                 foreach ($deletions['folders'as $deleted_folder)
  1241.                 {
  1242.                     JFolder::delete($destination '/' $deleted_folder);
  1243.                 }
  1244.  
  1245.                 foreach ($deletions['files'as $deleted_file)
  1246.                 {
  1247.                     JFile::delete($destination '/' $deleted_file);
  1248.                 }
  1249.             }
  1250.         }
  1251.  
  1252.         $path array();
  1253.  
  1254.         // Copy the MD5SUMS file if it exists
  1255.         if (file_exists($source '/MD5SUMS'))
  1256.         {
  1257.             $path['src'$source '/MD5SUMS';
  1258.             $path['dest'$destination '/MD5SUMS';
  1259.             $path['type''file';
  1260.             $copyfiles[$path;
  1261.         }
  1262.  
  1263.         // Process each file in the $files array (children of $tagName).
  1264.         foreach ($element->children(as $file)
  1265.         {
  1266.             $path['src'$source '/' $file;
  1267.             $path['dest'$destination '/' $file;
  1268.  
  1269.             // Is this path a file or folder?
  1270.             $path['type'($file->getName(== 'folder''folder' 'file';
  1271.  
  1272.             /*
  1273.              * Before we can add a file to the copyfiles array we need to ensure
  1274.              * that the folder we are copying our file to exits and if it doesn't,
  1275.              * we need to create it.
  1276.              */
  1277.  
  1278.             if (basename($path['dest']!= $path['dest'])
  1279.             {
  1280.                 $newdir dirname($path['dest']);
  1281.  
  1282.                 if (!JFolder::create($newdir))
  1283.                 {
  1284.                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY'$newdir)JLog::WARNING'jerror');
  1285.  
  1286.                     return false;
  1287.                 }
  1288.             }
  1289.  
  1290.             // Add the file to the copyfiles array
  1291.             $copyfiles[$path;
  1292.         }
  1293.  
  1294.         return $this->copyFiles($copyfiles);
  1295.     }
  1296.  
  1297.     /**
  1298.      * Method to parse through a languages element of the installation manifest and take appropriate
  1299.      * action.
  1300.      *
  1301.      * @param   SimpleXMLElement  $element  The XML node to process
  1302.      * @param   integer           $cid      Application ID of application to install to
  1303.      *
  1304.      * @return  boolean  True on success
  1305.      *
  1306.      * @since   3.1
  1307.      */
  1308.     public function parseLanguages(SimpleXMLElement $element$cid 0)
  1309.     {
  1310.         // TODO: work out why the below line triggers 'node no longer exists' errors with files
  1311.         if (!$element || !count($element->children()))
  1312.         {
  1313.             // Either the tag does not exist or has no children therefore we return zero files processed.
  1314.             return 0;
  1315.         }
  1316.  
  1317.         $copyfiles array();
  1318.  
  1319.         // Get the client info
  1320.         $client JApplicationHelper::getClientInfo($cid);
  1321.  
  1322.         // Here we set the folder we are going to copy the files to.
  1323.         // 'languages' Files are copied to JPATH_BASE/language/ folder
  1324.  
  1325.         $destination $client->path '/language';
  1326.  
  1327.         /*
  1328.          * Here we set the folder we are going to copy the files from.
  1329.          *
  1330.          * Does the element have a folder attribute?
  1331.          *
  1332.          * If so this indicates that the files are in a subdirectory of the source
  1333.          * folder and we should append the folder attribute to the source path when
  1334.          * copying files.
  1335.          */
  1336.  
  1337.         $folder = (string) $element->attributes()->folder;
  1338.  
  1339.         if ($folder && file_exists($this->getPath('source''/' $folder))
  1340.         {
  1341.             $source $this->getPath('source''/' $folder;
  1342.         }
  1343.         else
  1344.         {
  1345.             $source $this->getPath('source');
  1346.         }
  1347.  
  1348.         // Process each file in the $files array (children of $tagName).
  1349.         foreach ($element->children(as $file)
  1350.         {
  1351.             /*
  1352.              * Language files go in a subfolder based on the language code, ie.
  1353.              * <language tag="en-US">en-US.mycomponent.ini</language>
  1354.              * would go in the en-US subdirectory of the language folder.
  1355.              */
  1356.  
  1357.             // We will only install language files where a core language pack
  1358.             // already exists.
  1359.  
  1360.             if ((string) $file->attributes()->tag != '')
  1361.             {
  1362.                 $path['src'$source '/' $file;
  1363.  
  1364.                 if ((string) $file->attributes()->client != '')
  1365.                 {
  1366.                     // Override the client
  1367.                     $langclient JApplicationHelper::getClientInfo((string) $file->attributes()->clienttrue);
  1368.                     $path['dest'$langclient->path '/language/' $file->attributes()->tag '/' basename((string) $file);
  1369.                 }
  1370.                 else
  1371.                 {
  1372.                     // Use the default client
  1373.                     $path['dest'$destination '/' $file->attributes()->tag '/' basename((string) $file);
  1374.                 }
  1375.  
  1376.                 // If the language folder is not present, then the core pack hasn't been installed... ignore
  1377.                 if (!JFolder::exists(dirname($path['dest'])))
  1378.                 {
  1379.                     continue;
  1380.                 }
  1381.             }
  1382.             else
  1383.             {
  1384.                 $path['src'$source '/' $file;
  1385.                 $path['dest'$destination '/' $file;
  1386.             }
  1387.  
  1388.             /*
  1389.              * Before we can add a file to the copyfiles array we need to ensure
  1390.              * that the folder we are copying our file to exits and if it doesn't,
  1391.              * we need to create it.
  1392.              */
  1393.  
  1394.             if (basename($path['dest']!= $path['dest'])
  1395.             {
  1396.                 $newdir dirname($path['dest']);
  1397.  
  1398.                 if (!JFolder::create($newdir))
  1399.                 {
  1400.                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY'$newdir)JLog::WARNING'jerror');
  1401.  
  1402.                     return false;
  1403.                 }
  1404.             }
  1405.  
  1406.             // Add the file to the copyfiles array
  1407.             $copyfiles[$path;
  1408.         }
  1409.  
  1410.         return $this->copyFiles($copyfiles);
  1411.     }
  1412.  
  1413.     /**
  1414.      * Method to parse through a media element of the installation manifest and take appropriate
  1415.      * action.
  1416.      *
  1417.      * @param   SimpleXMLElement  $element  The XML node to process
  1418.      * @param   integer           $cid      Application ID of application to install to
  1419.      *
  1420.      * @return  boolean     True on success
  1421.      *
  1422.      * @since   3.1
  1423.      */
  1424.     public function parseMedia(SimpleXMLElement $element$cid 0)
  1425.     {
  1426.         if (!$element || !count($element->children()))
  1427.         {
  1428.             // Either the tag does not exist or has no children therefore we return zero files processed.
  1429.             return 0;
  1430.         }
  1431.  
  1432.         $copyfiles array();
  1433.  
  1434.         // Here we set the folder we are going to copy the files to.
  1435.         // Default 'media' Files are copied to the JPATH_BASE/media folder
  1436.  
  1437.         $folder ((string) $element->attributes()->destination'/' $element->attributes()->destination null;
  1438.         $destination JPath::clean(JPATH_ROOT '/media' $folder);
  1439.  
  1440.         // Here we set the folder we are going to copy the files from.
  1441.  
  1442.         /*
  1443.          * Does the element have a folder attribute?
  1444.          * If so this indicates that the files are in a subdirectory of the source
  1445.          * folder and we should append the folder attribute to the source path when
  1446.          * copying files.
  1447.          */
  1448.  
  1449.         $folder = (string) $element->attributes()->folder;
  1450.  
  1451.         if ($folder && file_exists($this->getPath('source''/' $folder))
  1452.         {
  1453.             $source $this->getPath('source''/' $folder;
  1454.         }
  1455.         else
  1456.         {
  1457.             $source $this->getPath('source');
  1458.         }
  1459.  
  1460.         // Process each file in the $files array (children of $tagName).
  1461.         foreach ($element->children(as $file)
  1462.         {
  1463.             $path['src'$source '/' $file;
  1464.             $path['dest'$destination '/' $file;
  1465.  
  1466.             // Is this path a file or folder?
  1467.             $path['type'($file->getName(== 'folder''folder' 'file';
  1468.  
  1469.             /*
  1470.              * Before we can add a file to the copyfiles array we need to ensure
  1471.              * that the folder we are copying our file to exits and if it doesn't,
  1472.              * we need to create it.
  1473.              */
  1474.  
  1475.             if (basename($path['dest']!= $path['dest'])
  1476.             {
  1477.                 $newdir dirname($path['dest']);
  1478.  
  1479.                 if (!JFolder::create($newdir))
  1480.                 {
  1481.                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY'$newdir)JLog::WARNING'jerror');
  1482.  
  1483.                     return false;
  1484.                 }
  1485.             }
  1486.  
  1487.             // Add the file to the copyfiles array
  1488.             $copyfiles[$path;
  1489.         }
  1490.  
  1491.         return $this->copyFiles($copyfiles);
  1492.     }
  1493.  
  1494.     /**
  1495.      * Method to parse the parameters of an extension, build the INI
  1496.      * string for its default parameters, and return the INI string.
  1497.      *
  1498.      * @return  string   INI string of parameter values
  1499.      *
  1500.      * @since   3.1
  1501.      */
  1502.     public function getParams()
  1503.     {
  1504.         // Validate that we have a fieldset to use
  1505.         if (!isset($this->manifest->config->fields->fieldset))
  1506.         {
  1507.             return '{}';
  1508.         }
  1509.         // Getting the fieldset tags
  1510.         $fieldsets $this->manifest->config->fields->fieldset;
  1511.  
  1512.         // Creating the data collection variable:
  1513.         $ini array();
  1514.  
  1515.         // Iterating through the fieldsets:
  1516.         foreach ($fieldsets as $fieldset)
  1517.         {
  1518.             if (!count($fieldset->children()))
  1519.             {
  1520.                 // Either the tag does not exist or has no children therefore we return zero files processed.
  1521.                 return null;
  1522.             }
  1523.  
  1524.             // Iterating through the fields and collecting the name/default values:
  1525.             foreach ($fieldset as $field)
  1526.             {
  1527.                 // Check against the null value since otherwise default values like "0"
  1528.                 // cause entire parameters to be skipped.
  1529.  
  1530.                 if (($name $field->attributes()->name=== null)
  1531.                 {
  1532.                     continue;
  1533.                 }
  1534.  
  1535.                 if (($value $field->attributes()->default=== null)
  1536.                 {
  1537.                     continue;
  1538.                 }
  1539.  
  1540.                 $ini[(string) $name= (string) $value;
  1541.             }
  1542.         }
  1543.  
  1544.         return json_encode($ini);
  1545.     }
  1546.  
  1547.     /**
  1548.      * Copyfiles
  1549.      *
  1550.      * Copy files from source directory to the target directory
  1551.      *
  1552.      * @param   array    $files      Array with filenames
  1553.      * @param   boolean  $overwrite  True if existing files can be replaced
  1554.      *
  1555.      * @return  boolean  True on success
  1556.      *
  1557.      * @since   3.1
  1558.      */
  1559.     public function copyFiles($files$overwrite null)
  1560.     {
  1561.         /*
  1562.          * To allow for manual override on the overwriting flag, we check to see if
  1563.          * the $overwrite flag was set and is a boolean value.  If not, use the object
  1564.          * allowOverwrite flag.
  1565.          */
  1566.  
  1567.         if (is_null($overwrite|| !is_bool($overwrite))
  1568.         {
  1569.             $overwrite $this->overwrite;
  1570.         }
  1571.  
  1572.         /*
  1573.          * $files must be an array of filenames.  Verify that it is an array with
  1574.          * at least one file to copy.
  1575.          */
  1576.         if (is_array($files&& count($files0)
  1577.         {
  1578.  
  1579.             foreach ($files as $file)
  1580.             {
  1581.                 // Get the source and destination paths
  1582.                 $filesource JPath::clean($file['src']);
  1583.                 $filedest JPath::clean($file['dest']);
  1584.                 $filetype array_key_exists('type'$file$file['type''file';
  1585.  
  1586.                 if (!file_exists($filesource))
  1587.                 {
  1588.                     /*
  1589.                      * The source file does not exist.  Nothing to copy so set an error
  1590.                      * and return false.
  1591.                      */
  1592.                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_NO_FILE'$filesource)JLog::WARNING'jerror');
  1593.  
  1594.                     return false;
  1595.                 }
  1596.                 elseif (($exists file_exists($filedest)) && !$overwrite)
  1597.                 {
  1598.  
  1599.                     // It's okay if the manifest already exists
  1600.                     if ($this->getPath('manifest'== $filesource)
  1601.                     {
  1602.                         continue;
  1603.                     }
  1604.  
  1605.                     // The destination file already exists and the overwrite flag is false.
  1606.                     // Set an error and return false.
  1607.                     JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_FILE_EXISTS'$filedest)JLog::WARNING'jerror');
  1608.  
  1609.                     return false;
  1610.                 }
  1611.                 else
  1612.                 {
  1613.                     // Copy the folder or file to the new location.
  1614.                     if ($filetype == 'folder')
  1615.                     {
  1616.                         if (!(JFolder::copy($filesource$filedestnull$overwrite)))
  1617.                         {
  1618.                             JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FOLDER'$filesource$filedest)JLog::WARNING'jerror');
  1619.  
  1620.                             return false;
  1621.                         }
  1622.  
  1623.                         $step array('type' => 'folder''path' => $filedest);
  1624.                     }
  1625.                     else
  1626.                     {
  1627.                         if (!(JFile::copy($filesource$filedestnull)))
  1628.                         {
  1629.                             JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FILE'$filesource$filedest)JLog::WARNING'jerror');
  1630.  
  1631.                             // In 3.2, TinyMCE language handling changed.  Display a special notice in case an older language pack is installed.
  1632.                             if (strpos($filedest'media/editors/tinymce/jscripts/tiny_mce/langs'))
  1633.                             {
  1634.                                 JLog::add(JText::_('JLIB_INSTALLER_NOT_ERROR')JLog::WARNING'jerror');
  1635.                             }
  1636.  
  1637.                             return false;
  1638.                         }
  1639.  
  1640.                         $step array('type' => 'file''path' => $filedest);
  1641.                     }
  1642.  
  1643.                     /*
  1644.                      * Since we copied a file/folder, we want to add it to the installation step stack so that
  1645.                      * in case we have to roll back the installation we can remove the files copied.
  1646.                      */
  1647.                     if (!$exists)
  1648.                     {
  1649.                         $this->stepStack[$step;
  1650.                     }
  1651.                 }
  1652.             }
  1653.         }
  1654.         else
  1655.         {
  1656.             // The $files variable was either not an array or an empty array
  1657.             return false;
  1658.         }
  1659.  
  1660.         return count($files);
  1661.     }
  1662.  
  1663.     /**
  1664.      * Method to parse through a files element of the installation manifest and remove
  1665.      * the files that were installed
  1666.      *
  1667.      * @param   object   $element  The XML node to process
  1668.      * @param   integer  $cid      Application ID of application to remove from
  1669.      *
  1670.      * @return  boolean  True on success
  1671.      *
  1672.      * @since   3.1
  1673.      */
  1674.     public function removeFiles($element$cid 0)
  1675.     {
  1676.         if (!$element || !count($element->children()))
  1677.         {
  1678.             // Either the tag does not exist or has no children therefore we return zero files processed.
  1679.             return true;
  1680.         }
  1681.  
  1682.         $retval true;
  1683.  
  1684.         // Get the client info if we're using a specific client
  1685.         if ($cid > -1)
  1686.         {
  1687.             $client JApplicationHelper::getClientInfo($cid);
  1688.         }
  1689.         else
  1690.         {
  1691.             $client null;
  1692.         }
  1693.  
  1694.         // Get the array of file nodes to process
  1695.         $files $element->children();
  1696.  
  1697.         if (count($files== 0)
  1698.         {
  1699.             // No files to process
  1700.             return true;
  1701.         }
  1702.  
  1703.         $folder '';
  1704.  
  1705.         /*
  1706.          * Here we set the folder we are going to remove the files from.  There are a few
  1707.          * special cases that need to be considered for certain reserved tags.
  1708.          */
  1709.         switch ($element->getName())
  1710.         {
  1711.             case 'media':
  1712.                 if ((string) $element->attributes()->destination)
  1713.                 {
  1714.                     $folder = (string) $element->attributes()->destination;
  1715.                 }
  1716.                 else
  1717.                 {
  1718.                     $folder '';
  1719.                 }
  1720.  
  1721.                 $source $client->path '/media/' $folder;
  1722.  
  1723.                 break;
  1724.  
  1725.             case 'languages':
  1726.                 $lang_client = (string) $element->attributes()->client;
  1727.  
  1728.                 if ($lang_client)
  1729.                 {
  1730.                     $client JApplicationHelper::getClientInfo($lang_clienttrue);
  1731.                     $source $client->path '/language';
  1732.                 }
  1733.                 else
  1734.                 {
  1735.                     if ($client)
  1736.                     {
  1737.                         $source $client->path '/language';
  1738.                     }
  1739.                     else
  1740.                     {
  1741.                         $source '';
  1742.                     }
  1743.                 }
  1744.  
  1745.                 break;
  1746.  
  1747.             default:
  1748.                 if ($client)
  1749.                 {
  1750.                     $pathname 'extension_' $client->name;
  1751.                     $source $this->getPath($pathname);
  1752.                 }
  1753.                 else
  1754.                 {
  1755.                     $pathname 'extension_root';
  1756.                     $source $this->getPath($pathname);
  1757.                 }
  1758.  
  1759.                 break;
  1760.         }
  1761.  
  1762.         // Process each file in the $files array (children of $tagName).
  1763.         foreach ($files as $file)
  1764.         {
  1765.             /*
  1766.              * If the file is a language, we must handle it differently.  Language files
  1767.              * go in a subdirectory based on the language code, ie.
  1768.              * <language tag="en_US">en_US.mycomponent.ini</language>
  1769.              * would go in the en_US subdirectory of the languages directory.
  1770.              */
  1771.  
  1772.             if ($file->getName(== 'language' && (string) $file->attributes()->tag != '')
  1773.             {
  1774.                 if ($source)
  1775.                 {
  1776.                     $path $source '/' $file->attributes()->tag '/' basename((string) $file);
  1777.                 }
  1778.                 else
  1779.                 {
  1780.                     $target_client JApplicationHelper::getClientInfo((string) $file->attributes()->clienttrue);
  1781.                     $path $target_client->path '/language/' $file->attributes()->tag '/' basename((string) $file);
  1782.                 }
  1783.  
  1784.                 // If the language folder is not present, then the core pack hasn't been installed... ignore
  1785.                 if (!JFolder::exists(dirname($path)))
  1786.                 {
  1787.                     continue;
  1788.                 }
  1789.             }
  1790.             else
  1791.             {
  1792.                 $path $source '/' $file;
  1793.             }
  1794.  
  1795.             // Actually delete the files/folders
  1796.  
  1797.             if (is_dir($path))
  1798.             {
  1799.                 $val JFolder::delete($path);
  1800.             }
  1801.             else
  1802.             {
  1803.                 $val JFile::delete($path);
  1804.             }
  1805.  
  1806.             if ($val === false)
  1807.             {
  1808.                 JLog::add('Failed to delete ' $pathJLog::WARNING'jerror');
  1809.                 $retval false;
  1810.             }
  1811.         }
  1812.  
  1813.         if (!empty($folder))
  1814.         {
  1815.             JFolder::delete($source);
  1816.         }
  1817.  
  1818.         return $retval;
  1819.     }
  1820.  
  1821.     /**
  1822.      * Copies the installation manifest file to the extension folder in the given client
  1823.      *
  1824.      * @param   integer  $cid  Where to copy the installfile [optional: defaults to 1 (admin)]
  1825.      *
  1826.      * @return  boolean  True on success, False on error
  1827.      *
  1828.      * @since   3.1
  1829.      */
  1830.     public function copyManifest($cid 1)
  1831.     {
  1832.         // Get the client info
  1833.         $client JApplicationHelper::getClientInfo($cid);
  1834.  
  1835.         $path['src'$this->getPath('manifest');
  1836.  
  1837.         if ($client)
  1838.         {
  1839.             $pathname 'extension_' $client->name;
  1840.             $path['dest'$this->getPath($pathname'/' basename($this->getPath('manifest'));
  1841.         }
  1842.         else
  1843.         {
  1844.             $pathname 'extension_root';
  1845.             $path['dest'$this->getPath($pathname'/' basename($this->getPath('manifest'));
  1846.         }
  1847.  
  1848.         return $this->copyFiles(array($path)true);
  1849.     }
  1850.  
  1851.     /**
  1852.      * Tries to find the package manifest file
  1853.      *
  1854.      * @return  boolean  True on success, False on error
  1855.      *
  1856.      * @since   3.1
  1857.      */
  1858.     public function findManifest()
  1859.     {
  1860.         // Main folder manifests (higher priority)
  1861.         $parentXmlfiles JFolder::files($this->getPath('source')'.xml$'falsetrue);
  1862.  
  1863.         // Search for children manifests (lower priority)
  1864.         $allXmlFiles    JFolder::files($this->getPath('source')'.xml$'1true);
  1865.  
  1866.         // Create an unique array of files ordered by priority
  1867.         $xmlfiles array_unique(array_merge($parentXmlfiles$allXmlFiles));
  1868.  
  1869.         // If at least one XML file exists
  1870.         if (!empty($xmlfiles))
  1871.         {
  1872.  
  1873.             foreach ($xmlfiles as $file)
  1874.             {
  1875.                 // Is it a valid Joomla installation manifest file?
  1876.                 $manifest $this->isManifest($file);
  1877.  
  1878.                 if (!is_null($manifest))
  1879.                 {
  1880.                     // If the root method attribute is set to upgrade, allow file overwrite
  1881.                     if ((string) $manifest->attributes()->method == 'upgrade')
  1882.                     {
  1883.                         $this->upgrade = true;
  1884.                         $this->overwrite = true;
  1885.                     }
  1886.  
  1887.                     // If the overwrite option is set, allow file overwriting
  1888.                     if ((string) $manifest->attributes()->overwrite == 'true')
  1889.                     {
  1890.                         $this->overwrite = true;
  1891.                     }
  1892.  
  1893.                     // Set the manifest object and path
  1894.                     $this->manifest = $manifest;
  1895.                     $this->setPath('manifest'$file);
  1896.  
  1897.                     // Set the installation source path to that of the manifest file
  1898.                     $this->setPath('source'dirname($file));
  1899.  
  1900.                     return true;
  1901.                 }
  1902.             }
  1903.  
  1904.             // None of the XML files found were valid install files
  1905.             JLog::add(JText::_('JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE')JLog::WARNING'jerror');
  1906.  
  1907.             return false;
  1908.         }
  1909.         else
  1910.         {
  1911.             // No XML files were found in the install folder
  1912.             JLog::add(JText::_('JLIB_INSTALLER_ERROR_NOTFINDXMLSETUPFILE')JLog::WARNING'jerror');
  1913.  
  1914.             return false;
  1915.         }
  1916.     }
  1917.  
  1918.     /**
  1919.      * Is the XML file a valid Joomla installation manifest file.
  1920.      *
  1921.      * @param   string  $file  An xmlfile path to check
  1922.      *
  1923.      * @return  mixed  A SimpleXMLElement, or null if the file failed to parse
  1924.      *
  1925.      * @since   3.1
  1926.      */
  1927.     public function isManifest($file)
  1928.     {
  1929.         $xml simplexml_load_file($file);
  1930.  
  1931.         // If we cannot load the XML file return null
  1932.         if (!$xml)
  1933.         {
  1934.             return null;
  1935.         }
  1936.  
  1937.         // Check for a valid XML root tag.
  1938.         if ($xml->getName(!= 'extension')
  1939.         {
  1940.             return null;
  1941.         }
  1942.  
  1943.         // Valid manifest file return the object
  1944.         return $xml;
  1945.     }
  1946.  
  1947.     /**
  1948.      * Generates a manifest cache
  1949.      *
  1950.      * @return string serialised manifest data
  1951.      *
  1952.      * @since   3.1
  1953.      */
  1954.     public function generateManifestCache()
  1955.     {
  1956.         return json_encode(self::parseXMLInstallFile($this->getPath('manifest')));
  1957.     }
  1958.  
  1959.     /**
  1960.      * Cleans up discovered extensions if they're being installed some other way
  1961.      *
  1962.      * @param   string   $type     The type of extension (component, etc)
  1963.      * @param   string   $element  Unique element identifier (e.g. com_content)
  1964.      * @param   string   $folder   The folder of the extension (plugins; e.g. system)
  1965.      * @param   integer  $client   The client application (administrator or site)
  1966.      *
  1967.      * @return  object    Result of query
  1968.      *
  1969.      * @since   3.1
  1970.      */
  1971.     public function cleanDiscoveredExtension($type$element$folder ''$client 0)
  1972.     {
  1973.         $db JFactory::getDbo();
  1974.         $query $db->getQuery(true)
  1975.             ->delete($db->quoteName('#__extensions'))
  1976.             ->where('type = ' $db->quote($type))
  1977.             ->where('element = ' $db->quote($element))
  1978.             ->where('folder = ' $db->quote($folder))
  1979.             ->where('client_id = ' . (int) $client)
  1980.             ->where('state = -1');
  1981.         $db->setQuery($query);
  1982.  
  1983.         return $db->execute();
  1984.     }
  1985.  
  1986.     /**
  1987.      * Compares two "files" entries to find deleted files/folders
  1988.      *
  1989.      * @param   array  $old_files  An array of SimpleXMLElement objects that are the old files
  1990.      * @param   array  $new_files  An array of SimpleXMLElement objects that are the new files
  1991.      *
  1992.      * @return  array  An array with the delete files and folders in findDeletedFiles[files] and findDeletedFiles[folders] respectively
  1993.      *
  1994.      * @since   3.1
  1995.      */
  1996.     public function findDeletedFiles($old_files$new_files)
  1997.     {
  1998.         // The magic find deleted files function!
  1999.         // The files that are new
  2000.         $files array();
  2001.  
  2002.         // The folders that are new
  2003.         $folders array();
  2004.  
  2005.         // The folders of the files that are new
  2006.         $containers array();
  2007.  
  2008.         // A list of files to delete
  2009.         $files_deleted array();
  2010.  
  2011.         // A list of folders to delete
  2012.         $folders_deleted array();
  2013.  
  2014.         foreach ($new_files as $file)
  2015.         {
  2016.             switch ($file->getName())
  2017.             {
  2018.                 case 'folder':
  2019.                     // Add any folders to the list
  2020.                     $folders[= (string) $file// add any folders to the list
  2021.                     break;
  2022.  
  2023.                 case 'file':
  2024.                 default:
  2025.                     // Add any files to the list
  2026.                     $files[= (string) $file;
  2027.  
  2028.                     // Now handle the folder part of the file to ensure we get any containers
  2029.                     // Break up the parts of the directory
  2030.                     $container_parts explode('/'dirname((string) $file));
  2031.  
  2032.                     // Make sure this is clean and empty
  2033.                     $container '';
  2034.  
  2035.                     foreach ($container_parts as $part)
  2036.                     {
  2037.                         // Iterate through each part
  2038.                         // Add a slash if its not empty
  2039.                         if (!empty($container))
  2040.                         {
  2041.                             $container .= '/';
  2042.                         }
  2043.  
  2044.                         // Aappend the folder part
  2045.                         $container .= $part;
  2046.  
  2047.                         if (!in_array($container$containers))
  2048.                         {
  2049.                             // Add the container if it doesn't already exist
  2050.                             $containers[$container;
  2051.                         }
  2052.                     }
  2053.                     break;
  2054.             }
  2055.         }
  2056.  
  2057.         foreach ($old_files as $file)
  2058.         {
  2059.             switch ($file->getName())
  2060.             {
  2061.                 case 'folder':
  2062.                     if (!in_array((string) $file$folders))
  2063.                     {
  2064.                         // See whether the folder exists in the new list
  2065.                         if (!in_array((string) $file$containers))
  2066.                         {
  2067.                             // Check if the folder exists as a container in the new list
  2068.                             // If it's not in the new list or a container then delete it
  2069.                             $folders_deleted[= (string) $file;
  2070.                         }
  2071.                     }
  2072.                     break;
  2073.  
  2074.                 case 'file':
  2075.                 default:
  2076.                     if (!in_array((string) $file$files))
  2077.                     {
  2078.                         // Look if the file exists in the new list
  2079.                         if (!in_array(dirname((string) $file)$folders))
  2080.                         {
  2081.                             // Look if the file is now potentially in a folder
  2082.                             $files_deleted[= (string) $file// not in a folder, doesn't exist, wipe it out!
  2083.                         }
  2084.                     }
  2085.                     break;
  2086.             }
  2087.         }
  2088.  
  2089.         return array('files' => $files_deleted'folders' => $folders_deleted);
  2090.     }
  2091.  
  2092.     /**
  2093.      * Loads an MD5SUMS file into an associative array
  2094.      *
  2095.      * @param   string  $filename  Filename to load
  2096.      *
  2097.      * @return  array  Associative array with filenames as the index and the MD5 as the value
  2098.      *
  2099.      * @since   3.1
  2100.      */
  2101.     public function loadMD5Sum($filename)
  2102.     {
  2103.         if (!file_exists($filename))
  2104.         {
  2105.             // Bail if the file doesn't exist
  2106.             return false;
  2107.         }
  2108.  
  2109.         $data file($filenameFILE_IGNORE_NEW_LINES FILE_SKIP_EMPTY_LINES);
  2110.         $retval array();
  2111.  
  2112.         foreach ($data as $row)
  2113.         {
  2114.             // Split up the data
  2115.             $results explode('  '$row);
  2116.  
  2117.             // Cull any potential prefix
  2118.             $results[1str_replace('./'''$results[1]);
  2119.  
  2120.             // Throw into the array
  2121.             $retval[$results[1]] $results[0];
  2122.         }
  2123.  
  2124.         return $retval;
  2125.     }
  2126.  
  2127.     /**
  2128.      * Parse a XML install manifest file.
  2129.      *
  2130.      * XML Root tag should be 'install' except for languages which use meta file.
  2131.      *
  2132.      * @param   string  $path  Full path to XML file.
  2133.      *
  2134.      * @return  array  XML metadata.
  2135.      *
  2136.      * @since   12.1
  2137.      */
  2138.     public static function parseXMLInstallFile($path)
  2139.     {
  2140.         // Read the file to see if it's a valid component XML file
  2141.         $xml simplexml_load_file($path);
  2142.  
  2143.         if (!$xml)
  2144.         {
  2145.             return false;
  2146.         }
  2147.  
  2148.         // Check for a valid XML root tag.
  2149.  
  2150.         // Extensions use 'extension' as the root tag.  Languages use 'metafile' instead
  2151.  
  2152.         if ($xml->getName(!= 'extension' && $xml->getName(!= 'metafile')
  2153.         {
  2154.             unset($xml);
  2155.  
  2156.             return false;
  2157.         }
  2158.  
  2159.         $data array();
  2160.  
  2161.         $data['name'= (string) $xml->name;
  2162.  
  2163.         // Check if we're a language. If so use metafile.
  2164.         $data['type'$xml->getName(== 'metafile' 'language' : (string) $xml->attributes()->type;
  2165.  
  2166.         $data['creationDate'((string) $xml->creationDate? (string) $xml->creationDate JText::_('Unknown');
  2167.         $data['author'((string) $xml->author? (string) $xml->author JText::_('Unknown');
  2168.  
  2169.         $data['copyright'= (string) $xml->copyright;
  2170.         $data['authorEmail'= (string) $xml->authorEmail;
  2171.         $data['authorUrl'= (string) $xml->authorUrl;
  2172.         $data['version'= (string) $xml->version;
  2173.         $data['description'= (string) $xml->description;
  2174.         $data['group'= (string) $xml->group;
  2175.  
  2176.         return $data;
  2177.     }
  2178. }

Documentation generated on Tue, 19 Nov 2013 15:05:47 +0100 by phpDocumentor 1.4.3