PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/libraries/joomla/installer/adapters/language.php

http://github.com/joomla/joomla-platform
PHP | 664 lines | 437 code | 70 blank | 157 comment | 56 complexity | 6abec7eb9cca95c157346ab74459d553 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. * Language installer
  13. *
  14. * @package Joomla.Platform
  15. * @subpackage Installer
  16. * @since 11.1
  17. */
  18. class JInstallerLanguage extends JAdapterInstance
  19. {
  20. /**
  21. * Core language pack flag
  22. *
  23. * @var boolean
  24. * @since 11.1
  25. */
  26. protected $_core = false;
  27. /**
  28. * Custom install method
  29. *
  30. * Note: This behaves badly due to hacks made in the middle of 1.5.x to add
  31. * the ability to install multiple distinct packs in one install. The
  32. * preferred method is to use a package to install multiple language packs.
  33. *
  34. * @return boolean True on success
  35. *
  36. * @since 11.1
  37. */
  38. public function install()
  39. {
  40. $source = $this->parent->getPath('source');
  41. if (!$source)
  42. {
  43. $this->parent
  44. ->setPath(
  45. 'source',
  46. ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/language/' . $this->parent->extension->element
  47. );
  48. }
  49. $this->manifest = $this->parent->getManifest();
  50. $root = $this->manifest->document;
  51. // Get the client application target
  52. if ($cname = (string) $this->manifest->attributes()->client)
  53. {
  54. // Attempt to map the client to a base path
  55. $client = JApplicationHelper::getClientInfo($cname, true);
  56. if ($client === null)
  57. {
  58. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname)));
  59. return false;
  60. }
  61. $basePath = $client->path;
  62. $clientId = $client->id;
  63. $element = $this->manifest->files;
  64. return $this->_install($cname, $basePath, $clientId, $element);
  65. }
  66. else
  67. {
  68. // No client attribute was found so we assume the site as the client
  69. $cname = 'site';
  70. $basePath = JPATH_SITE;
  71. $clientId = 0;
  72. $element = $this->manifest->files;
  73. return $this->_install($cname, $basePath, $clientId, $element);
  74. }
  75. }
  76. /**
  77. * Install function that is designed to handle individual clients
  78. *
  79. * @param string $cname Cname @todo: not used
  80. * @param string $basePath The base name.
  81. * @param integer $clientId The client id.
  82. * @param object &$element The XML element.
  83. *
  84. * @return boolean
  85. *
  86. * @since 11.1
  87. */
  88. protected function _install($cname, $basePath, $clientId, &$element)
  89. {
  90. $this->manifest = $this->parent->getManifest();
  91. // Get the language name
  92. // Set the extensions name
  93. $name = JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd');
  94. $this->set('name', $name);
  95. // Get the Language tag [ISO tag, eg. en-GB]
  96. $tag = (string) $this->manifest->tag;
  97. // Check if we found the tag - if we didn't, we may be trying to install from an older language package
  98. if (!$tag)
  99. {
  100. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG')));
  101. return false;
  102. }
  103. $this->set('tag', $tag);
  104. // Set the language installation path
  105. $this->parent->setPath('extension_site', $basePath . '/language/' . $tag);
  106. // Do we have a meta file in the file list? In other words... is this a core language pack?
  107. if ($element && count($element->children()))
  108. {
  109. $files = $element->children();
  110. foreach ($files as $file)
  111. {
  112. if ((string) $file->attributes()->file == 'meta')
  113. {
  114. $this->_core = true;
  115. break;
  116. }
  117. }
  118. }
  119. // Either we are installing a core pack or a core pack must exist for the language we are installing.
  120. if (!$this->_core)
  121. {
  122. if (!JFile::exists($this->parent->getPath('extension_site') . '/' . $this->get('tag') . '.xml'))
  123. {
  124. $this->parent
  125. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_NO_CORE_LANGUAGE', $this->get('tag'))));
  126. return false;
  127. }
  128. }
  129. // If the language directory does not exist, let's create it
  130. $created = false;
  131. if (!file_exists($this->parent->getPath('extension_site')))
  132. {
  133. if (!$created = JFolder::create($this->parent->getPath('extension_site')))
  134. {
  135. $this->parent
  136. ->abort(
  137. JText::sprintf(
  138. 'JLIB_INSTALLER_ABORT',
  139. JText::sprintf('JLIB_INSTALLER_ERROR_CREATE_FOLDER_FAILED', $this->parent->getPath('extension_site'))
  140. )
  141. );
  142. return false;
  143. }
  144. }
  145. else
  146. {
  147. // Look for an update function or update tag
  148. $updateElement = $this->manifest->update;
  149. /*
  150. * Upgrade manually set or
  151. * Update function available or
  152. * Update tag detected
  153. */
  154. if ($this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
  155. || $updateElement)
  156. {
  157. // Transfer control to the update function
  158. return $this->update();
  159. }
  160. elseif (!$this->parent->isOverwrite())
  161. {
  162. // Overwrite is set
  163. // We didn't have overwrite set, find an update function or find an update tag so lets call it safe
  164. if (file_exists($this->parent->getPath('extension_site')))
  165. {
  166. // If the site exists say so.
  167. JError::raiseWarning(
  168. 1,
  169. JText::sprintf(
  170. 'JLIB_INSTALLER_ABORT',
  171. JText::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_site'))
  172. )
  173. );
  174. }
  175. else
  176. {
  177. // If the admin exists say so.
  178. JError::raiseWarning(
  179. 1,
  180. JText::sprintf(
  181. 'JLIB_INSTALLER_ABORT',
  182. JText::sprintf('JLIB_INSTALLER_ERROR_FOLDER_IN_USE', $this->parent->getPath('extension_administrator'))
  183. )
  184. );
  185. }
  186. return false;
  187. }
  188. }
  189. /*
  190. * If we created the language directory we will want to remove it if we
  191. * have to roll back the installation, so let's add it to the installation
  192. * step stack
  193. */
  194. if ($created)
  195. {
  196. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_site')));
  197. }
  198. // Copy all the necessary files
  199. if ($this->parent->parseFiles($element) === false)
  200. {
  201. // Install failed, rollback changes
  202. $this->parent->abort();
  203. return false;
  204. }
  205. // Parse optional tags
  206. $this->parent->parseMedia($this->manifest->media);
  207. // Copy all the necessary font files to the common pdf_fonts directory
  208. $this->parent->setPath('extension_site', $basePath . '/language/pdf_fonts');
  209. $overwrite = $this->parent->setOverwrite(true);
  210. if ($this->parent->parseFiles($this->manifest->fonts) === false)
  211. {
  212. // Install failed, rollback changes
  213. $this->parent->abort();
  214. return false;
  215. }
  216. $this->parent->setOverwrite($overwrite);
  217. // Get the language description
  218. $description = (string) $this->manifest->description;
  219. if ($description)
  220. {
  221. $this->parent->set('message', JText::_($description));
  222. }
  223. else
  224. {
  225. $this->parent->set('message', '');
  226. }
  227. // Add an entry to the extension table with a whole heap of defaults
  228. $row = JTable::getInstance('extension');
  229. $row->set('name', $this->get('name'));
  230. $row->set('type', 'language');
  231. $row->set('element', $this->get('tag'));
  232. // There is no folder for languages
  233. $row->set('folder', '');
  234. $row->set('enabled', 1);
  235. $row->set('protected', 0);
  236. $row->set('access', 0);
  237. $row->set('client_id', $clientId);
  238. $row->set('params', $this->parent->getParams());
  239. $row->set('manifest_cache', $this->parent->generateManifestCache());
  240. if (!$row->store())
  241. {
  242. // Install failed, roll back changes
  243. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', $row->getError()));
  244. return false;
  245. }
  246. // Clobber any possible pending updates
  247. $update = JTable::getInstance('update');
  248. $uid = $update->find(array('element' => $this->get('tag'), 'type' => 'language', 'client_id' => '', 'folder' => ''));
  249. if ($uid)
  250. {
  251. $update->delete($uid);
  252. }
  253. return $row->get('extension_id');
  254. }
  255. /**
  256. * Custom update method
  257. *
  258. * @return boolean True on success, false on failure
  259. *
  260. * @since 11.1
  261. */
  262. public function update()
  263. {
  264. $xml = $this->parent->getManifest();
  265. $this->manifest = $xml;
  266. $cname = $xml->attributes()->client;
  267. // Attempt to map the client to a base path
  268. $client = JApplicationHelper::getClientInfo($cname, true);
  269. if ($client === null || (empty($cname) && $cname !== 0))
  270. {
  271. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_UNKNOWN_CLIENT_TYPE', $cname)));
  272. return false;
  273. }
  274. $basePath = $client->path;
  275. $clientId = $client->id;
  276. // Get the language name
  277. // Set the extensions name
  278. $name = (string) $this->manifest->name;
  279. $name = JFilterInput::getInstance()->clean($name, 'cmd');
  280. $this->set('name', $name);
  281. // Get the Language tag [ISO tag, eg. en-GB]
  282. $tag = (string) $xml->tag;
  283. // Check if we found the tag - if we didn't, we may be trying to install from an older language package
  284. if (!$tag)
  285. {
  286. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::_('JLIB_INSTALLER_ERROR_NO_LANGUAGE_TAG')));
  287. return false;
  288. }
  289. $this->set('tag', $tag);
  290. $folder = $tag;
  291. // Set the language installation path
  292. $this->parent->setPath('extension_site', $basePath . '/language/' . $this->get('tag'));
  293. // Do we have a meta file in the file list? In other words... is this a core language pack?
  294. if (count($xml->files->children()))
  295. {
  296. foreach ($xml->files->children() as $file)
  297. {
  298. if ((string) $file->attributes()->file == 'meta')
  299. {
  300. $this->_core = true;
  301. break;
  302. }
  303. }
  304. }
  305. // Either we are installing a core pack or a core pack must exist for the language we are installing.
  306. if (!$this->_core)
  307. {
  308. if (!JFile::exists($this->parent->getPath('extension_site') . '/' . $this->get('tag') . '.xml'))
  309. {
  310. $this->parent
  311. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT', JText::sprintf('JLIB_INSTALLER_ERROR_NO_CORE_LANGUAGE', $this->get('tag'))));
  312. return false;
  313. }
  314. }
  315. // Copy all the necessary files
  316. if ($this->parent->parseFiles($xml->files) === false)
  317. {
  318. // Install failed, rollback changes
  319. $this->parent->abort();
  320. return false;
  321. }
  322. // Parse optional tags
  323. $this->parent->parseMedia($xml->media);
  324. // Copy all the necessary font files to the common pdf_fonts directory
  325. $this->parent->setPath('extension_site', $basePath . '/language/pdf_fonts');
  326. $overwrite = $this->parent->setOverwrite(true);
  327. if ($this->parent->parseFiles($xml->fonts) === false)
  328. {
  329. // Install failed, rollback changes
  330. $this->parent->abort();
  331. return false;
  332. }
  333. $this->parent->setOverwrite($overwrite);
  334. // Get the language description and set it as message
  335. $this->parent->set('message', (string) $xml->description);
  336. // Finalization and Cleanup Section
  337. // Clobber any possible pending updates
  338. $update = JTable::getInstance('update');
  339. $uid = $update->find(array('element' => $this->get('tag'), 'type' => 'language', 'client_id' => $clientId));
  340. if ($uid)
  341. {
  342. $update->delete($uid);
  343. }
  344. // Update an entry to the extension table
  345. $row = JTable::getInstance('extension');
  346. $eid = $row->find(array('element' => strtolower($this->get('tag')), 'type' => 'language', 'client_id' => $clientId));
  347. if ($eid)
  348. {
  349. $row->load($eid);
  350. }
  351. else
  352. {
  353. // Set the defaults
  354. // There is no folder for language
  355. $row->set('folder', '');
  356. $row->set('enabled', 1);
  357. $row->set('protected', 0);
  358. $row->set('access', 0);
  359. $row->set('client_id', $clientId);
  360. $row->set('params', $this->parent->getParams());
  361. }
  362. $row->set('name', $this->get('name'));
  363. $row->set('type', 'language');
  364. $row->set('element', $this->get('tag'));
  365. $row->set('manifest_cache', $this->parent->generateManifestCache());
  366. if (!$row->store())
  367. {
  368. // Install failed, roll back changes
  369. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT', $row->getError()));
  370. return false;
  371. }
  372. // And now we run the postflight
  373. ob_start();
  374. ob_implicit_flush(false);
  375. if ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'postflight'))
  376. {
  377. $this->parent->manifestClass->postflight('update', $this);
  378. }
  379. // Append messages
  380. $msg .= ob_get_contents();
  381. ob_end_clean();
  382. if ($msg != '')
  383. {
  384. $this->parent->set('extension_message', $msg);
  385. }
  386. return $row->get('extension_id');
  387. }
  388. /**
  389. * Custom uninstall method
  390. *
  391. * @param string $eid The tag of the language to uninstall
  392. *
  393. * @return mixed Return value for uninstall method in component uninstall file
  394. *
  395. * @since 11.1
  396. */
  397. public function uninstall($eid)
  398. {
  399. // Load up the extension details
  400. $extension = JTable::getInstance('extension');
  401. $extension->load($eid);
  402. // Grab a copy of the client details
  403. $client = JApplicationHelper::getClientInfo($extension->get('client_id'));
  404. // Check the element isn't blank to prevent nuking the languages directory...just in case
  405. $element = $extension->get('element');
  406. if (empty($element))
  407. {
  408. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_ELEMENT_EMPTY'));
  409. return false;
  410. }
  411. // Check that the language is not protected, Normally en-GB.
  412. $protected = $extension->get('protected');
  413. if ($protected == 1)
  414. {
  415. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PROTECTED'));
  416. return false;
  417. }
  418. // Verify that it's not the default language for that client
  419. $params = JComponentHelper::getParams('com_languages');
  420. if ($params->get($client->name) == $element)
  421. {
  422. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DEFAULT'));
  423. return false;
  424. }
  425. // Construct the path from the client, the language and the extension element name
  426. $path = $client->path . '/language/' . $element;
  427. // Get the package manifest object and remove media
  428. $this->parent->setPath('source', $path);
  429. // We do findManifest to avoid problem when uninstalling a list of extension: getManifest cache its manifest file
  430. $this->parent->findManifest();
  431. $this->manifest = $this->parent->getManifest();
  432. $this->parent->removeFiles($this->manifest->media);
  433. // Check it exists
  434. if (!JFolder::exists($path))
  435. {
  436. // If the folder doesn't exist lets just nuke the row as well and presume the user killed it for us
  437. $extension->delete();
  438. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_PATH_EMPTY'));
  439. return false;
  440. }
  441. if (!JFolder::delete($path))
  442. {
  443. // If deleting failed we'll leave the extension entry in tact just in case
  444. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_LANG_UNINSTALL_DIRECTORY'));
  445. return false;
  446. }
  447. // Remove the extension table entry
  448. $extension->delete();
  449. // Setting the language of users which have this language as the default language
  450. $db = JFactory::getDbo();
  451. $query = $db->getQuery(true);
  452. $query->from('#__users');
  453. $query->select('*');
  454. $db->setQuery($query);
  455. $users = $db->loadObjectList();
  456. if ($client->name == 'administrator')
  457. {
  458. $param_name = 'admin_language';
  459. }
  460. else
  461. {
  462. $param_name = 'language';
  463. }
  464. $count = 0;
  465. foreach ($users as $user)
  466. {
  467. $registry = new JRegistry;
  468. $registry->loadString($user->params);
  469. if ($registry->get($param_name) == $element)
  470. {
  471. $registry->set($param_name, '');
  472. $query = $db->getQuery(true);
  473. $query->update('#__users');
  474. $query->set('params=' . $db->quote($registry));
  475. $query->where('id=' . (int) $user->id);
  476. $db->setQuery($query);
  477. $db->query();
  478. $count = $count + 1;
  479. }
  480. }
  481. if (!empty($count))
  482. {
  483. JError::raiseNotice(500, JText::plural('JLIB_INSTALLER_NOTICE_LANG_RESET_USERS', $count));
  484. }
  485. // All done!
  486. return true;
  487. }
  488. /**
  489. * Custom discover method
  490. * Finds language files
  491. *
  492. * @return boolean True on success
  493. *
  494. * @since 11.1
  495. */
  496. public function discover()
  497. {
  498. $results = array();
  499. $site_languages = JFolder::folders(JPATH_SITE . '/language');
  500. $admin_languages = JFolder::folders(JPATH_ADMINISTRATOR . '/language');
  501. foreach ($site_languages as $language)
  502. {
  503. if (file_exists(JPATH_SITE . '/language/' . $language . '/' . $language . '.xml'))
  504. {
  505. $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . '/language/' . $language . '/' . $language . '.xml');
  506. $extension = JTable::getInstance('extension');
  507. $extension->set('type', 'language');
  508. $extension->set('client_id', 0);
  509. $extension->set('element', $language);
  510. $extension->set('name', $language);
  511. $extension->set('state', -1);
  512. $extension->set('manifest_cache', json_encode($manifest_details));
  513. $results[] = $extension;
  514. }
  515. }
  516. foreach ($admin_languages as $language)
  517. {
  518. if (file_exists(JPATH_ADMINISTRATOR . '/language/' . $language . '/' . $language . '.xml'))
  519. {
  520. $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . '/language/' . $language . '/' . $language . '.xml');
  521. $extension = JTable::getInstance('extension');
  522. $extension->set('type', 'language');
  523. $extension->set('client_id', 1);
  524. $extension->set('element', $language);
  525. $extension->set('name', $language);
  526. $extension->set('state', -1);
  527. $extension->set('manifest_cache', json_encode($manifest_details));
  528. $results[] = $extension;
  529. }
  530. }
  531. return $results;
  532. }
  533. /**
  534. * Custom discover install method
  535. * Basically updates the manifest cache and leaves everything alone
  536. *
  537. * @return integer The extension id
  538. *
  539. * @since 11.1
  540. */
  541. public function discover_install()
  542. {
  543. // Need to find to find where the XML file is since we don't store this normally
  544. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  545. $short_element = $this->parent->extension->element;
  546. $manifestPath = $client->path . '/language/' . $short_element . '/' . $short_element . '.xml';
  547. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  548. $this->parent->setPath('manifest', $manifestPath);
  549. $this->parent->setPath('source', $client->path . '/language/' . $short_element);
  550. $this->parent->setPath('extension_root', $this->parent->getPath('source'));
  551. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  552. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  553. $this->parent->extension->state = 0;
  554. $this->parent->extension->name = $manifest_details['name'];
  555. $this->parent->extension->enabled = 1;
  556. // @todo remove code: $this->parent->extension->params = $this->parent->getParams();
  557. try
  558. {
  559. $this->parent->extension->store();
  560. }
  561. catch (JException $e)
  562. {
  563. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_LANG_DISCOVER_STORE_DETAILS'));
  564. return false;
  565. }
  566. return $this->parent->extension->get('extension_id');
  567. }
  568. /**
  569. * Refreshes the extension table cache
  570. *
  571. * @return boolean result of operation, true if updated, false on failure
  572. *
  573. * @since 11.1
  574. */
  575. public function refreshManifestCache()
  576. {
  577. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  578. $manifestPath = $client->path . '/language/' . $this->parent->extension->element . '/' . $this->parent->extension->element . '.xml';
  579. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  580. $this->parent->setPath('manifest', $manifestPath);
  581. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  582. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  583. $this->parent->extension->name = $manifest_details['name'];
  584. if ($this->parent->extension->store())
  585. {
  586. return true;
  587. }
  588. else
  589. {
  590. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_MOD_REFRESH_MANIFEST_CACHE'));
  591. return false;
  592. }
  593. }
  594. }