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

/libraries/cms/installer/adapter/component.php

https://bitbucket.org/pastor399/newcastleunifc
PHP | 1746 lines | 991 code | 297 blank | 458 comment | 156 complexity | b682de1c182a63464f9dbbe1fd45e870 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Libraries
  4. * @subpackage Installer
  5. *
  6. * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. jimport('joomla.base.adapterinstance');
  11. jimport('joomla.filesystem.folder');
  12. /**
  13. * Component installer
  14. *
  15. * @package Joomla.Libraries
  16. * @subpackage Installer
  17. * @since 3.1
  18. */
  19. class JInstallerAdapterComponent extends JAdapterInstance
  20. {
  21. /**
  22. * Copy of the XML manifest file
  23. *
  24. * @var string
  25. * @since 3.1
  26. */
  27. protected $manifest = null;
  28. /**
  29. * Name of the extension
  30. *
  31. * @var string
  32. * @since 3.1
  33. * */
  34. protected $name = null;
  35. /**
  36. * The unique identifier for the extension (e.g. mod_login)
  37. *
  38. * @var string
  39. * @since 3.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 3.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 3.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 3.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 3.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 3.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 3.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 3.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 3.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. ->delete('#__schemas')
  933. ->where('extension_id = ' . $id);
  934. $db->setQuery($query);
  935. $db->execute();
  936. // Remove the component container in the assets table.
  937. $asset = JTable::getInstance('Asset');
  938. if ($asset->loadByName($element))
  939. {
  940. $asset->delete();
  941. }
  942. // Remove categories for this component
  943. $query = $db->getQuery(true)
  944. ->delete('#__categories')
  945. ->where('extension=' . $db->quote($element), 'OR')
  946. ->where('extension LIKE ' . $db->quote($element . '.%'));
  947. $db->setQuery($query);
  948. $db->execute();
  949. // Clobber any possible pending updates
  950. $update = JTable::getInstance('update');
  951. $uid = $update->find(array('element' => $row->element, 'type' => 'component', 'client_id' => 1, 'folder' => ''));
  952. if ($uid)
  953. {
  954. $update->delete($uid);
  955. }
  956. // Now we need to delete the installation directories. This is the final step in uninstalling the component.
  957. if (trim($row->element))
  958. {
  959. // Delete the component site directory
  960. if (is_dir($this->parent->getPath('extension_site')))
  961. {
  962. if (!JFolder::delete($this->parent->getPath('extension_site')))
  963. {
  964. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_SITE'), JLog::WARNING, 'jerror');
  965. $retval = false;
  966. }
  967. }
  968. // Delete the component admin directory
  969. if (is_dir($this->parent->getPath('extension_administrator')))
  970. {
  971. if (!JFolder::delete($this->parent->getPath('extension_administrator')))
  972. {
  973. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_FAILED_REMOVE_DIRECTORY_ADMIN'), JLog::WARNING, 'jerror');
  974. $retval = false;
  975. }
  976. }
  977. // Now we will no longer need the extension object, so let's delete it and free up memory
  978. $row->delete($row->extension_id);
  979. unset($row);
  980. return $retval;
  981. }
  982. else
  983. {
  984. // No component option defined... cannot delete what we don't know about
  985. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_UNINSTALL_NO_OPTION'), JLog::WARNING, 'jerror');
  986. return false;
  987. }
  988. }
  989. /**
  990. * Method to build menu database entries for a component
  991. *
  992. * @return boolean True if successful
  993. *
  994. * @since 3.1
  995. */
  996. protected function _buildAdminMenus()
  997. {
  998. $db = $this->parent->getDbo();
  999. $table = JTable::getInstance('menu');
  1000. $option = $this->get('element');
  1001. // If a component exists with this option in the table then we don't need to add menus
  1002. $query = $db->getQuery(true)
  1003. ->select('m.id, e.extension_id')
  1004. ->from('#__menu AS m')
  1005. ->join('LEFT', '#__extensions AS e ON m.component_id = e.extension_id')
  1006. ->where('m.parent_id = 1')
  1007. ->where('m.client_id = 1')
  1008. ->where('e.element = ' . $db->quote($option));
  1009. $db->setQuery($query);
  1010. $componentrow = $db->loadObject();
  1011. // Check if menu items exist
  1012. if ($componentrow)
  1013. {
  1014. // Don't do anything if overwrite has not been enabled
  1015. if (!$this->parent->isOverwrite())
  1016. {
  1017. return true;
  1018. }
  1019. // Remove existing menu items if overwrite has been enabled
  1020. if ($option)
  1021. {
  1022. // If something goes wrong, there's no way to rollback TODO: Search for better solution
  1023. $this->_removeAdminMenus($componentrow);
  1024. }
  1025. $component_id = $componentrow->extension_id;
  1026. }
  1027. else
  1028. {
  1029. // Lets find the extension id
  1030. $query->clear()
  1031. ->select('e.extension_id')
  1032. ->from('#__extensions AS e')
  1033. ->where('e.element = ' . $db->quote($option));
  1034. $db->setQuery($query);
  1035. // TODO Find Some better way to discover the component_id
  1036. $component_id = $db->loadResult();
  1037. }
  1038. // Ok, now its time to handle the menus. Start with the component root menu, then handle submenus.
  1039. $menuElement = $this->manifest->administration->menu;
  1040. if ($menuElement)
  1041. {
  1042. $data = array();
  1043. $data['menutype'] = 'main';
  1044. $data['client_id'] = 1;
  1045. $data['title'] = (string) trim($menuElement);
  1046. $data['alias'] = (string) $menuElement;
  1047. $data['link'] = 'index.php?option=' . $option;
  1048. $data['type'] = 'component';
  1049. $data['published'] = 0;
  1050. $data['parent_id'] = 1;
  1051. $data['component_id'] = $component_id;
  1052. $data['img'] = ((string) $menuElement->attributes()->img) ? (string) $menuElement->attributes()->img : 'class:component';
  1053. $data['home'] = 0;
  1054. try
  1055. {
  1056. $table->setLocation(1, 'last-child');
  1057. }
  1058. catch (InvalidArgumentException $e)
  1059. {
  1060. JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
  1061. return false;
  1062. }
  1063. if (!$table->bind($data) || !$table->check() || !$table->store())
  1064. {
  1065. // The menu item already exists. Delete it and retry instead of throwing an error.
  1066. $query = $db->getQuery(true)
  1067. ->select('id')
  1068. ->from('#__menu')
  1069. ->where('menutype = ' . $db->quote('main'))
  1070. ->where('client_id = 1')
  1071. ->where('link = ' . $db->quote('index.php?option=' . $option))
  1072. ->where('type = ' . $db->quote('component'))
  1073. ->where('parent_id = 1')
  1074. ->where('home = 0');
  1075. $db->setQuery($query);
  1076. $menu_id = $db->loadResult();
  1077. if (!$menu_id)
  1078. {
  1079. // Oops! Could not get the menu ID. Go back and rollback changes.
  1080. JError::raiseWarning(1, $table->getError());
  1081. return false;
  1082. }
  1083. else
  1084. {
  1085. // Remove the old menu item
  1086. $query = $db->getQuery(true)
  1087. ->delete('#__menu')
  1088. ->where('id = ' . (int) $menu_id);
  1089. $db->setQuery($query);
  1090. $db->query();
  1091. // Retry creating the menu item
  1092. $table->setLocation(1, 'last-child');
  1093. if (!$table->bind($data) || !$table->check() || !$table->store())
  1094. {
  1095. // Install failed, warn user and rollback changes
  1096. JError::raiseWarning(1, $table->getError());
  1097. return false;
  1098. }
  1099. }
  1100. }
  1101. /*
  1102. * Since we have created a menu item, we add it to the installation step stack
  1103. * so that if we have to rollback the changes we can undo it.
  1104. */
  1105. $this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
  1106. }
  1107. // No menu element was specified, Let's make a generic menu item
  1108. else
  1109. {
  1110. $data = array();
  1111. $data['menutype'] = 'main';
  1112. $data['client_id'] = 1;
  1113. $data['title'] = $option;
  1114. $data['alias'] = $option;
  1115. $data['link'] = 'index.php?option=' . $option;
  1116. $data['type'] = 'component';
  1117. $data['published'] = 0;
  1118. $data['parent_id'] = 1;
  1119. $data['component_id'] = $component_id;
  1120. $data['img'] = 'class:component';
  1121. $data['home'] = 0;
  1122. try
  1123. {
  1124. $table->setLocation(1, 'last-child');
  1125. }
  1126. catch (InvalidArgumentException $e)
  1127. {
  1128. JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
  1129. return false;
  1130. }
  1131. if (!$table->bind($data) || !$table->check() || !$table->store())
  1132. {
  1133. // Install failed, warn user and rollback changes
  1134. JLog::add($table->getError(), JLog::WARNING, 'jerror');
  1135. return false;
  1136. }
  1137. /*
  1138. * Since we have created a menu item, we add it to the installation step stack
  1139. * so that if we have to rollback the changes we can undo it.
  1140. */
  1141. $this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
  1142. }
  1143. /*
  1144. * Process SubMenus
  1145. */
  1146. if (!$this->manifest->administration->submenu)
  1147. {
  1148. return true;
  1149. }
  1150. $parent_id = $table->id;
  1151. foreach ($this->manifest->administration->submenu->menu as $child)
  1152. {
  1153. $data = array();
  1154. $data['menutype'] = 'main';
  1155. $data['client_id'] = 1;
  1156. $data['title'] = (string) trim($child);
  1157. $data['alias'] = (string) $child;
  1158. $data['type'] = 'component';
  1159. $data['published'] = 0;
  1160. $data['parent_id'] = $parent_id;
  1161. $data['component_id'] = $component_id;
  1162. $data['img'] = ((string) $child->attributes()->img) ? (string) $child->attributes()->img : 'class:component';
  1163. $data['home'] = 0;
  1164. // Set the sub menu link
  1165. if ((string) $child->attributes()->link)
  1166. {
  1167. $data['link'] = 'index.php?' . $child->attributes()->link;
  1168. }
  1169. else
  1170. {
  1171. $request = array();
  1172. if ((string) $child->attributes()->act)
  1173. {
  1174. $request[] = 'act=' . $child->attributes()->act;
  1175. }
  1176. if ((string) $child->attributes()->task)
  1177. {
  1178. $request[] = 'task=' . $child->attributes()->task;
  1179. }
  1180. if ((string) $child->attributes()->controller)
  1181. {
  1182. $request[] = 'controller=' . $child->attributes()->controller;
  1183. }
  1184. if ((string) $child->attributes()->view)
  1185. {
  1186. $request[] = 'view=' . $child->attributes()->view;
  1187. }
  1188. if ((string) $child->attributes()->layout)
  1189. {
  1190. $request[] = 'layout=' . $child->attributes()->layout;
  1191. }
  1192. if ((string) $child->attributes()->sub)
  1193. {
  1194. $request[] = 'sub=' . $child->attributes()->sub;
  1195. }
  1196. $qstring = (count($request)) ? '&' . implode('&', $request) : '';
  1197. $data['link'] = 'index.php?option=' . $option . $qstring;
  1198. }
  1199. $table = JTable::getInstance('menu');
  1200. try
  1201. {
  1202. $table->setLocation($parent_id, 'last-child');
  1203. }
  1204. catch (InvalidArgumentException $e)
  1205. {
  1206. return false;
  1207. }
  1208. if (!$table->bind($data) || !$table->check() || !$table->store())
  1209. {
  1210. // Install failed, rollback changes
  1211. return false;
  1212. }
  1213. /*
  1214. * Since we have created a menu item, we add it to the installation step stack
  1215. * so that if we have to rollback the changes we can undo it.
  1216. */
  1217. $this->parent->pushStep(array('type' => 'menu', 'id' => $component_id));
  1218. }
  1219. return true;
  1220. }
  1221. /**
  1222. * Method to remove admin menu references to a component
  1223. *
  1224. * @param object &$row Component table object.
  1225. *
  1226. * @return boolean True if successful.
  1227. *
  1228. * @since 3.1
  1229. */
  1230. protected function _removeAdminMenus(&$row)
  1231. {
  1232. $db = $this->parent->getDbo();
  1233. $table = JTable::getInstance('menu');
  1234. $id = $row->extension_id;
  1235. // Get the ids of the menu items
  1236. $query = $db->getQuery(true)
  1237. ->select('id')
  1238. ->from('#__menu')
  1239. ->where($db->quoteName('client_id') . ' = 1')
  1240. ->where($db->quoteName('component_id') . ' = ' . (int) $id);
  1241. $db->setQuery($query);
  1242. $ids = $db->loadColumn();
  1243. // Check for error
  1244. if (!empty($ids))
  1245. {
  1246. // Iterate the items to delete each one.
  1247. foreach ($ids as $menuid)
  1248. {
  1249. if (!$table->delete((int) $menuid))
  1250. {
  1251. $this->setError($table->getError());
  1252. return false;
  1253. }
  1254. }
  1255. // Rebuild the whole tree
  1256. $table->rebuild();
  1257. }
  1258. return true;
  1259. }
  1260. /**
  1261. * Custom rollback method
  1262. * - Roll back the component menu item
  1263. *
  1264. * @param array $step Installation step to rollback.
  1265. *
  1266. * @return boolean True on success
  1267. *
  1268. * @since 3.1
  1269. */
  1270. protected function _rollback_menu($step)
  1271. {
  1272. return $this->_removeAdminMenus((object) array('extension_id' => $step['id']));
  1273. }
  1274. /**
  1275. * Discover unregistered extensions.
  1276. *
  1277. * @return array A list of extensions.
  1278. *
  1279. * @since 3.1
  1280. */
  1281. public function discover()
  1282. {
  1283. $results = array();
  1284. $site_components = JFolder::folders(JPATH_SITE . '/components');
  1285. $admin_components = JFolder::folders(JPATH_ADMINISTRATOR . '/components');
  1286. foreach ($site_components as $component)
  1287. {
  1288. if (file_exists(JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'))
  1289. {
  1290. $manifest_details = JInstaller::parseXMLInstallFile(
  1291. JPATH_SITE . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'
  1292. );
  1293. $extension = JTable::getInstance('extension');
  1294. $extension->set('type', 'component');
  1295. $extension->set('client_id', 0);
  1296. $extension->set('element', $component);
  1297. $extension->set('folder', '');
  1298. $extension->set('name', $component);
  1299. $extension->set('state', -1);
  1300. $extension->set('manifest_cache', json_encode($manifest_details));
  1301. $extension->set('params', '{}');
  1302. $results[] = $extension;
  1303. }
  1304. }
  1305. foreach ($admin_components as $component)
  1306. {
  1307. if (file_exists(JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'))
  1308. {
  1309. $manifest_details = JInstaller::parseXMLInstallFile(
  1310. JPATH_ADMINISTRATOR . '/components/' . $component . '/' . str_replace('com_', '', $component) . '.xml'
  1311. );
  1312. $extension = JTable::getInstance('extension');
  1313. $extension->set('type', 'component');
  1314. $extension->set('client_id', 1);
  1315. $extension->set('element', $component);
  1316. $extension->set('folder', '');
  1317. $extension->set('name', $component);
  1318. $extension->set('state', -1);
  1319. $extension->set('manifest_cache', json_encode($manifest_details));
  1320. $extension->set('params', '{}');
  1321. $results[] = $extension;
  1322. }
  1323. }
  1324. return $results;
  1325. }
  1326. /**
  1327. * Install unregistered extensions that have been discovered.
  1328. *
  1329. * @return mixed
  1330. *
  1331. * @since 3.1
  1332. */
  1333. public function discover_install()
  1334. {
  1335. // Need to find to find where the XML file is since we don't store this normally
  1336. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  1337. $short_element = str_replace('com_', '', $this->parent->extension->element);
  1338. $manifestPath = $client->path . '/components/' . $this->parent->extension->element . '/' . $short_element . '.xml';
  1339. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  1340. $this->parent->setPath('manifest', $manifestPath);
  1341. $this->parent->setPath('source', $client->path . '/components/' . $this->parent->extension->element);
  1342. $this->parent->setPath('extension_root', $this->parent->getPath('source'));
  1343. $manifest_details = JInstaller::parseXMLInstallFile($this->parent->getPath('manifest'));
  1344. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  1345. $this->parent->extension->state = 0;
  1346. $this->parent->extension->name = $manifest_details['name'];
  1347. $this->parent->extension->enabled = 1;
  1348. $this->parent->extension->params = $this->parent->getParams();
  1349. try
  1350. {
  1351. $this->parent->extension->store();
  1352. }
  1353. catch (RuntimeException $e)
  1354. {
  1355. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_DISCOVER_STORE_DETAILS'), JLog::WARNING, 'jerror');
  1356. return false;
  1357. }
  1358. // Now we need to run any SQL it has, languages, media or menu stuff
  1359. // Get a database connector object
  1360. $db = $this->parent->getDbo();
  1361. // Get the extension manifest object
  1362. $this->manifest = $this->parent->getManifest();
  1363. /**
  1364. * ---------------------------------------------------------------------------------------------
  1365. * Manifest Document Setup Section
  1366. * ---------------------------------------------------------------------------------------------
  1367. */
  1368. // Set the extensions name
  1369. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  1370. if (substr($name, 0, 4) == 'com_')
  1371. {
  1372. $element = $name;
  1373. }
  1374. else
  1375. {
  1376. $element = 'com_' . $name;
  1377. }
  1378. $this->set('name', $name);
  1379. $this->set('element', $element);
  1380. // Get the component description
  1381. $description = (string) $this->manifest->description;
  1382. if ($description)
  1383. {
  1384. $this->parent->set('message', JText::_((string) $description));
  1385. }
  1386. else
  1387. {
  1388. $this->parent->set('message', '');
  1389. }
  1390. // Set the installation target paths
  1391. $this->parent->setPath('extension_site', JPath::clean(JPATH_SITE . '/components/' . $this->get('element')));
  1392. $this->parent->setPath('extension_administrator', JPath::clean(JPATH_ADMINISTRATOR . '/components/' . $this->get('element')));
  1393. // Copy the admin path as it's used as a common base
  1394. $this->parent->setPath('extension_root', $this->parent->getPath('extension_administrator'));
  1395. /**
  1396. * ---------------------------------------------------------------------------------------------
  1397. * Basic Checks Section
  1398. * ---------------------------------------------------------------------------------------------
  1399. */
  1400. // Make sure that we have an admin element
  1401. if (!$this->manifest->administration)
  1402. {
  1403. JLog::add(JText::_('JLIB_INSTALLER_ERROR_COMP_INSTALL_ADMIN_ELEMENT'), JLog::WARNING, 'jerror');
  1404. return false;
  1405. }
  1406. /**
  1407. * ---------------------------------------------------------------------------------------------
  1408. * Installer Trigger Loading
  1409. * ---------------------------------------------------------------------------------------------
  1410. */
  1411. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  1412. $manifestScript = (string) $this->manifest->scriptfile;
  1413. if ($manifestScript)
  1414. {
  1415. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  1416. if (is_file($manifestScriptFile))
  1417. {
  1418. // Load the file
  1419. include_once $manifestScriptFile;
  1420. }
  1421. // Set the class name
  1422. $classname = $element . 'InstallerScript';
  1423. if (class_exists($classname))
  1424. {
  1425. // Create a new instance
  1426. $this->parent->manifestClass = new $classname($this);
  1427. // And set this so we can copy it later
  1428. $this->set('manifest_script', $manifestScript);
  1429. }
  1430. }
  1431. // Run preflight if possible (since we know we're not an update)
  1432. ob_start();
  1433. ob_implicit_flush(false);
  1434. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  1435. {
  1436. if ($this->parent->manifestClass->preflight('discover_install', $this) === false)
  1437. {
  1438. // Install failed, rollback changes
  1439. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_COMP_INSTALL_CUSTOM_INSTALL_FAILURE'));
  1440. return false;
  1441. }
  1442. }
  1443. // Create msg object; first use here
  1444. $msg = ob_get_contents();
  1445. ob_end_clean();
  1446. /*
  1447. *
  1448. * Normally we would copy files and create directories, lets skip to the optional files
  1449. * Note: need to dereferenc