PageRenderTime 60ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/joomla/installer/adapters/module.php

http://github.com/joomla/joomla-platform
PHP | 968 lines | 568 code | 148 blank | 252 comment | 73 complexity | 6f9cdcce3d1eaba83ad2630136e5e7fc MD5 | raw file
Possible License(s): GPL-2.0, 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. /**
  12. * Module installer
  13. *
  14. * @package Joomla.Platform
  15. * @subpackage Installer
  16. * @since 11.1
  17. */
  18. class JInstallerModule extends JAdapterInstance
  19. {
  20. /**
  21. * Install function routing
  22. *
  23. * @var string
  24. * @since 11.1
  25. */
  26. protected $route = 'Install';
  27. /**
  28. * @var
  29. * @since 11.1
  30. */
  31. protected $manifest = null;
  32. /**
  33. * @var
  34. * @since 11.1
  35. */
  36. protected $manifest_script = null;
  37. /**
  38. * Extension name
  39. *
  40. * @var
  41. * @since 11.1
  42. */
  43. protected $name = null;
  44. /**
  45. * @var
  46. * @since 11.1
  47. */
  48. protected $element = null;
  49. /**
  50. * @var string
  51. * @since 11.1
  52. */
  53. protected $scriptElement = null;
  54. /**
  55. * Custom loadLanguage method
  56. *
  57. * @param string $path The path where we find language files
  58. *
  59. * @return void
  60. *
  61. * @since 11.1
  62. */
  63. public function loadLanguage($path = null)
  64. {
  65. $source = $this->parent->getPath('source');
  66. if (!$source)
  67. {
  68. $this->parent
  69. ->setPath(
  70. 'source',
  71. ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $this->parent->extension->element
  72. );
  73. }
  74. $this->manifest = $this->parent->getManifest();
  75. if ($this->manifest->files)
  76. {
  77. $element = $this->manifest->files;
  78. $extension = '';
  79. if (count($element->children()))
  80. {
  81. foreach ($element->children() as $file)
  82. {
  83. if ((string) $file->attributes()->module)
  84. {
  85. $extension = strtolower((string) $file->attributes()->module);
  86. break;
  87. }
  88. }
  89. }
  90. if ($extension)
  91. {
  92. $lang = JFactory::getLanguage();
  93. $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $extension;
  94. $folder = (string) $element->attributes()->folder;
  95. if ($folder && file_exists("$path/$folder"))
  96. {
  97. $source = "$path/$folder";
  98. }
  99. $client = (string) $this->manifest->attributes()->client;
  100. $lang->load($extension . '.sys', $source, null, false, false)
  101. || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), null, false, false)
  102. || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false)
  103. || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), $lang->getDefault(), false, false);
  104. }
  105. }
  106. }
  107. /**
  108. * Custom install method
  109. *
  110. * @return boolean True on success
  111. *
  112. * @since 11.1
  113. */
  114. public function install()
  115. {
  116. // Get a database connector object
  117. $db = $this->parent->getDbo();
  118. // Get the extension manifest object
  119. $this->manifest = $this->parent->getManifest();
  120. // Manifest Document Setup Section
  121. // Set the extensions name
  122. $name = (string) $this->manifest->name;
  123. $name = JFilterInput::getInstance()->clean($name, 'string');
  124. $this->set('name', $name);
  125. // Get the component description
  126. $description = (string) $this->manifest->description;
  127. if ($description)
  128. {
  129. $this->parent->set('message', JText::_($description));
  130. }
  131. else
  132. {
  133. $this->parent->set('message', '');
  134. }
  135. // Target Application Section
  136. // Get the target application
  137. if ($cname = (string) $this->manifest->attributes()->client)
  138. {
  139. // Attempt to map the client to a base path
  140. $client = JApplicationHelper::getClientInfo($cname, true);
  141. if ($client === false)
  142. {
  143. $this->parent
  144. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_UNKNOWN_CLIENT', JText::_('JLIB_INSTALLER_' . $this->route), $client->name));
  145. return false;
  146. }
  147. $basePath = $client->path;
  148. $clientId = $client->id;
  149. }
  150. else
  151. {
  152. // No client attribute was found so we assume the site as the client
  153. $cname = 'site';
  154. $basePath = JPATH_SITE;
  155. $clientId = 0;
  156. }
  157. // Set the installation path
  158. $element = '';
  159. if (count($this->manifest->files->children()))
  160. {
  161. foreach ($this->manifest->files->children() as $file)
  162. {
  163. if ((string) $file->attributes()->module)
  164. {
  165. $element = (string) $file->attributes()->module;
  166. $this->set('element', $element);
  167. break;
  168. }
  169. }
  170. }
  171. if (!empty($element))
  172. {
  173. $this->parent->setPath('extension_root', $basePath . '/modules/' . $element);
  174. }
  175. else
  176. {
  177. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_INSTALL_NOFILE', JText::_('JLIB_INSTALLER_' . $this->route)));
  178. return false;
  179. }
  180. /*
  181. * Check to see if a module by the same name is already installed
  182. * If it is, then update the table because if the files aren't there
  183. * we can assume that it was (badly) uninstalled
  184. * If it isn't, add an entry to extensions
  185. */
  186. $query = $db->getQuery(true);
  187. $query->select($query->qn('extension_id'))->from($query->qn('#__extensions'));
  188. $query->where($query->qn('element') . ' = ' . $query->q($element))->where($query->qn('client_id') . ' = ' . (int) $clientId);
  189. $db->setQuery($query);
  190. try
  191. {
  192. $db->Query();
  193. }
  194. catch (JException $e)
  195. {
  196. // Install failed, roll back changes
  197. $this->parent
  198. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)));
  199. return false;
  200. }
  201. $id = $db->loadResult();
  202. /*
  203. * If the module directory already exists, then we will assume that the
  204. * module is already installed or another module is using that
  205. * directory.
  206. * Check that this is either an issue where its not overwriting or it is
  207. * set to upgrade anyway
  208. */
  209. if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->isOverwrite() || $this->parent->isUpgrade()))
  210. {
  211. // Look for an update function or update tag
  212. $updateElement = $this->manifest->update;
  213. /*
  214. * Upgrade manually set or
  215. * Update function available or
  216. * Update tag detected
  217. */
  218. if ($this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
  219. || $updateElement)
  220. {
  221. // Force this one
  222. $this->parent->setOverwrite(true);
  223. $this->parent->setUpgrade(true);
  224. if ($id)
  225. {
  226. // If there is a matching extension mark this as an update; semantics really
  227. $this->route = 'Update';
  228. }
  229. }
  230. elseif (!$this->parent->isOverwrite())
  231. {
  232. // Overwrite is set
  233. // We didn't have overwrite set, find an update function or find an update tag so lets call it safe
  234. $this->parent
  235. ->abort(
  236. JText::sprintf(
  237. 'JLIB_INSTALLER_ABORT_MOD_INSTALL_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route),
  238. $this->parent->getPath('extension_root')
  239. )
  240. );
  241. return false;
  242. }
  243. }
  244. // Installer Trigger Loading
  245. // If there is an manifest class file, let's load it; we'll copy it later (don't have destination yet)
  246. $this->scriptElement = $this->manifest->scriptfile;
  247. $manifestScript = (string) $this->manifest->scriptfile;
  248. if ($manifestScript)
  249. {
  250. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  251. if (is_file($manifestScriptFile))
  252. {
  253. // Load the file
  254. include_once $manifestScriptFile;
  255. }
  256. // Set the class name
  257. $classname = $element . 'InstallerScript';
  258. if (class_exists($classname))
  259. {
  260. // Create a new instance.
  261. $this->parent->manifestClass = new $classname($this);
  262. // And set this so we can copy it later.
  263. $this->set('manifest_script', $manifestScript);
  264. // Note: if we don't find the class, don't bother to copy the file.
  265. }
  266. }
  267. // Run preflight if possible (since we know we're not an update)
  268. ob_start();
  269. ob_implicit_flush(false);
  270. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  271. {
  272. if ($this->parent->manifestClass->preflight($this->route, $this) === false)
  273. {
  274. // Install failed, rollback changes
  275. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_CUSTOM_INSTALL_FAILURE'));
  276. return false;
  277. }
  278. }
  279. // Create msg object; first use here
  280. $msg = ob_get_contents();
  281. ob_end_clean();
  282. // Filesystem Processing Section
  283. // If the module directory does not exist, lets create it
  284. $created = false;
  285. if (!file_exists($this->parent->getPath('extension_root')))
  286. {
  287. if (!$created = JFolder::create($this->parent->getPath('extension_root')))
  288. {
  289. $this->parent
  290. ->abort(
  291. JText::sprintf(
  292. 'JLIB_INSTALLER_ABORT_MOD_INSTALL_CREATE_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route),
  293. $this->parent->getPath('extension_root')
  294. )
  295. );
  296. return false;
  297. }
  298. }
  299. /*
  300. * Since we created the module directory and will want to remove it if
  301. * we have to roll back the installation, let's add it to the
  302. * installation step stack
  303. */
  304. if ($created)
  305. {
  306. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root')));
  307. }
  308. // Copy all necessary files
  309. if ($this->parent->parseFiles($this->manifest->files, -1) === false)
  310. {
  311. // Install failed, roll back changes
  312. $this->parent->abort();
  313. return false;
  314. }
  315. // If there is a manifest script, let's copy it.
  316. if ($this->get('manifest_script'))
  317. {
  318. $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script');
  319. $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script');
  320. if (!file_exists($path['dest']) || $this->parent->isOverwrite())
  321. {
  322. if (!$this->parent->copyFiles(array($path)))
  323. {
  324. // Install failed, rollback changes
  325. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_MANIFEST'));
  326. return false;
  327. }
  328. }
  329. }
  330. // Parse optional tags
  331. $this->parent->parseMedia($this->manifest->media, $clientId);
  332. $this->parent->parseLanguages($this->manifest->languages, $clientId);
  333. // Parse deprecated tags
  334. $this->parent->parseFiles($this->manifest->images, -1);
  335. // Database Processing Section
  336. $row = JTable::getInstance('extension');
  337. // Was there a module already installed with the same name?
  338. if ($id)
  339. {
  340. // Load the entry and update the manifest_cache
  341. $row->load($id);
  342. // Update name
  343. $row->name = $this->get('name');
  344. // Update manifest
  345. $row->manifest_cache = $this->parent->generateManifestCache();
  346. if (!$row->store())
  347. {
  348. // Install failed, roll back changes
  349. $this->parent
  350. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)));
  351. return false;
  352. }
  353. }
  354. else
  355. {
  356. $row->set('name', $this->get('name'));
  357. $row->set('type', 'module');
  358. $row->set('element', $this->get('element'));
  359. // There is no folder for modules
  360. $row->set('folder', '');
  361. $row->set('enabled', 1);
  362. $row->set('protected', 0);
  363. $row->set('access', $clientId == 1 ? 2 : 0);
  364. $row->set('client_id', $clientId);
  365. $row->set('params', $this->parent->getParams());
  366. // Custom data
  367. $row->set('custom_data', '');
  368. $row->set('manifest_cache', $this->parent->generateManifestCache());
  369. if (!$row->store())
  370. {
  371. // Install failed, roll back changes
  372. $this->parent
  373. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_ROLLBACK', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true)));
  374. return false;
  375. }
  376. // Set the insert id
  377. $row->extension_id = $db->insertid();
  378. // Since we have created a module item, we add it to the installation step stack
  379. // so that if we have to rollback the changes we can undo it.
  380. $this->parent->pushStep(array('type' => 'extension', 'extension_id' => $row->extension_id));
  381. // Create unpublished module in jos_modules
  382. $name = preg_replace('#[\*?]#', '', JText::_($this->get('name')));
  383. $module = JTable::getInstance('module');
  384. $module->set('title', $name);
  385. $module->set('module', $this->get('element'));
  386. $module->set('access', '1');
  387. $module->set('showtitle', '1');
  388. $module->set('client_id', $clientId);
  389. $module->set('language', '*');
  390. $module->store();
  391. }
  392. /*
  393. * Let's run the queries for the module
  394. * If Joomla 1.5 compatible, with discrete sql files, execute appropriate
  395. * file for utf-8 support or non-utf-8 support
  396. */
  397. // Try for Joomla 1.5 type queries
  398. // Second argument is the utf compatible version attribute
  399. if (strtolower($this->route) == 'install')
  400. {
  401. $utfresult = $this->parent->parseSQLFiles($this->manifest->install->sql);
  402. if ($utfresult === false)
  403. {
  404. // Install failed, rollback changes
  405. $this->parent
  406. ->abort(
  407. JText::sprintf('JLIB_INSTALLER_ABORT_MOD_INSTALL_SQL_ERROR', JText::_('JLIB_INSTALLER_' . $this->route), $db->stderr(true))
  408. );
  409. return false;
  410. }
  411. // Set the schema version to be the latest update version
  412. if ($this->manifest->update)
  413. {
  414. $this->parent->setSchemaVersion($this->manifest->update->schemas, $row->extension_id);
  415. }
  416. }
  417. elseif (strtolower($this->route) == 'update')
  418. {
  419. if ($this->manifest->update)
  420. {
  421. $result = $this->parent->parseSchemaUpdates($this->manifest->update->schemas, $row->extension_id);
  422. if ($result === false)
  423. {
  424. // Install failed, rollback changes
  425. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_MOD_UPDATE_SQL_ERROR', $db->stderr(true)));
  426. return false;
  427. }
  428. }
  429. }
  430. // Start Joomla! 1.6
  431. ob_start();
  432. ob_implicit_flush(false);
  433. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route))
  434. {
  435. if ($this->parent->manifestClass->{$this->route}($this) === false)
  436. {
  437. // Install failed, rollback changes
  438. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_CUSTOM_INSTALL_FAILURE'));
  439. return false;
  440. }
  441. }
  442. // Append messages
  443. $msg .= ob_get_contents();
  444. ob_end_clean();
  445. // Finalization and Cleanup Section
  446. // Lastly, we will copy the manifest file to its appropriate place.
  447. if (!$this->parent->copyManifest(-1))
  448. {
  449. // Install failed, rollback changes
  450. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_MOD_INSTALL_COPY_SETUP'));
  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($this->route, $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->get('extension_id');
  468. }
  469. /**
  470. * Custom update method
  471. *
  472. * This is really a shell for the install system
  473. *
  474. * @return boolean True on success.
  475. *
  476. * @since 11.1
  477. */
  478. public function update()
  479. {
  480. // Set the overwrite setting
  481. $this->parent->setOverwrite(true);
  482. $this->parent->setUpgrade(true);
  483. // Set the route for the install
  484. $this->route = 'Update';
  485. // Go to install which handles updates properly
  486. return $this->install();
  487. }
  488. /**
  489. * Custom discover method
  490. *
  491. * @return array JExtension list of extensions available
  492. *
  493. * @since 11.1
  494. */
  495. public function discover()
  496. {
  497. $results = array();
  498. $site_list = JFolder::folders(JPATH_SITE . '/modules');
  499. $admin_list = JFolder::folders(JPATH_ADMINISTRATOR . '/modules');
  500. $site_info = JApplicationHelper::getClientInfo('site', true);
  501. $admin_info = JApplicationHelper::getClientInfo('administrator', true);
  502. foreach ($site_list as $module)
  503. {
  504. $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . "/modules/$module/$module.xml");
  505. $extension = JTable::getInstance('extension');
  506. $extension->set('type', 'module');
  507. $extension->set('client_id', $site_info->id);
  508. $extension->set('element', $module);
  509. $extension->set('name', $module);
  510. $extension->set('state', -1);
  511. $extension->set('manifest_cache', json_encode($manifest_details));
  512. $results[] = clone $extension;
  513. }
  514. foreach ($admin_list as $module)
  515. {
  516. $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/modules/$module/$module.xml");
  517. $extension = JTable::getInstance('extension');
  518. $extension->set('type', 'module');
  519. $extension->set('client_id', $admin_info->id);
  520. $extension->set('element', $module);
  521. $extension->set('name', $module);
  522. $extension->set('state', -1);
  523. $extension->set('manifest_cache', json_encode($manifest_details));
  524. $results[] = clone $extension;
  525. }
  526. return $results;
  527. }
  528. /**
  529. * Custom discover_install method
  530. *
  531. * @return mixed Extension ID on success, boolean false on failure
  532. *
  533. * @since 11.1
  534. */
  535. public function discover_install()
  536. {
  537. // Modules are like templates, and are one of the easiest
  538. // If its not in the extensions table we just add it
  539. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  540. $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml';
  541. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  542. $description = (string) $this->parent->manifest->description;
  543. if ($description)
  544. {
  545. $this->parent->set('message', JText::_($description));
  546. }
  547. else
  548. {
  549. $this->parent->set('message', '');
  550. }
  551. $this->parent->setPath('manifest', $manifestPath);
  552. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  553. // TODO: Re-evaluate this; should we run installation triggers? postflight perhaps?
  554. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  555. $this->parent->extension->state = 0;
  556. $this->parent->extension->name = $manifest_details['name'];
  557. $this->parent->extension->enabled = 1;
  558. $this->parent->extension->params = $this->parent->getParams();
  559. if ($this->parent->extension->store())
  560. {
  561. return $this->parent->extension->get('extension_id');
  562. }
  563. else
  564. {
  565. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_DISCOVER_STORE_DETAILS'));
  566. return false;
  567. }
  568. }
  569. /**
  570. * Refreshes the extension table cache
  571. *
  572. * @return boolean Result of operation, true if updated, false on failure.
  573. *
  574. * @since 11.1
  575. */
  576. public function refreshManifestCache()
  577. {
  578. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  579. $manifestPath = $client->path . '/modules/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml';
  580. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  581. $this->parent->setPath('manifest', $manifestPath);
  582. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  583. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  584. $this->parent->extension->name = $manifest_details['name'];
  585. if ($this->parent->extension->store())
  586. {
  587. return true;
  588. }
  589. else
  590. {
  591. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE'));
  592. return false;
  593. }
  594. }
  595. /**
  596. * Custom uninstall method
  597. *
  598. * @param integer $id The id of the module to uninstall
  599. *
  600. * @return boolean True on success
  601. *
  602. * @since 11.1
  603. */
  604. public function uninstall($id)
  605. {
  606. // Initialise variables.
  607. $row = null;
  608. $retval = true;
  609. $db = $this->parent->getDbo();
  610. // First order of business will be to load the module object table from the database.
  611. // This should give us the necessary information to proceed.
  612. $row = JTable::getInstance('extension');
  613. if (!$row->load((int) $id) || !strlen($row->element))
  614. {
  615. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_ERRORUNKOWNEXTENSION'));
  616. return false;
  617. }
  618. // Is the module we are trying to uninstall a core one?
  619. // Because that is not a good idea...
  620. if ($row->protected)
  621. {
  622. JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_WARNCOREMODULE', $row->name));
  623. return false;
  624. }
  625. // Get the extension root path
  626. $element = $row->element;
  627. $client = JApplicationHelper::getClientInfo($row->client_id);
  628. if ($client === false)
  629. {
  630. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_UNKNOWN_CLIENT', $row->client_id));
  631. return false;
  632. }
  633. $this->parent->setPath('extension_root', $client->path . '/modules/' . $element);
  634. $this->parent->setPath('source', $this->parent->getPath('extension_root'));
  635. // Get the package manifest objecct
  636. // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file.
  637. $this->parent->findManifest();
  638. $this->manifest = $this->parent->getManifest();
  639. // Attempt to load the language file; might have uninstall strings
  640. $this->loadLanguage(($row->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $element);
  641. // If there is an manifest class file, let's load it
  642. $this->scriptElement = $this->manifest->scriptfile;
  643. $manifestScript = (string) $this->manifest->scriptfile;
  644. if ($manifestScript)
  645. {
  646. $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript;
  647. if (is_file($manifestScriptFile))
  648. {
  649. // Load the file
  650. include_once $manifestScriptFile;
  651. }
  652. // Set the class name
  653. $classname = $element . 'InstallerScript';
  654. if (class_exists($classname))
  655. {
  656. // Create a new instance
  657. $this->parent->manifestClass = new $classname($this);
  658. // And set this so we can copy it later
  659. $this->set('manifest_script', $manifestScript);
  660. // Note: if we don't find the class, don't bother to copy the file
  661. }
  662. }
  663. ob_start();
  664. ob_implicit_flush(false);
  665. // Run uninstall if possible
  666. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall'))
  667. {
  668. $this->parent->manifestClass->uninstall($this);
  669. }
  670. $msg = ob_get_contents();
  671. ob_end_clean();
  672. if (!($this->manifest instanceof JXMLElement))
  673. {
  674. // Make sure we delete the folders
  675. JFolder::delete($this->parent->getPath('extension_root'));
  676. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_INVALID_NOTFOUND_MANIFEST'));
  677. return false;
  678. }
  679. /*
  680. * Let's run the uninstall queries for the component
  681. * If Joomla 1.5 compatible, with discreet sql files - execute appropriate
  682. * file for utf-8 support or non-utf support
  683. */
  684. // Try for Joomla 1.5 type queries
  685. // Second argument is the utf compatible version attribute
  686. $utfresult = $this->parent->parseSQLFiles($this->manifest->uninstall->sql);
  687. if ($utfresult === false)
  688. {
  689. // Install failed, rollback changes
  690. JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_SQL_ERROR', $db->stderr(true)));
  691. $retval = false;
  692. }
  693. // Remove the schema version
  694. $query = $db->getQuery(true);
  695. $query->delete()->from('#__schemas')->where('extension_id = ' . $row->extension_id);
  696. $db->setQuery($query);
  697. $db->Query();
  698. // Remove other files
  699. $this->parent->removeFiles($this->manifest->media);
  700. $this->parent->removeFiles($this->manifest->languages, $row->client_id);
  701. // Let's delete all the module copies for the type we are uninstalling
  702. $query = $db->getQuery(true);
  703. $query->select($query->qn('id'))->from($query->qn('#__modules'));
  704. $query->where($query->qn('module') . ' = ' . $query->q($row->element));
  705. $query->where($query->qn('client_id') . ' = ' . (int) $row->client_id);
  706. $db->setQuery($query);
  707. try
  708. {
  709. $modules = $db->loadColumn();
  710. }
  711. catch (JException $e)
  712. {
  713. $modules = array();
  714. }
  715. // Do we have any module copies?
  716. if (count($modules))
  717. {
  718. // Ensure the list is sane
  719. JArrayHelper::toInteger($modules);
  720. $modID = implode(',', $modules);
  721. // Wipe out any items assigned to menus
  722. $query = 'DELETE' . ' FROM #__modules_menu' . ' WHERE moduleid IN (' . $modID . ')';
  723. $db->setQuery($query);
  724. try
  725. {
  726. $db->query();
  727. }
  728. catch (JException $e)
  729. {
  730. JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $db->stderr(true)));
  731. $retval = false;
  732. }
  733. // Wipe out any instances in the modules table
  734. $query = 'DELETE' . ' FROM #__modules' . ' WHERE id IN (' . $modID . ')';
  735. $db->setQuery($query);
  736. try
  737. {
  738. $db->query();
  739. }
  740. catch (JException $e)
  741. {
  742. JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_MOD_UNINSTALL_EXCEPTION', $db->stderr(true)));
  743. $retval = false;
  744. }
  745. }
  746. // Now we will no longer need the module object, so let's delete it and free up memory
  747. $row->delete($row->extension_id);
  748. $query = 'DELETE FROM #__modules WHERE module = ' . $db->Quote($row->element) . ' AND client_id = ' . $row->client_id;
  749. $db->setQuery($query);
  750. try
  751. {
  752. // Clean up any other ones that might exist as well
  753. $db->Query();
  754. }
  755. catch (JException $e)
  756. {
  757. // Ignore the error...
  758. }
  759. unset($row);
  760. // Remove the installation folder
  761. if (!JFolder::delete($this->parent->getPath('extension_root')))
  762. {
  763. // JFolder should raise an error
  764. $retval = false;
  765. }
  766. return $retval;
  767. }
  768. /**
  769. * Custom rollback method
  770. * - Roll back the menu item
  771. *
  772. * @param array $arg Installation step to rollback
  773. *
  774. * @return boolean True on success
  775. *
  776. * @since 11.1
  777. */
  778. protected function _rollback_menu($arg)
  779. {
  780. // Get database connector object
  781. $db = $this->parent->getDbo();
  782. // Remove the entry from the #__modules_menu table
  783. $query = 'DELETE' . ' FROM #__modules_menu' . ' WHERE moduleid=' . (int) $arg['id'];
  784. $db->setQuery($query);
  785. try
  786. {
  787. return $db->query();
  788. }
  789. catch (JException $e)
  790. {
  791. return false;
  792. }
  793. }
  794. /**
  795. * Custom rollback method
  796. * - Roll back the module item
  797. *
  798. * @param array $arg Installation step to rollback
  799. *
  800. * @return boolean True on success
  801. *
  802. * @since 11.1
  803. */
  804. protected function _rollback_module($arg)
  805. {
  806. // Get database connector object
  807. $db = $this->parent->getDbo();
  808. // Remove the entry from the #__modules table
  809. $query = 'DELETE' . ' FROM #__modules' . ' WHERE id=' . (int) $arg['id'];
  810. $db->setQuery($query);
  811. try
  812. {
  813. return $db->query();
  814. }
  815. catch (JException $e)
  816. {
  817. return false;
  818. }
  819. }
  820. }