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

/libraries/joomla/installer/adapters/component.php

https://bitbucket.org/eternaware/joomus
PHP | 1810 lines | 1023 code | 288 blank | 499 comment | 163 complexity | 7ae3306d694466c1cc74b6404906fde7 MD5 | raw file
Possible License(s): LGPL-2.1
  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.base.adapterinstance');
  11. jimport('joomla.filesystem.folder');
  12. /**
  13. * Component installer
  14. *
  15. * @package Joomla.Platform
  16. * @subpackage Installer
  17. * @since 11.1
  18. */
  19. class JInstallerComponent extends JAdapterInstance
  20. {
  21. /**
  22. * Copy of the XML manifest file
  23. *
  24. * @var string
  25. * @since 11.1
  26. */
  27. protected $manifest = null;
  28. /**
  29. * Name of the extension
  30. *
  31. * @var string
  32. * @since 11.1
  33. * */
  34. protected $name = null;
  35. /**
  36. * The unique identifier for the extension (e.g. mod_login)
  37. *
  38. * @var string
  39. * @since 11.1
  40. * */
  41. protected $element = null;
  42. /**
  43. * The list of current files fo the Joomla! CMS administrator that are installed and is read
  44. * from the manifest on disk in the update area to handle doing a diff
  45. * and deleting files that are in the old files list and not in the new
  46. * files list.
  47. *
  48. * @var array
  49. * @since 11.1
  50. * */
  51. protected $oldAdminFiles = null;
  52. /**
  53. * The list of current files that are installed and is read
  54. * from the manifest on disk in the update area to handle doing a diff
  55. * and deleting files that are in the old files list and not in the new
  56. * files list.
  57. *
  58. * @var array
  59. * @since 11.1
  60. * */
  61. protected $oldFiles = null;
  62. /**
  63. * A path to the PHP file that the scriptfile declaration in
  64. * the manifest refers to.
  65. *
  66. * @var string
  67. * @since 11.1
  68. * */
  69. protected $manifest_script = null;
  70. /**
  71. * For legacy installations this is a path to the PHP file that the scriptfile declaration in the
  72. * manifest refers to.
  73. *
  74. * @var string
  75. * @since 11.1
  76. * */
  77. protected $install_script = null;
  78. /**
  79. * Custom loadLanguage method
  80. *
  81. * @param string $path The path language files are on.
  82. *
  83. * @return void
  84. *
  85. * @since 11.1
  86. */
  87. public function loadLanguage($path = null)
  88. {
  89. $source = $this->parent->getPath('source');
  90. if (!$source)
  91. {
  92. $this->parent
  93. ->setPath(
  94. 'source',
  95. ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) .
  96. '/components/' . $this->parent->extension->element
  97. );
  98. }
  99. $this->manifest = $this->parent->getManifest();
  100. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  101. if (substr($name, 0, 4) == 'com_')
  102. {
  103. $extension = $name;
  104. }
  105. else
  106. {
  107. $extension = 'com_' . $name;
  108. }
  109. $lang = JFactory::getLanguage();
  110. $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/components/' . $extension;
  111. if ($this->manifest->administration->files)
  112. {
  113. $element = $this->manifest->administration->files;
  114. }
  115. elseif ($this->manifest->files)
  116. {
  117. $element = $this->manifest->files;
  118. }
  119. else
  120. {
  121. $element = null;
  122. }
  123. if ($element)
  124. {
  125. $folder = (string) $element->attributes()->folder;
  126. if ($folder && file_exists($path . '/' . $folder))
  127. {
  128. $source = $path . '/' . $folder;
  129. }
  130. }
  131. $lang->load($extension . '.sys', $source, null, false, false) || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, null, false, false)
  132. || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false)
  133. || $lang->load($extension . '.sys', JPATH_ADMINISTRATOR, $lang->getDefault(), false, false);
  134. }
  135. /**
  136. * Custom install method for components
  137. *
  138. * @return boolean True on success
  139. *
  140. * @since 11.1
  141. */
  142. public function install()
  143. {
  144. // Get a database connector object
  145. $db = $this->parent->getDbo();
  146. // Get the extension manifest object
  147. $this->manifest = $this->parent->getManifest();
  148. /*
  149. * ---------------------------------------------------------------------------------------------
  150. * Manifest Document Setup Section
  151. * ---------------------------------------------------------------------------------------------
  152. */
  153. // Set the extension's name
  154. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  155. if (substr($name, 0, 4) == 'com_')
  156. {
  157. $element = $name;
  158. }
  159. else
  160. {
  161. $element = 'com_' . $name;
  162. }
  163. $this->set('name', $name);
  164. $this->set('element', $element);
  165. // Get the component description
  166. $this->parent->set('message', JText::_((string) $this->manifest->description));
  167. // Set the installation target paths
  168. $this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
  169. $this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
  170. // Copy the admin path as it's used as a common base
  171. $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
  172. /*
  173. * ---------------------------------------------------------------------------------------------
  174. * Basic Checks Section
  175. * ---------------------------------------------------------------------------------------------
  176. */
  177. // Make sure that we have an admin element
  178. if (!$this->manifest->administration)
  179. {
  180. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
  181. return false;
  182. }
  183. /*
  184. * ---------------------------------------------------------------------------------------------
  185. * Filesystem Processing Section
  186. * ---------------------------------------------------------------------------------------------
  187. */
  188. /*
  189. * If the component site or admin directory already exists, then we will assume that the component is already
  190. * installed or another component is using that directory.
  191. */
  192. if (file_exists($this->parent->getPath('extension_site')) || file_exists($this->parent->getPath('extension_administrator')))
  193. {
  194. // Look for an update function or update tag
  195. $updateElement = $this->manifest->update;
  196. // Upgrade manually set or update function available or update tag detected
  197. if ($this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
  198. || $updateElement)
  199. {
  200. // Transfer control to the update function
  201. return $this->update();
  202. }
  203. elseif (!$this->parent->isOverwrite())
  204. {
  205. // Overwrite is set.
  206. // We didn't have overwrite set, find an update function or find an update tag so lets call it safe
  207. if (file_exists($this->parent->getPath('extension_site')))
  208. {
  209. // If the site exists say so.
  210. JLog::add(
  211. JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_SITE', $this->parent->getPath('extension_site')),
  212. JLog::WARNING, 'jerror'
  213. );
  214. }
  215. else
  216. {
  217. // If the admin exists say so
  218. JLog::add(
  219. JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_DIR_ADMIN', $this->parent->getPath('extension_administrator')),
  220. JLog::WARNING, 'jerror'
  221. );
  222. }
  223. return false;
  224. }
  225. }
  226. /*
  227. * ---------------------------------------------------------------------------------------------
  228. * Installer Trigger Loading
  229. * ---------------------------------------------------------------------------------------------
  230. */
  231. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  232. $manifestScript = (string) $this->manifest->scriptfile;
  233. if ($manifestScript)
  234. {
  235. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  236. if (is_file($manifestScriptFile))
  237. {
  238. // Load the file
  239. include_once $manifestScriptFile;
  240. }
  241. // Set the class name
  242. $classname = $this->get('element') . 'InstallerScript';
  243. if (class_exists($classname))
  244. {
  245. // Create a new instance
  246. $this->parent->manifestClass = new $classname($this);
  247. // And set this so we can copy it later
  248. $this->set('manifest_script', $manifestScript);
  249. }
  250. }
  251. // Run preflight if possible (since we know we're not an update)
  252. ob_start();
  253. ob_implicit_flush(false);
  254. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  255. {
  256. if ($this->parent->manifestClass->preflight('install', $this) === false)
  257. {
  258. // Install failed, rollback changes
  259. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  260. return false;
  261. }
  262. }
  263. // Create msg object; first use here
  264. $msg = ob_get_contents();
  265. ob_end_clean();
  266. // If the component directory does not exist, let's create it
  267. $created = false;
  268. if (!file_exists($this->parent->getPath('extension_site')))
  269. {
  270. if (!$created = JFolder::create($this->parent->getPath('extension_site')))
  271. {
  272. JLog::add(
  273. JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_FAILED_TO_CREATE_DIRECTORY_SITE', $this->parent->getPath('extension_site')),
  274. JLog::WARNING, 'jerror'
  275. );
  276. return false;
  277. }
  278. }
  279. /*
  280. * Since we created the component directory and we will want to remove it if we have to roll back
  281. * the installation, let's add it to the installation step stack
  282. */
  283. if ($created)
  284. {
  285. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site')));
  286. }
  287. // If the component admin directory does not exist, let's create it
  288. $created = false;
  289. if (!file_exists($this->parent->getPath('extension_administrator')))
  290. {
  291. if (!$created = JFolder::create($this->parent->getPath('extension_administrator')))
  292. {
  293. JLog::add(
  294. JText::sprintf('JLIB_INSTALLER_ERROR_COMP_INSTALL_FAILED_TO_CREATE_DIRECTORY_ADMIN', $this->parent->getPath('extension_administrator')),
  295. JLog::WARNING, 'jerror'
  296. );
  297. // Install failed, rollback any changes
  298. $this->parent->abort();
  299. return false;
  300. }
  301. }
  302. /*
  303. * Since we created the component admin directory and we will want to remove it if we have to roll
  304. * back the installation, let's add it to the installation step stack
  305. */
  306. if ($created)
  307. {
  308. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_administrator')));
  309. }
  310. // Copy site files
  311. if ($this->manifest->files)
  312. {
  313. if ($this->parent->parseFiles($this->manifest->files) === false)
  314. {
  315. // Install failed, rollback any changes
  316. $this->parent->abort();
  317. return false;
  318. }
  319. }
  320. // Copy admin files
  321. if ($this->manifest->administration->files)
  322. {
  323. if ($this->parent->parseFiles($this->manifest->administration->files, 1) === false)
  324. {
  325. // Install failed, rollback any changes
  326. $this->parent->abort();
  327. return false;
  328. }
  329. }
  330. // Parse optional tags
  331. $this->parent->parseMedia($this->manifest->media);
  332. $this->parent->parseLanguages($this->manifest->languages);
  333. $this->parent->parseLanguages($this->manifest->administration->languages, 1);
  334. // If there is a manifest script, let's copy it.
  335. if ($this->get('manifest_script'))
  336. {
  337. $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script');
  338. $path['dest'] = $this->parent->getPath('extension_administrator') . '/' . $this->get('manifest_script');
  339. if (!file_exists($path['dest']) || $this->parent->isOverwrite())
  340. {
  341. if (!$this->parent->copyFiles(array($path)))
  342. {
  343. // Install failed, rollback changes
  344. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_MANIFEST'));
  345. return false;
  346. }
  347. }
  348. }
  349. /*
  350. * ---------------------------------------------------------------------------------------------
  351. * Database Processing Section
  352. * ---------------------------------------------------------------------------------------------
  353. */
  354. // Run the install queries for the component
  355. if (isset($this->manifest->install->sql))
  356. {
  357. $result = $this->parent->parseSQLFiles($this->manifest->install->sql);
  358. if ($result === false)
  359. {
  360. // Install failed, rollback changes
  361. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_SQL_ERROR', $db->stderr(true)));
  362. return false;
  363. }
  364. }
  365. /**
  366. * ---------------------------------------------------------------------------------------------
  367. * Custom Installation Script Section
  368. * ---------------------------------------------------------------------------------------------
  369. */
  370. /*
  371. * If we have an install script, let's include it, execute the custom
  372. * install method, and append the return value from the custom install
  373. * method to the installation message.
  374. */
  375. ob_start();
  376. ob_implicit_flush(false);
  377. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'install'))
  378. {
  379. if ($this->parent->manifestClass->install($this) === false)
  380. {
  381. // Install failed, rollback changes
  382. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  383. return false;
  384. }
  385. }
  386. // Append messages
  387. $msg .= ob_get_contents();
  388. ob_end_clean();
  389. /**
  390. * ---------------------------------------------------------------------------------------------
  391. * Finalization and Cleanup Section
  392. * ---------------------------------------------------------------------------------------------
  393. */
  394. // Add an entry to the extension table with a whole heap of defaults
  395. $row = JTable::getInstance('extension');
  396. $row->set('name', $this->get('name'));
  397. $row->set('type', 'component');
  398. $row->set('element', $this->get('element'));
  399. // There is no folder for components
  400. $row->set('folder', '');
  401. $row->set('enabled', 1);
  402. $row->set('protected', 0);
  403. $row->set('access', 0);
  404. $row->set('client_id', 1);
  405. $row->set('params', $this->parent->getParams());
  406. $row->set('manifest_cache', $this->parent->generateManifestCache());
  407. if (!$row->store())
  408. {
  409. // Install failed, roll back changes
  410. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
  411. return false;
  412. }
  413. $eid = $row->extension_id;
  414. // Clobber any possible pending updates
  415. $update = JTable::getInstance('update');
  416. $uid = $update->find(array('element' => $this->get('element'), 'type' => 'component', 'client_id' => 1, 'folder' => ''));
  417. if ($uid)
  418. {
  419. $update->delete($uid);
  420. }
  421. // We will copy the manifest file to its appropriate place.
  422. if (!$this->parent->copyManifest())
  423. {
  424. // Install failed, rollback changes
  425. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_COPY_SETUP'));
  426. return false;
  427. }
  428. // Time to build the admin menus
  429. if (!$this->_buildAdminMenus($row->extension_id))
  430. {
  431. JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_BUILDADMINMENUS_FAILED'), JLog::WARNING, 'jerror');
  432. // @todo remove code: $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
  433. // @todo remove code: return false;
  434. }
  435. // Set the schema version to be the latest update version
  436. if ($this->manifest->update)
  437. {
  438. $this->parent->setSchemaVersion($this->manifest->update->schemas, $eid);
  439. }
  440. // Register the component container just under root in the assets table.
  441. $asset = JTable::getInstance('Asset');
  442. $asset->name = $row->element;
  443. $asset->parent_id = 1;
  444. $asset->rules = '{}';
  445. $asset->title = $row->name;
  446. $asset->setLocation(1, 'last-child');
  447. if (!$asset->store())
  448. {
  449. // Install failed, roll back changes
  450. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
  451. return false;
  452. }
  453. // And now we run the postflight
  454. ob_start();
  455. ob_implicit_flush(false);
  456. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
  457. {
  458. $this->parent->manifestClass->postflight('install', $this);
  459. }
  460. // Append messages
  461. $msg .= ob_get_contents();
  462. ob_end_clean();
  463. if ($msg != '')
  464. {
  465. $this->parent->set('extension_message', $msg);
  466. }
  467. return $row->extension_id;
  468. }
  469. /**
  470. * Custom update method for components
  471. *
  472. * @return boolean True on success
  473. *
  474. * @since 11.1
  475. */
  476. public function update()
  477. {
  478. // Get a database connector object
  479. $db = $this->parent->getDbo();
  480. // Set the overwrite setting
  481. $this->parent->setOverwrite(true);
  482. // Get the extension manifest object
  483. $this->manifest = $this->parent->getManifest();
  484. /**
  485. * ---------------------------------------------------------------------------------------------
  486. * Manifest Document Setup Section
  487. * ---------------------------------------------------------------------------------------------
  488. */
  489. // Set the extension's name
  490. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  491. if (substr($name, 0, 4) == 'com_')
  492. {
  493. $element = $name;
  494. }
  495. else
  496. {
  497. $element = 'com_' . $name;
  498. }
  499. $this->set('name', $name);
  500. $this->set('element', $element);
  501. // Get the component description
  502. $description = (string) $this->manifest->description;
  503. if ($description)
  504. {
  505. $this->parent->set('message', JText::_($description));
  506. }
  507. else
  508. {
  509. $this->parent->set('message', '');
  510. }
  511. // Set the installation target paths
  512. $this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
  513. $this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
  514. // Copy the admin path as it's used as a common base
  515. $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
  516. // Hunt for the original XML file
  517. $old_manifest = null;
  518. // Create a new installer because findManifest sets stuff
  519. // Look in the administrator first
  520. $tmpInstaller = new JInstaller;
  521. $tmpInstaller->setPath('source', $this->parent->getPath('extension_administrator'));
  522. if (!$tmpInstaller->findManifest())
  523. {
  524. // Then the site
  525. $tmpInstaller->setPath('source', $this->parent->getPath('extension_site'));
  526. if ($tmpInstaller->findManifest())
  527. {
  528. $old_manifest = $tmpInstaller->getManifest();
  529. }
  530. }
  531. else
  532. {
  533. $old_manifest = $tmpInstaller->getManifest();
  534. }
  535. // Should do this above perhaps?
  536. if ($old_manifest)
  537. {
  538. $this->oldAdminFiles = $old_manifest->administration->files;
  539. $this->oldFiles = $old_manifest->files;
  540. }
  541. else
  542. {
  543. $this->oldAdminFiles = null;
  544. $this->oldFiles = null;
  545. }
  546. /**
  547. * ---------------------------------------------------------------------------------------------
  548. * Basic Checks Section
  549. * ---------------------------------------------------------------------------------------------
  550. */
  551. // Make sure that we have an admin element
  552. if (!$this->manifest->administration)
  553. {
  554. JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_UPDATE_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
  555. return false;
  556. }
  557. /**
  558. * ---------------------------------------------------------------------------------------------
  559. * Installer Trigger Loading
  560. * ---------------------------------------------------------------------------------------------
  561. */
  562. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  563. $manifestScript = (string) $this->manifest->scriptfile;
  564. if ($manifestScript)
  565. {
  566. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  567. if (is_file($manifestScriptFile))
  568. {
  569. // Load the file
  570. include_once $manifestScriptFile;
  571. }
  572. // Set the class name
  573. $classname = $element . 'InstallerScript';
  574. if (class_exists($classname))
  575. {
  576. // Create a new instance
  577. $this->parent->manifestClass = new $classname($this);
  578. // And set this so we can copy it later
  579. $this->set('manifest_script', $manifestScript);
  580. }
  581. }
  582. // Run preflight if possible (since we know we're not an update)
  583. ob_start();
  584. ob_implicit_flush(false);
  585. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  586. {
  587. if ($this->parent->manifestClass->preflight('update', $this) === false)
  588. {
  589. // Install failed, rollback changes
  590. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  591. return false;
  592. }
  593. }
  594. // Create msg object; first use here
  595. $msg = ob_get_contents();
  596. ob_end_clean();
  597. /**
  598. * ---------------------------------------------------------------------------------------------
  599. * Filesystem Processing Section
  600. * ---------------------------------------------------------------------------------------------
  601. */
  602. // If the component directory does not exist, let's create it
  603. $created = false;
  604. if (!file_exists($this->parent->getPath('extension_site')))
  605. {
  606. if (!$created = JFolder::create($this->parent->getPath('extension_site')))
  607. {
  608. JLog::add(
  609. JText::sprintf('JLIB_INSTALLER_ERROR_COMP_UPDATE_FAILED_TO_CREATE_DIRECTORY_SITE', $this->parent->getPath('extension_site')),
  610. JLog::WARNING, 'jerror'
  611. );
  612. return false;
  613. }
  614. }
  615. /*
  616. * Since we created the component directory and will want to remove it if we have to roll back
  617. * the installation, lets add it to the installation step stack
  618. */
  619. if ($created)
  620. {
  621. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site')));
  622. }
  623. // If the component admin directory does not exist, let's create it
  624. $created = false;
  625. if (!file_exists($this->parent->getPath('extension_administrator')))
  626. {
  627. if (!$created = JFolder::create($this->parent->getPath('extension_administrator')))
  628. {
  629. JLog::add(
  630. JText::sprintf('JLIB_INSTALLER_ERROR_COMP_UPDATE_FAILED_TO_CREATE_DIRECTORY_ADMIN', $this->parent->getPath('extension_administrator')),
  631. JLog::WARNING, 'jerror'
  632. );
  633. // Install failed, rollback any changes
  634. $this->parent->abort();
  635. return false;
  636. }
  637. }
  638. /*
  639. * Since we created the component admin directory and we will want to remove it if we have to roll
  640. * back the installation, let's add it to the installation step stack
  641. */
  642. if ($created)
  643. {
  644. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_administrator')));
  645. }
  646. // Find files to copy
  647. if ($this->manifest->files)
  648. {
  649. if ($this->parent->parseFiles($this->manifest->files, 0, $this->oldFiles) === false)
  650. {
  651. // Install failed, rollback any changes
  652. $this->parent->abort();
  653. return false;
  654. }
  655. }
  656. if ($this->manifest->administration->files)
  657. {
  658. if ($this->parent->parseFiles($this->manifest->administration->files, 1, $this->oldAdminFiles) === false)
  659. {
  660. // Install failed, rollback any changes
  661. $this->parent->abort();
  662. return false;
  663. }
  664. }
  665. // Parse optional tags
  666. $this->parent->parseMedia($this->manifest->media);
  667. $this->parent->parseLanguages($this->manifest->languages);
  668. $this->parent->parseLanguages($this->manifest->administration->languages, 1);
  669. // If there is a manifest script, let's copy it.
  670. if ($this->get('manifest_script'))
  671. {
  672. $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script');
  673. $path['dest'] = $this->parent->getPath('extension_administrator') . '/' . $this->get('manifest_script');
  674. if (!file_exists($path['dest']) || $this->parent->isOverwrite())
  675. {
  676. if (!$this->parent->copyFiles(array($path)))
  677. {
  678. // Install failed, rollback changes
  679. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_UPDATE_MANIFEST'));
  680. return false;
  681. }
  682. }
  683. }
  684. /**
  685. * ---------------------------------------------------------------------------------------------
  686. * Database Processing Section
  687. * ---------------------------------------------------------------------------------------------
  688. */
  689. // Let's run the update queries for the component
  690. $row = JTable::getInstance('extension');
  691. $eid = $row->find(array('element' => strtolower($this->get('element')), 'type' => 'component'));
  692. if ($this->manifest->update)
  693. {
  694. $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $eid);
  695. if ($result === false)
  696. {
  697. // Install failed, rollback changes
  698. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_UPDATE_SQL_ERROR', $db->stderr(true)));
  699. return false;
  700. }
  701. }
  702. // Time to build the admin menus
  703. if (!$this->_buildAdminMenus($eid))
  704. {
  705. JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_BUILDADMINMENUS_FAILED'), JLog::WARNING, 'jerror');
  706. // $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
  707. // Return false;
  708. }
  709. /**
  710. * ---------------------------------------------------------------------------------------------
  711. * Custom Installation Script Section
  712. * ---------------------------------------------------------------------------------------------
  713. */
  714. /*
  715. * If we have an install script, let's include it, execute the custom
  716. * update method, and append the return value from the custom update
  717. * method to the installation message.
  718. */
  719. ob_start();
  720. ob_implicit_flush(false);
  721. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
  722. {
  723. if ($this->parent->manifestClass->update($this) === false)
  724. {
  725. // Install failed, rollback changes
  726. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  727. return false;
  728. }
  729. }
  730. // Append messages
  731. $msg .= ob_get_contents();
  732. ob_end_clean();
  733. /**
  734. * ---------------------------------------------------------------------------------------------
  735. * Finalization and Cleanup Section
  736. * ---------------------------------------------------------------------------------------------
  737. */
  738. // Clobber any possible pending updates
  739. $update = JTable::getInstance('update');
  740. $uid = $update->find(array('element' => $this->get('element'), 'type' => 'component', 'client_id' => 1, 'folder' => ''));
  741. if ($uid)
  742. {
  743. $update->delete($uid);
  744. }
  745. // Update an entry to the extension table
  746. if ($eid)
  747. {
  748. $row->load($eid);
  749. }
  750. else
  751. {
  752. // Set the defaults
  753. // There is no folder for components
  754. $row->folder = '';
  755. $row->enabled = 1;
  756. $row->protected = 0;
  757. $row->access = 1;
  758. $row->client_id = 1;
  759. $row->params = $this->parent->getParams();
  760. }
  761. $row->name = $this->get('name');
  762. $row->type = 'component';
  763. $row->element = $this->get('element');
  764. $row->manifest_cache = $this->parent->generateManifestCache();
  765. if (!$row->store())
  766. {
  767. // Install failed, roll back changes
  768. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_UPDATE_ROLLBACK', $db->stderr(true)));
  769. return false;
  770. }
  771. // We will copy the manifest file to its appropriate place.
  772. if (!$this->parent->copyManifest())
  773. {
  774. // Install failed, rollback changes
  775. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_UPDATE_COPY_SETUP'));
  776. return false;
  777. }
  778. // And now we run the postflight
  779. ob_start();
  780. ob_implicit_flush(false);
  781. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
  782. {
  783. $this->parent->manifestClass->postflight('update', $this);
  784. }
  785. // Append messages
  786. $msg .= ob_get_contents();
  787. ob_end_clean();
  788. if ($msg != '')
  789. {
  790. $this->parent->set('extension_message', $msg);
  791. }
  792. return $row->extension_id;
  793. }
  794. /**
  795. * Custom uninstall method for components
  796. *
  797. * @param integer $id The unique extension id of the component to uninstall
  798. *
  799. * @return mixed Return value for uninstall method in component uninstall file
  800. *
  801. * @since 11.1
  802. */
  803. public function uninstall($id)
  804. {
  805. $db = $this->parent->getDbo();
  806. $row = null;
  807. $retval = true;
  808. // First order of business will be to load the component object table from the database.
  809. // This should give us the necessary information to proceed.
  810. $row = JTable::getInstance('extension');
  811. if (!$row->load((int) $id))
  812. {
  813. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_ERRORUNKOWNEXTENSION'), JLog::WARNING, 'jerror');
  814. return false;
  815. }
  816. // Is the component we are trying to uninstall a core one?
  817. // Because that is not a good idea...
  818. if ($row->protected)
  819. {
  820. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_WARNCORECOMPONENT'), JLog::WARNING, 'jerror');
  821. return false;
  822. }
  823. // Get the admin and site paths for the component
  824. $this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $row->element));
  825. $this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $row->element));
  826. // Copy the admin path as it's used as a common base
  827. $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
  828. /**
  829. * ---------------------------------------------------------------------------------------------
  830. * Manifest Document Setup Section
  831. * ---------------------------------------------------------------------------------------------
  832. */
  833. // Find and load the XML install file for the component
  834. $this->parent->setPath('source', $this->parent->getPath('extension_administrator'));
  835. // Get the package manifest object
  836. // We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file
  837. $this->parent->findManifest();
  838. $this->manifest = $this->parent->getManifest();
  839. if (!$this->manifest)
  840. {
  841. // Make sure we delete the folders if no manifest exists
  842. JFolder::delete($this->parent->getPath('extension_administrator'));
  843. JFolder::delete($this->parent->getPath('extension_site'));
  844. // Remove the menu
  845. $this->_removeAdminMenus($row);
  846. // Raise a warning
  847. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_ERRORREMOVEMANUALLY'), JLog::WARNING, 'jerror');
  848. // Return
  849. return false;
  850. }
  851. // Set the extensions name
  852. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  853. if (substr($name, 0, 4) == 'com_')
  854. {
  855. $element = $name;
  856. }
  857. else
  858. {
  859. $element = 'com_' . $name;
  860. }
  861. $this->set('name', $name);
  862. $this->set('element', $element);
  863. // Attempt to load the admin language file; might have uninstall strings
  864. $this->loadLanguage(JPATH_ADMINISTRATOR . '/components/' . $element);
  865. /**
  866. * ---------------------------------------------------------------------------------------------
  867. * Installer Trigger Loading and Uninstall
  868. * ---------------------------------------------------------------------------------------------
  869. */
  870. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  871. $scriptFile = (string) $this->manifest->scriptfile;
  872. if ($scriptFile)
  873. {
  874. $manifestScriptFile = $this->parent->getPath('source') . '/' . $scriptFile;
  875. if (is_file($manifestScriptFile))
  876. {
  877. // Load the file
  878. include_once $manifestScriptFile;
  879. }
  880. // Set the class name
  881. $classname = $row->element . 'InstallerScript';
  882. if (class_exists($classname))
  883. {
  884. // Create a new instance
  885. $this->parent->manifestClass = new $classname($this);
  886. // And set this so we can copy it later
  887. $this->set('manifest_script', $scriptFile);
  888. }
  889. }
  890. ob_start();
  891. ob_implicit_flush(false);
  892. // Run uninstall if possible
  893. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall'))
  894. {
  895. $this->parent->manifestClass->uninstall($this);
  896. }
  897. $msg = ob_get_contents();
  898. ob_end_clean();
  899. if ($msg != '')
  900. {
  901. $this->parent->set('extension_message', $msg);
  902. }
  903. /**
  904. * ---------------------------------------------------------------------------------------------
  905. * Database Processing Section
  906. * ---------------------------------------------------------------------------------------------
  907. */
  908. // Let's run the uninstall queries for the component
  909. if (isset($this->manifest->uninstall->sql))
  910. {
  911. $result = $this->parent->parseSQLFiles($this->manifest->uninstall->sql);
  912. if ($result === false)
  913. {
  914. // Install failed, rollback changes
  915. JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_SQL_ERROR', $db->stderr(true)), JLog::WARNING, 'jerror');
  916. $retval = false;
  917. }
  918. }
  919. $this->_removeAdminMenus($row);
  920. /**
  921. * ---------------------------------------------------------------------------------------------
  922. * Filesystem Processing Section
  923. * ---------------------------------------------------------------------------------------------
  924. */
  925. // Let's remove those language files and media in the JROOT/images/ folder that are
  926. // associated with the component we are uninstalling
  927. $this->parent->removeFiles($this->manifest->media);
  928. $this->parent->removeFiles($this->manifest->languages);
  929. $this->parent->removeFiles($this->manifest->administration->languages, 1);
  930. // Remove the schema version
  931. $query = $db->getQuery(true);
  932. $query->delete()->from('#__schemas')->where('extension_id = ' . $id);
  933. $db->setQuery($query);
  934. $db->execute();
  935. // Remove the component container in the assets table.
  936. $asset = JTable::getInstance('Asset');
  937. if ($asset->loadByName($element))
  938. {
  939. $asset->delete();
  940. }
  941. // Remove categories for this component
  942. $query = $db->getQuery(true);
  943. $query->delete()->from('#__categories')->where('extension=' . $db->quote($element), 'OR')
  944. ->where('extension LIKE ' . $db->quote($element . '.%'));
  945. $db->setQuery($query);
  946. $db->execute();
  947. // Clobber any possible pending updates
  948. $update = JTable::getInstance('update');
  949. $uid = $update->find(array('element' => $row->element, 'type' => 'component', 'client_id' => 1, 'folder' => ''));
  950. if ($uid)
  951. {
  952. $update->delete($uid);
  953. }
  954. // Now we need to delete the installation directories. This is the final step in uninstalling the component.
  955. if (trim($row->element))
  956. {
  957. // Delete the component site directory
  958. if (is_dir($this->parent->getPath('extension_site')))
  959. {
  960. if (!JFolder::delete($this->parent->getPath('extension_site')))
  961. {
  962. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_SITE'), JLog::WARNING, 'jerror');
  963. $retval = false;
  964. }
  965. }
  966. // Delete the component admin directory
  967. if (is_dir($this->parent->getPath('extension_administrator')))
  968. {
  969. if (!JFolder::delete($this->parent->getPath('extension_administrator')))
  970. {
  971. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_ADMIN'), JLog::WARNING, 'jerror');
  972. $retval = false;
  973. }
  974. }
  975. // Now we will no longer need the extension object, so let's delete it and free up memory
  976. $row->delete($row->extension_id);
  977. unset($row);
  978. return $retval;
  979. }
  980. else
  981. {
  982. // No component option defined... cannot delete what we don't know about
  983. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_NO_OPTION'), JLog::WARNING, 'jerror');
  984. return false;
  985. }
  986. }
  987. /**
  988. * Method to build menu database entries for a component
  989. *
  990. * @return boolean True if successful
  991. *
  992. * @since 11.1
  993. */
  994. protected function _buildAdminMenus()
  995. {
  996. $db = $this->parent->getDbo();
  997. $table = JTable::getInstance('menu');
  998. $option = $this->get('element');
  999. // If a component exists with this option in the table then we don't need to add menus
  1000. $query = $db->getQuery(true);
  1001. $query->select('m.id, e.extension_id');
  1002. $query->from('#__menu AS m');
  1003. $query->leftJoin('#__extensions AS e ON m.component_id = e.extension_id');
  1004. $query->where('m.parent_id = 1');
  1005. $query->where('m.client_id = 1');
  1006. $query->where('e.element = ' . $db->quote($option));
  1007. $db->setQuery($query);
  1008. $componentrow = $db->loadObject();
  1009. // Check if menu items exist
  1010. if ($componentrow)
  1011. {
  1012. // Don't do anything if overwrite has not been enabled
  1013. if (!$this->parent->isOverwrite())
  1014. {
  1015. return true;
  1016. }
  1017. // Remove existing menu items if overwrite has been enabled
  1018. if ($option)
  1019. {
  1020. // If something goes wrong, there's no way to rollback TODO: Search for better solution
  1021. $this->_removeAdminMenus($componentrow);
  1022. }
  1023. $component_id = $componentrow->extension_id;
  1024. }
  1025. else
  1026. {
  1027. // Lets find the extension id
  1028. $query->clear();
  1029. $query->select('e.extension_id');
  1030. $query->from('#__extensions AS e');
  1031. $query->where('e.element = ' . $db->quote($option));
  1032. $db->setQuery($query);
  1033. // TODO Find Some better way to discover the component_id
  1034. $component_id = $db->loadResult();
  1035. }
  1036. // Ok, now its time to handle the menus. Start with the component root menu, then handle submenus.
  1037. $menuElement = $this->manifest->administration->menu;
  1038. if ($menuElement)
  1039. {
  1040. $data = array();
  1041. $data['menutype'] = 'main';
  1042. $data['client_id'] = 1;
  1043. $data['title'] = (string) $menuElement;
  1044. $data['alias'] = (string) $menuElement;
  1045. $data['link'] = 'index.php?option=' . $option;
  1046. $data['type'] = 'component';
  1047. $data['published'] = 0;
  1048. $data['parent_id'] = 1;
  1049. $data['component_id'] = $component_id;
  1050. $data['img'] = ((string) $menuElement->attributes()->img) ? (string) $menuElement->attributes()->img : 'class:component';
  1051. $data['home'] = 0;
  1052. try
  1053. {
  1054. $table->setLocation(1, 'last-child');
  1055. }
  1056. catch (InvalidArgumentException $e)
  1057. {
  1058. JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
  1059. return false;
  1060. }
  1061. if (!$table->bind($data) || !$table->check() || !$table->store())
  1062. {
  1063. // Install failed, warn user and rollback changes
  1064. JLog::add($table->getError(), JLog::WARNING, 'jerror');
  1065. return false;
  1066. }
  1067. /*
  1068. * Since we have created a menu item, we add it to the installation step stack
  1069. * so that if we have to rollback the changes we can undo it.
  1070. */
  1071. $this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
  1072. }
  1073. // No menu element was specified, Let's make a generic menu item
  1074. else
  1075. {
  1076. $data = array();
  1077. $data['menutype'] = 'main';
  1078. $data['client_id'] = 1;
  1079. $data['title'] = $option;
  1080. $data['alias'] = $option;
  1081. $data['link'] = 'index.php?option=' . $option;
  1082. $data['type'] = 'component';
  1083. $data['published'] = 0;
  1084. $data['parent_id'] = 1;
  1085. $data['component_id'] = $component_id;
  1086. $data['img'] = 'class:component';
  1087. $data['home'] = 0;
  1088. try
  1089. {
  1090. $table->setLocation(1, 'last-child');
  1091. }
  1092. catch (InvalidArgumentException $e)
  1093. {
  1094. JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
  1095. return false;
  1096. }
  1097. if (!$table->bind($data) || !$table->check() || !$table->store())
  1098. {
  1099. // Install failed, warn user and rollback changes
  1100. JLog::add($table->getError(), JLog::WARNING, 'jerror');
  1101. return false;
  1102. }
  1103. /*
  1104. * Since we have created a menu item, we add it to the installation step stack
  1105. * so that if we have to rollback the changes we can undo it.
  1106. */
  1107. $this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
  1108. }
  1109. /*
  1110. * Process SubMenus
  1111. */
  1112. if (!$this->manifest->administration->submenu)
  1113. {
  1114. return true;
  1115. }
  1116. $parent_id = $table->id;
  1117. foreach ($this->manifest->administration->submenu->menu as $child)
  1118. {
  1119. $data = array();
  1120. $data['menutype'] = 'main';
  1121. $data['client_id'] = 1;
  1122. $data['title'] = (string) $child;
  1123. $data['alias'] = (string) $child;
  1124. $data['type'] = 'component';
  1125. $data['published'] = 0;
  1126. $data['parent_id'] = $parent_id;
  1127. $data['component_id'] = $component_id;
  1128. $data['img'] = ((string) $child->attributes()->img) ? (string) $child->attributes()->img : 'class:component';
  1129. $data['home'] = 0;
  1130. // Set the sub menu link
  1131. if ((string) $child->attributes()->link)
  1132. {
  1133. $data['link'] = 'index.php?' . $child->attributes()->link;
  1134. }
  1135. else
  1136. {
  1137. $request = array();
  1138. if ((string) $child->attributes()->act)
  1139. {
  1140. $request[] = 'act=' . $child->attributes()->act;
  1141. }
  1142. if ((string) $child->attributes()->task)
  1143. {
  1144. $request[] = 'task=' . $child->attributes()->task;
  1145. }
  1146. if ((string) $child->attributes()->controller)
  1147. {
  1148. $request[] = 'controller=' . $child->attributes()->controller;
  1149. }
  1150. if ((string) $child->attributes()->view)
  1151. {
  1152. $request[] = 'view=' . $child->attributes()->view;
  1153. }
  1154. if ((string) $child->attributes()->layout)
  1155. {
  1156. $request[] = 'layout=' . $child->attributes()->layout;
  1157. }
  1158. if ((string) $child->attributes()->sub)
  1159. {
  1160. $request[] = 'sub=' . $child->attributes()->sub;
  1161. }
  1162. $qstring = (count($request)) ? '&' . implode('&', $request) : '';
  1163. $data['link'] = 'index.php?option=' . $option . $qstring;
  1164. }
  1165. $table = JTable::getInstance('menu');
  1166. try
  1167. {
  1168. $table->setLocation($parent_id, 'last-child');
  1169. }
  1170. catch (InvalidArgumentException $e)
  1171. {
  1172. return false;
  1173. }
  1174. if (!$table->bind($data) || !$table->check() || !$table->store())
  1175. {
  1176. // Install failed, rollback changes
  1177. return false;
  1178. }
  1179. /*
  1180. * Since we have created a menu item, we add it to the installation step stack
  1181. * so that if we have to rollback the changes we can undo it.
  1182. */
  1183. $this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
  1184. }
  1185. return true;
  1186. }
  1187. /**
  1188. * Method to remove admin menu references to a component
  1189. *
  1190. * @param object &$row Component table object.
  1191. *
  1192. * @return boolean True if successful.
  1193. *
  1194. * @since 11.1
  1195. */
  1196. protected function _removeAdminMenus(&$row)
  1197. {
  1198. $db = $this->parent->getDbo();
  1199. $table = JTable::getInstance('menu');
  1200. $id = $row->extension_id;
  1201. // Get the ids of the menu items
  1202. $query = $db->getQuery(true);
  1203. $query->select('id');
  1204. $query->from('#__menu');
  1205. $query->where($query->qn('client_id') . ' = 1');
  1206. $query->where($query->qn('component_id') . ' = ' . (int) $id);
  1207. $db->setQuery($query);
  1208. $ids = $db->loadColumn();
  1209. // Check for error
  1210. if (!empty($ids))
  1211. {
  1212. // Iterate the items to delete each one.
  1213. foreach ($ids as $menuid)
  1214. {
  1215. if (!$table->delete((int) $menuid))
  1216. {
  1217. $this->setError($table->getError());
  1218. return false;
  1219. }
  1220. }
  1221. // Rebuild the whole tree
  1222. $table->rebuild();
  1223. }
  1224. return true;
  1225. }
  1226. /**
  1227. * Custom rollback method
  1228. * - Roll back the component menu item
  1229. *
  1230. * @param array $step Installation step to rollback.
  1231. *
  1232. * @return boolean True on success
  1233. *
  1234. * @since 11.1
  1235. */
  1236. protected function _rollback_menu($step)
  1237. {
  1238. return $this->_removeAdminMenus((object) array('extension_id' => $step['id']));
  1239. }
  1240. /**
  1241. * Discover unregistered extensions.
  1242. *
  1243. * @return array A list of extensions.
  1244. *
  1245. * @since 11.1
  1246. */
  1247. public function discover()
  1248. {
  1249. $results = array();
  1250. $site_components = JFolder::folders(JPATH_SITE . '/components');
  1251. $admin_components = JFolder::folders(JPATH_ADMINISTRATOR . '/components');
  1252. foreach ($site_components as $component)
  1253. {
  1254. if (file_exists(JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'))
  1255. {
  1256. $manifest_details = JInstaller::parseXMLInstallFile(
  1257. JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'
  1258. );
  1259. $extension = JTable::getInstance('extension');
  1260. $extension->set('type', 'component');
  1261. $extension->set('client_id', 0);
  1262. $extension->set('element', $component);
  1263. $extension->set('name', $component);
  1264. $extension->set('state', -1);
  1265. $extension->set('manifest_cache', json_encode($manifest_details));
  1266. $results[] = $extension;
  1267. }
  1268. }
  1269. foreach ($admin_components as $component)
  1270. {
  1271. if (file_exists(JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'))
  1272. {
  1273. $manifest_details = JInstaller::parseXMLInstallFile(
  1274. JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'
  1275. );
  1276. $extension = JTable::getInstance('extension');
  1277. $extension->set('type', 'component');
  1278. $extension->set('client_id', 1);
  1279. $extension->set('element', $component);
  1280. $extension->set('name', $component);
  1281. $extension->set('state', -1);
  1282. $extension->set('manifest_cache', json_encode($manifest_details));
  1283. $results[] = $extension;
  1284. }
  1285. }
  1286. return $results;
  1287. }
  1288. /**
  1289. * Install unregistered extensions that have been discovered.
  1290. *
  1291. * @return mixed
  1292. *
  1293. * @since 11.1
  1294. */
  1295. public function discover_install()
  1296. {
  1297. // Need to find to find where the XML file is since we don't store this normally
  1298. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  1299. $short_element = str_replace('com_', '', $this->parent->extension->element);
  1300. $manifestPath = $client->path . '/components/' . $this->parent->extension->element . '/' . $short_element . '.xml';
  1301. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  1302. $this->parent->setPath('manifest', $manifestPath);
  1303. $this->parent->setPath('source', $client->path . '/components/' . $this->parent->extension->element);
  1304. $this->parent->setPath('extension_root', $this->parent->getPath('source'));
  1305. $manifest_details = JInstaller::parseXMLInstallFile($this->parent->getPath('manifest'));
  1306. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  1307. $this->parent->extension->state = 0;
  1308. $this->parent->extension->name = $manifest_details['name'];
  1309. $this->parent->extension->enabled = 1;
  1310. $this->parent->extension->params = $this->parent->getParams();
  1311. try
  1312. {
  1313. $this->parent->extension->store();
  1314. }
  1315. catch (RuntimeException $e)
  1316. {
  1317. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_DISCOVER_STORE_DETAILS'), JLog::WARNING, 'jerror');
  1318. return false;
  1319. }
  1320. // Now we need to run any SQL it has, languages, media or menu stuff
  1321. // Get a database connector object
  1322. $db = $this->parent->getDbo();
  1323. // Get the extension manifest object
  1324. $this->manifest = $this->parent->getManifest();
  1325. /**
  1326. * ---------------------------------------------------------------------------------------------
  1327. * Manifest Document Setup Section
  1328. * ---------------------------------------------------------------------------------------------
  1329. */
  1330. // Set the extensions name
  1331. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  1332. if (substr($name, 0, 4) == 'com_')
  1333. {
  1334. $element = $name;
  1335. }
  1336. else
  1337. {
  1338. $element = 'com_' . $name;
  1339. }
  1340. $this->set('name', $name);
  1341. $this->set('element', $element);
  1342. // Get the component description
  1343. $description = (string) $this->manifest->description;
  1344. if ($description)
  1345. {
  1346. $this->parent->set('message', JText::_((string) $description));
  1347. }
  1348. else
  1349. {
  1350. $this->parent->set('message', '');
  1351. }
  1352. // Set the installation target paths
  1353. $this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
  1354. $this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
  1355. // Copy the admin path as it's used as a common base
  1356. $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
  1357. /**
  1358. * ---------------------------------------------------------------------------------------------
  1359. * Basic Checks Section
  1360. * ---------------------------------------------------------------------------------------------
  1361. */
  1362. // Make sure that we have an admin element
  1363. if (!$this->manifest->administration)
  1364. {
  1365. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
  1366. return false;
  1367. }
  1368. /**
  1369. * ---------------------------------------------------------------------------------------------
  1370. * Installer Trigger Loading
  1371. * ---------------------------------------------------------------------------------------------
  1372. */
  1373. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  1374. $manifestScript = (string) $this->manifest->scriptfile;
  1375. if ($manifestScript)
  1376. {
  1377. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  1378. if (is_file($manifestScriptFile))
  1379. {
  1380. // Load the file
  1381. include_once $manifestScriptFile;
  1382. }
  1383. // Set the class name
  1384. $classname = $element . 'InstallerScript';
  1385. if (class_exists($classname))
  1386. {
  1387. // Create a new instance
  1388. $this->parent->manifestClass = new $classname($this);
  1389. // And set this so we can copy it later
  1390. $this->set('manifest_script', $manifestScript);
  1391. }
  1392. }
  1393. // Run preflight if possible (since we know we're not an update)
  1394. ob_start();
  1395. ob_implicit_flush(false);
  1396. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  1397. {
  1398. if ($this->parent->manifestClass->preflight('discover_install', $this) === false)
  1399. {
  1400. // Install failed, rollback changes
  1401. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  1402. return false;
  1403. }
  1404. }
  1405. // Create msg object; first use here
  1406. $msg = ob_get_contents();
  1407. ob_end_clean();
  1408. /*
  1409. *
  1410. * Normally we would copy files and create directories, lets skip to the optional files
  1411. * Note: need to dereference things!
  1412. * Parse optional tags
  1413. * @todo remove code: $this->parent->parseMedia($this->manifest->media);
  1414. *
  1415. * We don't do language because 1.6 suggests moving to extension based languages
  1416. * @todo remove code: $this->parent->parseLanguages($this->manifest->languages);
  1417. * @todo remove code: $this->parent->parseLanguages($this->manifest->administration->languages, 1);
  1418. */
  1419. /**
  1420. * ---------------------------------------------------------------------------------------------
  1421. * Database Processing Section
  1422. * ---------------------------------------------------------------------------------------------
  1423. */
  1424. // Let's run the install queries for the component
  1425. if (isset($this->manifest->install->sql))
  1426. {
  1427. $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql);
  1428. if ($utfresult === false)
  1429. {
  1430. // Install failed, rollback changes
  1431. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_SQL_ERROR', $db->stderr(true)));
  1432. return false;
  1433. }
  1434. }
  1435. // Time to build the admin menus
  1436. if (!$this->_buildAdminMenus($this->parent->extension->extension_id))
  1437. {
  1438. JLog::add(JText::_('JLIB_INSTALLER_ABORT_COMP_BUILDADMINMENUS_FAILED'), JLog::WARNING, 'jerror');
  1439. // @todo remove code: $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $db->stderr(true)));
  1440. // @todo remove code: return false;
  1441. }
  1442. /**
  1443. * ---------------------------------------------------------------------------------------------
  1444. * Custom Installation Script Section
  1445. * ---------------------------------------------------------------------------------------------
  1446. */
  1447. /*
  1448. * If we have an install script, lets include it, execute the custom
  1449. * discover_install method, and append the return value from the custom discover_install
  1450. * method to the installation message.
  1451. */
  1452. ob_start();
  1453. ob_implicit_flush(false);
  1454. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'install'))
  1455. {
  1456. if ($this->parent->manifestClass->install($this) === false)
  1457. {
  1458. // Install failed, rollback changes
  1459. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  1460. return false;
  1461. }
  1462. }
  1463. // Append messages
  1464. $msg .= ob_get_contents();
  1465. ob_end_clean();
  1466. /**
  1467. * ---------------------------------------------------------------------------------------------
  1468. * Finalization and Cleanup Section
  1469. * ---------------------------------------------------------------------------------------------
  1470. */
  1471. // Clobber any possible pending updates
  1472. $update = JTable::getInstance('update');
  1473. $uid = $update->find(array('element' => $this->get('element'), 'type' => 'component', 'client_id' => 1, 'folder' => ''));
  1474. if ($uid)
  1475. {
  1476. $update->delete($uid);
  1477. }
  1478. // And now we run the postflight
  1479. ob_start();
  1480. ob_implicit_flush(false);
  1481. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
  1482. {
  1483. $this->parent->manifestClass->postflight('discover_install', $this);
  1484. }
  1485. // Append messages
  1486. $msg .= ob_get_contents();
  1487. ob_end_clean();
  1488. if ($msg != '')
  1489. {
  1490. $this->parent->set('extension_message', $msg);
  1491. }
  1492. return $this->parent->extension->extension_id;
  1493. }
  1494. /**
  1495. * Refreshes the extension table cache
  1496. *
  1497. * @return boolean Result of operation, true if updated, false on failure
  1498. *
  1499. * @since 11.1
  1500. */
  1501. public function refreshManifestCache()
  1502. {
  1503. // Need to find to find where the XML file is since we don't store this normally
  1504. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  1505. $short_element = str_replace('com_', '', $this->parent->extension->element);
  1506. $manifestPath = $client->path . '/components/' . $this->parent->extension->element . '/' . $short_element . '.xml';
  1507. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  1508. $this->parent->setPath('manifest', $manifestPath);
  1509. $manifest_details = JInstaller::parseXMLInstallFile($this->parent->getPath('manifest'));
  1510. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  1511. $this->parent->extension->name = $manifest_details['name'];
  1512. try
  1513. {
  1514. return $this->parent->extension->store();
  1515. }
  1516. catch (RuntimeException $e)
  1517. {
  1518. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_REFRESH_MANIFEST_CACHE'), JLog::WARNING, 'jerror');
  1519. return false;
  1520. }
  1521. }
  1522. }