[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

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

   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  }


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