PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/installer/adapters/file.php

https://bitbucket.org/izubizarreta/https-bitbucket.org-bityvip-alpes
PHP | 733 lines | 448 code | 107 blank | 178 comment | 58 complexity | 33d719bea16fdd91fab5e1d374297c08 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, MIT, LGPL-3.0, LGPL-2.0, JSON
  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage Installer
  5. *
  6. * @copyright Copyright (C) 2005 - 2012 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.installer.filemanifest');
  11. jimport('joomla.base.adapterinstance');
  12. /**
  13. * File installer
  14. *
  15. * @package Joomla.Platform
  16. * @subpackage Installer
  17. * @since 11.1
  18. */
  19. class JInstallerFile extends JAdapterInstance
  20. {
  21. protected $route = 'install';
  22. /**
  23. * Custom loadLanguage method
  24. *
  25. * @param string $path The path on which to find language files.
  26. *
  27. * @return void
  28. *
  29. * @since 11.1
  30. */
  31. public function loadLanguage($path)
  32. {
  33. $this->manifest = $this->parent->getManifest();
  34. $extension = 'files_' . str_replace('files_', '', strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd')));
  35. $lang = JFactory::getLanguage();
  36. $source = $path;
  37. $lang->load($extension . '.sys', $source, null, false, false)
  38. || $lang->load($extension . '.sys', JPATH_SITE, null, false, false)
  39. || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false)
  40. || $lang->load($extension . '.sys', JPATH_SITE, $lang->getDefault(), false, false);
  41. }
  42. /**
  43. * Custom install method
  44. *
  45. * @return boolean True on success
  46. *
  47. * @since 11.1
  48. */
  49. public function install()
  50. {
  51. // Get the extension manifest object
  52. $this->manifest = $this->parent->getManifest();
  53. // Manifest Document Setup Section
  54. // Set the extension's name
  55. $name = JFilterInput::getInstance()->clean((string) $this->manifest->name, 'string');
  56. $this->set('name', $name);
  57. // Set element
  58. $manifestPath = JPath::clean($this->parent->getPath('manifest'));
  59. $element = preg_replace('/\.xml/', '', basename($manifestPath));
  60. $this->set('element', $element);
  61. // Get the component description
  62. $description = (string) $this->manifest->description;
  63. if ($description)
  64. {
  65. $this->parent->set('message', JText::_($description));
  66. }
  67. else
  68. {
  69. $this->parent->set('message', '');
  70. }
  71. //Check if the extension by the same name is already installed
  72. if ($this->extensionExistsInSystem($element))
  73. {
  74. // Package with same name already exists
  75. if (!$this->parent->isOverwrite())
  76. {
  77. // we're not overwriting so abort
  78. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_SAME_NAME'));
  79. return false;
  80. }
  81. else
  82. {
  83. // swap to the update route
  84. $this->route = 'update';
  85. }
  86. }
  87. // Set the file root path
  88. $this->parent->setPath('extension_root', JPATH_ROOT);
  89. /**
  90. * ---------------------------------------------------------------------------------------------
  91. * Installer Trigger Loading
  92. * ---------------------------------------------------------------------------------------------
  93. */
  94. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  95. $this->scriptElement = $this->manifest->scriptfile;
  96. $manifestScript = (string) $this->manifest->scriptfile;
  97. if ($manifestScript)
  98. {
  99. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  100. if (is_file($manifestScriptFile))
  101. {
  102. // load the file
  103. include_once $manifestScriptFile;
  104. }
  105. // Set the class name
  106. $classname = $element . 'InstallerScript';
  107. if (class_exists($classname))
  108. {
  109. // create a new instance
  110. $this->parent->manifestClass = new $classname($this);
  111. // and set this so we can copy it later
  112. $this->set('manifest_script', $manifestScript);
  113. // Note: if we don't find the class, don't bother to copy the file
  114. }
  115. }
  116. // run preflight if possible (since we know we're not an update)
  117. ob_start();
  118. ob_implicit_flush(false);
  119. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  120. {
  121. if ($this->parent->manifestClass->preflight($this->route, $this) === false)
  122. {
  123. // Install failed, rollback changes
  124. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_INSTALL_CUSTOM_INSTALL_FAILURE'));
  125. return false;
  126. }
  127. }
  128. $msg = ob_get_contents(); // create msg object; first use here
  129. ob_end_clean();
  130. // Populate File and Folder List to copy
  131. $this->populateFilesAndFolderList();
  132. // Filesystem Processing Section
  133. // Now that we have folder list, lets start creating them
  134. foreach ($this->folderList as $folder)
  135. {
  136. if (!JFolder::exists($folder))
  137. {
  138. if (!$created = JFolder::create($folder))
  139. {
  140. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_FAIL_SOURCE_DIRECTORY', $folder));
  141. // If installation fails, rollback
  142. $this->parent->abort();
  143. return false;
  144. }
  145. // Since we created a directory and will want to remove it if we have to roll back.
  146. // the installation due to some errors, let's add it to the installation step stack.
  147. if ($created)
  148. {
  149. $this->parent->pushStep(array('type' => 'folder', 'path' => $folder));
  150. }
  151. }
  152. }
  153. // Now that we have file list, let's start copying them
  154. $this->parent->copyFiles($this->fileList);
  155. // Parse optional tags
  156. $this->parent->parseLanguages($this->manifest->languages);
  157. // Finalization and Cleanup Section
  158. // Get a database connector object
  159. $db = $this->parent->getDbo();
  160. // Check to see if a module by the same name is already installed
  161. // If it is, then update the table because if the files aren't there
  162. // we can assume that it was (badly) uninstalled
  163. // If it isn't, add an entry to extensions
  164. $query = $db->getQuery(true);
  165. $query->select($query->qn('extension_id'))
  166. ->from($query->qn('#__extensions'));
  167. $query->where($query->qn('type') . ' = ' . $query->q('file'))
  168. ->where($query->qn('element') . ' = ' . $query->q($element));
  169. $db->setQuery($query);
  170. try
  171. {
  172. $db->execute();
  173. }
  174. catch (JException $e)
  175. {
  176. // Install failed, roll back changes
  177. $this->parent->abort(
  178. JText::sprintf('JLIB_INSTALLER_ABORT_FILE_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))
  179. );
  180. return false;
  181. }
  182. $id = $db->loadResult();
  183. $row = JTable::getInstance('extension');
  184. if ($id)
  185. {
  186. // Load the entry and update the manifest_cache
  187. $row->load($id);
  188. // Update name
  189. $row->set('name', $this->get('name'));
  190. // Update manifest
  191. $row->manifest_cache = $this->parent->generateManifestCache();
  192. if (!$row->store())
  193. {
  194. // Install failed, roll back changes
  195. $this->parent->abort(
  196. JText::sprintf('JLIB_INSTALLER_ABORT_FILE_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))
  197. );
  198. return false;
  199. }
  200. }
  201. else
  202. {
  203. // Add an entry to the extension table with a whole heap of defaults
  204. $row->set('name', $this->get('name'));
  205. $row->set('type', 'file');
  206. $row->set('element', $this->get('element'));
  207. // There is no folder for files so leave it blank
  208. $row->set('folder', '');
  209. $row->set('enabled', 1);
  210. $row->set('protected', 0);
  211. $row->set('access', 0);
  212. $row->set('client_id', 0);
  213. $row->set('params', '');
  214. $row->set('system_data', '');
  215. $row->set('manifest_cache', $this->parent->generateManifestCache());
  216. if (!$row->store())
  217. {
  218. // Install failed, roll back changes
  219. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_ROLLBACK', $db->stderr(true)));
  220. return false;
  221. }
  222. // Set the insert id
  223. $row->set('extension_id', $db->insertid());
  224. // Since we have created a module item, we add it to the installation step stack
  225. // so that if we have to rollback the changes we can undo it.
  226. $this->parent->pushStep(array('type' => 'extension', 'extension_id' => $row->extension_id));
  227. }
  228. /*
  229. * Let's run the queries for the file
  230. */
  231. // second argument is the utf compatible version attribute
  232. if (strtolower($this->route) == 'install')
  233. {
  234. $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql);
  235. if ($utfresult === false)
  236. {
  237. // Install failed, rollback changes
  238. $this->parent->abort(
  239. JText::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))
  240. );
  241. return false;
  242. }
  243. // Set the schema version to be the latest update version
  244. if ($this->manifest->update)
  245. {
  246. $this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id);
  247. }
  248. }
  249. elseif (strtolower($this->route) == 'update')
  250. {
  251. if ($this->manifest->update)
  252. {
  253. $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id);
  254. if ($result === false)
  255. {
  256. // Install failed, rollback changes
  257. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_FILE_UPDATE_SQL_ERROR', $db->stderr(true)));
  258. return false;
  259. }
  260. }
  261. }
  262. // Start Joomla! 1.6
  263. ob_start();
  264. ob_implicit_flush(false);
  265. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route))
  266. {
  267. if ($this->parent->manifestClass->{$this->route}($this) === false)
  268. {
  269. // Install failed, rollback changes
  270. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_INSTALL_CUSTOM_INSTALL_FAILURE'));
  271. return false;
  272. }
  273. }
  274. $msg .= ob_get_contents(); // append messages
  275. ob_end_clean();
  276. // Lastly, we will copy the manifest file to its appropriate place.
  277. $manifest = array();
  278. $manifest['src'] = $this->parent->getPath('manifest');
  279. $manifest['dest'] = JPATH_MANIFESTS . '/files/' . basename($this->parent->getPath('manifest'));
  280. if (!$this->parent->copyFiles(array($manifest), true))
  281. {
  282. // Install failed, rollback changes
  283. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_INSTALL_COPY_SETUP'));
  284. return false;
  285. }
  286. // Clobber any possible pending updates
  287. $update = JTable::getInstance('update');
  288. $uid = $update->find(
  289. array('element' => $this->get('element'), 'type' => 'file', 'client_id' => '', 'folder' => '')
  290. );
  291. if ($uid)
  292. {
  293. $update->delete($uid);
  294. }
  295. // And now we run the postflight
  296. ob_start();
  297. ob_implicit_flush(false);
  298. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
  299. {
  300. $this->parent->manifestClass->postflight($this->route, $this);
  301. }
  302. $msg .= ob_get_contents(); // append messages
  303. ob_end_clean();
  304. if ($msg != '')
  305. {
  306. $this->parent->set('extension_message', $msg);
  307. }
  308. return $row->get('extension_id');
  309. }
  310. /**
  311. * Custom update method
  312. *
  313. * @return boolean True on success
  314. *
  315. * @since 11.1
  316. */
  317. public function update()
  318. {
  319. // Set the overwrite setting
  320. $this->parent->setOverwrite(true);
  321. $this->parent->setUpgrade(true);
  322. $this->route = 'update';
  323. // ...and adds new files
  324. return $this->install();
  325. }
  326. /**
  327. * Custom uninstall method
  328. *
  329. * @param string $id The id of the file to uninstall
  330. *
  331. * @return boolean True on success
  332. *
  333. * @since 11.1
  334. */
  335. public function uninstall($id)
  336. {
  337. // Initialise variables.
  338. $row = JTable::getInstance('extension');
  339. if (!$row->load($id))
  340. {
  341. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_LOAD_ENTRY'));
  342. return false;
  343. }
  344. if ($row->protected)
  345. {
  346. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_WARNCOREFILE'));
  347. return false;
  348. }
  349. $retval = true;
  350. $manifestFile = JPATH_MANIFESTS . '/files/' . $row->element . '.xml';
  351. // Because files may not have their own folders we cannot use the standard method of finding an installation manifest
  352. if (file_exists($manifestFile))
  353. {
  354. // Set the plugin root path
  355. $this->parent->setPath('extension_root', JPATH_ROOT); // . '/files/' . $manifest->filename);
  356. $xml = JFactory::getXML($manifestFile);
  357. // If we cannot load the XML file return null
  358. if (!$xml)
  359. {
  360. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_LOAD_MANIFEST'));
  361. return false;
  362. }
  363. /*
  364. * Check for a valid XML root tag.
  365. */
  366. if ($xml->getName() != 'extension')
  367. {
  368. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_INVALID_MANIFEST'));
  369. return false;
  370. }
  371. $this->manifest = $xml;
  372. // If there is an manifest class file, let's load it
  373. $this->scriptElement = $this->manifest->scriptfile;
  374. $manifestScript = (string) $this->manifest->scriptfile;
  375. if ($manifestScript)
  376. {
  377. $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript;
  378. if (is_file($manifestScriptFile))
  379. {
  380. // Load the file
  381. include_once $manifestScriptFile;
  382. }
  383. // Set the class name
  384. $classname = $row->element . 'InstallerScript';
  385. if (class_exists($classname))
  386. {
  387. // Create a new instance
  388. $this->parent->manifestClass = new $classname($this);
  389. // And set this so we can copy it later
  390. $this->set('manifest_script', $manifestScript);
  391. // Note: if we don't find the class, don't bother to copy the file
  392. }
  393. }
  394. ob_start();
  395. ob_implicit_flush(false);
  396. // Run uninstall if possible
  397. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall'))
  398. {
  399. $this->parent->manifestClass->uninstall($this);
  400. }
  401. $msg = ob_get_contents();
  402. ob_end_clean();
  403. /*
  404. * Let's run the uninstall queries for the component
  405. * If Joomla 1.5 compatible, with discreet sql files - execute appropriate
  406. * file for utf-8 support or non-utf support
  407. */
  408. // Try for Joomla 1.5 type queries
  409. // Second argument is the utf compatible version attribute
  410. $utfresult = $this->parent->parseSQLFiles($this->manifest->uninstall->sql);
  411. if ($utfresult === false)
  412. {
  413. // Install failed, rollback changes
  414. JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_SQL_ERROR', $db->stderr(true)));
  415. $retval = false;
  416. }
  417. // Remove the schema version
  418. $db = JFactory::getDbo();
  419. $query = $db->getQuery(true);
  420. $query->delete()
  421. ->from('#__schemas')
  422. ->where('extension_id = ' . $row->extension_id);
  423. $db->setQuery($query);
  424. $db->execute();
  425. // Set root folder names
  426. $packagePath = $this->parent->getPath('source');
  427. $jRootPath = JPath::clean(JPATH_ROOT);
  428. // Loop through all elements and get list of files and folders
  429. foreach ($xml->fileset->files as $eFiles)
  430. {
  431. $folder = (string) $eFiles->attributes()->folder;
  432. $target = (string) $eFiles->attributes()->target;
  433. // Create folder path
  434. if (empty($target))
  435. {
  436. $targetFolder = JPATH_ROOT;
  437. }
  438. else
  439. {
  440. $targetFolder = JPATH_ROOT . '/' . $target;
  441. }
  442. $folderList = array();
  443. // Check if all children exists
  444. if (count($eFiles->children()) > 0)
  445. {
  446. // Loop through all filenames elements
  447. foreach ($eFiles->children() as $eFileName)
  448. {
  449. if ($eFileName->getName() == 'folder')
  450. {
  451. $folderList[] = $targetFolder . '/' . $eFileName;
  452. }
  453. else
  454. {
  455. $fileName = $targetFolder . '/' . $eFileName;
  456. JFile::delete($fileName);
  457. }
  458. }
  459. }
  460. // Delete any folders that don't have any content in them.
  461. foreach ($folderList as $folder)
  462. {
  463. $files = JFolder::files($folder);
  464. if (!count($files))
  465. {
  466. JFolder::delete($folder);
  467. }
  468. }
  469. }
  470. JFile::delete($manifestFile);
  471. }
  472. else
  473. {
  474. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_INVALID_NOTFOUND_MANIFEST'));
  475. // Delete the row because its broken
  476. $row->delete();
  477. return false;
  478. }
  479. $this->parent->removeFiles($xml->languages);
  480. $row->delete();
  481. return $retval;
  482. }
  483. /**
  484. * Function used to check if extension is already installed
  485. *
  486. * @param string $extension The element name of the extension to install
  487. *
  488. * @return boolean True if extension exists
  489. *
  490. * @since 11.1
  491. */
  492. protected function extensionExistsInSystem($extension = null)
  493. {
  494. // Get a database connector object
  495. $db = $this->parent->getDBO();
  496. $query = $db->getQuery(true);
  497. $query->select($query->qn('extension_id'))
  498. ->from($query->qn('#__extensions'));
  499. $query->where($query->qn('type') . ' = ' . $query->q('file'))
  500. ->where($query->qn('element') . ' = ' . $query->q($extension));
  501. $db->setQuery($query);
  502. try
  503. {
  504. $db->execute();
  505. }
  506. catch (JException $e)
  507. {
  508. // Install failed, roll back changes
  509. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_FILE_ROLLBACK', $db->stderr(true)));
  510. return false;
  511. }
  512. $id = $db->loadResult();
  513. if (empty($id))
  514. {
  515. return false;
  516. }
  517. return true;
  518. }
  519. /**
  520. * Function used to populate files and folder list
  521. *
  522. * @return boolean none
  523. *
  524. * @since 11.1
  525. */
  526. protected function populateFilesAndFolderList()
  527. {
  528. // Initialise variable
  529. $this->folderList = array();
  530. $this->fileList = array();
  531. // Get fileset
  532. $eFileset = $this->manifest->fileset->files;
  533. // Set root folder names
  534. $packagePath = $this->parent->getPath('source');
  535. $jRootPath = JPath::clean(JPATH_ROOT);
  536. // Loop through all elements and get list of files and folders
  537. foreach ($this->manifest->fileset->files as $eFiles)
  538. {
  539. // Check if the element is files element
  540. $folder = (string) $eFiles->attributes()->folder;
  541. $target = (string) $eFiles->attributes()->target;
  542. //Split folder names into array to get folder names. This will
  543. // help in creating folders
  544. $arrList = preg_split("#/|\\/#", $target);
  545. $folderName = $jRootPath;
  546. foreach ($arrList as $dir)
  547. {
  548. if (empty($dir))
  549. {
  550. continue;
  551. }
  552. $folderName .= '/' . $dir;
  553. // Check if folder exists, if not then add to the array for folder creation
  554. if (!JFolder::exists($folderName))
  555. {
  556. array_push($this->folderList, $folderName);
  557. }
  558. }
  559. // Create folder path
  560. $sourceFolder = empty($folder) ? $packagePath : $packagePath . '/' . $folder;
  561. $targetFolder = empty($target) ? $jRootPath : $jRootPath . '/' . $target;
  562. // Check if source folder exists
  563. if (!JFolder::exists($sourceFolder))
  564. {
  565. JError::raiseWarning(1, JText::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_FAIL_SOURCE_DIRECTORY', $sourceFolder));
  566. // If installation fails, rollback
  567. $this->parent->abort();
  568. return false;
  569. }
  570. // Check if all children exists
  571. if (count($eFiles->children()))
  572. {
  573. // Loop through all filenames elements
  574. foreach ($eFiles->children() as $eFileName)
  575. {
  576. $path['src'] = $sourceFolder . '/' . $eFileName;
  577. $path['dest'] = $targetFolder . '/' . $eFileName;
  578. $path['type'] = 'file';
  579. if ($eFileName->getName() == 'folder')
  580. {
  581. $folderName = $targetFolder . '/' . $eFileName;
  582. array_push($this->folderList, $folderName);
  583. $path['type'] = 'folder';
  584. }
  585. array_push($this->fileList, $path);
  586. }
  587. }
  588. else
  589. {
  590. $files = JFolder::files($sourceFolder);
  591. foreach ($files as $file)
  592. {
  593. $path['src'] = $sourceFolder . '/' . $file;
  594. $path['dest'] = $targetFolder . '/' . $file;
  595. array_push($this->fileList, $path);
  596. }
  597. }
  598. }
  599. }
  600. /**
  601. * Refreshes the extension table cache
  602. *
  603. * @return boolean result of operation, true if updated, false on failure
  604. *
  605. * @since 11.1
  606. */
  607. public function refreshManifestCache()
  608. {
  609. // Need to find to find where the XML file is since we don't store this normally
  610. $manifestPath = JPATH_MANIFESTS . '/files/' . $this->parent->extension->element . '.xml';
  611. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  612. $this->parent->setPath('manifest', $manifestPath);
  613. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  614. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  615. $this->parent->extension->name = $manifest_details['name'];
  616. try
  617. {
  618. return $this->parent->extension->store();
  619. }
  620. catch (JException $e)
  621. {
  622. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE'));
  623. return false;
  624. }
  625. }
  626. }