[ 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\Filesystem\File; 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 * Plugin installer 29 * 30 * @since 3.1 31 */ 32 class PluginAdapter extends InstallerAdapter 33 { 34 /** 35 * `<scriptfile>` element of the extension manifest 36 * 37 * @var object 38 * @since 3.1 39 */ 40 protected $scriptElement = null; 41 42 /** 43 * `<files>` element of the old extension manifest 44 * 45 * @var object 46 * @since 3.1 47 */ 48 protected $oldFiles = null; 49 50 /** 51 * Method to check if the extension is already present in the database 52 * 53 * @return void 54 * 55 * @since 3.4 56 * @throws \RuntimeException 57 */ 58 protected function checkExistingExtension() 59 { 60 try { 61 $this->currentExtensionId = $this->extension->find( 62 array('type' => $this->type, 'element' => $this->element, 'folder' => $this->group) 63 ); 64 } catch (\RuntimeException $e) { 65 // Install failed, roll back changes 66 throw new \RuntimeException( 67 Text::sprintf( 68 'JLIB_INSTALLER_ABORT_ROLLBACK', 69 Text::_('JLIB_INSTALLER_' . $this->route), 70 $e->getMessage() 71 ), 72 $e->getCode(), 73 $e 74 ); 75 } 76 } 77 78 /** 79 * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file 80 * 81 * @return void 82 * 83 * @since 3.4 84 * @throws \RuntimeException 85 */ 86 protected function copyBaseFiles() 87 { 88 // Copy all necessary files 89 if ($this->parent->parseFiles($this->getManifest()->files, -1, $this->oldFiles) === false) { 90 throw new \RuntimeException( 91 Text::sprintf( 92 'JLIB_INSTALLER_ABORT_PLG_COPY_FILES', 93 Text::_('JLIB_INSTALLER_' . $this->route) 94 ) 95 ); 96 } 97 98 // If there is a manifest script, let's copy it. 99 if ($this->manifest_script) { 100 $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; 101 $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; 102 103 if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { 104 if (!$this->parent->copyFiles(array($path))) { 105 // Install failed, rollback changes 106 throw new \RuntimeException( 107 Text::sprintf( 108 'JLIB_INSTALLER_ABORT_MANIFEST', 109 Text::_('JLIB_INSTALLER_' . $this->route) 110 ) 111 ); 112 } 113 } 114 } 115 } 116 117 /** 118 * Method to create the extension root path if necessary 119 * 120 * @return void 121 * 122 * @since 3.4 123 * @throws \RuntimeException 124 */ 125 protected function createExtensionRoot() 126 { 127 // Run the common create code first 128 parent::createExtensionRoot(); 129 130 // If we're updating at this point when there is always going to be an extension_root find the old XML files 131 if ($this->route === 'update') { 132 // Create a new installer because findManifest sets stuff; side effects! 133 $tmpInstaller = new Installer(); 134 $tmpInstaller->setDatabase($this->getDatabase()); 135 136 // Look in the extension root 137 $tmpInstaller->setPath('source', $this->parent->getPath('extension_root')); 138 139 if ($tmpInstaller->findManifest()) { 140 $old_manifest = $tmpInstaller->getManifest(); 141 $this->oldFiles = $old_manifest->files; 142 } 143 } 144 } 145 146 /** 147 * Method to finalise the installation processing 148 * 149 * @return void 150 * 151 * @since 3.4 152 * @throws \RuntimeException 153 */ 154 protected function finaliseInstall() 155 { 156 // Clobber any possible pending updates 157 /** @var Update $update */ 158 $update = Table::getInstance('update'); 159 $uid = $update->find( 160 array( 161 'element' => $this->element, 162 'type' => $this->type, 163 'folder' => $this->group, 164 ) 165 ); 166 167 if ($uid) { 168 $update->delete($uid); 169 } 170 171 // Lastly, we will copy the manifest file to its appropriate place. 172 if ($this->route !== 'discover_install') { 173 if (!$this->parent->copyManifest(-1)) { 174 // Install failed, rollback changes 175 throw new \RuntimeException( 176 Text::sprintf( 177 'JLIB_INSTALLER_ABORT_COPY_SETUP', 178 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) 179 ) 180 ); 181 } 182 } 183 } 184 185 /** 186 * Method to finalise the uninstallation processing 187 * 188 * @return boolean 189 * 190 * @since 4.0.0 191 * @throws \RuntimeException 192 */ 193 protected function finaliseUninstall(): bool 194 { 195 $extensionId = $this->extension->extension_id; 196 197 $db = $this->getDatabase(); 198 199 // Remove the schema version 200 $query = $db->getQuery(true) 201 ->delete('#__schemas') 202 ->where('extension_id = :extension_id') 203 ->bind(':extension_id', $extensionId, ParameterType::INTEGER); 204 $db->setQuery($query); 205 $db->execute(); 206 207 // Now we will no longer need the plugin object, so let's delete it 208 $this->extension->delete($this->extension->extension_id); 209 210 // Remove the plugin's folder 211 Folder::delete($this->parent->getPath('extension_root')); 212 213 return true; 214 } 215 216 /** 217 * Get the filtered extension element from the manifest 218 * 219 * @param string $element Optional element name to be converted 220 * 221 * @return string The filtered element 222 * 223 * @since 3.4 224 */ 225 public function getElement($element = null) 226 { 227 if ($element || !$this->getManifest()) { 228 return $element; 229 } 230 231 // Backward Compatibility 232 // @todo Deprecate in future version 233 if (!\count($this->getManifest()->files->children())) { 234 return $element; 235 } 236 237 $type = (string) $this->getManifest()->attributes()->type; 238 239 foreach ($this->getManifest()->files->children() as $file) { 240 if ((string) $file->attributes()->$type) { 241 $element = (string) $file->attributes()->$type; 242 243 break; 244 } 245 } 246 247 return $element; 248 } 249 250 /** 251 * Get the class name for the install adapter script. 252 * 253 * @return string The class name. 254 * 255 * @since 3.4 256 */ 257 protected function getScriptClassName() 258 { 259 return 'Plg' . str_replace('-', '', $this->group) . $this->element . 'InstallerScript'; 260 } 261 262 /** 263 * Custom loadLanguage method 264 * 265 * @param string $path The path where to find language files. 266 * 267 * @return void 268 * 269 * @since 3.1 270 */ 271 public function loadLanguage($path = null) 272 { 273 $source = $this->parent->getPath('source'); 274 275 if (!$source) { 276 $this->parent->setPath( 277 'source', 278 JPATH_PLUGINS . '/' . $this->parent->extension->folder . '/' . $this->parent->extension->element 279 ); 280 } 281 282 $element = $this->getManifest()->files; 283 284 if ($element) { 285 $group = strtolower((string) $this->getManifest()->attributes()->group); 286 $name = ''; 287 288 if (\count($element->children())) { 289 foreach ($element->children() as $file) { 290 if ((string) $file->attributes()->plugin) { 291 $name = strtolower((string) $file->attributes()->plugin); 292 break; 293 } 294 } 295 } 296 297 if ($name) { 298 $extension = "plg_$group}_$name}"; 299 $source = $path ?: JPATH_PLUGINS . "/$group/$name"; 300 $folder = (string) $element->attributes()->folder; 301 302 if ($folder && file_exists("$path/$folder")) { 303 $source = "$path/$folder"; 304 } 305 306 $this->doLoadLanguage($extension, $source, JPATH_ADMINISTRATOR); 307 } 308 } 309 } 310 311 /** 312 * Method to parse optional tags in the manifest 313 * 314 * @return void 315 * 316 * @since 3.4 317 */ 318 protected function parseOptionalTags() 319 { 320 // Parse optional tags -- media and language files for plugins go in admin app 321 $this->parent->parseMedia($this->getManifest()->media, 1); 322 $this->parent->parseLanguages($this->getManifest()->languages, 1); 323 } 324 325 /** 326 * Prepares the adapter for a discover_install task 327 * 328 * @return void 329 * 330 * @since 3.4 331 */ 332 public function prepareDiscoverInstall() 333 { 334 $client = ApplicationHelper::getClientInfo($this->extension->client_id); 335 $basePath = $client->path . '/plugins/' . $this->extension->folder; 336 $manifestPath = $basePath . '/' . $this->extension->element . '/' . $this->extension->element . '.xml'; 337 338 $this->parent->manifest = $this->parent->isManifest($manifestPath); 339 $this->parent->setPath('manifest', $manifestPath); 340 $this->setManifest($this->parent->getManifest()); 341 } 342 343 /** 344 * Removes this extension's files 345 * 346 * @return void 347 * 348 * @since 4.0.0 349 * @throws \RuntimeException 350 */ 351 protected function removeExtensionFiles() 352 { 353 // Remove the plugin files 354 $this->parent->removeFiles($this->getManifest()->files, -1); 355 356 // Remove all media and languages as well 357 $this->parent->removeFiles($this->getManifest()->media); 358 $this->parent->removeFiles($this->getManifest()->languages, 1); 359 } 360 361 /** 362 * Method to do any prechecks and setup the install paths for the extension 363 * 364 * @return void 365 * 366 * @since 3.4 367 * @throws \RuntimeException 368 */ 369 protected function setupInstallPaths() 370 { 371 $this->group = (string) $this->getManifest()->attributes()->group; 372 373 if (empty($this->element) && empty($this->group)) { 374 throw new \RuntimeException( 375 Text::sprintf( 376 'JLIB_INSTALLER_ABORT_PLG_INSTALL_NO_FILE', 377 Text::_('JLIB_INSTALLER_' . $this->route) 378 ) 379 ); 380 } 381 382 $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $this->group . '/' . $this->element); 383 } 384 385 /** 386 * Method to do any prechecks and setup the uninstall job 387 * 388 * @return void 389 * 390 * @since 4.0.0 391 */ 392 protected function setupUninstall() 393 { 394 // Get the plugin folder so we can properly build the plugin path 395 if (trim($this->extension->folder) === '') { 396 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PLG_UNINSTALL_FOLDER_FIELD_EMPTY')); 397 } 398 399 // Set the plugin root path 400 $this->parent->setPath('extension_root', JPATH_PLUGINS . '/' . $this->extension->folder . '/' . $this->extension->element); 401 402 $this->parent->setPath('source', $this->parent->getPath('extension_root')); 403 404 $this->parent->findManifest(); 405 $this->setManifest($this->parent->getManifest()); 406 407 if ($this->getManifest()) { 408 $this->group = (string) $this->getManifest()->attributes()->group; 409 } 410 411 // Attempt to load the language file; might have uninstall strings 412 $this->loadLanguage($this->parent->getPath('source')); 413 } 414 415 /** 416 * Method to store the extension to the database 417 * 418 * @return void 419 * 420 * @since 3.4 421 * @throws \RuntimeException 422 */ 423 protected function storeExtension() 424 { 425 // Discover installs are stored a little differently 426 if ($this->route === 'discover_install') { 427 $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); 428 429 $this->extension->manifest_cache = json_encode($manifest_details); 430 $this->extension->state = 0; 431 $this->extension->name = $manifest_details['name']; 432 $this->extension->enabled = 'editors' === $this->extension->folder ? 1 : 0; 433 $this->extension->params = $this->parent->getParams(); 434 435 if (!$this->extension->store()) { 436 // Install failed, roll back changes 437 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_PLG_DISCOVER_STORE_DETAILS')); 438 } 439 440 return; 441 } 442 443 // Was there a plugin with the same name already installed? 444 if ($this->currentExtensionId) { 445 if (!$this->parent->isOverwrite()) { 446 // Install failed, roll back changes 447 throw new \RuntimeException( 448 Text::sprintf( 449 'JLIB_INSTALLER_ABORT_ALREADY_EXISTS', 450 Text::_('JLIB_INSTALLER_' . $this->route), 451 $this->name 452 ) 453 ); 454 } 455 456 // Load the entry and update the manifest_cache 457 $this->extension->load($this->currentExtensionId); 458 459 // Update name 460 $this->extension->name = $this->name; 461 462 // Update namespace 463 $this->extension->namespace = (string) $this->manifest->namespace; 464 465 // Update changelogurl 466 $this->extension->changelogurl = (string) $this->manifest->changelogurl; 467 468 // Update manifest 469 $this->extension->manifest_cache = $this->parent->generateManifestCache(); 470 471 // Update the manifest cache and name 472 $this->extension->store(); 473 } else { 474 // Store in the extensions table (1.6) 475 $this->extension->name = $this->name; 476 $this->extension->type = 'plugin'; 477 $this->extension->ordering = 0; 478 $this->extension->element = $this->element; 479 $this->extension->folder = $this->group; 480 $this->extension->enabled = 0; 481 $this->extension->protected = 0; 482 $this->extension->access = 1; 483 $this->extension->client_id = 0; 484 $this->extension->params = $this->parent->getParams(); 485 $this->extension->changelogurl = $this->changelogurl; 486 487 // Update the manifest cache for the entry 488 $this->extension->manifest_cache = $this->parent->generateManifestCache(); 489 490 // Editor plugins are published by default 491 if ($this->group === 'editors') { 492 $this->extension->enabled = 1; 493 } 494 495 if (!$this->extension->store()) { 496 // Install failed, roll back changes 497 throw new \RuntimeException( 498 Text::sprintf( 499 'JLIB_INSTALLER_ABORT_PLG_INSTALL_ROLLBACK', 500 Text::_('JLIB_INSTALLER_' . $this->route), 501 $this->extension->getError() 502 ) 503 ); 504 } 505 506 // Since we have created a plugin item, we add it to the installation step stack 507 // so that if we have to rollback the changes we can undo it. 508 $this->parent->pushStep(array('type' => 'extension', 'id' => $this->extension->extension_id)); 509 } 510 } 511 512 /** 513 * Custom discover method 514 * 515 * @return array Extension) list of extensions available 516 * 517 * @since 3.1 518 */ 519 public function discover() 520 { 521 $results = array(); 522 $folder_list = Folder::folders(JPATH_SITE . '/plugins'); 523 524 foreach ($folder_list as $folder) { 525 $file_list = Folder::files(JPATH_SITE . '/plugins/' . $folder, '\.xml$'); 526 527 foreach ($file_list as $file) { 528 $manifest_details = Installer::parseXMLInstallFile(JPATH_SITE . '/plugins/' . $folder . '/' . $file); 529 $file = File::stripExt($file); 530 531 // Ignore example plugins 532 if ($file === 'example' || $manifest_details === false) { 533 continue; 534 } 535 536 $element = empty($manifest_details['filename']) ? $file : $manifest_details['filename']; 537 538 $extension = Table::getInstance('extension'); 539 $extension->set('type', 'plugin'); 540 $extension->set('client_id', 0); 541 $extension->set('element', $element); 542 $extension->set('folder', $folder); 543 $extension->set('name', $manifest_details['name']); 544 $extension->set('state', -1); 545 $extension->set('manifest_cache', json_encode($manifest_details)); 546 $extension->set('params', '{}'); 547 $results[] = $extension; 548 } 549 550 $folder_list = Folder::folders(JPATH_SITE . '/plugins/' . $folder); 551 552 foreach ($folder_list as $plugin_folder) { 553 $file_list = Folder::files(JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder, '\.xml$'); 554 555 foreach ($file_list as $file) { 556 $manifest_details = Installer::parseXMLInstallFile( 557 JPATH_SITE . '/plugins/' . $folder . '/' . $plugin_folder . '/' . $file 558 ); 559 $file = File::stripExt($file); 560 561 if ($file === 'example' || $manifest_details === false) { 562 continue; 563 } 564 565 $element = empty($manifest_details['filename']) ? $file : $manifest_details['filename']; 566 567 // Ignore example plugins 568 $extension = Table::getInstance('extension'); 569 $extension->set('type', 'plugin'); 570 $extension->set('client_id', 0); 571 $extension->set('element', $element); 572 $extension->set('folder', $folder); 573 $extension->set('name', $manifest_details['name']); 574 $extension->set('state', -1); 575 $extension->set('manifest_cache', json_encode($manifest_details)); 576 $extension->set('params', '{}'); 577 $results[] = $extension; 578 } 579 } 580 } 581 582 return $results; 583 } 584 585 /** 586 * Refreshes the extension table cache. 587 * 588 * @return boolean Result of operation, true if updated, false on failure. 589 * 590 * @since 3.1 591 */ 592 public function refreshManifestCache() 593 { 594 /* 595 * Plugins use the extensions table as their primary store 596 * Similar to modules and templates, rather easy 597 * If it's not in the extensions table we just add it 598 */ 599 $client = ApplicationHelper::getClientInfo($this->parent->extension->client_id); 600 $manifestPath = $client->path . '/plugins/' . $this->parent->extension->folder . '/' . $this->parent->extension->element . '/' 601 . $this->parent->extension->element . '.xml'; 602 $this->parent->manifest = $this->parent->isManifest($manifestPath); 603 $this->parent->setPath('manifest', $manifestPath); 604 $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); 605 $this->parent->extension->manifest_cache = json_encode($manifest_details); 606 607 $this->parent->extension->name = $manifest_details['name']; 608 609 if ($this->parent->extension->store()) { 610 return true; 611 } else { 612 Log::add(Text::_('JLIB_INSTALLER_ERROR_PLG_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); 613 614 return false; 615 } 616 } 617 }
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 |