[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2005 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\Installer\Adapter; 11 12 use Joomla\CMS\Application\ApplicationHelper; 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Filesystem\Folder; 15 use Joomla\CMS\Installer\Installer; 16 use Joomla\CMS\Installer\InstallerAdapter; 17 use Joomla\CMS\Language\Text; 18 use Joomla\CMS\Log\Log; 19 use Joomla\CMS\Table\Table; 20 use Joomla\CMS\Table\Update; 21 use Joomla\Database\ParameterType; 22 23 // phpcs:disable PSR1.Files.SideEffects 24 \defined('JPATH_PLATFORM') or die; 25 // phpcs:enable PSR1.Files.SideEffects 26 27 /** 28 * Template installer 29 * 30 * @since 3.1 31 */ 32 class TemplateAdapter extends InstallerAdapter 33 { 34 /** 35 * The install client ID 36 * 37 * @var integer 38 * @since 3.4 39 */ 40 protected $clientId; 41 42 /** 43 * Method to check if the extension is already present in the database 44 * 45 * @return void 46 * 47 * @since 3.4 48 * @throws \RuntimeException 49 */ 50 protected function checkExistingExtension() 51 { 52 try { 53 $this->currentExtensionId = $this->extension->find( 54 array( 55 'element' => $this->element, 56 'type' => $this->type, 57 'client_id' => $this->clientId, 58 ) 59 ); 60 } catch (\RuntimeException $e) { 61 // Install failed, roll back changes 62 throw new \RuntimeException( 63 Text::sprintf( 64 'JLIB_INSTALLER_ABORT_ROLLBACK', 65 Text::_('JLIB_INSTALLER_' . $this->route), 66 $e->getMessage() 67 ), 68 $e->getCode(), 69 $e 70 ); 71 } 72 } 73 74 /** 75 * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file 76 * 77 * @return void 78 * 79 * @since 3.4 80 * @throws \RuntimeException 81 */ 82 protected function copyBaseFiles() 83 { 84 // Copy all the necessary files 85 if ($this->parent->parseFiles($this->getManifest()->files, -1) === false) { 86 throw new \RuntimeException( 87 Text::sprintf( 88 'JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_FILES', 89 'files' 90 ) 91 ); 92 } 93 94 if ($this->parent->parseFiles($this->getManifest()->images, -1) === false) { 95 throw new \RuntimeException( 96 Text::sprintf( 97 'JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_FILES', 98 'images' 99 ) 100 ); 101 } 102 103 if ($this->parent->parseFiles($this->getManifest()->css, -1) === false) { 104 throw new \RuntimeException( 105 Text::sprintf( 106 'JLIB_INSTALLER_ABORT_TPL_INSTALL_COPY_FILES', 107 'css' 108 ) 109 ); 110 } 111 112 // If there is a manifest script, let's copy it. 113 if ($this->manifest_script) { 114 $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; 115 $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; 116 117 if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { 118 if (!$this->parent->copyFiles(array($path))) { 119 throw new \RuntimeException( 120 Text::sprintf( 121 'JLIB_INSTALLER_ABORT_MANIFEST', 122 Text::_('JLIB_INSTALLER_' . strtoupper($this->getRoute())) 123 ) 124 ); 125 } 126 } 127 } 128 } 129 130 /** 131 * Method to finalise the installation processing 132 * 133 * @return void 134 * 135 * @since 3.1 136 * @throws \RuntimeException 137 */ 138 protected function finaliseInstall() 139 { 140 // Clobber any possible pending updates 141 /** @var Update $update */ 142 $update = Table::getInstance('update'); 143 144 $uid = $update->find( 145 array( 146 'element' => $this->element, 147 'type' => $this->type, 148 'client_id' => $this->clientId, 149 ) 150 ); 151 152 if ($uid) { 153 $update->delete($uid); 154 } 155 156 // Lastly, we will copy the manifest file to its appropriate place. 157 if ($this->route !== 'discover_install') { 158 if (!$this->parent->copyManifest(-1)) { 159 // Install failed, rollback changes 160 throw new \RuntimeException( 161 Text::sprintf( 162 'JLIB_INSTALLER_ABORT_COPY_SETUP', 163 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) 164 ) 165 ); 166 } 167 } 168 } 169 170 /** 171 * Method to finalise the uninstallation processing 172 * 173 * @return boolean 174 * 175 * @since 4.0.0 176 * @throws \RuntimeException 177 */ 178 protected function finaliseUninstall(): bool 179 { 180 $db = $this->getDatabase(); 181 $query = $db->getQuery(true); 182 183 $element = $this->extension->element; 184 $clientId = $this->extension->client_id; 185 $extensionId = $this->extension->extension_id; 186 187 // Set menu that assigned to the template back to default template 188 $subQuery = $db->getQuery(true) 189 ->select($db->quoteName('s.id')) 190 ->from($db->quoteName('#__template_styles', 's')) 191 ->where( 192 [ 193 $db->quoteName('s.template') . ' = :element', 194 $db->quoteName('s.client_id') . ' = :clientId', 195 ] 196 ); 197 198 $query->bind(':element', $element) 199 ->bind(':clientId', $clientId, ParameterType::INTEGER); 200 201 $query->update($db->quoteName('#__menu')) 202 ->set($db->quoteName('template_style_id') . ' = 0') 203 ->where($db->quoteName('template_style_id') . ' IN (' . (string) $subQuery . ')'); 204 205 $db->setQuery($query); 206 $db->execute(); 207 208 // Remove the template's styles 209 $query = $db->getQuery(true) 210 ->delete($db->quoteName('#__template_styles')) 211 ->where( 212 [ 213 $db->quoteName('template') . ' = :template', 214 $db->quoteName('client_id') . ' = :client_id', 215 ] 216 ) 217 ->bind(':template', $element) 218 ->bind(':client_id', $clientId, ParameterType::INTEGER); 219 $db->setQuery($query); 220 $db->execute(); 221 222 // Remove the schema version 223 $query = $db->getQuery(true) 224 ->delete($db->quoteName('#__schemas')) 225 ->where($db->quoteName('extension_id') . ' = :extension_id') 226 ->bind(':extension_id', $extensionId, ParameterType::INTEGER); 227 $db->setQuery($query); 228 $db->execute(); 229 230 // Clobber any possible pending updates 231 $update = Table::getInstance('update'); 232 $uid = $update->find( 233 [ 234 'element' => $this->extension->element, 235 'type' => $this->type, 236 'client_id' => $this->extension->client_id, 237 ] 238 ); 239 240 if ($uid) { 241 $update->delete($uid); 242 } 243 244 $this->extension->delete(); 245 246 return true; 247 } 248 249 /** 250 * Custom loadLanguage method 251 * 252 * @param string $path The path where to find language files. 253 * 254 * @return void 255 * 256 * @since 3.1 257 */ 258 public function loadLanguage($path = null) 259 { 260 $source = $this->parent->getPath('source'); 261 $basePath = $this->parent->extension->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE; 262 263 if (!$source) { 264 $this->parent->setPath('source', $basePath . '/templates/' . $this->parent->extension->element); 265 } 266 267 $this->setManifest($this->parent->getManifest()); 268 269 $client = (string) $this->getManifest()->attributes()->client; 270 271 // Load administrator language if not set. 272 if (!$client) { 273 $client = 'ADMINISTRATOR'; 274 } 275 276 $base = \constant('JPATH_' . strtoupper($client)); 277 $extension = 'tpl_' . $this->getName(); 278 $source = $path ?: $base . '/templates/' . $this->getName(); 279 280 $this->doLoadLanguage($extension, $source, $base); 281 } 282 283 /** 284 * Method to parse optional tags in the manifest 285 * 286 * @return void 287 * 288 * @since 3.4 289 */ 290 protected function parseOptionalTags() 291 { 292 $this->parent->parseMedia($this->getManifest()->media); 293 $this->parent->parseLanguages($this->getManifest()->languages, $this->clientId); 294 } 295 296 /** 297 * Overloaded method to parse queries for template installations 298 * 299 * @return void 300 * 301 * @since 3.4 302 * @throws \RuntimeException 303 */ 304 protected function parseQueries() 305 { 306 if (\in_array($this->route, array('install', 'discover_install'))) { 307 $db = $this->getDatabase(); 308 $query = $db->getQuery(true); 309 $lang = Factory::getLanguage(); 310 $debug = $lang->setDebug(false); 311 312 $columns = [ 313 $db->quoteName('template'), 314 $db->quoteName('client_id'), 315 $db->quoteName('home'), 316 $db->quoteName('title'), 317 $db->quoteName('params'), 318 $db->quoteName('inheritable'), 319 $db->quoteName('parent'), 320 ]; 321 322 $values = $query->bindArray( 323 [ 324 $this->extension->element, 325 $this->extension->client_id, 326 '0', 327 Text::sprintf('JLIB_INSTALLER_DEFAULT_STYLE', Text::_($this->extension->name)), 328 $this->extension->params, 329 (int) $this->manifest->inheritable, 330 (string) $this->manifest->parent ?: '', 331 ], 332 [ 333 ParameterType::STRING, 334 ParameterType::INTEGER, 335 ParameterType::STRING, 336 ParameterType::STRING, 337 ParameterType::STRING, 338 ParameterType::INTEGER, 339 ParameterType::STRING, 340 ] 341 ); 342 343 $lang->setDebug($debug); 344 345 // Insert record in #__template_styles 346 $query->insert($db->quoteName('#__template_styles')) 347 ->columns($columns) 348 ->values(implode(',', $values)); 349 350 // There is a chance this could fail but we don't care... 351 $db->setQuery($query)->execute(); 352 } 353 } 354 355 /** 356 * Prepares the adapter for a discover_install task 357 * 358 * @return void 359 * 360 * @since 3.4 361 */ 362 public function prepareDiscoverInstall() 363 { 364 $client = ApplicationHelper::getClientInfo($this->extension->client_id); 365 $manifestPath = $client->path . '/templates/' . $this->extension->element . '/templateDetails.xml'; 366 $this->parent->manifest = $this->parent->isManifest($manifestPath); 367 $this->parent->setPath('manifest', $manifestPath); 368 $this->setManifest($this->parent->getManifest()); 369 } 370 371 /** 372 * Removes this extension's files 373 * 374 * @return void 375 * 376 * @since 4.0.0 377 * @throws \RuntimeException 378 */ 379 protected function removeExtensionFiles() 380 { 381 // Remove files 382 $this->parent->removeFiles($this->getManifest()->media); 383 $this->parent->removeFiles($this->getManifest()->languages, $this->extension->client_id); 384 385 // Delete the template directory 386 if (Folder::exists($this->parent->getPath('extension_root'))) { 387 Folder::delete($this->parent->getPath('extension_root')); 388 } else { 389 Log::add(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DIRECTORY'), Log::WARNING, 'jerror'); 390 } 391 } 392 393 /** 394 * Method to do any prechecks and setup the install paths for the extension 395 * 396 * @return void 397 * 398 * @since 3.4 399 * @throws \RuntimeException 400 */ 401 protected function setupInstallPaths() 402 { 403 // Get the client application target 404 $cname = (string) $this->getManifest()->attributes()->client; 405 406 if ($cname) { 407 // Attempt to map the client to a base path 408 $client = ApplicationHelper::getClientInfo($cname, true); 409 410 if ($client === false) { 411 throw new \RuntimeException(Text::sprintf('JLIB_INSTALLER_ABORT_TPL_INSTALL_UNKNOWN_CLIENT', $cname)); 412 } 413 414 $basePath = $client->path; 415 $this->clientId = $client->id; 416 } else { 417 // No client attribute was found so we assume the site as the client 418 $basePath = JPATH_SITE; 419 $this->clientId = 0; 420 } 421 422 // Set the template root path 423 if (empty($this->element)) { 424 throw new \RuntimeException( 425 Text::sprintf( 426 'JLIB_INSTALLER_ABORT_MOD_INSTALL_NOFILE', 427 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) 428 ) 429 ); 430 } 431 432 $this->parent->setPath('extension_root', $basePath . '/templates/' . $this->element); 433 } 434 435 /** 436 * Method to do any prechecks and setup the uninstall job 437 * 438 * @return void 439 * 440 * @since 4.0.0 441 */ 442 protected function setupUninstall() 443 { 444 $this->parent->extension = $this->extension; 445 446 $db = $this->getDatabase(); 447 $name = $this->extension->element; 448 $clientId = $this->extension->client_id; 449 450 // For a template the id will be the template name which represents the subfolder of the templates folder that the template resides in. 451 if (!$name) { 452 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_ID_EMPTY')); 453 } 454 455 // Deny removing a parent template if there are children 456 $query = $db->getQuery(true) 457 ->select('COUNT(*)') 458 ->from($db->quoteName('#__template_styles')) 459 ->where( 460 [ 461 $db->quoteName('parent') . ' = :template', 462 $db->quoteName('client_id') . ' = :client_id', 463 ] 464 ) 465 ->bind(':template', $name) 466 ->bind(':client_id', $clientId); 467 $db->setQuery($query); 468 469 if ($db->loadResult() != 0) { 470 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_PARENT_TEMPLATE')); 471 } 472 473 // Deny remove default template 474 $query = $db->getQuery(true) 475 ->select('COUNT(*)') 476 ->from($db->quoteName('#__template_styles')) 477 ->where( 478 [ 479 $db->quoteName('home') . ' = ' . $db->quote('1'), 480 $db->quoteName('template') . ' = :template', 481 $db->quoteName('client_id') . ' = :client_id', 482 ] 483 ) 484 ->bind(':template', $name) 485 ->bind(':client_id', $clientId); 486 $db->setQuery($query); 487 488 if ($db->loadResult() != 0) { 489 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_TEMPLATE_DEFAULT')); 490 } 491 492 // Get the template root path 493 $client = ApplicationHelper::getClientInfo($clientId); 494 495 if (!$client) { 496 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_CLIENT')); 497 } 498 499 $this->parent->setPath('extension_root', $client->path . '/templates/' . strtolower($name)); 500 $this->parent->setPath('source', $this->parent->getPath('extension_root')); 501 502 // We do findManifest to avoid problem when uninstalling a list of extensions: getManifest cache its manifest file 503 $this->parent->findManifest(); 504 $manifest = $this->parent->getManifest(); 505 506 if (!($manifest instanceof \SimpleXMLElement)) { 507 // Kill the extension entry 508 $this->extension->delete($this->extension->extension_id); 509 510 // Make sure we delete the folders 511 Folder::delete($this->parent->getPath('extension_root')); 512 513 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); 514 } 515 516 // Attempt to load the language file; might have uninstall strings 517 $this->loadLanguage(); 518 } 519 520 /** 521 * Method to store the extension to the database 522 * 523 * @return void 524 * 525 * @since 3.4 526 * @throws \RuntimeException 527 */ 528 protected function storeExtension() 529 { 530 // Discover installs are stored a little differently 531 if ($this->route === 'discover_install') { 532 $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); 533 534 $this->extension->manifest_cache = json_encode($manifest_details); 535 $this->extension->state = 0; 536 $this->extension->name = $manifest_details['name']; 537 $this->extension->enabled = 1; 538 $this->extension->params = $this->parent->getParams(); 539 540 if (!$this->extension->store()) { 541 // Install failed, roll back changes 542 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_TPL_DISCOVER_STORE_DETAILS')); 543 } 544 545 return; 546 } 547 548 // Was there a template already installed with the same name? 549 if ($this->currentExtensionId) { 550 if (!$this->parent->isOverwrite()) { 551 // Install failed, roll back changes 552 throw new \RuntimeException( 553 Text::_('JLIB_INSTALLER_ABORT_TPL_INSTALL_ALREADY_INSTALLED') 554 ); 555 } 556 557 // Load the entry and update the manifest_cache 558 $this->extension->load($this->currentExtensionId); 559 } else { 560 $this->extension->type = 'template'; 561 $this->extension->element = $this->element; 562 563 // There is no folder for templates 564 $this->extension->folder = ''; 565 $this->extension->enabled = 1; 566 $this->extension->protected = 0; 567 $this->extension->access = 1; 568 $this->extension->client_id = $this->clientId; 569 $this->extension->params = $this->parent->getParams(); 570 $this->extension->changelogurl = $this->changelogurl; 571 } 572 573 // Name might change in an update 574 $this->extension->name = $this->name; 575 576 // Update the manifest cache for the entry 577 $this->extension->manifest_cache = $this->parent->generateManifestCache(); 578 579 $this->extension->changelogurl = $this->changelogurl; 580 581 if (!$this->extension->store()) { 582 // Install failed, roll back changes 583 throw new \RuntimeException( 584 Text::sprintf( 585 'JLIB_INSTALLER_ABORT_ROLLBACK', 586 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), 587 $this->extension->getError() 588 ) 589 ); 590 } 591 } 592 593 /** 594 * Discover existing but uninstalled templates 595 * 596 * @return array Extension list 597 */ 598 public function discover() 599 { 600 $results = array(); 601 $site_list = Folder::folders(JPATH_SITE . '/templates'); 602 $admin_list = Folder::folders(JPATH_ADMINISTRATOR . '/templates'); 603 $site_info = ApplicationHelper::getClientInfo('site', true); 604 $admin_info = ApplicationHelper::getClientInfo('administrator', true); 605 606 foreach ($site_list as $template) { 607 if (file_exists(JPATH_SITE . "/templates/$template/templateDetails.xml")) { 608 if ($template === 'system') { 609 // Ignore special system template 610 continue; 611 } 612 613 $manifest_details = Installer::parseXMLInstallFile(JPATH_SITE . "/templates/$template/templateDetails.xml"); 614 $extension = Table::getInstance('extension'); 615 $extension->set('type', 'template'); 616 $extension->set('client_id', $site_info->id); 617 $extension->set('element', $template); 618 $extension->set('folder', ''); 619 $extension->set('name', $template); 620 $extension->set('state', -1); 621 $extension->set('manifest_cache', json_encode($manifest_details)); 622 $extension->set('params', '{}'); 623 $results[] = $extension; 624 } 625 } 626 627 foreach ($admin_list as $template) { 628 if (file_exists(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml")) { 629 if ($template === 'system') { 630 // Ignore special system template 631 continue; 632 } 633 634 $manifest_details = Installer::parseXMLInstallFile(JPATH_ADMINISTRATOR . "/templates/$template/templateDetails.xml"); 635 $extension = Table::getInstance('extension'); 636 $extension->set('type', 'template'); 637 $extension->set('client_id', $admin_info->id); 638 $extension->set('element', $template); 639 $extension->set('folder', ''); 640 $extension->set('name', $template); 641 $extension->set('state', -1); 642 $extension->set('manifest_cache', json_encode($manifest_details)); 643 $extension->set('params', '{}'); 644 $results[] = $extension; 645 } 646 } 647 648 return $results; 649 } 650 651 /** 652 * Refreshes the extension table cache 653 * 654 * @return boolean Result of operation, true if updated, false on failure 655 * 656 * @since 3.1 657 */ 658 public function refreshManifestCache() 659 { 660 // Need to find to find where the XML file is since we don't store this normally. 661 $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); 662 $manifestPath = $client->path . '/templates/' . $this->parent->extension->element . '/templateDetails.xml'; 663 $this->parent->manifest = $this->parent->isManifest($manifestPath); 664 $this->parent->setPath('manifest', $manifestPath); 665 666 $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); 667 $this->parent->extension->manifest_cache = json_encode($manifest_details); 668 $this->parent->extension->name = $manifest_details['name']; 669 670 try { 671 return $this->parent->extension->store(); 672 } catch (\RuntimeException $e) { 673 Log::add(Text::_('JLIB_INSTALLER_ERROR_TPL_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); 674 675 return false; 676 } 677 } 678 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |