/libraries/joomla/installer/adapters/template.php

https://bitbucket.org/kraymitchell/fcd · PHP · 602 lines · 394 code · 82 blank · 126 comment · 47 complexity · 90fb37749179e7120066021039413b51 MD5 · raw file

  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.installer.extension');
  11. jimport('joomla.base.adapterinstance');
  12. /**
  13. * Template installer
  14. *
  15. * @package Joomla.Platform
  16. * @subpackage Installer
  17. * @since 11.1
  18. */
  19. class JInstallerTemplate extends JAdapterInstance
  20. {
  21. protected $name = null;
  22. protected $element = null;
  23. protected $route = 'install';
  24. /**
  25. * Custom loadLanguage method
  26. *
  27. * @param string $path The path where to find language files.
  28. *
  29. * @return JInstallerTemplate
  30. *
  31. * @since 11.1
  32. */
  33. public function loadLanguage($path = null)
  34. {
  35. $source = $this->parent->getPath('source');
  36. if (!$source)
  37. {
  38. $this->parent
  39. ->setPath(
  40. 'source',
  41. ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/templates/' . $this->parent->extension->element
  42. );
  43. }
  44. $clientId = isset($this->parent->extension) ? $this->parent->extension->client_id : 0;
  45. $this->manifest = $this->parent->getManifest();
  46. $name = strtolower(JFilterInput::getInstance()->clean((string) $this->manifest->name, 'cmd'));
  47. $client = (string) $this->manifest->attributes()->client;
  48. // Load administrator language if not set.
  49. if (!$client)
  50. {
  51. $client = 'ADMINISTRATOR';
  52. }
  53. $extension = "tpl_$name";
  54. $lang = JFactory::getLanguage();
  55. $source = $path ? $path : ($this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/templates/' . $name;
  56. $lang->load($extension . '.sys', $source, null, false, false)
  57. || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), null, false, false)
  58. || $lang->load($extension . '.sys', $source, $lang->getDefault(), false, false)
  59. || $lang->load($extension . '.sys', constant('JPATH_' . strtoupper($client)), $lang->getDefault(), false, false);
  60. }
  61. /**
  62. * Custom install method
  63. *
  64. * @return boolean True on success
  65. *
  66. * @since 11.1
  67. */
  68. public function install()
  69. {
  70. // Get a database connector object
  71. $db = $this->parent->getDbo();
  72. $lang = JFactory::getLanguage();
  73. $xml = $this->parent->getManifest();
  74. // Get the client application target
  75. if ($cname = (string) $xml->attributes()->client)
  76. {
  77. // Attempt to map the client to a base path
  78. $client = JApplicationHelper::getClientInfo($cname, true);
  79. if ($client === false)
  80. {
  81. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_UNKNOWN_CLIENT', $cname));
  82. return false;
  83. }
  84. $basePath = $client->path;
  85. $clientId = $client->id;
  86. }
  87. else
  88. {
  89. // No client attribute was found so we assume the site as the client
  90. $cname = 'site';
  91. $basePath = JPATH_SITE;
  92. $clientId = 0;
  93. }
  94. // Set the extension's name
  95. $name = JFilterInput::getInstance()->clean((string) $xml->name, 'cmd');
  96. $element = strtolower(str_replace(" ", "_", $name));
  97. $this->set('name', $name);
  98. $this->set('element', $element);
  99. // Check to see if a template by the same name is already installed.
  100. $query = $db->getQuery(true);
  101. $query->select($query->qn('extension_id'))->from($query->qn('#__extensions'));
  102. $query->where($query->qn('type') . ' = ' . $query->q('template'));
  103. $query->where($query->qn('element') . ' = ' . $query->q($element));
  104. $db->setQuery($query);
  105. try
  106. {
  107. $id = $db->loadResult();
  108. }
  109. catch (RuntimeException $e)
  110. {
  111. // Install failed, roll back changes
  112. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK'), $e->getMessage());
  113. return false;
  114. }
  115. // Legacy error handling switch based on the JError::$legacy switch.
  116. // @deprecated 12.1
  117. if (JError::$legacy && $db->getErrorNum())
  118. {
  119. // Install failed, roll back changes
  120. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK', $db->stderr(true)));
  121. return false;
  122. }
  123. // Set the template root path
  124. $this->parent->setPath('extension_root', $basePath . '/templates/' . $element);
  125. // if it's on the fs...
  126. if (file_exists($this->parent->getPath('extension_root')) && (!$this->parent->isOverwrite() || $this->parent->isUpgrade()))
  127. {
  128. $updateElement = $xml->update;
  129. // Upgrade manually set or
  130. // Update function available or
  131. // Update tag detected
  132. if ($this->parent->isUpgrade() || ($this->parent->manifestClass && method_exists($this->parent->manifestClass, 'update'))
  133. || $updateElement)
  134. {
  135. // Force this one
  136. $this->parent->setOverwrite(true);
  137. $this->parent->setUpgrade(true);
  138. if ($id)
  139. {
  140. // if there is a matching extension mark this as an update; semantics really
  141. $this->route = 'update';
  142. }
  143. }
  144. elseif (!$this->parent->isOverwrite())
  145. {
  146. // Overwrite is not set
  147. // If we didn't have overwrite set, find an update function or find an update tag so let's call it safe
  148. $this->parent
  149. ->abort(
  150. JText::sprintf(
  151. 'JLIB_INSTALLER_ABORT_TPL_INSTALL_ANOTHER_TEMPLATE_USING_DIRECTORY', JText::_('JLIB_INSTALLER_' . $this->route),
  152. $this->parent->getPath('extension_root')
  153. )
  154. );
  155. return false;
  156. }
  157. }
  158. /*
  159. * If the template directory already exists, then we will assume that the template is already
  160. * installed or another template is using that directory.
  161. */
  162. if (file_exists($this->parent->getPath('extension_root')) && !$this->parent->isOverwrite())
  163. {
  164. JError::raiseWarning(
  165. 100,
  166. JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ANOTHER_TEMPLATE_USING_DIRECTORY', $this->parent->getPath('extension_root'))
  167. );
  168. return false;
  169. }
  170. // If the template directory does not exist, let's create it
  171. $created = false;
  172. if (!file_exists($this->parent->getPath('extension_root')))
  173. {
  174. if (!$created = JFolder::create($this->parent->getPath('extension_root')))
  175. {
  176. $this->parent
  177. ->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_FAILED_CREATE_DIRECTORY', $this->parent->getPath('extension_root')));
  178. return false;
  179. }
  180. }
  181. // If we created the template directory and will want to remove it if we have to roll back
  182. // the installation, let's add it to the installation step stack
  183. if ($created)
  184. {
  185. $this->parent->pushStep(array('type' => 'folder', 'path' => $this->parent->getPath('extension_root')));
  186. }
  187. // Copy all the necessary files
  188. if ($this->parent->parseFiles($xml->files, -1) === false)
  189. {
  190. // Install failed, rollback changes
  191. $this->parent->abort();
  192. return false;
  193. }
  194. if ($this->parent->parseFiles($xml->images, -1) === false)
  195. {
  196. // Install failed, rollback changes
  197. $this->parent->abort();
  198. return false;
  199. }
  200. if ($this->parent->parseFiles($xml->css, -1) === false)
  201. {
  202. // Install failed, rollback changes
  203. $this->parent->abort();
  204. return false;
  205. }
  206. // Parse optional tags
  207. $this->parent->parseMedia($xml->media);
  208. $this->parent->parseLanguages($xml->languages, $clientId);
  209. // Get the template description
  210. $this->parent->set('message', JText::_((string) $xml->description));
  211. // Lastly, we will copy the manifest file to its appropriate place.
  212. if (!$this->parent->copyManifest(-1))
  213. {
  214. // Install failed, rollback changes
  215. $this->parent->abort(JText::_('JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_SETUP'));
  216. return false;
  217. }
  218. // Extension Registration
  219. $row = JTable::getInstance('extension');
  220. if ($this->route == 'update' && $id)
  221. {
  222. $row->load($id);
  223. }
  224. else
  225. {
  226. $row->type = 'template';
  227. $row->element = $this->get('element');
  228. // There is no folder for templates
  229. $row->folder = '';
  230. $row->enabled = 1;
  231. $row->protected = 0;
  232. $row->access = 1;
  233. $row->client_id = $clientId;
  234. $row->params = $this->parent->getParams();
  235. $row->custom_data = ''; // custom data
  236. }
  237. $row->name = $this->get('name'); // name might change in an update
  238. $row->manifest_cache = $this->parent->generateManifestCache();
  239. if (!$row->store())
  240. {
  241. // Install failed, roll back changes
  242. $this->parent->abort(JText::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_ROLLBACK', $db->stderr(true)));
  243. return false;
  244. }
  245. if ($this->route == 'install')
  246. {
  247. //insert record in #__template_styles
  248. $query = $db->getQuery(true);
  249. $query->insert($db->quoteName('#__template_styles'));
  250. $debug = $lang->setDebug(false);
  251. $columns = array($db->quoteName('template'),
  252. $db->quoteName('client_id'),
  253. $db->quoteName('home'),
  254. $db->quoteName('title'),
  255. $db->quoteName('params')
  256. );
  257. $query->columns($columns);
  258. $query->values(
  259. $db->Quote($row->element)
  260. . ',' . $db->Quote($clientId)
  261. . ',' . $db->Quote(0)
  262. . ',' . $db->Quote(JText::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', JText::_($this->get('name'))))
  263. . ',' . $db->Quote($row->params)
  264. );
  265. $lang->setDebug($debug);
  266. $db->setQuery($query);
  267. // There is a chance this could fail but we don't care...
  268. $db->execute();
  269. }
  270. return $row->get('extension_id');
  271. }
  272. /**
  273. * Custom update method for components
  274. *
  275. * @return boolean True on success
  276. *
  277. * @since 11.1
  278. */
  279. public function update()
  280. {
  281. return $this->install();
  282. }
  283. /**
  284. * Custom uninstall method
  285. *
  286. * @param integer $id The extension ID
  287. *
  288. * @return boolean True on success
  289. *
  290. * @since 11.1
  291. */
  292. public function uninstall($id)
  293. {
  294. // Initialise variables.
  295. $retval = true;
  296. // First order of business will be to load the template object table from the database.
  297. // This should give us the necessary information to proceed.
  298. $row = JTable::getInstance('extension');
  299. if (!$row->load((int) $id) || !strlen($row->element))
  300. {
  301. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_ERRORUNKOWNEXTENSION'));
  302. return false;
  303. }
  304. // Is the template we are trying to uninstall a core one?
  305. // Because that is not a good idea...
  306. if ($row->protected)
  307. {
  308. JError::raiseWarning(100, JText::sprintf('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_WARNCORETEMPLATE', $row->name));
  309. return false;
  310. }
  311. $name = $row->element;
  312. $clientId = $row->client_id;
  313. // For a template the id will be the template name which represents the subfolder of the templates folder that the template resides in.
  314. if (!$name)
  315. {
  316. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_ID_EMPTY'));
  317. return false;
  318. }
  319. // Deny remove default template
  320. $db = $this->parent->getDbo();
  321. $query = 'SELECT COUNT(*) FROM #__template_styles' . ' WHERE home = 1 AND template = ' . $db->Quote($name);
  322. $db->setQuery($query);
  323. if ($db->loadResult() != 0)
  324. {
  325. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DEFAULT'));
  326. return false;
  327. }
  328. // Get the template root path
  329. $client = JApplicationHelper::getClientInfo($clientId);
  330. if (!$client)
  331. {
  332. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_CLIENT'));
  333. return false;
  334. }
  335. $this->parent->setPath('extension_root', $client->path . '/templates/' . strtolower($name));
  336. $this->parent->setPath('source', $this->parent->getPath('extension_root'));
  337. // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file
  338. $this->parent->findManifest();
  339. $manifest = $this->parent->getManifest();
  340. if (!($manifest instanceof SimpleXMLElement))
  341. {
  342. // Kill the extension entry
  343. $row->delete($row->extension_id);
  344. unset($row);
  345. // Make sure we delete the folders
  346. JFolder::delete($this->parent->getPath('extension_root'));
  347. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_NOTFOUND_MANIFEST'));
  348. return false;
  349. }
  350. // Remove files
  351. $this->parent->removeFiles($manifest->media);
  352. $this->parent->removeFiles($manifest->languages, $clientId);
  353. // Delete the template directory
  354. if (JFolder::exists($this->parent->getPath('extension_root')))
  355. {
  356. $retval = JFolder::delete($this->parent->getPath('extension_root'));
  357. }
  358. else
  359. {
  360. JError::raiseWarning(100, JText::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DIRECTORY'));
  361. $retval = false;
  362. }
  363. // Set menu that assigned to the template back to default template
  364. $query = 'UPDATE #__menu INNER JOIN #__template_styles' . ' ON #__template_styles.id = #__menu.template_style_id'
  365. . ' SET #__menu.template_style_id = 0' . ' WHERE #__template_styles.template = ' . $db->Quote(strtolower($name))
  366. . ' AND #__template_styles.client_id = ' . $db->Quote($clientId);
  367. $db->setQuery($query);
  368. $db->execute();
  369. $query = 'DELETE FROM #__template_styles' . ' WHERE template = ' . $db->Quote($name) . ' AND client_id = ' . $db->Quote($clientId);
  370. $db->setQuery($query);
  371. $db->execute();
  372. $row->delete($row->extension_id);
  373. unset($row);
  374. return $retval;
  375. }
  376. /**
  377. * Discover existing but uninstalled templates
  378. *
  379. * @return array JExtensionTable list
  380. */
  381. public function discover()
  382. {
  383. $results = array();
  384. $site_list = JFolder::folders(JPATH_SITE . '/templates');
  385. $admin_list = JFolder::folders(JPATH_ADMINISTRATOR . '/templates');
  386. $site_info = JApplicationHelper::getClientInfo('site', true);
  387. $admin_info = JApplicationHelper::getClientInfo('administrator', true);
  388. foreach ($site_list as $template)
  389. {
  390. if ($template == 'system')
  391. {
  392. continue;
  393. // Ignore special system template
  394. }
  395. $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_SITE . "/templates/$template/templateDetails.xml");
  396. $extension = JTable::getInstance('extension');
  397. $extension->set('type', 'template');
  398. $extension->set('client_id', $site_info->id);
  399. $extension->set('element', $template);
  400. $extension->set('name', $template);
  401. $extension->set('state', -1);
  402. $extension->set('manifest_cache', json_encode($manifest_details));
  403. $results[] = $extension;
  404. }
  405. foreach ($admin_list as $template)
  406. {
  407. if ($template == 'system')
  408. {
  409. continue;
  410. // Ignore special system template
  411. }
  412. $manifest_details = JApplicationHelper::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml");
  413. $extension = JTable::getInstance('extension');
  414. $extension->set('type', 'template');
  415. $extension->set('client_id', $admin_info->id);
  416. $extension->set('element', $template);
  417. $extension->set('name', $template);
  418. $extension->set('state', -1);
  419. $extension->set('manifest_cache', json_encode($manifest_details));
  420. $results[] = $extension;
  421. }
  422. return $results;
  423. }
  424. /**
  425. * Discover_install
  426. * Perform an install for a discovered extension
  427. *
  428. * @return boolean
  429. *
  430. * @since 11.1
  431. */
  432. public function discover_install()
  433. {
  434. // Templates are one of the easiest
  435. // If its not in the extensions table we just add it
  436. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  437. $lang = JFactory::getLanguage();
  438. $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml';
  439. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  440. $description = (string) $this->parent->manifest->description;
  441. if ($description)
  442. {
  443. $this->parent->set('message', JText::_($description));
  444. }
  445. else
  446. {
  447. $this->parent->set('message', '');
  448. }
  449. $this->parent->setPath('manifest', $manifestPath);
  450. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  451. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  452. $this->parent->extension->state = 0;
  453. $this->parent->extension->name = $manifest_details['name'];
  454. $this->parent->extension->enabled = 1;
  455. $data = new JObject;
  456. foreach ($manifest_details as $key => $value)
  457. {
  458. $data->set($key, $value);
  459. }
  460. $this->parent->extension->params = $this->parent->getParams();
  461. if ($this->parent->extension->store())
  462. {
  463. //insert record in #__template_styles
  464. $db = $this->parent->getDbo();
  465. $query = $db->getQuery(true);
  466. $query->insert($db->quoteName('#__template_styles'));
  467. $debug = $lang->setDebug(false);
  468. $columns = array($db->quoteName('template'),
  469. $db->quoteName('client_id'),
  470. $db->quoteName('home'),
  471. $db->quoteName('title'),
  472. $db->quoteName('params')
  473. );
  474. $query->columns($columns);
  475. $query->values(
  476. $db->Quote($this->parent->extension->element)
  477. . ',' . $db->Quote($this->parent->extension->client_id)
  478. . ',' . $db->Quote(0)
  479. . ',' . $db->Quote(JText::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', $this->parent->extension->name))
  480. . ',' . $db->Quote($this->parent->extension->params)
  481. );
  482. $lang->setDebug($debug);
  483. $db->setQuery($query);
  484. $db->execute();
  485. return $this->parent->extension->get('extension_id');
  486. }
  487. else
  488. {
  489. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_TPL_DISCOVER_STORE_DETAILS'));
  490. return false;
  491. }
  492. }
  493. /**
  494. * Refreshes the extension table cache
  495. *
  496. * @return boolean Result of operation, true if updated, false on failure
  497. *
  498. * @since 11.1
  499. */
  500. public function refreshManifestCache()
  501. {
  502. // Need to find to find where the XML file is since we don't store this normally.
  503. $client = JApplicationHelper::getClientInfo($this->parent->extension->client_id);
  504. $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml';
  505. $this->parent->manifest = $this->parent->isManifest($manifestPath);
  506. $this->parent->setPath('manifest', $manifestPath);
  507. $manifest_details = JApplicationHelper::parseXMLInstallFile($this->parent->getPath('manifest'));
  508. $this->parent->extension->manifest_cache = json_encode($manifest_details);
  509. $this->parent->extension->name = $manifest_details['name'];
  510. try
  511. {
  512. return $this->parent->extension->store();
  513. }
  514. catch (JException $e)
  515. {
  516. JError::raiseWarning(101, JText::_('JLIB_INSTALLER_ERROR_TPL_REFRESH_MANIFEST_CACHE'));
  517. return false;
  518. }
  519. }
  520. }