PageRenderTime 51ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/cms/installer/adapter/package.php

https://bitbucket.org/pastor399/newcastleunifc
PHP | 631 lines | 361 code | 103 blank | 167 comment | 50 complexity | 116bce17281ac0a3ef1781dc27f96ebc 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. /**
  12. * Package installer
  13. *
  14. * @package Joomla.Libraries
  15. * @subpackage Installer
  16. * @since 3.1
  17. */
  18. class JInstallerAdapterPackage extends JAdapterInstance
  19. {
  20. /**
  21. * Method of system
  22. *
  23. * @var string
  24. *
  25. * @since 3.1
  26. */
  27. protected $route = 'install';
  28. /**
  29. * Load language from a path
  30. *
  31. * @param string $path The path of the language.
  32. *
  33. * @return void
  34. *
  35. * @since 3.1
  36. */
  37. public function loadLanguage($path)
  38. {
  39. $this->manifest = $this->parent->getManifest();
  40. $extension = 'pkg_' . strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->packagename, 'cmd'));
  41. $lang = JFactory::getLanguage();
  42. $source = $path;
  43. $lang->load($extension . '.sys', $source, null, false, false)
  44. || $lang->load($extension . '.sys', JPATH_SITE, null, false, false)
  45. || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false)
  46. || $lang->load($extension . '.sys', JPATH_SITE, $lang->getDefault(), false, false);
  47. }
  48. /**
  49. * Custom install method
  50. *
  51. * @return int The extension id
  52. *
  53. * @since 3.1
  54. */
  55. public function install()
  56. {
  57. // Get the extension manifest object
  58. $this->manifest = $this->parent->getManifest();
  59. /*
  60. * ---------------------------------------------------------------------------------------------
  61. * Manifest Document Setup Section
  62. * ---------------------------------------------------------------------------------------------
  63. */
  64. // Set the extensions name
  65. $filter = JFilterInput::getInstance();
  66. $name = (string) $this->manifest->packagename;
  67. $name = $filter->clean($name, 'cmd');
  68. $this->set('name', $name);
  69. $element = 'pkg_' . $filter->clean($this->manifest->packagename, 'cmd');
  70. $this->set('element', $element);
  71. // Get the component description
  72. $description = (string) $this->manifest->description;
  73. if ($description)
  74. {
  75. $this->parent->set('message', JText::_($description));
  76. }
  77. else
  78. {
  79. $this->parent->set('message', '');
  80. }
  81. // Set the installation path
  82. $files = $this->manifest->files;
  83. $group = (string) $this->manifest->packagename;
  84. if (!empty($group))
  85. {
  86. $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . implode(DIRECTORY_SEPARATOR, explode('/', $group)));
  87. }
  88. else
  89. {
  90. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_PACK', JText::_('JLIB_INSTALLER_' . strtoupper($this->route))));
  91. return false;
  92. }
  93. /*
  94. * If the package manifest already exists, then we will assume that the package is already
  95. * installed.
  96. */
  97. if (file_exists(JPATH_MANIFESTS . '/packages/' . basename($this->parent->getPath('manifest'))))
  98. {
  99. // Look for an update function or update tag
  100. $updateElement = $this->manifest->update;
  101. // If $this->upgrade has already been set, or an update property exists in the manifest, update the extensions
  102. if ($this->parent->isUpgrade() || $updateElement)
  103. {
  104. // Use the update route for all packaged extensions
  105. $this->route = 'update';
  106. }
  107. }
  108. /**
  109. * ---------------------------------------------------------------------------------------------
  110. * Installer Trigger Loading
  111. * ---------------------------------------------------------------------------------------------
  112. */
  113. // If there is an manifest class file, lets load it; we'll copy it later (don't have dest yet)
  114. $this->scriptElement = $this->manifest->scriptfile;
  115. $manifestScript = (string) $this->manifest->scriptfile;
  116. if ($manifestScript)
  117. {
  118. $manifestScriptFile = $this->parent->getPath('source') . '/' . $manifestScript;
  119. if (is_file($manifestScriptFile))
  120. {
  121. // Load the file
  122. include_once $manifestScriptFile;
  123. }
  124. // Set the class name
  125. $classname = $element . 'InstallerScript';
  126. if (class_exists($classname))
  127. {
  128. // Create a new instance
  129. $this->parent->manifestClass = new $classname($this);
  130. // And set this so we can copy it later
  131. $this->set('manifest_script', $manifestScript);
  132. }
  133. }
  134. // Run preflight if possible (since we know we're not an update)
  135. ob_start();
  136. ob_implicit_flush(false);
  137. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'preflight'))
  138. {
  139. if ($this->parent->manifestClass->preflight($this->route, $this) === false)
  140. {
  141. // Preflight failed, rollback changes
  142. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PACKAGE_INSTALL_CUSTOM_INSTALL_FAILURE'));
  143. return false;
  144. }
  145. }
  146. // Create msg object; first use here
  147. $msg = ob_get_contents();
  148. ob_end_clean();
  149. /*
  150. * ---------------------------------------------------------------------------------------------
  151. * Filesystem Processing Section
  152. * ---------------------------------------------------------------------------------------------
  153. */
  154. if ($folder = $files->attributes()->folder)
  155. {
  156. $source = $this->parent->getPath('source') . '/' . $folder;
  157. }
  158. else
  159. {
  160. $source = $this->parent->getPath('source');
  161. }
  162. // Install all necessary files
  163. if (count($this->manifest->files->children()))
  164. {
  165. $i = 0;
  166. foreach ($this->manifest->files->children() as $child)
  167. {
  168. $file = $source . '/' . $child;
  169. if (is_dir($file))
  170. {
  171. // If it's actually a directory then fill it up
  172. $package = array();
  173. $package['dir'] = $file;
  174. $package['type'] = JInstallerHelper::detectType($file);
  175. }
  176. else
  177. {
  178. // If it's an archive
  179. $package = JInstallerHelper::unpack($file);
  180. }
  181. $tmpInstaller = new JInstaller;
  182. $installResult = $tmpInstaller->{$this->route}($package['dir']);
  183. if (!$installResult)
  184. {
  185. $this->parent->abort(
  186. JText::sprintf(
  187. 'JLIB_INSTALLER_ABORT_PACK_INSTALL_ERROR_EXTENSION', JText::_('JLIB_INSTALLER_' . strtoupper($this->route)),
  188. basename($file)
  189. )
  190. );
  191. return false;
  192. }
  193. else
  194. {
  195. $results[$i] = array(
  196. 'name' => $tmpInstaller->manifest->name,
  197. 'result' => $installResult
  198. );
  199. }
  200. $i++;
  201. }
  202. }
  203. else
  204. {
  205. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES', JText::_('JLIB_INSTALLER_' . strtoupper($this->route))));
  206. return false;
  207. }
  208. // Parse optional tags
  209. $this->parent->parseLanguages($this->manifest->languages);
  210. /*
  211. * ---------------------------------------------------------------------------------------------
  212. * Extension Registration
  213. * ---------------------------------------------------------------------------------------------
  214. */
  215. $row = JTable::getInstance('extension');
  216. $eid = $row->find(array('element' => strtolower($this->get('element')), 'type' => 'package'));
  217. if ($eid)
  218. {
  219. $row->load($eid);
  220. }
  221. else
  222. {
  223. $row->name = $this->get('name');
  224. $row->type = 'package';
  225. $row->element = $this->get('element');
  226. // There is no folder for modules
  227. $row->folder = '';
  228. $row->enabled = 1;
  229. $row->protected = 0;
  230. $row->access = 1;
  231. $row->client_id = 0;
  232. // Custom data
  233. $row->custom_data = '';
  234. $row->params = $this->parent->getParams();
  235. }
  236. // Update the manifest cache for the entry
  237. $row->manifest_cache = $this->parent->generateManifestCache();
  238. if (!$row->store())
  239. {
  240. // Install failed, roll back changes
  241. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_ROLLBACK', $row->getError()));
  242. return false;
  243. }
  244. /*
  245. * ---------------------------------------------------------------------------------------------
  246. * Finalization and Cleanup Section
  247. * ---------------------------------------------------------------------------------------------
  248. */
  249. // Run the custom method based on the route
  250. ob_start();
  251. ob_implicit_flush(false);
  252. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, $this->route))
  253. {
  254. if ($this->parent->manifestClass->{$this->route}($this) === false)
  255. {
  256. // Install failed, rollback changes
  257. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_FILE_INSTALL_CUSTOM_INSTALL_FAILURE'));
  258. return false;
  259. }
  260. }
  261. // Append messages
  262. $msg .= ob_get_contents();
  263. ob_end_clean();
  264. // Lastly, we will copy the manifest file to its appropriate place.
  265. $manifest = array();
  266. $manifest['src'] = $this->parent->getPath('manifest');
  267. $manifest['dest'] = JPATH_MANIFESTS . '/packages/' . basename($this->parent->getPath('manifest'));
  268. if (!$this->parent->copyFiles(array($manifest), true))
  269. {
  270. // Install failed, rollback changes
  271. $this->parent->abort(
  272. JText::sprintf('JLIB_INSTALLER_ABORT_PACK_INSTALL_COPY_SETUP', JText::_('JLIB_INSTALLER_ABORT_PACK_INSTALL_NO_FILES'))
  273. );
  274. return false;
  275. }
  276. // If there is a manifest script, let's copy it.
  277. if ($this->get('manifest_script'))
  278. {
  279. // First, we have to create a folder for the script if one isn't present
  280. if (!file_exists($this->parent->getPath('extension_root')))
  281. {
  282. JFolder::create($this->parent->getPath('extension_root'));
  283. }
  284. $path['src'] = $this->parent->getPath('source') . '/' . $this->get('manifest_script');
  285. $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->get('manifest_script');
  286. if (!file_exists($path['dest']) || $this->parent->isOverwrite())
  287. {
  288. if (!$this->parent->copyFiles(array($path)))
  289. {
  290. // Install failed, rollback changes
  291. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_PACKAGE_INSTALL_MANIFEST'));
  292. return false;
  293. }
  294. }
  295. }
  296. // And now we run the postflight
  297. ob_start();
  298. ob_implicit_flush(false);
  299. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
  300. {
  301. $this->parent->manifestClass->postflight($this->route, $this, $results);
  302. }
  303. // Append messages
  304. $msg .= ob_get_contents();
  305. ob_end_clean();
  306. if ($msg != '')
  307. {
  308. $this->parent->set('extension_message', $msg);
  309. }
  310. return $row->extension_id;
  311. }
  312. /**
  313. * Updates a package
  314. *
  315. * The only difference between an update and a full install
  316. * is how we handle the database
  317. *
  318. * @return void
  319. *
  320. * @since 3.1
  321. */
  322. public function update()
  323. {
  324. $this->route = 'update';
  325. $this->install();
  326. }
  327. /**
  328. * Custom uninstall method
  329. *
  330. * @param integer $id The id of the package to uninstall.
  331. *
  332. * @return boolean True on success
  333. *
  334. * @since 3.1
  335. */
  336. public function uninstall($id)
  337. {
  338. $row = null;
  339. $retval = true;
  340. $row = JTable::getInstance('extension');
  341. $row->load($id);
  342. if ($row->protected)
  343. {
  344. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_WARNCOREPACK'), JLog::WARNING, 'jerror');
  345. return false;
  346. }
  347. $manifestFile = JPATH_MANIFESTS . '/packages/' . $row->get('element') . '.xml';
  348. $manifest = new JInstallerManifestPackage($manifestFile);
  349. // Set the package root path
  350. $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/packages/' . $manifest->packagename);
  351. // Because packages may not have their own folders we cannot use the standard method of finding an installation manifest
  352. if (!file_exists($manifestFile))
  353. {
  354. // TODO: Fail?
  355. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MISSINGMANIFEST'), JLog::WARNING, 'jerror');
  356. return false;
  357. }
  358. $xml = simplexml_load_file($manifestFile);
  359. // If we cannot load the XML file return false
  360. if (!$xml)
  361. {
  362. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_LOAD_MANIFEST'), JLog::WARNING, 'jerror');
  363. return false;
  364. }
  365. // Check for a valid XML root tag.
  366. if ($xml->getName() != 'extension')
  367. {
  368. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_INVALID_MANIFEST'), JLog::WARNING, 'jerror');
  369. return false;
  370. }
  371. // If there is an manifest class file, let's load it
  372. $this->scriptElement = $manifest->scriptfile;
  373. $manifestScript = (string) $manifest->scriptfile;
  374. if ($manifestScript)
  375. {
  376. $manifestScriptFile = $this->parent->getPath('extension_root') . '/' . $manifestScript;
  377. if (is_file($manifestScriptFile))
  378. {
  379. // Load the file
  380. include_once $manifestScriptFile;
  381. }
  382. // Set the class name
  383. $classname = $row->element . 'InstallerScript';
  384. if (class_exists($classname))
  385. {
  386. // Create a new instance
  387. $this->parent->manifestClass = new $classname($this);
  388. // And set this so we can copy it later
  389. $this->set('manifest_script', $manifestScript);
  390. }
  391. }
  392. ob_start();
  393. ob_implicit_flush(false);
  394. // Run uninstall if possible
  395. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'uninstall'))
  396. {
  397. $this->parent->manifestClass->uninstall($this);
  398. }
  399. $msg = ob_get_contents();
  400. ob_end_clean();
  401. if ($msg != '')
  402. {
  403. $this->parent->set('extension_message', $msg);
  404. }
  405. $error = false;
  406. foreach ($manifest->filelist as $extension)
  407. {
  408. $tmpInstaller = new JInstaller;
  409. $id = $this->_getExtensionID($extension->type, $extension->id, $extension->client, $extension->group);
  410. $client = JApplicationHelper::getClientInfo($extension->client, true);
  411. if ($id)
  412. {
  413. if (!$tmpInstaller->uninstall($extension->type, $id, $client->id))
  414. {
  415. $error = true;
  416. JLog::add(JText::sprintf('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_NOT_PROPER', basename($extension->filename)), JLog::WARNING, 'jerror');
  417. }
  418. }
  419. else
  420. {
  421. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_UNKNOWN_EXTENSION'), JLog::WARNING, 'jerror');
  422. }
  423. }
  424. // Remove any language files
  425. $this->parent->removeFiles($xml->languages);
  426. // Clean up manifest file after we're done if there were no errors
  427. if (!$error)
  428. {
  429. JFile::delete($manifestFile);
  430. $folder = $this->parent->getPath('extension_root');
  431. if (JFolder::exists($folder))
  432. {
  433. JFolder::delete($folder);
  434. }
  435. $row->delete();
  436. }
  437. else
  438. {
  439. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_UNINSTALL_MANIFEST_NOT_REMOVED'), JLog::WARNING, 'jerror');
  440. }
  441. // Return the result up the line
  442. return $retval;
  443. }
  444. /**
  445. * Gets the extension id.
  446. *
  447. * @param string $type The extension type.
  448. * @param string $id The name of the extension (the element field).
  449. * @param integer $client The application id (0: Joomla CMS site; 1: Joomla CMS administrator).
  450. * @param string $group The extension group (mainly for plugins).
  451. *
  452. * @return integer
  453. *
  454. * @since 3.1
  455. */
  456. protected function _getExtensionID($type, $id, $client, $group)
  457. {
  458. $db = $this->parent->getDbo();
  459. $query = $db->getQuery(true)
  460. ->select('extension_id')
  461. ->from('#__extensions')
  462. ->where('type = ' . $db->quote($type))
  463. ->where('element = ' . $db->quote($id));
  464. switch ($type)
  465. {
  466. case 'plugin':
  467. // Plugins have a folder but not a client
  468. $query->where('folder = ' . $db->quote($group));
  469. break;
  470. case 'library':
  471. case 'package':
  472. case 'component':
  473. // Components, packages and libraries don't have a folder or client.
  474. // Included for completeness.
  475. break;
  476. case 'language':
  477. case 'module':
  478. case 'template':
  479. // Languages, modules and templates have a client but not a folder
  480. $client = JApplicationHelper::getClientInfo($client, true);
  481. $query->where('client_id = ' . (int) $client->id);
  482. break;
  483. }
  484. $db->setQuery($query);
  485. $result = $db->loadResult();
  486. // Note: For templates, libraries and packages their unique name is their key.
  487. // This means they come out the same way they came in.
  488. return $result;
  489. }
  490. /**
  491. * Refreshes the extension table cache
  492. *
  493. * @return boolean Result of operation, true if updated, false on failure
  494. *
  495. * @since 3.1
  496. */
  497. public function refreshManifestCache()
  498. {
  499. // Need to find to find where the XML file is since we don't store this normally
  500. $manifestPath = JPATH_MANIFESTS . '/packages/' . $this->parent->extension->element . '.xml';
  501. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  502. $this->parent->setPath('manifest', $manifestPath);
  503. $manifest_details = JInstaller::parseXMLInstallFile($this->parent->getPath('manifest'));
  504. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  505. $this->parent->extension->name = $manifest_details['name'];
  506. try
  507. {
  508. return $this->parent->extension->store();
  509. }
  510. catch (RuntimeException $e)
  511. {
  512. JLog::add(JText::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE'), JLog::WARNING, 'jerror');
  513. return false;
  514. }
  515. }
  516. }
  517. /**
  518. * Deprecated class placeholder. You should use JInstallerAdapterPackage instead.
  519. *
  520. * @package Joomla.Libraries
  521. * @subpackage Installer
  522. * @since 3.1
  523. * @deprecated 4.0
  524. * @codeCoverageIgnore
  525. */
  526. class JInstallerPackage extends JInstallerAdapterPackage
  527. {
  528. }