PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/joomla/installer/installer.php

https://bitbucket.org/elin/joomla-platform-ll
PHP | 1788 lines | 1030 code | 216 blank | 542 comment | 218 complexity | d64a80050990e0ff134b6edc0e3bfb8f MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Installer
  5. *
  6. * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. jimport('joomla.filesystem.file');
  11. jimport('joomla.filesystem.folder');
  12. jimport('joomla.filesystem.archive');
  13. jimport('joomla.filesystem.path');
  14. jimport('joomla.base.adapter');
  15. /**
  16. * Joomla base installer class
  17. *
  18. * @package Joomla.Platform
  19. * @subpackage Installer
  20. * @since 11.1
  21. */
  22. class JInstaller extends JAdapter
  23. {
  24. /**
  25. * Array of paths needed by the installer
  26. * @var array
  27. */
  28. protected $_paths = array();
  29. /**
  30. * True if packakge is an upgrade
  31. * @var boolean
  32. */
  33. protected $_upgrade = null;
  34. /**
  35. * The manifest trigger class
  36. * @var object
  37. */
  38. public $manifestClass = null;
  39. /**
  40. * True if existing files can be overwritten
  41. * @var boolean
  42. */
  43. protected $_overwrite = false;
  44. /**
  45. * Stack of installation steps
  46. * - Used for installation rollback
  47. * @var array
  48. */
  49. protected $_stepStack = array();
  50. /**
  51. * Extension Table Entry
  52. * @var JTableExtension
  53. */
  54. public $extension = null;
  55. /**
  56. * The output from the install/uninstall scripts
  57. * @var string
  58. */
  59. public $message = null;
  60. /**
  61. * The installation manifest XML object
  62. * @var object
  63. */
  64. public $manifest = null;
  65. /**
  66. * The extension message that appears
  67. * @var string
  68. */
  69. protected $extension_message = null;
  70. /**
  71. * The redirect URL if this extension (can be null if no redirect)
  72. * @var string
  73. */
  74. protected $redirect_url = null;
  75. /**
  76. * Constructor
  77. *
  78. */
  79. public function __construct()
  80. {
  81. parent::__construct(dirname(__FILE__),'JInstaller');
  82. }
  83. /**
  84. * Returns the global Installer object, only creating it
  85. * if it doesn't already exist.
  86. *
  87. * @return object An installer object
  88. * @since 11.1
  89. */
  90. public static function getInstance()
  91. {
  92. static $instance;
  93. if (!isset ($instance)) {
  94. $instance = new JInstaller();
  95. }
  96. return $instance;
  97. }
  98. /**
  99. * Get the allow overwrite switch
  100. *
  101. * @return boolean Allow overwrite switch
  102. * @since 11.1
  103. */
  104. public function getOverwrite()
  105. {
  106. return $this->_overwrite;
  107. }
  108. /**
  109. * Set the allow overwrite switch
  110. *
  111. * @param boolean $state Overwrite switch state
  112. *
  113. * @return boolean Previous value
  114. * @since 11.1
  115. */
  116. public function setOverwrite($state=false)
  117. {
  118. $tmp = $this->_overwrite;
  119. if ($state) {
  120. $this->_overwrite = true;
  121. }
  122. else {
  123. $this->_overwrite = false;
  124. }
  125. return $tmp;
  126. }
  127. /**
  128. * Get the redirect location
  129. *
  130. * @return string Redirect location (or null)
  131. * @since 11.1
  132. */
  133. public function getRedirectURL()
  134. {
  135. return $this->redirect_url;
  136. }
  137. /**
  138. * Set the redirct location
  139. *
  140. * @param string New redirect location
  141. * @since 11.1
  142. */
  143. public function setRedirectURL($newurl) {
  144. $this->redirect_url = $newurl;
  145. }
  146. /**
  147. * Get the allow overwrite switch
  148. *
  149. * @return boolean Allow overwrite switch
  150. * @since 11.1
  151. */
  152. public function getUpgrade()
  153. {
  154. return $this->_upgrade;
  155. }
  156. /**
  157. * Set the allow overwrite switch
  158. *
  159. * @param boolean $state Overwrite switch state
  160. * @return boolean Previous value
  161. * @since 11.1
  162. */
  163. public function setUpgrade($state=false)
  164. {
  165. $tmp = $this->_upgrade;
  166. if ($state) {
  167. $this->_upgrade = true;
  168. }
  169. else {
  170. $this->_upgrade = false;
  171. }
  172. return $tmp;
  173. }
  174. /**
  175. * Get the installation manifest object
  176. *
  177. * @return object Manifest object
  178. * @since 11.1
  179. */
  180. public function getManifest()
  181. {
  182. if (!is_object($this->manifest)) {
  183. $this->findManifest();
  184. }
  185. return $this->manifest;
  186. }
  187. /**
  188. * Get an installer path by name
  189. *
  190. * @param string $name Path name
  191. * @param string $default Default value
  192. *
  193. * @return string Path
  194. * @since 11.1
  195. */
  196. public function getPath($name, $default=null)
  197. {
  198. return (!empty($this->_paths[$name])) ? $this->_paths[$name] : $default;
  199. }
  200. /**
  201. * Sets an installer path by name
  202. *
  203. * @param string $name Path name
  204. * @param string $value Path
  205. *
  206. * @return void
  207. * @since 11.1
  208. */
  209. public function setPath($name, $value)
  210. {
  211. $this->_paths[$name] = $value;
  212. }
  213. /**
  214. * Pushes a step onto the installer stack for rolling back steps
  215. *
  216. * @param array $step Installer step
  217. * @return void
  218. *
  219. * @since 11.1
  220. */
  221. public function pushStep($step)
  222. {
  223. $this->_stepStack[] = $step;
  224. }
  225. /**
  226. * Installation abort method
  227. *
  228. * @param string $msg Abort message from the installer
  229. * @param string $type Package type if defined
  230. *
  231. * @return boolean True if successful
  232. * @since 11.1
  233. */
  234. public function abort($msg=null, $type=null)
  235. {
  236. // Initialise variables.
  237. $retval = true;
  238. $step = array_pop($this->_stepStack);
  239. // Raise abort warning
  240. if ($msg) {
  241. JError::raiseWarning(100, $msg);
  242. }
  243. while ($step != null)
  244. {
  245. switch ($step['type'])
  246. {
  247. case 'file' :
  248. // Remove the file
  249. $stepval = JFile::delete($step['path']);
  250. break;
  251. case 'folder' :
  252. // Remove the folder
  253. $stepval = JFolder::delete($step['path']);
  254. break;
  255. case 'query' :
  256. // Placeholder in case this is necessary in the future
  257. // $stepval is always false because if this step was called it invariably failed
  258. $stepval = false;
  259. break;
  260. case 'extension' :
  261. // Get database connector object
  262. $db = $this->getDBO();
  263. // Remove the entry from the #__extensions table
  264. $query = 'DELETE' .
  265. ' FROM `#__extensions`' .
  266. ' WHERE extension_id = '.(int)$step['id'];
  267. $db->setQuery($query);
  268. $stepval = $db->Query();
  269. break;
  270. default :
  271. if ($type && is_object($this->_adapters[$type])) {
  272. // Build the name of the custom rollback method for the type
  273. $method = '_rollback_'.$step['type'];
  274. // Custom rollback method handler
  275. if (method_exists($this->_adapters[$type], $method)) {
  276. $stepval = $this->_adapters[$type]->$method($step);
  277. }
  278. } else {
  279. $stepval = false; // set it to false
  280. }
  281. break;
  282. }
  283. // Only set the return value if it is false
  284. if ($stepval === false) {
  285. $retval = false;
  286. }
  287. // Get the next step and continue
  288. $step = array_pop($this->_stepStack);
  289. }
  290. $conf = JFactory::getConfig();
  291. $debug = $conf->get('debug');
  292. if($debug) {
  293. JError::raiseError(500, JText::_('JLIB_INSTALLER_ABORT_DEBUG').$msg);
  294. }
  295. return $retval;
  296. }
  297. // Adapter functions
  298. /**
  299. * Package installation method
  300. *
  301. * @param string $path Path to package source folder
  302. *
  303. * @return boolean True if successful
  304. * @since 11.1
  305. */
  306. public function install($path=null)
  307. {
  308. if ($path && JFolder::exists($path)) {
  309. $this->setPath('source', $path);
  310. }
  311. else
  312. {
  313. $this->abort(JText::_('JLIB_INSTALLER_ABORT_NOINSTALLPATH'));
  314. return false;
  315. }
  316. if (!$this->setupInstall())
  317. {
  318. $this->abort(JText::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST'));
  319. return false;
  320. }
  321. $type = (string)$this->manifest->attributes()->type;
  322. if (is_object($this->_adapters[$type]))
  323. {
  324. // Add the languages from the package itself
  325. if (method_exists($this->_adapters[$type], 'loadLanguage'))
  326. {
  327. $this->_adapters[$type]->loadLanguage($path);
  328. }
  329. // Fire the onExtensionBeforeInstall event.
  330. JPluginHelper::importPlugin('extension');
  331. $dispatcher = JDispatcher::getInstance();
  332. $dispatcher->trigger('onExtensionBeforeInstall', array('method'=>'install', 'type'=>$type, 'manifest'=>$this->manifest, 'extension'=>0));
  333. // Run the install
  334. $result = $this->_adapters[$type]->install();
  335. // Fire the onExtensionAfterInstall
  336. $dispatcher->trigger('onExtensionAfterInstall', array('installer'=>clone $this, 'eid'=> $result));
  337. if ($result !== false) {
  338. return true;
  339. }
  340. else {
  341. return false;
  342. }
  343. }
  344. return false;
  345. }
  346. /*
  347. * Discovered package installation method
  348. *
  349. * @param integer $eid Extension ID
  350. * @return boolean True if successful
  351. * @since 11.1
  352. */
  353. public function discover_install($eid=null)
  354. {
  355. if ($eid)
  356. {
  357. $this->extension = JTable::getInstance('extension');
  358. if (!$this->extension->load($eid))
  359. {
  360. $this->abort(JText::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS'));
  361. return false;
  362. }
  363. if ($this->extension->state != -1)
  364. {
  365. $this->abort(JText::_('JLIB_INSTALLER_ABORT_ALREADYINSTALLED'));
  366. return false;
  367. }
  368. // Lazy load the adapter
  369. if (!isset($this->_adapters[$this->extension->type]) || !is_object($this->_adapters[$this->extension->type]))
  370. {
  371. if (!$this->setAdapter($this->extension->type)) {
  372. return false;
  373. }
  374. }
  375. if (is_object($this->_adapters[$this->extension->type]))
  376. {
  377. if (method_exists($this->_adapters[$this->extension->type], 'discover_install'))
  378. {
  379. // Add the languages from the package itself
  380. if (method_exists($this->_adapters[$this->extension->type], 'loadLanguage'))
  381. {
  382. $this->_adapters[$this->extension->type]->loadLanguage();
  383. }
  384. // Fire the onExtensionBeforeInstall event.
  385. JPluginHelper::importPlugin('extension');
  386. $dispatcher = JDispatcher::getInstance();
  387. $dispatcher->trigger('onExtensionBeforeInstall', array('method'=>'discover_install', 'type'=>$this->extension->get('type'), 'manifest'=>null, 'extension'=>$this->extension->get('extension_id')));
  388. // Run the install
  389. $result = $this->_adapters[$this->extension->type]->discover_install();
  390. // Fire the onExtensionAfterInstall
  391. $dispatcher->trigger('onExtensionAfterInstall', array('installer'=>clone $this, 'eid'=> $result));
  392. if ($result !== false) return true; else return false;
  393. }
  394. else
  395. {
  396. $this->abort(JText::_('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED'));
  397. return false;
  398. }
  399. }
  400. return false;
  401. }
  402. else
  403. {
  404. $this->abort(JText::_('JLIB_INSTALLER_ABORT_EXTENSIONNOTVALID'));
  405. return false;
  406. }
  407. }
  408. /**
  409. * Extension discover method
  410. * Asks each adapter to find extensions
  411. *
  412. * @return array JExtension
  413. */
  414. public function discover()
  415. {
  416. $this->loadAllAdapters();
  417. $results = Array();
  418. foreach ($this->_adapters as $adapter)
  419. {
  420. // Joomla! 1.5 installation adapter legacy support
  421. if (method_exists($adapter,'discover'))
  422. {
  423. $tmp = $adapter->discover();
  424. // if its an array and has entries
  425. if (is_array($tmp) && count($tmp))
  426. {
  427. // merge it into the system
  428. $results = array_merge($results, $tmp);
  429. }
  430. }
  431. }
  432. return $results;
  433. }
  434. /**
  435. * Package update method
  436. *
  437. * @param string $path Path to package source folder
  438. *
  439. * @return boolean True if successful
  440. * @since 11.1
  441. */
  442. public function update($path=null)
  443. {
  444. if ($path && JFolder::exists($path)) {
  445. $this->setPath('source', $path);
  446. }
  447. else {
  448. $this->abort(JText::_('JLIB_INSTALLER_ABORT_NOUPDATEPATH'));
  449. }
  450. if (!$this->setupInstall()) {
  451. return $this->abort(JText::_('JLIB_INSTALLER_ABORT_DETECTMANIFEST'));
  452. }
  453. $type = (string)$this->manifest->attributes()->type;
  454. if (is_object($this->_adapters[$type]))
  455. {
  456. // Add the languages from the package itself
  457. if (method_exists($this->_adapters[$type], 'loadLanguage'))
  458. {
  459. $this->_adapters[$type]->loadLanguage($path);
  460. }
  461. // Fire the onExtensionBeforeUpdate event.
  462. JPluginHelper::importPlugin('extension');
  463. $dispatcher = JDispatcher::getInstance();
  464. $dispatcher->trigger('onExtensionBeforeUpdate', array('type'=>$type, 'manifest'=>$this->manifest));
  465. // Run the update
  466. $result = $this->_adapters[$type]->update();
  467. // Fire the onExtensionAfterUpdate
  468. $dispatcher->trigger('onExtensionAfterUpdate', array('installer'=>clone $this, 'eid'=> $result));
  469. if ($result !== false) {
  470. return true;
  471. }
  472. else {
  473. return false;
  474. }
  475. }
  476. return false;
  477. }
  478. /**
  479. * Package uninstallation method
  480. *
  481. * @param string $type Package type
  482. * @param mixed $identifier Package identifier for adapter
  483. * @param integer $cid Application ID; deprecated in 1.6
  484. *
  485. * @return boolean True if successful
  486. * @since 11.1
  487. */
  488. public function uninstall($type, $identifier, $cid=0)
  489. {
  490. if (!isset($this->_adapters[$type]) || !is_object($this->_adapters[$type]))
  491. {
  492. if (!$this->setAdapter($type)) {
  493. return false; // We failed to get the right adapter
  494. }
  495. }
  496. if (is_object($this->_adapters[$type]))
  497. {
  498. // We don't load languages here, we get the extension adapter to work it out
  499. // Fire the onExtensionBeforeUninstall event.
  500. JPluginHelper::importPlugin('extension');
  501. $dispatcher = JDispatcher::getInstance();
  502. $dispatcher->trigger('onExtensionBeforeUninstall', array('eid' => $identifier));
  503. // Run the uninstall
  504. $result = $this->_adapters[$type]->uninstall($identifier);
  505. // Fire the onExtensionAfterInstall
  506. $dispatcher->trigger('onExtensionAfterUninstall', array('installer'=>clone $this, 'eid'=> $identifier, 'result' => $result));
  507. return $result;
  508. }
  509. return false;
  510. }
  511. /**
  512. * Refreshes the manifest cache stored in #__extensions
  513. *
  514. * @param integer $eid Extension ID
  515. *
  516. * @return mixed void on success | false on error @todo missing return value ?
  517. */
  518. function refreshManifestCache($eid)
  519. {
  520. if ($eid)
  521. {
  522. $this->extension = JTable::getInstance('extension');
  523. if (!$this->extension->load($eid))
  524. {
  525. $this->abort(JText::_('JLIB_INSTALLER_ABORT_LOAD_DETAILS'));
  526. return false;
  527. }
  528. if ($this->extension->state == -1)
  529. {
  530. $this->abort(JText::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE'));
  531. return false;
  532. }
  533. // Lazy load the adapter
  534. if (!isset($this->_adapters[$this->extension->type]) || !is_object($this->_adapters[$this->extension->type]))
  535. {
  536. if (!$this->setAdapter($this->extension->type)) {
  537. return false;
  538. }
  539. }
  540. if (is_object($this->_adapters[$this->extension->type]))
  541. {
  542. if (method_exists($this->_adapters[$this->extension->type], 'refreshManifestCache'))
  543. {
  544. $result = $this->_adapters[$this->extension->type]->refreshManifestCache();
  545. if ($result !== false) {
  546. return true;
  547. } else {
  548. return false;
  549. }
  550. }
  551. else
  552. {
  553. $this->abort(JText::sprintf('JLIB_INSTALLER_ABORT_METHODNOTSUPPORTED_TYPE', $this->extension->type));
  554. return false;
  555. }
  556. }
  557. return false;
  558. }
  559. else
  560. {
  561. $this->abort(JText::_('JLIB_INSTALLER_ABORT_REFRESH_MANIFEST_CACHE_VALID'));
  562. return false;
  563. }
  564. }
  565. // Utility functions
  566. /**
  567. * Prepare for installation: this method sets the installation directory, finds
  568. * and checks the installation file and verifies the installation type.
  569. *
  570. * @return boolean True on success
  571. * @since 11.1
  572. */
  573. public function setupInstall()
  574. {
  575. // We need to find the installation manifest file
  576. if (!$this->findManifest()) {
  577. return false;
  578. }
  579. // Load the adapter(s) for the install manifest
  580. $type = (string)$this->manifest->attributes()->type;
  581. // Lazy load the adapter
  582. if (!isset($this->_adapters[$type]) || !is_object($this->_adapters[$type]))
  583. {
  584. if (!$this->setAdapter($type)) {
  585. return false;
  586. }
  587. }
  588. return true;
  589. }
  590. /**
  591. * Backward compatible method to parse through a queries element of the
  592. * installation manifest file and take appropriate action.
  593. *
  594. * @param JXMLElement $element The XML node to process
  595. *
  596. * @return mixed Number of queries processed or False on error
  597. * @since 11.1
  598. */
  599. public function parseQueries($element)
  600. {
  601. // Get the database connector object
  602. $db = & $this->_db;
  603. if ( ! $element || ! count($element->children()))
  604. {
  605. // Either the tag does not exist or has no children therefore we return zero files processed.
  606. return 0;
  607. }
  608. // Get the array of query nodes to process
  609. $queries = $element->children();
  610. if (count($queries) == 0)
  611. {
  612. // No queries to process
  613. return 0;
  614. }
  615. // Process each query in the $queries array (children of $tagName).
  616. foreach ($queries as $query)
  617. {
  618. $db->setQuery($query->data());
  619. if (!$db->query())
  620. {
  621. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $db->stderr(true)));
  622. return false;
  623. }
  624. }
  625. return (int) count($queries);
  626. }
  627. /**
  628. * Method to extract the name of a discreet installation sql file from the installation manifest file.
  629. *
  630. * @param object $element The XML node to process
  631. *
  632. * @return mixed Number of queries processed or False on error
  633. * @since 11.1
  634. */
  635. public function parseSQLFiles($element)
  636. {
  637. if ( ! $element || ! count($element->children())) {
  638. // The tag does not exist.
  639. return 0;
  640. }
  641. // Initialise variables.
  642. $queries = array();
  643. $db = & $this->_db;
  644. $dbDriver = strtolower($db->get('name'));
  645. if ($dbDriver == 'mysqli') {
  646. $dbDriver = 'mysql';
  647. }
  648. $dbCharset = ($db->hasUTF()) ? 'utf8' : '';
  649. // Get the name of the sql file to process
  650. $sqlfile = '';
  651. foreach ($element->children() as $file)
  652. {
  653. $fCharset = (strtolower($file->attributes()->charset) == 'utf8') ? 'utf8' : '';
  654. $fDriver = strtolower($file->attributes()->driver);
  655. if ($fDriver == 'mysqli') {
  656. $fDriver = 'mysql';
  657. }
  658. if ($fCharset == $dbCharset && $fDriver == $dbDriver)
  659. {
  660. $sqlfile = $this->getPath('extension_root').DS.$file;
  661. // Check that sql files exists before reading. Otherwise raise error for rollback
  662. if (!file_exists($sqlfile))
  663. {
  664. JError::raiseWarning(1,JText::sprintf('JLIB_INSTALLER_ERROR_SQL_FILENOTFOUND', $sqlfile));
  665. return false;
  666. }
  667. $buffer = file_get_contents($sqlfile);
  668. // Graceful exit and rollback if read not successful
  669. if ($buffer === false)
  670. {
  671. JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_SQL_READBUFFER'));
  672. return false;
  673. }
  674. // Create an array of queries from the sql file
  675. jimport('joomla.installer.helper');
  676. $queries = JInstallerHelper::splitSql($buffer);
  677. if (count($queries) == 0) {
  678. // No queries to process
  679. return 0;
  680. }
  681. // Process each query in the $queries array (split out of sql file).
  682. foreach ($queries as $query)
  683. {
  684. $query = trim($query);
  685. if ($query != '' && $query{0} != '#')
  686. {
  687. $db->setQuery($query);
  688. if (!$db->query())
  689. {
  690. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $db->stderr(true)));
  691. return false;
  692. }
  693. }
  694. }
  695. }
  696. }
  697. return (int) count($queries);
  698. }
  699. /**
  700. * Set the schema version for an extension by looking at its latest update
  701. * @param JXMLElement $schema Schema Tag
  702. * @param integer $eid Extension ID
  703. *
  704. * @return void
  705. */
  706. public function setSchemaVersion($schema, $eid)
  707. {
  708. if($eid && $schema)
  709. {
  710. $db = JFactory::getDBO();
  711. $schemapaths = $schema->children();
  712. if(!$schemapaths) {
  713. return;
  714. }
  715. if(count($schemapaths))
  716. {
  717. $dbDriver = strtolower($db->get('name'));
  718. if ($dbDriver == 'mysqli') {
  719. $dbDriver = 'mysql';
  720. }
  721. $schemapath = '';
  722. foreach($schemapaths as $entry)
  723. {
  724. $attrs = $entry->attributes();
  725. if($attrs['type'] == $dbDriver)
  726. {
  727. $schemapath = $entry;
  728. break;
  729. }
  730. }
  731. if(strlen($schemapath))
  732. {
  733. $files = str_replace('.sql','', JFolder::files($this->getPath('extension_root').DS.$schemapath,'\.sql$'));
  734. usort($files,'version_compare');
  735. // Update the database
  736. $query = $db->getQuery(true);
  737. $query->delete()->from('#__schemas')->where('extension_id = ' . $eid);
  738. $db->setQuery($query);
  739. if($db->Query()) {
  740. $query->clear();
  741. $query->insert('#__schemas')->set('extension_id = '. $eid)->set('version_id = '. $db->quote(end($files)));
  742. $db->setQuery($query);
  743. $db->Query();
  744. }
  745. }
  746. }
  747. }
  748. }
  749. /**
  750. * Method to process the updates for an item
  751. *
  752. * @param JXMLElement $schema The XML node to process
  753. * @param integer $eid Extension Identifier
  754. *
  755. * @return boolean Result of the operations
  756. * @since 11.1
  757. */
  758. public function parseSchemaUpdates($schema, $eid)
  759. {
  760. $files = Array();
  761. $update_count = 0;
  762. // Ensure we have an XML element and a valid extension id
  763. if($eid && $schema)
  764. {
  765. $db = JFactory::getDBO();
  766. $schemapaths = $schema->children();
  767. if(count($schemapaths)) {
  768. $dbDriver = strtolower($db->get('name'));
  769. if ($dbDriver == 'mysqli') {
  770. $dbDriver = 'mysql';
  771. }
  772. $schemapath = '';
  773. foreach($schemapaths as $entry)
  774. {
  775. $attrs = $entry->attributes();
  776. if($attrs['type'] == $dbDriver)
  777. {
  778. $schemapath = $entry;
  779. break;
  780. }
  781. }
  782. if(strlen($schemapath))
  783. {
  784. $files = str_replace('.sql','', JFolder::files($this->getPath('extension_root').DS.$schemapath,'\.sql$'));
  785. usort($files,'version_compare');
  786. if(!count($files))
  787. {
  788. return false;
  789. }
  790. $query = $db->getQuery(true);
  791. $query->select('version_id')->from('#__schemas')->where('extension_id = ' . $eid);
  792. $db->setQuery($query);
  793. $version = $db->loadResult();
  794. if($version)
  795. {
  796. // we have a version!
  797. foreach($files as $file)
  798. {
  799. if(version_compare($file,$version)>0)
  800. {
  801. $buffer = file_get_contents($this->getPath('extension_root').DS.$schemapath.DS.$file.'.sql');
  802. // Graceful exit and rollback if read not successful
  803. if ($buffer === false)
  804. {
  805. JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_SQL_READBUFFER'));
  806. return false;
  807. }
  808. // Create an array of queries from the sql file
  809. jimport('joomla.installer.helper');
  810. $queries = JInstallerHelper::splitSql($buffer);
  811. if (count($queries) == 0)
  812. {
  813. // No queries to process
  814. continue;
  815. }
  816. // Process each query in the $queries array (split out of sql file).
  817. foreach ($queries as $query)
  818. {
  819. $query = trim($query);
  820. if ($query != '' && $query{0} != '#')
  821. {
  822. $db->setQuery($query);
  823. if (!$db->query())
  824. {
  825. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $db->stderr(true)));
  826. return false;
  827. }
  828. $update_count++;
  829. }
  830. }
  831. }
  832. }
  833. }
  834. // Update the database
  835. $query = $db->getQuery(true);
  836. $query->delete()->from('#__schemas')->where('extension_id = ' . $eid);
  837. $db->setQuery($query);
  838. if($db->Query()) {
  839. $query->clear();
  840. $query->insert('#__schemas')->set('extension_id = '. $eid)->set('version_id = '. $db->quote(end($files)));
  841. $db->setQuery($query);
  842. $db->Query();
  843. }
  844. }
  845. }
  846. }
  847. return $update_count;
  848. }
  849. /**
  850. * Method to parse through a files element of the installation manifest and take appropriate
  851. * action.
  852. *
  853. * @param JXMLElement $element The XML node to process
  854. * @param integer $cid Application ID of application to install to
  855. * @param array $oldFiles List of old files (JXMLElement's)
  856. * @param array $oldMD5 List of old MD5 sums (indexed by filename with value as MD5)
  857. *
  858. * @return boolean True on success
  859. * @since 11.1
  860. */
  861. public function parseFiles($element, $cid=0, $oldFiles=null, $oldMD5=null)
  862. {
  863. // Get the array of file nodes to process; we checked whether this had children above.
  864. if ( ! $element || ! count($element->children()))
  865. {
  866. // Either the tag does not exist or has no children (hence no files to process) therefore we return zero files processed.
  867. return 0;
  868. }
  869. // Initialise variables.
  870. $copyfiles = array ();
  871. // Get the client info
  872. jimport('joomla.application.helper');
  873. $client = JApplicationHelper::getClientInfo($cid);
  874. /*
  875. * Here we set the folder we are going to remove the files from.
  876. */
  877. if ($client)
  878. {
  879. $pathname = 'extension_'.$client->name;
  880. $destination = $this->getPath($pathname);
  881. }
  882. else
  883. {
  884. $pathname = 'extension_root';
  885. $destination = $this->getPath($pathname);
  886. }
  887. // Here we set the folder we are going to copy the files from.
  888. // Does the element have a folder attribute?
  889. //
  890. // If so this indicates that the files are in a subdirectory of the source
  891. // folder and we should append the folder attribute to the source path when
  892. // copying files.
  893. $folder = (string)$element->attributes()->folder;
  894. if ($folder && file_exists($this->getPath('source').DS.$folder))
  895. {
  896. $source = $this->getPath('source').DS.$folder;
  897. }
  898. else {
  899. $source = $this->getPath('source');
  900. }
  901. // Work out what files have been deleted
  902. if ($oldFiles && is_a($oldFiles, 'JXMLElement'))
  903. {
  904. $oldEntries = $oldFiles->children();
  905. if (count($oldEntries))
  906. {
  907. $deletions = $this->findDeletedFiles($oldEntries, $element);
  908. foreach ($deletions['folders'] as $deleted_folder) {
  909. JFolder::delete($destination.DS.$deleted_folder);
  910. }
  911. foreach ($deletions['files'] as $deleted_file) {
  912. JFile::delete($destination.DS.$deleted_file);
  913. }
  914. }
  915. }
  916. // Copy the MD5SUMS file if it exists
  917. if (file_exists($source.DS.'MD5SUMS'))
  918. {
  919. $path['src'] = $source.DS.'MD5SUMS';
  920. $path['dest'] = $destination.DS.'MD5SUMS';
  921. $path['type'] = 'file';
  922. $copyfiles[] = $path;
  923. }
  924. // Process each file in the $files array (children of $tagName).
  925. foreach ($element->children() as $file)
  926. {
  927. $path['src'] = $source.DS.$file;
  928. $path['dest'] = $destination.DS.$file;
  929. // Is this path a file or folder?
  930. $path['type'] = ($file->getName() == 'folder') ? 'folder' : 'file';
  931. // Before we can add a file to the copyfiles array we need to ensure
  932. // that the folder we are copying our file to exits and if it doesn't,
  933. // we need to create it.
  934. if (basename($path['dest']) != $path['dest'])
  935. {
  936. $newdir = dirname($path['dest']);
  937. if (!JFolder::create($newdir))
  938. {
  939. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir));
  940. return false;
  941. }
  942. }
  943. // Add the file to the copyfiles array
  944. $copyfiles[] = $path;
  945. }
  946. return $this->copyFiles($copyfiles);
  947. }
  948. /**
  949. * Method to parse through a languages element of the installation manifest and take appropriate
  950. * action.
  951. *
  952. * @param JXMLElement $element The XML node to process
  953. * @param integer $cid Application ID of application to install to
  954. *
  955. * @return boolean True on success
  956. * @since 11.1
  957. */
  958. public function parseLanguages($element, $cid=0)
  959. {
  960. // TODO: work out why the below line triggers 'node no longer exists' errors with files
  961. if ( ! $element || ! count($element->children()))
  962. {
  963. // Either the tag does not exist or has no children therefore we return zero files processed.
  964. return 0;
  965. }
  966. // Initialise variables.
  967. $copyfiles = array ();
  968. // Get the client info
  969. jimport('joomla.application.helper');
  970. $client = JApplicationHelper::getClientInfo($cid);
  971. // Here we set the folder we are going to copy the files to.
  972. // 'languages' Files are copied to JPATH_BASE/language/ folder
  973. $destination = $client->path.DS.'language';
  974. // Here we set the folder we are going to copy the files from.
  975. // Does the element have a folder attribute?
  976. // If so this indicates that the files are in a subdirectory of the source
  977. // folder and we should append the folder attribute to the source path when
  978. // copying files.
  979. $folder = (string)$element->attributes()->folder;
  980. if ($folder && file_exists($this->getPath('source').DS.$folder)) {
  981. $source = $this->getPath('source').DS.$folder;
  982. }
  983. else {
  984. $source = $this->getPath('source');
  985. }
  986. // Process each file in the $files array (children of $tagName).
  987. foreach ($element->children() as $file)
  988. {
  989. // Language files go in a subfolder based on the language code, ie.
  990. // <language tag="en-US">en-US.mycomponent.ini</language>
  991. // would go in the en-US subdirectory of the language folder.
  992. // We will only install language files where a core language pack
  993. // already exists.
  994. if ((string)$file->attributes()->tag != '')
  995. {
  996. $path['src'] = $source.DS.$file;
  997. if ((string)$file->attributes()->client != '')
  998. {
  999. // Override the client
  1000. $langclient = JApplicationHelper::getClientInfo((string)$file->attributes()->client, true);
  1001. $path['dest'] = $langclient->path.DS.'language'.DS.$file->attributes()->tag.DS.basename((string)$file);
  1002. }
  1003. else
  1004. {
  1005. // Use the default client
  1006. $path['dest'] = $destination.DS.$file->attributes()->tag.DS.basename((string)$file);
  1007. }
  1008. // If the language folder is not present, then the core pack hasn't been installed... ignore
  1009. if (!JFolder::exists(dirname($path['dest']))) {
  1010. continue;
  1011. }
  1012. }
  1013. else
  1014. {
  1015. $path['src'] = $source.DS.$file;
  1016. $path['dest'] = $destination.DS.$file;
  1017. }
  1018. //
  1019. // Before we can add a file to the copyfiles array we need to ensure
  1020. // that the folder we are copying our file to exits and if it doesn't,
  1021. // we need to create it.
  1022. if (basename($path['dest']) != $path['dest'])
  1023. {
  1024. $newdir = dirname($path['dest']);
  1025. if (!JFolder::create($newdir))
  1026. {
  1027. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir));
  1028. return false;
  1029. }
  1030. }
  1031. // Add the file to the copyfiles array
  1032. $copyfiles[] = $path;
  1033. }
  1034. return $this->copyFiles($copyfiles);
  1035. }
  1036. /**
  1037. * Method to parse through a media element of the installation manifest and take appropriate
  1038. * action.
  1039. *
  1040. * @param JXMLElement $element The XML node to process
  1041. * @param integer $cid Application ID of application to install to
  1042. *
  1043. * @return boolean True on success
  1044. * @since 11.1
  1045. */
  1046. public function parseMedia($element, $cid=0)
  1047. {
  1048. if ( ! $element || ! count($element->children()))
  1049. {
  1050. // Either the tag does not exist or has no children therefore we return zero files processed.
  1051. return 0;
  1052. }
  1053. // Initialise variables.
  1054. $copyfiles = array ();
  1055. // Get the client info
  1056. jimport('joomla.application.helper');
  1057. $client = JApplicationHelper::getClientInfo($cid);
  1058. // Here we set the folder we are going to copy the files to.
  1059. // Default 'media' Files are copied to the JPATH_BASE/media folder
  1060. $folder = ((string)$element->attributes()->destination) ? DS.$element->attributes()->destination : null;
  1061. $destination = JPath::clean(JPATH_ROOT.DS.'media'.$folder);
  1062. // Here we set the folder we are going to copy the files from.
  1063. // Does the element have a folder attribute?
  1064. // If so this indicates that the files are in a subdirectory of the source
  1065. // folder and we should append the folder attribute to the source path when
  1066. // copying files.
  1067. $folder = (string)$element->attributes()->folder;
  1068. if ($folder && file_exists($this->getPath('source').DS.$folder)) {
  1069. $source = $this->getPath('source').DS.$folder;
  1070. }
  1071. else {
  1072. $source = $this->getPath('source');
  1073. }
  1074. // Process each file in the $files array (children of $tagName).
  1075. foreach ($element->children() as $file)
  1076. {
  1077. $path['src'] = $source.DS.$file;
  1078. $path['dest'] = $destination.DS.$file;
  1079. // Is this path a file or folder?
  1080. $path['type'] = ($file->getName() == 'folder') ? 'folder' : 'file';
  1081. // Before we can add a file to the copyfiles array we need to ensure
  1082. // that the folder we are copying our file to exits and if it doesn't,
  1083. // we need to create it.
  1084. if (basename($path['dest']) != $path['dest'])
  1085. {
  1086. $newdir = dirname($path['dest']);
  1087. if (!JFolder::create($newdir))
  1088. {
  1089. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir));
  1090. return false;
  1091. }
  1092. }
  1093. // Add the file to the copyfiles array
  1094. $copyfiles[] = $path;
  1095. }
  1096. return $this->copyFiles($copyfiles);
  1097. }
  1098. /**
  1099. * Method to parse the parameters of an extension, build the INI
  1100. * string for its default parameters, and return the INI string.
  1101. *
  1102. * @return string INI string of parameter values
  1103. *
  1104. * @since 11.1
  1105. */
  1106. public function getParams()
  1107. {
  1108. // Validate that we have a fieldset to use
  1109. if(!isset($this->manifest->config->fields->fieldset)) {
  1110. return '{}';
  1111. }
  1112. // Getting the fieldset tags
  1113. $fieldsets = $this->manifest->config->fields->fieldset;
  1114. // Creating the data collection variable:
  1115. $ini = array();
  1116. // Iterating through the fieldsets:
  1117. foreach($fieldsets as $fieldset) {
  1118. if( ! count($fieldset->children())) {
  1119. // Either the tag does not exist or has no children therefore we return zero files processed.
  1120. return null;
  1121. }
  1122. // Iterating through the fields and collecting the name/default values:
  1123. foreach ($fieldset as $field)
  1124. {
  1125. // Check against the null value since otherwise default values like "0"
  1126. // cause entire parameters to be skipped.
  1127. if (($name = $field->attributes()->name) === null) {
  1128. continue;
  1129. }
  1130. if (($value = $field->attributes()->default) === null) {
  1131. continue;
  1132. }
  1133. $ini[(string) $name] = (string) $value;
  1134. }
  1135. }
  1136. return json_encode($ini);
  1137. }
  1138. /**
  1139. * Copyfiles
  1140. *
  1141. * Copy files from source directory to the target directory
  1142. *
  1143. * @param array $files array with filenames
  1144. * @param boolean $overwrite True if existing files can be replaced
  1145. *
  1146. * @return boolean True on success
  1147. * @since 11.1
  1148. */
  1149. public function copyFiles($files, $overwrite=null)
  1150. {
  1151. // To allow for manual override on the overwriting flag, we check to see if
  1152. // the $overwrite flag was set and is a boolean value. If not, use the object
  1153. // allowOverwrite flag.
  1154. if (is_null($overwrite) || !is_bool($overwrite)) {
  1155. $overwrite = $this->_overwrite;
  1156. }
  1157. /*
  1158. * $files must be an array of filenames. Verify that it is an array with
  1159. * at least one file to copy.
  1160. */
  1161. if (is_array($files) && count($files) > 0)
  1162. {
  1163. foreach ($files as $file)
  1164. {
  1165. // Get the source and destination paths
  1166. $filesource = JPath::clean($file['src']);
  1167. $filedest = JPath::clean($file['dest']);
  1168. $filetype = array_key_exists('type', $file) ? $file['type'] : 'file';
  1169. if (!file_exists($filesource))
  1170. {
  1171. /*
  1172. * The source file does not exist. Nothing to copy so set an error
  1173. * and return false.
  1174. */
  1175. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_NO_FILE', $filesource));
  1176. return false;
  1177. }
  1178. elseif (file_exists($filedest) && !$overwrite)
  1179. {
  1180. // It's okay if the manifest already exists
  1181. if ($this->getPath('manifest') == $filesource) {
  1182. continue;
  1183. }
  1184. // The destination file already exists and the overwrite flag is false.
  1185. // Set an error and return false.
  1186. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_FILE_EXISTS', $filedest));
  1187. return false;
  1188. }
  1189. else
  1190. {
  1191. // Copy the folder or file to the new location.
  1192. if ($filetype == 'folder')
  1193. {
  1194. if (!(JFolder::copy($filesource, $filedest, null, $overwrite)))
  1195. {
  1196. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FOLDER', $filesource, $filedest));
  1197. return false;
  1198. }
  1199. $step = array ('type' => 'folder', 'path' => $filedest);
  1200. }
  1201. else
  1202. {
  1203. if (!(JFile::copy($filesource, $filedest,null)))
  1204. {
  1205. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ERROR_FAIL_COPY_FILE', $filesource, $filedest));
  1206. return false;
  1207. }
  1208. $step = array ('type' => 'file', 'path' => $filedest);
  1209. }
  1210. /*
  1211. * Since we copied a file/folder, we want to add it to the installation step stack so that
  1212. * in case we have to roll back the installation we can remove the files copied.
  1213. */
  1214. $this->_stepStack[] = $step;
  1215. }
  1216. }
  1217. }
  1218. else
  1219. {
  1220. // The $files variable was either not an array or an empty array
  1221. return false;
  1222. }
  1223. return count($files);
  1224. }
  1225. /**
  1226. * Method to parse through a files element of the installation manifest and remove
  1227. * the files that were installed
  1228. *
  1229. * @param object $element The XML node to process
  1230. * @param integer $cid Application ID of application to remove from
  1231. *
  1232. * @return boolean True on success
  1233. * @since 11.1
  1234. */
  1235. public function removeFiles($element, $cid=0)
  1236. {
  1237. if (!$element || !count($element->children()))
  1238. {
  1239. // Either the tag does not exist or has no children therefore we return zero files processed.
  1240. return true;
  1241. }
  1242. // Initialise variables.
  1243. $removefiles = array ();
  1244. $retval = true;
  1245. $debug = false;
  1246. if(isset($GLOBALS['installerdebug']) && $GLOBALS['installerdebug']) {
  1247. $debug = true;
  1248. }
  1249. // Get the client info if we're using a specific client
  1250. jimport('joomla.application.helper');
  1251. if ($cid > -1) {
  1252. $client = JApplicationHelper::getClientInfo($cid);
  1253. }
  1254. else {
  1255. $client = null;
  1256. }
  1257. // Get the array of file nodes to process
  1258. $files = $element->children();
  1259. if (count($files) == 0) {
  1260. // No files to process
  1261. return true;
  1262. }
  1263. $folder = '';
  1264. /*
  1265. * Here we set the folder we are going to remove the files from. There are a few
  1266. * special cases that need to be considered for certain reserved tags.
  1267. */
  1268. switch ($element->getName())
  1269. {
  1270. case 'media':
  1271. if ((string)$element->attributes()->destination) {
  1272. $folder = (string)$element->attributes()->destination;
  1273. }
  1274. else {
  1275. $folder = '';
  1276. }
  1277. $source = $client->path.DS.'media'.DS.$folder;
  1278. break;
  1279. case 'languages':
  1280. $lang_client = (string)$element->attributes()->client;
  1281. if($lang_client) {
  1282. $client = JApplicationHelper::getClientInfo($lang_client, true);
  1283. $source = $client->path.DS.'language';
  1284. } else {
  1285. if ($client) {
  1286. $source = $client->path.DS.'language';
  1287. }
  1288. else {
  1289. $source = '';
  1290. }
  1291. }
  1292. break;
  1293. default:
  1294. if ($client)
  1295. {
  1296. $pathname = 'extension_'.$client->name;
  1297. $source = $this->getPath($pathname);
  1298. }
  1299. else
  1300. {
  1301. $pathname = 'extension_root';
  1302. $source = $this->getPath($pathname);
  1303. }
  1304. break;
  1305. }
  1306. // Process each file in the $files array (children of $tagName).
  1307. foreach ($files as $file)
  1308. {
  1309. // If the file is a language, we must handle it differently. Language files
  1310. // go in a subdirectory based on the language code, ie.
  1311. // <language tag="en_US">en_US.mycomponent.ini</language>
  1312. // would go in the en_US subdirectory of the languages directory.
  1313. if ($file->getName() == 'language' && (string)$file->attributes()->tag != '')
  1314. {
  1315. if ($source) {
  1316. $path = $source.DS.$file->attributes()->tag.DS.basename((string)$file);
  1317. }
  1318. else
  1319. {
  1320. $target_client = JApplicationHelper::getClientInfo((string)$file->attributes()->client, true);
  1321. $path = $target_client->path.DS.'language'.DS.$file->attributes()->tag.DS.basename((string)$file);
  1322. }
  1323. // If the language folder is not present, then the core pack hasn't been installed... ignore
  1324. if (!JFolder::exists(dirname($path)))
  1325. {
  1326. continue;
  1327. }
  1328. }
  1329. else {
  1330. $path = $source.DS.$file;
  1331. }
  1332. // Actually delete the files/folders
  1333. if (is_dir($path)) {
  1334. $val = JFolder::delete($path);
  1335. }
  1336. else {
  1337. $val = JFile::delete($path);
  1338. }
  1339. if ($val === false)
  1340. {
  1341. JError::raiseWarning(43, 'Failed to delete '. $path);
  1342. $retval = false;
  1343. }
  1344. }
  1345. if (!empty($folder)) {
  1346. $val = JFolder::delete($source);
  1347. }
  1348. return $retval;
  1349. }
  1350. /**
  1351. * Copies the installation manifest file to the extension folder in the given client
  1352. *
  1353. * @param integer $cid Where to copy the installfile [optional: defaults to 1 (admin)]
  1354. *
  1355. * @return boolean True on success, False on error
  1356. * @since 11.1
  1357. */
  1358. public function copyManifest($cid=1)
  1359. {
  1360. // Get the client info
  1361. jimport('joomla.application.helper');
  1362. $client = JApplicationHelper::getClientInfo($cid);
  1363. $path['src'] = $this->getPath('manifest');
  1364. if ($client)
  1365. {
  1366. $pathname = 'extension_'.$client->name;
  1367. $path['dest'] = $this->getPath($pathname).DS.basename($this->getPath('manifest'));
  1368. }
  1369. else
  1370. {
  1371. $pathname = 'extension_root';
  1372. $path['dest'] = $this->getPath($pathname).DS.basename($this->getPath('manifest'));
  1373. }
  1374. return $this->copyFiles(array ($path), true);
  1375. }
  1376. /**
  1377. * Tries to find the package manifest file
  1378. *
  1379. * @return boolean True on success, False on error
  1380. * @since 1.0
  1381. */
  1382. public function findManifest()
  1383. {
  1384. // Get an array of all the XML files from the installation directory
  1385. $xmlfiles = JFolder::files($this->getPath('source'), '.xml$', 1, true);
  1386. // If at least one XML file exists
  1387. if (!empty($xmlfiles))
  1388. {
  1389. foreach ($xmlfiles as $file)
  1390. {
  1391. // Is it a valid Joomla installation manifest file?
  1392. $manifest = $this->isManifest($file);
  1393. if (!is_null($manifest))
  1394. {
  1395. // If the root method attribute is set to upgrade, allow file overwrite
  1396. if ((string)$manifest->attributes()->method == 'upgrade')
  1397. {
  1398. $this->_upgrade = true;
  1399. $this->_overwrite = true;
  1400. }
  1401. // If the overwrite option is set, allow file overwriting
  1402. if ((string)$manifest->attributes()->overwrite == 'true') {
  1403. $this->_overwrite = true;
  1404. }
  1405. // Set the manifest object and path
  1406. $this->manifest = $manifest;
  1407. $this->setPath('manifest', $file);
  1408. // Set the installation source path to that of the manifest file
  1409. $this->setPath('source', dirname($file));
  1410. return true;
  1411. }
  1412. }
  1413. // None of the XML files found were valid install files
  1414. JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_NOTFINDJOOMLAXMLSETUPFILE'));
  1415. return false;
  1416. }
  1417. else
  1418. {
  1419. // No XML files were found in the install folder
  1420. JError::raiseWarning(1, JText::_('JLIB_INSTALLER_ERROR_NOTFINDXMLSETUPFILE'));
  1421. return false;
  1422. }
  1423. }
  1424. /**
  1425. * Is the XML file a valid Joomla installation manifest file
  1426. *
  1427. * @param string $file An xmlfile path to check
  1428. *
  1429. * @return mixed A JXMLElement, or null if the file failed to parse
  1430. * @since 11.1
  1431. */
  1432. public function isManifest($file)
  1433. {
  1434. // Initialise variables.
  1435. $xml = JFactory::getXML($file);
  1436. // If we cannot load the XML file return null
  1437. if( ! $xml)
  1438. {
  1439. return null;
  1440. }
  1441. // Check for a valid XML root tag.
  1442. // @todo: Remove backwards compatability in a future version
  1443. // Should be 'extension', but for backward compatability we will accept 'extension' or 'install'.
  1444. // 1.5 uses 'install'
  1445. // 1.6 uses 'extension'
  1446. if($xml->getName() != 'install' && $xml->getName() != 'extension')
  1447. {
  1448. return null;
  1449. }
  1450. // Valid manifest file return the object
  1451. return $xml;
  1452. }
  1453. /**
  1454. * Generates a manifest cache
  1455. *
  1456. * @return string serialised manifest data
  1457. */
  1458. public function generateManifestCache()
  1459. {
  1460. return json_encode(JApplicationHelper::parseXMLInstallFile($this->getPath('manifest')));
  1461. }
  1462. /**
  1463. * Cleans up discovered extensions if they're being installed somehow else
  1464. * @param string $type The type of extension (component, etc)
  1465. * @param string $element Unique element identifier (e.g. com_content)
  1466. * @param string $folder The folder of the extension (plugins; e.g. system)
  1467. * @param integer $client The client application (administrator or site)
  1468. *
  1469. * @return result of query
  1470. */
  1471. public function cleanDiscoveredExtension($type, $element, $folder='', $client=0)
  1472. {
  1473. $dbo = JFactory::getDBO();
  1474. $dbo->setQuery('DELETE FROM #__extensions WHERE type = '. $dbo->Quote($type).' AND element = '. $dbo->Quote($element) .' AND folder = '. $dbo->Quote($folder). ' AND client_id = '. intval($client).' AND state = -1');
  1475. return $dbo->Query();
  1476. }
  1477. /**
  1478. * Compares two "files" entries to find deleted files/folders
  1479. * @param array $old_files An array of JXMLElement objects that are the old files
  1480. * @param array $new_files An array of JXMLElement objects that are the new files
  1481. *
  1482. * @return array An array with the delete files and folders in findDeletedFiles[files] and findDeletedFiles[folders] resepctively
  1483. */
  1484. public function findDeletedFiles($old_files, $new_files)
  1485. {
  1486. // The magic find deleted files function!
  1487. // The files that are new
  1488. $files = Array();
  1489. // The folders that are new
  1490. $folders = Array();
  1491. // The folders of the files that are new
  1492. $containers = Array();
  1493. // A list of files to delete
  1494. $files_deleted = Array();
  1495. // A list of folders to delete
  1496. $folders_deleted = Array();
  1497. foreach ($new_files as $file)
  1498. {
  1499. switch($file->getName())
  1500. {
  1501. case 'folder':
  1502. $folders[] = (string)$file; // add any folders to the list
  1503. break;
  1504. case 'file':
  1505. default:
  1506. // Add any files to the list
  1507. $files[] = (string)$file;
  1508. // Now handle the folder part of the file to ensure we get any containers
  1509. // Break up the parts of the directory
  1510. $container_parts = explode('/',dirname((string)$file));
  1511. // Make sure this is clean and empty
  1512. $container = '';
  1513. foreach ($container_parts as $part)
  1514. {
  1515. // Iterate through each part
  1516. // Add a slash if its not empty
  1517. if (!empty($container)) $container .= '/';
  1518. $container .= $part; // append the folder part
  1519. if (!in_array($container, $containers)) $containers[] = $container; // add the container if it doesn't already exist
  1520. }
  1521. break;
  1522. }
  1523. }
  1524. foreach ($old_files as $file)
  1525. {
  1526. switch($file->getName())
  1527. {
  1528. case 'folder':
  1529. if (!in_array((string)$file, $folders))
  1530. {
  1531. // See whether the folder exists in the new list
  1532. if (!in_array((string)$file, $containers)) {
  1533. // Check if the folder exists as a container in the new list
  1534. // If it's not in the new list or a container then delete it
  1535. $folders_deleted[] = (string)$file;
  1536. }
  1537. }
  1538. break;
  1539. case 'file':
  1540. default:
  1541. if (!in_array((string)$file, $files))
  1542. {
  1543. // look if the file exists in the new list
  1544. if (!in_array(dirname((string)$file), $folders)) {
  1545. // look if the file is now potentially in a folder
  1546. $files_deleted[] = (string)$file; // not in a folder, doesn't exist, wipe it out!
  1547. }
  1548. }
  1549. break;
  1550. }
  1551. }
  1552. return Array('files'=>$files_deleted, 'folders'=>$folders_deleted);
  1553. }
  1554. /**
  1555. * Loads an MD5SUMS file into an associative array
  1556. * @param string Filename to load
  1557. * @return array Associative array with filenames as the index and the MD5 as the value
  1558. */
  1559. function loadMD5Sum($filename)
  1560. {
  1561. if (!file_exists($filename)) return false; // bail if the file doesn't exist
  1562. $data = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  1563. $retval = Array();
  1564. foreach ($data as $row)
  1565. {
  1566. $results = explode(' ', $row); // split up the data
  1567. $results[1] = str_replace('./','', $results[1]); // cull any potential prefix
  1568. $retval[$results[1]] = $results[0]; // throw into the array
  1569. }
  1570. return $retval;
  1571. }
  1572. }