[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Installer/Adapter/ -> LibraryAdapter.php (source)

   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\Installer\Installer;
  15  use Joomla\CMS\Installer\InstallerAdapter;
  16  use Joomla\CMS\Installer\Manifest\LibraryManifest;
  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   * Library installer
  29   *
  30   * @since  3.1
  31   */
  32  class LibraryAdapter extends InstallerAdapter
  33  {
  34      /**
  35       * Method to check if the extension is present in the filesystem, flags the route as update if so
  36       *
  37       * @return  void
  38       *
  39       * @since   3.4
  40       * @throws  \RuntimeException
  41       */
  42      protected function checkExtensionInFilesystem()
  43      {
  44          if ($this->currentExtensionId) {
  45              // Already installed, can we upgrade?
  46              if ($this->parent->isOverwrite() || $this->parent->isUpgrade()) {
  47                  // We can upgrade, so uninstall the old one
  48  
  49                  // We don't want to compromise this instance!
  50                  $installer = new Installer();
  51                  $installer->setDatabase($this->getDatabase());
  52                  $installer->setPackageUninstall(true);
  53                  $installer->uninstall('library', $this->currentExtensionId);
  54  
  55                  // Clear the cached data
  56                  $this->currentExtensionId = null;
  57                  $this->extension = Table::getInstance('Extension', 'JTable', array('dbo' => $this->getDatabase()));
  58  
  59                  // From this point we'll consider this an update
  60                  $this->setRoute('update');
  61              } else {
  62                  // Abort the install, no upgrade possible
  63                  throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_ALREADY_INSTALLED'));
  64              }
  65          }
  66      }
  67  
  68      /**
  69       * Method to copy the extension's base files from the `<files>` tag(s) and the manifest file
  70       *
  71       * @return  void
  72       *
  73       * @since   3.4
  74       * @throws  \RuntimeException
  75       */
  76      protected function copyBaseFiles()
  77      {
  78          if ($this->parent->parseFiles($this->getManifest()->files, -1) === false) {
  79              throw new \RuntimeException(Text::sprintf('JLIB_INSTALLER_ABORT_LIB_COPY_FILES', $this->element));
  80          }
  81      }
  82  
  83      /**
  84       * Method to finalise the installation processing
  85       *
  86       * @return  void
  87       *
  88       * @since   3.4
  89       * @throws  \RuntimeException
  90       */
  91      protected function finaliseInstall()
  92      {
  93          // Clobber any possible pending updates
  94          /** @var Update $update */
  95          $update = Table::getInstance('update');
  96          $uid    = $update->find(
  97              array(
  98                  'element' => $this->element,
  99                  'type'    => $this->type,
 100              )
 101          );
 102  
 103          if ($uid) {
 104              $update->delete($uid);
 105          }
 106  
 107          // Lastly, we will copy the manifest file to its appropriate place.
 108          if ($this->route !== 'discover_install') {
 109              $manifest         = array();
 110              $manifest['src']  = $this->parent->getPath('manifest');
 111              $manifest['dest'] = JPATH_MANIFESTS . '/libraries/' . $this->element . '.xml';
 112  
 113              $destFolder = \dirname($manifest['dest']);
 114  
 115              if (!is_dir($destFolder) && !@mkdir($destFolder)) {
 116                  // Install failed, rollback changes
 117                  throw new \RuntimeException(
 118                      Text::sprintf(
 119                          'JLIB_INSTALLER_ABORT_COPY_SETUP',
 120                          Text::_('JLIB_INSTALLER_' . strtoupper($this->route))
 121                      )
 122                  );
 123              }
 124  
 125              if (!$this->parent->copyFiles(array($manifest), true)) {
 126                  // Install failed, rollback changes
 127                  throw new \RuntimeException(
 128                      Text::sprintf(
 129                          'JLIB_INSTALLER_ABORT_COPY_SETUP',
 130                          Text::_('JLIB_INSTALLER_' . strtoupper($this->route))
 131                      )
 132                  );
 133              }
 134  
 135              // If there is a manifest script, let's copy it.
 136              if ($this->manifest_script) {
 137                  $path['src']  = $this->parent->getPath('source') . '/' . $this->manifest_script;
 138                  $path['dest'] = $this->parent->getPath('extension_root') . '/' . $this->manifest_script;
 139  
 140                  if ($this->parent->isOverwrite() || !file_exists($path['dest'])) {
 141                      if (!$this->parent->copyFiles(array($path))) {
 142                          // Install failed, rollback changes
 143                          throw new \RuntimeException(
 144                              Text::sprintf(
 145                                  'JLIB_INSTALLER_ABORT_MANIFEST',
 146                                  Text::_('JLIB_INSTALLER_' . strtoupper($this->route))
 147                              )
 148                          );
 149                      }
 150                  }
 151              }
 152          }
 153      }
 154  
 155      /**
 156       * Method to finalise the uninstallation processing
 157       *
 158       * @return  boolean
 159       *
 160       * @since   4.0.0
 161       * @throws  \RuntimeException
 162       */
 163      protected function finaliseUninstall(): bool
 164      {
 165          $extensionId = $this->extension->extension_id;
 166  
 167          $db = $this->getDatabase();
 168  
 169          // Remove the schema version
 170          $query = $db->getQuery(true)
 171              ->delete('#__schemas')
 172              ->where('extension_id = :extension_id')
 173              ->bind(':extension_id', $extensionId, ParameterType::INTEGER);
 174          $db->setQuery($query);
 175          $db->execute();
 176  
 177          // Clobber any possible pending updates
 178          $update = Table::getInstance('update');
 179          $uid    = $update->find(
 180              [
 181                  'element' => $this->extension->element,
 182                  'type'    => $this->type,
 183              ]
 184          );
 185  
 186          if ($uid) {
 187              $update->delete($uid);
 188          }
 189  
 190          $this->extension->delete();
 191  
 192          return true;
 193      }
 194  
 195      /**
 196       * Get the filtered extension element from the manifest
 197       *
 198       * @param   string  $element  Optional element name to be converted
 199       *
 200       * @return  string  The filtered element
 201       *
 202       * @since   3.4
 203       */
 204      public function getElement($element = null)
 205      {
 206          if (!$element) {
 207              $element  = (string) $this->getManifest()->libraryname;
 208          }
 209  
 210          return $element;
 211      }
 212  
 213      /**
 214       * Custom loadLanguage method
 215       *
 216       * @param   string  $path  The path where to find language files.
 217       *
 218       * @return  void
 219       *
 220       * @since   3.1
 221       */
 222      public function loadLanguage($path = null)
 223      {
 224          $source = $this->parent->getPath('source');
 225  
 226          if (!$source) {
 227              $this->parent->setPath('source', JPATH_PLATFORM . '/' . $this->getElement());
 228          }
 229  
 230          $extension   = 'lib_' . str_replace('/', '_', $this->getElement());
 231          $librarypath = (string) $this->getManifest()->libraryname;
 232          $source      = $path ?: JPATH_PLATFORM . '/' . $librarypath;
 233  
 234          $this->doLoadLanguage($extension, $source, JPATH_SITE);
 235      }
 236  
 237      /**
 238       * Method to parse optional tags in the manifest
 239       *
 240       * @return  void
 241       *
 242       * @since   3.4
 243       */
 244      protected function parseOptionalTags()
 245      {
 246          $this->parent->parseLanguages($this->getManifest()->languages);
 247          $this->parent->parseMedia($this->getManifest()->media);
 248      }
 249  
 250      /**
 251       * Prepares the adapter for a discover_install task
 252       *
 253       * @return  void
 254       *
 255       * @since   3.4
 256       */
 257      public function prepareDiscoverInstall()
 258      {
 259          $manifestPath           = JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml';
 260          $this->parent->manifest = $this->parent->isManifest($manifestPath);
 261          $this->parent->setPath('manifest', $manifestPath);
 262          $this->setManifest($this->parent->getManifest());
 263      }
 264  
 265      /**
 266       * Removes this extension's files
 267       *
 268       * @return  void
 269       *
 270       * @since   4.0.0
 271       * @throws  \RuntimeException
 272       */
 273      protected function removeExtensionFiles()
 274      {
 275          $this->parent->removeFiles($this->getManifest()->files, -1);
 276          File::delete(JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml');
 277  
 278          // @todo: Change this so it walked up the path backwards so we clobber multiple empties
 279          // If the folder is empty, let's delete it
 280          if (Folder::exists($this->parent->getPath('extension_root'))) {
 281              if (is_dir($this->parent->getPath('extension_root'))) {
 282                  $files = Folder::files($this->parent->getPath('extension_root'));
 283  
 284                  if (!\count($files)) {
 285                      Folder::delete($this->parent->getPath('extension_root'));
 286                  }
 287              }
 288          }
 289  
 290          $this->parent->removeFiles($this->getManifest()->media);
 291          $this->parent->removeFiles($this->getManifest()->languages);
 292  
 293          $elementParts = explode('/', $this->extension->element);
 294  
 295          // Delete empty vendor folders
 296          if (2 === \count($elementParts)) {
 297              $folders = Folder::folders(JPATH_PLATFORM . '/' . $elementParts[0]);
 298  
 299              if (empty($folders)) {
 300                  Folder::delete(JPATH_MANIFESTS . '/libraries/' . $elementParts[0]);
 301                  Folder::delete(JPATH_PLATFORM . '/' . $elementParts[0]);
 302              }
 303          }
 304      }
 305  
 306      /**
 307       * Method to do any prechecks and setup the install paths for the extension
 308       *
 309       * @return  void
 310       *
 311       * @since   3.4
 312       * @throws  \RuntimeException
 313       */
 314      protected function setupInstallPaths()
 315      {
 316          $group = (string) $this->getManifest()->libraryname;
 317  
 318          if (!$group) {
 319              throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_NOFILE'));
 320          }
 321  
 322          // Don't install libraries which would override core folders
 323          $restrictedFolders = array('php-encryption', 'phpass', 'src', 'vendor');
 324  
 325          if (in_array($group, $restrictedFolders)) {
 326              throw new \RuntimeException(Text::_('JLIB_INSTALLER_ABORT_LIB_INSTALL_CORE_FOLDER'));
 327          }
 328  
 329          $this->parent->setPath('extension_root', JPATH_PLATFORM . '/' . implode(DIRECTORY_SEPARATOR, explode('/', $group)));
 330      }
 331  
 332      /**
 333       * Method to do any prechecks and setup the uninstall job
 334       *
 335       * @return  void
 336       *
 337       * @since   4.0.0
 338       */
 339      protected function setupUninstall()
 340      {
 341          $manifestFile = JPATH_MANIFESTS . '/libraries/' . $this->extension->element . '.xml';
 342  
 343          // Because libraries may not have their own folders we cannot use the standard method of finding an installation manifest
 344          if (!file_exists($manifestFile)) {
 345              // Remove this row entry since its invalid
 346              $this->extension->delete($this->extension->extension_id);
 347  
 348              throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_INVALID_NOTFOUND_MANIFEST'));
 349          }
 350  
 351          $manifest = new LibraryManifest($manifestFile);
 352  
 353          // Set the library root path
 354          $this->parent->setPath('extension_root', JPATH_PLATFORM . '/' . $manifest->libraryname);
 355  
 356          // Set the source path to the library root, the manifest script may be found
 357          $this->parent->setPath('source', $this->parent->getPath('extension_root'));
 358  
 359          $xml = simplexml_load_file($manifestFile);
 360  
 361          // If we cannot load the XML file return null
 362          if (!$xml) {
 363              throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_LOAD_MANIFEST'));
 364          }
 365  
 366          // Check for a valid XML root tag.
 367          if ($xml->getName() !== 'extension') {
 368              throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_UNINSTALL_INVALID_MANIFEST'));
 369          }
 370  
 371          $this->setManifest($xml);
 372  
 373          // Attempt to load the language file; might have uninstall strings
 374          $this->loadLanguage();
 375      }
 376  
 377      /**
 378       * Method to store the extension to the database
 379       *
 380       * @return  void
 381       *
 382       * @since   3.4
 383       * @throws  \RuntimeException
 384       */
 385      protected function storeExtension()
 386      {
 387          // Discover installs are stored a little differently
 388          if ($this->route === 'discover_install') {
 389              $manifest_details = Installer::parseXMLInstallFile($this->parent->getPath('manifest'));
 390  
 391              $this->extension->manifest_cache = json_encode($manifest_details);
 392              $this->extension->state          = 0;
 393              $this->extension->name           = $manifest_details['name'];
 394              $this->extension->enabled        = 1;
 395              $this->extension->params         = $this->parent->getParams();
 396  
 397              if (!$this->extension->store()) {
 398                  // Install failed, roll back changes
 399                  throw new \RuntimeException(Text::_('JLIB_INSTALLER_ERROR_LIB_DISCOVER_STORE_DETAILS'));
 400              }
 401  
 402              return;
 403          }
 404  
 405          $this->extension->name         = $this->name;
 406          $this->extension->type         = 'library';
 407          $this->extension->element      = $this->element;
 408          $this->extension->changelogurl = $this->changelogurl;
 409  
 410          // There is no folder for libraries
 411          $this->extension->folder    = '';
 412          $this->extension->enabled   = 1;
 413          $this->extension->protected = 0;
 414          $this->extension->access    = 1;
 415          $this->extension->client_id = 0;
 416          $this->extension->params    = $this->parent->getParams();
 417  
 418          // Update the manifest cache for the entry
 419          $this->extension->manifest_cache = $this->parent->generateManifestCache();
 420  
 421          if (!$this->extension->store()) {
 422              // Install failed, roll back changes
 423              throw new \RuntimeException(
 424                  Text::sprintf(
 425                      'JLIB_INSTALLER_ABORT_LIB_INSTALL_ROLLBACK',
 426                      $this->extension->getError()
 427                  )
 428              );
 429          }
 430  
 431          // Since we have created a library item, we add it to the installation step stack
 432          // so that if we have to rollback the changes we can undo it.
 433          $this->parent->pushStep(array('type' => 'extension', 'id' => $this->extension->extension_id));
 434      }
 435  
 436      /**
 437       * Custom discover method
 438       *
 439       * @return  array  Extension  list of extensions available
 440       *
 441       * @since   3.1
 442       */
 443      public function discover()
 444      {
 445          $results = array();
 446  
 447          $mainFolder = JPATH_MANIFESTS . '/libraries';
 448          $folder = new \RecursiveDirectoryIterator($mainFolder);
 449          $iterator = new \RegexIterator(
 450              new \RecursiveIteratorIterator($folder),
 451              '/\.xml$/i',
 452              \RecursiveRegexIterator::GET_MATCH
 453          );
 454  
 455          foreach ($iterator as $file => $pattern) {
 456              $element       = str_replace(array($mainFolder . DIRECTORY_SEPARATOR, '.xml'), '', $file);
 457              $manifestCache = Installer::parseXMLInstallFile($file);
 458  
 459              $extension = Table::getInstance('extension');
 460              $extension->set('type', 'library');
 461              $extension->set('client_id', 0);
 462              $extension->set('element', $element);
 463              $extension->set('folder', '');
 464              $extension->set('name', $element);
 465              $extension->set('state', -1);
 466              $extension->set('manifest_cache', json_encode($manifestCache));
 467              $extension->set('params', '{}');
 468              $results[] = $extension;
 469          }
 470  
 471          return $results;
 472      }
 473  
 474      /**
 475       * Refreshes the extension table cache
 476       *
 477       * @return  boolean  Result of operation, true if updated, false on failure
 478       *
 479       * @since   3.1
 480       */
 481      public function refreshManifestCache()
 482      {
 483          // Need to find to find where the XML file is since we don't store this normally
 484          $manifestPath           = JPATH_MANIFESTS . '/libraries/' . $this->parent->extension->element . '.xml';
 485          $this->parent->manifest = $this->parent->isManifest($manifestPath);
 486          $this->parent->setPath('manifest', $manifestPath);
 487  
 488          $manifest_details                        = Installer::parseXMLInstallFile($this->parent->getPath('manifest'));
 489          $this->parent->extension->manifest_cache = json_encode($manifest_details);
 490          $this->parent->extension->name           = $manifest_details['name'];
 491  
 492          try {
 493              return $this->parent->extension->store();
 494          } catch (\RuntimeException $e) {
 495              Log::add(Text::_('JLIB_INSTALLER_ERROR_LIB_REFRESH_MANIFEST_CACHE'), Log::WARNING, 'jerror');
 496  
 497              return false;
 498          }
 499      }
 500  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer