PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/src/Installer/Adapter/LibraryAdapter.php

https://bitbucket.org/saltwaterdev/offshorefinancial.com
PHP | 526 lines | 290 code | 74 blank | 162 comment | 30 complexity | 0994cb8a391c803eb54371f435659e9c MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, 0BSD, MIT, Apache-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Joomla! Content Management System
  4. *
  5. * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
  6. * @license GNU General Public License version 2 or later; see LICENSE.txt
  7. */
  8. namespace Joomla\CMS\Installer\Adapter;
  9. defined('JPATH_PLATFORM') or die;
  10. use Joomla\CMS\Installer\Installer;
  11. use Joomla\CMS\Installer\InstallerAdapter;
  12. use Joomla\CMS\Installer\Manifest\LibraryManifest;
  13. use Joomla\CMS\Table\Table;
  14. use Joomla\CMS\Table\Update;
  15. \JLoader::import('joomla.filesystem.folder');
  16. /**
  17. * Library installer
  18. *
  19. * @since 3.1
  20. */
  21. class LibraryAdapter extends InstallerAdapter
  22. {
  23. /**
  24. * Method to check if the extension is present in the filesystem, flags the route as update if so
  25. *
  26. * @return void
  27. *
  28. * @since 3.4
  29. * @throws \RuntimeException
  30. */
  31. protected function checkExtensionInFilesystem()
  32. {
  33. if ($this->currentExtensionId)
  34. {
  35. // Already installed, can we upgrade?
  36. if ($this->parent->isOverwrite() || $this->parent->isUpgrade())
  37. {
  38. // We can upgrade, so uninstall the old one
  39. $installer = new Installer; // we don't want to compromise this instance!
  40. $installer->setPackageUninstall(true);
  41. $installer->uninstall('library', $this->currentExtensionId);
  42. // Clear the cached data
  43. $this->currentExtensionId = null;
  44. $this->extension = Table::getInstance('Extension', 'JTable', array('dbo' => $this->db));
  45. // From this point we'll consider this an update
  46. $this->setRoute('update');
  47. }
  48. else
  49. {
  50. // Abort the install, no upgrade possible
  51. throw new \RuntimeException(\JText::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_ALREADY_INSTALLED'));
  52. }
  53. }
  54. }
  55. /**
  56. * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file
  57. *
  58. * @return void
  59. *
  60. * @since 3.4
  61. * @throws \RuntimeException
  62. */
  63. protected function copyBaseFiles()
  64. {
  65. if ($this->parent->parseFiles($this->getManifest()->files, -1) === false)
  66. {
  67. throw new \RuntimeException(\JText::_('JLIB_INSTALLER_ABORT_LIB_COPY_FILES'));
  68. }
  69. }
  70. /**
  71. * Method to finalise the installation processing
  72. *
  73. * @return void
  74. *
  75. * @since 3.4
  76. * @throws \RuntimeException
  77. */
  78. protected function finaliseInstall()
  79. {
  80. // Clobber any possible pending updates
  81. /** @var Update $update */
  82. $update = Table::getInstance('update');
  83. $uid = $update->find(
  84. array(
  85. 'element' => $this->element,
  86. 'type' => $this->type,
  87. )
  88. );
  89. if ($uid)
  90. {
  91. $update->delete($uid);
  92. }
  93. // Lastly, we will copy the manifest file to its appropriate place.
  94. if ($this->route !== 'discover_install')
  95. {
  96. $manifest = array();
  97. $manifest['src'] = $this->parent->getPath('manifest');
  98. $manifest['dest'] = JPATH_MANIFESTS . '/libraries/' . basename($this->parent->getPath('manifest'));
  99. if (!$this->parent->copyFiles(array($manifest), true))
  100. {
  101. // Install failed, rollback changes
  102. throw new \RuntimeException(\JText::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_COPY_SETUP'));
  103. }
  104. // If there is a manifest script, let's copy it.
  105. if ($this->manifest_script)
  106. {
  107. $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script;
  108. $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script;
  109. if ($this->parent->isOverwrite() || !file_exists($path['dest']))
  110. {
  111. if (!$this->parent->copyFiles(array($path)))
  112. {
  113. // Install failed, rollback changes
  114. throw new \RuntimeException(
  115. \JText::sprintf(
  116. 'JLIB_INSTALLER_ABORT_MANIFEST',
  117. \JText::_('JLIB_INSTALLER_' . strtoupper($this->route))
  118. )
  119. );
  120. }
  121. }
  122. }
  123. }
  124. }
  125. /**
  126. * Get the filtered extension element from the manifest
  127. *
  128. * @param string $element Optional element name to be converted
  129. *
  130. * @return string The filtered element
  131. *
  132. * @since 3.4
  133. */
  134. public function getElement($element = null)
  135. {
  136. if (!$element)
  137. {
  138. $manifestPath = \JPath::clean($this->parent->getPath('manifest'));
  139. $element = preg_replace('/\.xml/', '', basename($manifestPath));
  140. }
  141. return $element;
  142. }
  143. /**
  144. * Custom loadLanguage method
  145. *
  146. * @param string $path The path where to find language files.
  147. *
  148. * @return void
  149. *
  150. * @since 3.1
  151. */
  152. public function loadLanguage($path = null)
  153. {
  154. $source = $this->parent->getPath('source');
  155. if (!$source)
  156. {
  157. $this->parent->setPath('source', JPATH_PLATFORM . '/' . $this->getElement());
  158. }
  159. $extension = 'lib_' . $this->getElement();
  160. $librarypath = (string) $this->getManifest()->libraryname;
  161. $source = $path ?: JPATH_PLATFORM . '/' . $librarypath;
  162. $this->doLoadLanguage($extension, $source, JPATH_SITE);
  163. }
  164. /**
  165. * Method to parse optional tags in the manifest
  166. *
  167. * @return void
  168. *
  169. * @since 3.4
  170. */
  171. protected function parseOptionalTags()
  172. {
  173. $this->parent->parseLanguages($this->getManifest()->languages);
  174. $this->parent->parseMedia($this->getManifest()->media);
  175. }
  176. /**
  177. * Prepares the adapter for a discover_install task
  178. *
  179. * @return void
  180. *
  181. * @since 3.4
  182. */
  183. public function prepareDiscoverInstall()
  184. {
  185. $manifestPath = JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml';
  186. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  187. $this->parent->setPath('manifest', $manifestPath);
  188. $this->setManifest($this->parent->getManifest());
  189. }
  190. /**
  191. * Method to do any prechecks and setup the install paths for the extension
  192. *
  193. * @return void
  194. *
  195. * @since 3.4
  196. * @throws \RuntimeException
  197. */
  198. protected function setupInstallPaths()
  199. {
  200. $group = (string) $this->getManifest()->libraryname;
  201. if (!$group)
  202. {
  203. throw new \RuntimeException(\JText::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_NOFILE'));
  204. }
  205. $this->parent->setPath('extension_root', JPATH_PLATFORM . '/' . implode(DIRECTORY_SEPARATOR, explode('/', $group)));
  206. }
  207. /**
  208. * Method to store the extension to the database
  209. *
  210. * @return void
  211. *
  212. * @since 3.4
  213. * @throws \RuntimeException
  214. */
  215. protected function storeExtension()
  216. {
  217. // Discover installs are stored a little differently
  218. if ($this->route === 'discover_install')
  219. {
  220. $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest'));
  221. $this->extension->manifest_cache = json_encode($manifest_details);
  222. $this->extension->state = 0;
  223. $this->extension->name = $manifest_details['name'];
  224. $this->extension->enabled = 1;
  225. $this->extension->params = $this->parent->getParams();
  226. if (!$this->extension->store())
  227. {
  228. // Install failed, roll back changes
  229. throw new \RuntimeException(\JText::_('JLIB_INSTALLER_ERROR_LIB_DISCOVER_STORE_DETAILS'));
  230. }
  231. return;
  232. }
  233. $this->extension->name = $this->name;
  234. $this->extension->type = 'library';
  235. $this->extension->element = $this->element;
  236. // There is no folder for libraries
  237. $this->extension->folder = '';
  238. $this->extension->enabled = 1;
  239. $this->extension->protected = 0;
  240. $this->extension->access = 1;
  241. $this->extension->client_id = 0;
  242. $this->extension->params = $this->parent->getParams();
  243. // Custom data
  244. $this->extension->custom_data = '';
  245. $this->extension->system_data = '';
  246. // Update the manifest cache for the entry
  247. $this->extension->manifest_cache = $this->parent->generateManifestCache();
  248. if (!$this->extension->store())
  249. {
  250. // Install failed, roll back changes
  251. throw new \RuntimeException(
  252. \JText::sprintf(
  253. 'JLIB_INSTALLER_ABORT_LIB_INSTALL_ROLLBACK',
  254. $this->extension->getError()
  255. )
  256. );
  257. }
  258. // Since we have created a library item, we add it to the installation step stack
  259. // so that if we have to rollback the changes we can undo it.
  260. $this->parent->pushStep(array('type' => 'extension', 'id' => $this->extension->extension_id));
  261. }
  262. /**
  263. * Custom update method
  264. *
  265. * @return boolean|integer The extension ID on success, boolean false on failure
  266. *
  267. * @since 3.1
  268. */
  269. public function update()
  270. {
  271. // Since this is just files, an update removes old files
  272. // Get the extension manifest object
  273. $this->setManifest($this->parent->getManifest());
  274. // Set the overwrite setting
  275. $this->parent->setOverwrite(true);
  276. $this->parent->setUpgrade(true);
  277. // And make sure the route is set correctly
  278. $this->setRoute('update');
  279. /*
  280. * ---------------------------------------------------------------------------------------------
  281. * Manifest Document Setup Section
  282. * ---------------------------------------------------------------------------------------------
  283. */
  284. // Set the extensions name
  285. $name = (string) $this->getManifest()->name;
  286. $name = \JFilterInput::getInstance()->clean($name, 'string');
  287. $element = str_replace('.xml', '', basename($this->parent->getPath('manifest')));
  288. $this->set('name', $name);
  289. $this->set('element', $element);
  290. // We don't want to compromise this instance!
  291. $installer = new Installer;
  292. $db = $this->parent->getDbo();
  293. $query = $db->getQuery(true)
  294. ->select($db->quoteName('extension_id'))
  295. ->from($db->quoteName('#__extensions'))
  296. ->where($db->quoteName('type') . ' = ' . $db->quote('library'))
  297. ->where($db->quoteName('element') . ' = ' . $db->quote($element));
  298. $db->setQuery($query);
  299. $result = $db->loadResult();
  300. if ($result)
  301. {
  302. // Already installed, which would make sense
  303. $installer->setPackageUninstall(true);
  304. $installer->uninstall('library', $result);
  305. // Clear the cached data
  306. $this->currentExtensionId = null;
  307. $this->extension = Table::getInstance('Extension', 'JTable', array('dbo' => $this->db));
  308. }
  309. // Now create the new files
  310. return $this->install();
  311. }
  312. /**
  313. * Custom uninstall method
  314. *
  315. * @param string $id The id of the library to uninstall.
  316. *
  317. * @return boolean True on success
  318. *
  319. * @since 3.1
  320. */
  321. public function uninstall($id)
  322. {
  323. $retval = true;
  324. // First order of business will be to load the module object table from the database.
  325. // This should give us the necessary information to proceed.
  326. $row = Table::getInstance('extension');
  327. if (!$row->load((int) $id) || $row->element === '')
  328. {
  329. \JLog::add(\JText::_('ERRORUNKOWNEXTENSION'), \JLog::WARNING, 'jerror');
  330. return false;
  331. }
  332. // Is the library we are trying to uninstall a core one?
  333. // Because that is not a good idea...
  334. if ($row->protected)
  335. {
  336. \JLog::add(\JText::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_WARNCORELIBRARY'), \JLog::WARNING, 'jerror');
  337. return false;
  338. }
  339. /*
  340. * Does this extension have a parent package?
  341. * If so, check if the package disallows individual extensions being uninstalled if the package is not being uninstalled
  342. */
  343. if ($row->package_id && !$this->parent->isPackageUninstall() && !$this->canUninstallPackageChild($row->package_id))
  344. {
  345. \JLog::add(\JText::sprintf('JLIB_INSTALLER_ERROR_CANNOT_UNINSTALL_CHILD_OF_PACKAGE', $row->name), \JLog::WARNING, 'jerror');
  346. return false;
  347. }
  348. $manifestFile = JPATH_MANIFESTS . '/libraries/' . $row->element . '.xml';
  349. // Because libraries may not have their own folders we cannot use the standard method of finding an installation manifest
  350. if (file_exists($manifestFile))
  351. {
  352. $manifest = new LibraryManifest($manifestFile);
  353. // Set the library root path
  354. $this->parent->setPath('extension_root', JPATH_PLATFORM . '/' . $manifest->libraryname);
  355. $xml = simplexml_load_file($manifestFile);
  356. // If we cannot load the XML file return null
  357. if (!$xml)
  358. {
  359. \JLog::add(\JText::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_LOAD_MANIFEST'), \JLog::WARNING, 'jerror');
  360. return false;
  361. }
  362. // Check for a valid XML root tag.
  363. if ($xml->getName() !== 'extension')
  364. {
  365. \JLog::add(\JText::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_INVALID_MANIFEST'), \JLog::WARNING, 'jerror');
  366. return false;
  367. }
  368. $this->parent->removeFiles($xml->files, -1);
  369. \JFile::delete($manifestFile);
  370. }
  371. else
  372. {
  373. // Remove this row entry since its invalid
  374. $row->delete($row->extension_id);
  375. unset($row);
  376. \JLog::add(\JText::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_INVALID_NOTFOUND_MANIFEST'), \JLog::WARNING, 'jerror');
  377. return false;
  378. }
  379. // TODO: Change this so it walked up the path backwards so we clobber multiple empties
  380. // If the folder is empty, let's delete it
  381. if (\JFolder::exists($this->parent->getPath('extension_root')))
  382. {
  383. if (is_dir($this->parent->getPath('extension_root')))
  384. {
  385. $files = \JFolder::files($this->parent->getPath('extension_root'));
  386. if (!count($files))
  387. {
  388. \JFolder::delete($this->parent->getPath('extension_root'));
  389. }
  390. }
  391. }
  392. $this->parent->removeFiles($xml->media);
  393. $this->parent->removeFiles($xml->languages);
  394. $row->delete($row->extension_id);
  395. unset($row);
  396. return $retval;
  397. }
  398. /**
  399. * Custom discover method
  400. *
  401. * @return array Extension list of extensions available
  402. *
  403. * @since 3.1
  404. */
  405. public function discover()
  406. {
  407. $results = array();
  408. $file_list = \JFolder::files(JPATH_MANIFESTS . '/libraries', '\.xml$');
  409. foreach ($file_list as $file)
  410. {
  411. $manifest_details = Installer::parseXMLInstallFile(JPATH_MANIFESTS . '/libraries/' . $file);
  412. $file = \JFile::stripExt($file);
  413. $extension = Table::getInstance('extension');
  414. $extension->set('type', 'library');
  415. $extension->set('client_id', 0);
  416. $extension->set('element', $file);
  417. $extension->set('folder', '');
  418. $extension->set('name', $file);
  419. $extension->set('state', -1);
  420. $extension->set('manifest_cache', json_encode($manifest_details));
  421. $extension->set('params', '{}');
  422. $results[] = $extension;
  423. }
  424. return $results;
  425. }
  426. /**
  427. * Refreshes the extension table cache
  428. *
  429. * @return boolean Result of operation, true if updated, false on failure
  430. *
  431. * @since 3.1
  432. */
  433. public function refreshManifestCache()
  434. {
  435. // Need to find to find where the XML file is since we don't store this normally
  436. $manifestPath = JPATH_MANIFESTS . '/libraries/' . $this->parent->extension->element . '.xml';
  437. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  438. $this->parent->setPath('manifest', $manifestPath);
  439. $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest'));
  440. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  441. $this->parent->extension->name = $manifest_details['name'];
  442. try
  443. {
  444. return $this->parent->extension->store();
  445. }
  446. catch (\RuntimeException $e)
  447. {
  448. \JLog::add(\JText::_('JLIB_INSTALLER_ERROR_LIB_REFRESH_MANIFEST_CACHE'), \JLog::WARNING, 'jerror');
  449. return false;
  450. }
  451. }
  452. }