[ 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) 2008 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\Filesystem\File; 13 use Joomla\CMS\Filesystem\Folder; 14 use Joomla\CMS\Filesystem\Path; 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\Database\ParameterType; 21 22 // phpcs:disable PSR1.Files.SideEffects 23 \defined('JPATH_PLATFORM') or die; 24 // phpcs:enable PSR1.Files.SideEffects 25 26 /** 27 * File installer 28 * 29 * @since 3.1 30 */ 31 class FileAdapter extends InstallerAdapter 32 { 33 /** 34 * `<scriptfile>` element of the extension manifest 35 * 36 * @var object 37 * @since 3.1 38 */ 39 protected $scriptElement = null; 40 41 /** 42 * Flag if the adapter supports discover installs 43 * 44 * Adapters should override this and set to false if discover install is unsupported 45 * 46 * @var boolean 47 * @since 3.4 48 */ 49 protected $supportsDiscoverInstall = false; 50 51 /** 52 * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file 53 * 54 * @return void 55 * 56 * @since 3.4 57 * @throws \RuntimeException 58 */ 59 protected function copyBaseFiles() 60 { 61 // Populate File and Folder List to copy 62 $this->populateFilesAndFolderList(); 63 64 // Now that we have folder list, lets start creating them 65 foreach ($this->folderList as $folder) { 66 if (!Folder::exists($folder)) { 67 if (!$created = Folder::create($folder)) { 68 throw new \RuntimeException( 69 Text::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_FAIL_SOURCE_DIRECTORY', $folder) 70 ); 71 } 72 73 // Since we created a directory and will want to remove it if we have to roll back. 74 // The installation due to some errors, let's add it to the installation step stack. 75 if ($created) { 76 $this->parent->pushStep(array('type' => 'folder', 'path' => $folder)); 77 } 78 } 79 } 80 81 // Now that we have file list, let's start copying them 82 $this->parent->copyFiles($this->fileList); 83 } 84 85 /** 86 * Method to finalise the installation processing 87 * 88 * @return void 89 * 90 * @since 3.4 91 * @throws \RuntimeException 92 */ 93 protected function finaliseInstall() 94 { 95 // Clobber any possible pending updates 96 $update = Table::getInstance('update'); 97 98 $uid = $update->find( 99 array( 100 'element' => $this->element, 101 'type' => $this->type, 102 ) 103 ); 104 105 if ($uid) { 106 $update->delete($uid); 107 } 108 109 // Lastly, we will copy the manifest file to its appropriate place. 110 $manifest = array(); 111 $manifest['src'] = $this->parent->getPath('manifest'); 112 $manifest['dest'] = JPATH_MANIFESTS . '/files/' . basename($this->parent->getPath('manifest')); 113 114 if (!$this->parent->copyFiles(array($manifest), true)) { 115 // Install failed, rollback changes 116 throw new \RuntimeException( 117 Text::sprintf( 118 'JLIB_INSTALLER_ABORT_COPY_SETUP', 119 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) 120 ) 121 ); 122 } 123 124 // If there is a manifest script, let's copy it. 125 if ($this->manifest_script) { 126 // First, we have to create a folder for the script if one isn't present 127 if (!file_exists($this->parent->getPath('extension_root'))) { 128 Folder::create($this->parent->getPath('extension_root')); 129 } 130 131 $path['src'] = $this->parent->getPath('source') . '/' . $this->manifest_script; 132 $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script; 133 134 if ($this->parent->isOverwrite() || !file_exists($path['dest'])) { 135 if (!$this->parent->copyFiles(array($path))) { 136 // Install failed, rollback changes 137 throw new \RuntimeException( 138 Text::sprintf( 139 'JLIB_INSTALLER_ABORT_MANIFEST', 140 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)) 141 ) 142 ); 143 } 144 } 145 } 146 } 147 148 /** 149 * Method to finalise the uninstallation processing 150 * 151 * @return boolean 152 * 153 * @since 4.0.0 154 * @throws \RuntimeException 155 */ 156 protected function finaliseUninstall(): bool 157 { 158 File::delete(JPATH_MANIFESTS . '/files/' . $this->extension->element . '.xml'); 159 160 $extensionId = $this->extension->extension_id; 161 162 $db = $this->getDatabase(); 163 164 // Remove the schema version 165 $query = $db->getQuery(true) 166 ->delete('#__schemas') 167 ->where('extension_id = :extension_id') 168 ->bind(':extension_id', $extensionId, ParameterType::INTEGER); 169 $db->setQuery($query); 170 $db->execute(); 171 172 // Clobber any possible pending updates 173 $update = Table::getInstance('update'); 174 $uid = $update->find( 175 [ 176 'element' => $this->extension->element, 177 'type' => $this->type, 178 ] 179 ); 180 181 if ($uid) { 182 $update->delete($uid); 183 } 184 185 $this->extension->delete(); 186 187 return true; 188 } 189 190 /** 191 * Get the filtered extension element from the manifest 192 * 193 * @param string $element Optional element name to be converted 194 * 195 * @return string The filtered element 196 * 197 * @since 3.4 198 */ 199 public function getElement($element = null) 200 { 201 if (!$element) { 202 $manifestPath = Path::clean($this->parent->getPath('manifest')); 203 $element = preg_replace('/\.xml/', '', basename($manifestPath)); 204 } 205 206 return $element; 207 } 208 209 /** 210 * Custom loadLanguage method 211 * 212 * @param string $path The path on which to find language files. 213 * 214 * @return void 215 * 216 * @since 3.1 217 */ 218 public function loadLanguage($path) 219 { 220 $extension = 'files_' . strtolower(str_replace('files_', '', $this->getElement())); 221 222 $this->doLoadLanguage($extension, $path, JPATH_SITE); 223 } 224 225 /** 226 * Method to parse optional tags in the manifest 227 * 228 * @return void 229 * 230 * @since 3.4 231 */ 232 protected function parseOptionalTags() 233 { 234 // Parse optional tags 235 $this->parent->parseLanguages($this->getManifest()->languages); 236 } 237 238 /** 239 * Removes this extension's files 240 * 241 * @return void 242 * 243 * @since 4.0.0 244 * @throws \RuntimeException 245 */ 246 protected function removeExtensionFiles() 247 { 248 // Loop through all elements and get list of files and folders 249 foreach ($this->getManifest()->fileset->files as $eFiles) { 250 $target = (string) $eFiles->attributes()->target; 251 252 // Create folder path 253 if (empty($target)) { 254 $targetFolder = JPATH_ROOT; 255 } else { 256 $targetFolder = JPATH_ROOT . '/' . $target; 257 } 258 259 $folderList = []; 260 261 // Check if all children exists 262 if (\count($eFiles->children()) > 0) { 263 // Loop through all filenames elements 264 foreach ($eFiles->children() as $eFileName) { 265 if ($eFileName->getName() === 'folder') { 266 $folderList[] = $targetFolder . '/' . $eFileName; 267 } else { 268 $fileName = $targetFolder . '/' . $eFileName; 269 File::delete($fileName); 270 } 271 } 272 } 273 274 // Delete any folders that don't have any content in them. 275 foreach ($folderList as $folder) { 276 $files = Folder::files($folder); 277 278 if ($files !== false && !\count($files)) { 279 Folder::delete($folder); 280 } 281 } 282 } 283 284 // Lastly, remove the extension_root 285 $folder = $this->parent->getPath('extension_root'); 286 287 if (Folder::exists($folder)) { 288 Folder::delete($folder); 289 } 290 291 $this->parent->removeFiles($this->getManifest()->languages); 292 } 293 294 /** 295 * Method to do any prechecks and setup the install paths for the extension 296 * 297 * @return void 298 * 299 * @since 3.4 300 */ 301 protected function setupInstallPaths() 302 { 303 // Set the file root path 304 if ($this->name === 'files_joomla') { 305 // If we are updating the Joomla core, set the root path to the root of Joomla 306 $this->parent->setPath('extension_root', JPATH_ROOT); 307 } else { 308 $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/files/' . $this->element); 309 } 310 } 311 312 /** 313 * Method to do any prechecks and setup the uninstall job 314 * 315 * @return void 316 * 317 * @since 4.0.0 318 */ 319 protected function setupUninstall() 320 { 321 $manifestFile = JPATH_MANIFESTS . '/files/' . $this->extension->element . '.xml'; 322 323 // Because libraries may not have their own folders we cannot use the standard method of finding an installation manifest 324 if (!file_exists($manifestFile)) { 325 // Remove this row entry since its invalid 326 $this->extension->delete($this->extension->extension_id); 327 328 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_INVALID_NOTFOUND_MANIFEST')); 329 } 330 331 // Set the files root path 332 $this->parent->setPath('extension_root', JPATH_MANIFESTS . '/files/' . $this->extension->element); 333 334 // Set the source path for compatibility with the API 335 $this->parent->setPath('source', $this->parent->getPath('extension_root')); 336 337 $xml = simplexml_load_file($manifestFile); 338 339 // If we cannot load the XML file return null 340 if (!$xml) { 341 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_LOAD_MANIFEST')); 342 } 343 344 // Check for a valid XML root tag. 345 if ($xml->getName() !== 'extension') { 346 throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_FILE_UNINSTALL_INVALID_MANIFEST')); 347 } 348 349 $this->setManifest($xml); 350 351 // Attempt to load the language file; might have uninstall strings 352 $this->loadLanguage(JPATH_MANIFESTS . '/files'); 353 } 354 355 /** 356 * Method to store the extension to the database 357 * 358 * @return void 359 * 360 * @since 3.4 361 * @throws \RuntimeException 362 */ 363 protected function storeExtension() 364 { 365 if ($this->currentExtensionId) { 366 // Load the entry and update the manifest_cache 367 $this->extension->load($this->currentExtensionId); 368 369 // Update name 370 $this->extension->name = $this->name; 371 372 // Update manifest 373 $this->extension->manifest_cache = $this->parent->generateManifestCache(); 374 375 if (!$this->extension->store()) { 376 // Install failed, roll back changes 377 throw new \RuntimeException( 378 Text::sprintf( 379 'JLIB_INSTALLER_ABORT_ROLLBACK', 380 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), 381 $this->extension->getError() 382 ) 383 ); 384 } 385 } else { 386 // Add an entry to the extension table with a whole heap of defaults 387 $this->extension->name = $this->name; 388 $this->extension->type = 'file'; 389 $this->extension->element = $this->element; 390 $this->extension->changelogurl = $this->changelogurl; 391 392 // There is no folder for files so leave it blank 393 $this->extension->folder = ''; 394 $this->extension->enabled = 1; 395 $this->extension->protected = 0; 396 $this->extension->access = 0; 397 $this->extension->client_id = 0; 398 $this->extension->params = ''; 399 400 // Update the manifest cache for the entry 401 $this->extension->manifest_cache = $this->parent->generateManifestCache(); 402 403 if (!$this->extension->store()) { 404 // Install failed, roll back changes 405 throw new \RuntimeException( 406 Text::sprintf( 407 'JLIB_INSTALLER_ABORT_ROLLBACK', 408 Text::_('JLIB_INSTALLER_' . strtoupper($this->route)), 409 $this->extension->getError() 410 ) 411 ); 412 } 413 414 // Since we have created a module item, we add it to the installation step stack 415 // so that if we have to rollback the changes we can undo it. 416 $this->parent->pushStep(array('type' => 'extension', 'extension_id' => $this->extension->extension_id)); 417 } 418 } 419 420 /** 421 * Function used to check if extension is already installed 422 * 423 * @param string $extension The element name of the extension to install 424 * 425 * @return boolean True if extension exists 426 * 427 * @since 3.1 428 */ 429 protected function extensionExistsInSystem($extension = null) 430 { 431 // Get a database connector object 432 $db = $this->getDatabase(); 433 434 $query = $db->getQuery(true) 435 ->select($db->quoteName('extension_id')) 436 ->from($db->quoteName('#__extensions')) 437 ->where($db->quoteName('type') . ' = ' . $db->quote('file')) 438 ->where($db->quoteName('element') . ' = :extension') 439 ->bind(':extension', $extension); 440 $db->setQuery($query); 441 442 try { 443 $db->execute(); 444 } catch (\RuntimeException $e) { 445 // Install failed, rollback changes - error logged by the installer 446 return false; 447 } 448 449 $id = $db->loadResult(); 450 451 if (empty($id)) { 452 return false; 453 } 454 455 return true; 456 } 457 458 /** 459 * Function used to populate files and folder list 460 * 461 * @return boolean none 462 * 463 * @since 3.1 464 */ 465 protected function populateFilesAndFolderList() 466 { 467 // Initialise variable 468 $this->folderList = array(); 469 $this->fileList = array(); 470 471 // Set root folder names 472 $packagePath = $this->parent->getPath('source'); 473 $jRootPath = Path::clean(JPATH_ROOT); 474 475 // Loop through all elements and get list of files and folders 476 foreach ($this->getManifest()->fileset->files as $eFiles) { 477 // Check if the element is files element 478 $folder = (string) $eFiles->attributes()->folder; 479 $target = (string) $eFiles->attributes()->target; 480 481 // Split folder names into array to get folder names. This will help in creating folders 482 $arrList = preg_split("#/|\\/#", $target); 483 484 $folderName = $jRootPath; 485 486 foreach ($arrList as $dir) { 487 if (empty($dir)) { 488 continue; 489 } 490 491 $folderName .= '/' . $dir; 492 493 // Check if folder exists, if not then add to the array for folder creation 494 if (!Folder::exists($folderName)) { 495 $this->folderList[] = $folderName; 496 } 497 } 498 499 // Create folder path 500 $sourceFolder = empty($folder) ? $packagePath : $packagePath . '/' . $folder; 501 $targetFolder = empty($target) ? $jRootPath : $jRootPath . '/' . $target; 502 503 // Check if source folder exists 504 if (!Folder::exists($sourceFolder)) { 505 Log::add(Text::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_FAIL_SOURCE_DIRECTORY', $sourceFolder), Log::WARNING, 'jerror'); 506 507 // If installation fails, rollback 508 $this->parent->abort(); 509 510 return false; 511 } 512 513 // Check if all children exists 514 if (\count($eFiles->children())) { 515 // Loop through all filenames elements 516 foreach ($eFiles->children() as $eFileName) { 517 $path['src'] = $sourceFolder . '/' . $eFileName; 518 $path['dest'] = $targetFolder . '/' . $eFileName; 519 $path['type'] = 'file'; 520 521 if ($eFileName->getName() === 'folder') { 522 $folderName = $targetFolder . '/' . $eFileName; 523 $this->folderList[] = $folderName; 524 $path['type'] = 'folder'; 525 } 526 527 $this->fileList[] = $path; 528 } 529 } else { 530 $files = Folder::files($sourceFolder); 531 532 foreach ($files as $file) { 533 $path['src'] = $sourceFolder . '/' . $file; 534 $path['dest'] = $targetFolder . '/' . $file; 535 536 $this->fileList[] = $path; 537 } 538 } 539 } 540 } 541 542 /** 543 * Refreshes the extension table cache 544 * 545 * @return boolean result of operation, true if updated, false on failure 546 * 547 * @since 3.1 548 */ 549 public function refreshManifestCache() 550 { 551 // Need to find to find where the XML file is since we don't store this normally 552 $manifestPath = JPATH_MANIFESTS . '/files/' . $this->parent->extension->element . '.xml'; 553 $this->parent->manifest = $this->parent->isManifest($manifestPath); 554 $this->parent->setPath('manifest', $manifestPath); 555 556 $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest')); 557 $this->parent->extension->manifest_cache = json_encode($manifest_details); 558 $this->parent->extension->name = $manifest_details['name']; 559 560 try { 561 return $this->parent->extension->store(); 562 } catch (\RuntimeException $e) { 563 Log::add(Text::_('JLIB_INSTALLER_ERROR_PACK_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror'); 564 565 return false; 566 } 567 } 568 }
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 |