PageRenderTime 55ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

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

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