[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Administrator 5 * @subpackage com_installer 6 * 7 * @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Installer\Administrator\Model; 12 13 use Joomla\CMS\Extension\ExtensionHelper; 14 use Joomla\CMS\Factory; 15 use Joomla\CMS\Filesystem\File; 16 use Joomla\CMS\Form\Form; 17 use Joomla\CMS\Installer\Installer; 18 use Joomla\CMS\Installer\InstallerHelper; 19 use Joomla\CMS\Language\Text; 20 use Joomla\CMS\MVC\Factory\MVCFactoryInterface; 21 use Joomla\CMS\MVC\Model\ListModel; 22 use Joomla\CMS\Plugin\PluginHelper; 23 use Joomla\CMS\Updater\Update; 24 use Joomla\CMS\Updater\Updater; 25 use Joomla\Database\DatabaseQuery; 26 use Joomla\Database\Exception\ExecutionFailureException; 27 use Joomla\Database\ParameterType; 28 use Joomla\Utilities\ArrayHelper; 29 30 // phpcs:disable PSR1.Files.SideEffects 31 \defined('_JEXEC') or die; 32 // phpcs:enable PSR1.Files.SideEffects 33 34 /** 35 * Installer Update Model 36 * 37 * @since 1.6 38 */ 39 class UpdateModel extends ListModel 40 { 41 /** 42 * Constructor. 43 * 44 * @param array $config An optional associative array of configuration settings. 45 * @param MVCFactoryInterface $factory The factory. 46 * 47 * @see \Joomla\CMS\MVC\Model\ListModel 48 * @since 1.6 49 */ 50 public function __construct($config = array(), MVCFactoryInterface $factory = null) 51 { 52 if (empty($config['filter_fields'])) { 53 $config['filter_fields'] = array( 54 'name', 'u.name', 55 'client_id', 'u.client_id', 'client_translated', 56 'type', 'u.type', 'type_translated', 57 'folder', 'u.folder', 'folder_translated', 58 'extension_id', 'u.extension_id', 59 ); 60 } 61 62 parent::__construct($config, $factory); 63 } 64 65 /** 66 * Method to auto-populate the model state. 67 * 68 * Note. Calling getState in this method will result in recursion. 69 * 70 * @param string $ordering An optional ordering field. 71 * @param string $direction An optional direction (asc|desc). 72 * 73 * @return void 74 * 75 * @since 1.6 76 */ 77 protected function populateState($ordering = 'u.name', $direction = 'asc') 78 { 79 $this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string')); 80 $this->setState('filter.client_id', $this->getUserStateFromRequest($this->context . '.filter.client_id', 'filter_client_id', null, 'int')); 81 $this->setState('filter.type', $this->getUserStateFromRequest($this->context . '.filter.type', 'filter_type', '', 'string')); 82 $this->setState('filter.folder', $this->getUserStateFromRequest($this->context . '.filter.folder', 'filter_folder', '', 'string')); 83 84 $app = Factory::getApplication(); 85 $this->setState('message', $app->getUserState('com_installer.message')); 86 $this->setState('extension_message', $app->getUserState('com_installer.extension_message')); 87 $app->setUserState('com_installer.message', ''); 88 $app->setUserState('com_installer.extension_message', ''); 89 90 parent::populateState($ordering, $direction); 91 } 92 93 /** 94 * Method to get the database query 95 * 96 * @return \Joomla\Database\DatabaseQuery The database query 97 * 98 * @since 1.6 99 */ 100 protected function getListQuery() 101 { 102 $db = $this->getDatabase(); 103 104 // Grab updates ignoring new installs 105 $query = $db->getQuery(true) 106 ->select('u.*') 107 ->select($db->quoteName('e.manifest_cache')) 108 ->from($db->quoteName('#__updates', 'u')) 109 ->join( 110 'LEFT', 111 $db->quoteName('#__extensions', 'e'), 112 $db->quoteName('e.extension_id') . ' = ' . $db->quoteName('u.extension_id') 113 ) 114 ->where($db->quoteName('u.extension_id') . ' != 0'); 115 116 // Process select filters. 117 $clientId = $this->getState('filter.client_id'); 118 $type = $this->getState('filter.type'); 119 $folder = $this->getState('filter.folder'); 120 $extensionId = $this->getState('filter.extension_id'); 121 122 if ($type) { 123 $query->where($db->quoteName('u.type') . ' = :type') 124 ->bind(':type', $type); 125 } 126 127 if ($clientId != '') { 128 $clientId = (int) $clientId; 129 $query->where($db->quoteName('u.client_id') . ' = :clientid') 130 ->bind(':clientid', $clientId, ParameterType::INTEGER); 131 } 132 133 if ($folder != '' && in_array($type, array('plugin', 'library', ''))) { 134 $folder = $folder === '*' ? '' : $folder; 135 $query->where($db->quoteName('u.folder') . ' = :folder') 136 ->bind(':folder', $folder); 137 } 138 139 if ($extensionId) { 140 $extensionId = (int) $extensionId; 141 $query->where($db->quoteName('u.extension_id') . ' = :extensionid') 142 ->bind(':extensionid', $extensionId, ParameterType::INTEGER); 143 } else { 144 $eid = ExtensionHelper::getExtensionRecord('joomla', 'file')->extension_id; 145 $query->where($db->quoteName('u.extension_id') . ' != 0') 146 ->where($db->quoteName('u.extension_id') . ' != :eid') 147 ->bind(':eid', $eid, ParameterType::INTEGER); 148 } 149 150 // Process search filter. 151 $search = $this->getState('filter.search'); 152 153 if (!empty($search)) { 154 if (stripos($search, 'eid:') !== false) { 155 $sid = (int) substr($search, 4); 156 $query->where($db->quoteName('u.extension_id') . ' = :sid') 157 ->bind(':sid', $sid, ParameterType::INTEGER); 158 } else { 159 if (stripos($search, 'uid:') !== false) { 160 $suid = (int) substr($search, 4); 161 $query->where($db->quoteName('u.update_site_id') . ' = :suid') 162 ->bind(':suid', $suid, ParameterType::INTEGER); 163 } elseif (stripos($search, 'id:') !== false) { 164 $uid = (int) substr($search, 3); 165 $query->where($db->quoteName('u.update_id') . ' = :uid') 166 ->bind(':uid', $uid, ParameterType::INTEGER); 167 } else { 168 $search = '%' . str_replace(' ', '%', trim($search)) . '%'; 169 $query->where($db->quoteName('u.name') . ' LIKE :search') 170 ->bind(':search', $search); 171 } 172 } 173 } 174 175 return $query; 176 } 177 178 /** 179 * Translate a list of objects 180 * 181 * @param array $items The array of objects 182 * 183 * @return array The array of translated objects 184 * 185 * @since 3.5 186 */ 187 protected function translate(&$items) 188 { 189 foreach ($items as &$item) { 190 $item->client_translated = Text::_([0 => 'JSITE', 1 => 'JADMINISTRATOR', 3 => 'JAPI'][$item->client_id] ?? 'JSITE'); 191 $manifest = json_decode($item->manifest_cache); 192 $item->current_version = $manifest->version ?? Text::_('JLIB_UNKNOWN'); 193 $item->description = $item->description !== '' ? $item->description : Text::_('COM_INSTALLER_MSG_UPDATE_NODESC'); 194 $item->type_translated = Text::_('COM_INSTALLER_TYPE_' . strtoupper($item->type)); 195 $item->folder_translated = $item->folder ?: Text::_('COM_INSTALLER_TYPE_NONAPPLICABLE'); 196 $item->install_type = $item->extension_id ? Text::_('COM_INSTALLER_MSG_UPDATE_UPDATE') : Text::_('COM_INSTALLER_NEW_INSTALL'); 197 } 198 199 return $items; 200 } 201 202 /** 203 * Returns an object list 204 * 205 * @param DatabaseQuery $query The query 206 * @param int $limitstart Offset 207 * @param int $limit The number of records 208 * 209 * @return array 210 * 211 * @since 3.5 212 */ 213 protected function _getList($query, $limitstart = 0, $limit = 0) 214 { 215 $db = $this->getDatabase(); 216 $listOrder = $this->getState('list.ordering', 'u.name'); 217 $listDirn = $this->getState('list.direction', 'asc'); 218 219 // Process ordering. 220 if (in_array($listOrder, array('client_translated', 'folder_translated', 'type_translated'))) { 221 $db->setQuery($query); 222 $result = $db->loadObjectList(); 223 $this->translate($result); 224 $result = ArrayHelper::sortObjects($result, $listOrder, strtolower($listDirn) === 'desc' ? -1 : 1, true, true); 225 $total = count($result); 226 227 if ($total < $limitstart) { 228 $limitstart = 0; 229 $this->setState('list.start', 0); 230 } 231 232 return array_slice($result, $limitstart, $limit ?: null); 233 } else { 234 $query->order($db->quoteName($listOrder) . ' ' . $db->escape($listDirn)); 235 236 $result = parent::_getList($query, $limitstart, $limit); 237 $this->translate($result); 238 239 return $result; 240 } 241 } 242 243 /** 244 * Get the count of disabled update sites 245 * 246 * @return integer 247 * 248 * @since 3.4 249 */ 250 public function getDisabledUpdateSites() 251 { 252 $db = $this->getDatabase(); 253 254 $query = $db->getQuery(true) 255 ->select('COUNT(*)') 256 ->from($db->quoteName('#__update_sites')) 257 ->where($db->quoteName('enabled') . ' = 0'); 258 259 $db->setQuery($query); 260 261 return $db->loadResult(); 262 } 263 264 /** 265 * Finds updates for an extension. 266 * 267 * @param int $eid Extension identifier to look for 268 * @param int $cacheTimeout Cache timeout 269 * @param int $minimumStability Minimum stability for updates {@see Updater} (0=dev, 1=alpha, 2=beta, 3=rc, 4=stable) 270 * 271 * @return boolean Result 272 * 273 * @since 1.6 274 */ 275 public function findUpdates($eid = 0, $cacheTimeout = 0, $minimumStability = Updater::STABILITY_STABLE) 276 { 277 Updater::getInstance()->findUpdates($eid, $cacheTimeout, $minimumStability); 278 279 return true; 280 } 281 282 /** 283 * Removes all of the updates from the table. 284 * 285 * @return boolean result of operation 286 * 287 * @since 1.6 288 */ 289 public function purge() 290 { 291 $db = $this->getDatabase(); 292 293 try { 294 $db->truncateTable('#__updates'); 295 } catch (ExecutionFailureException $e) { 296 $this->_message = Text::_('JLIB_INSTALLER_FAILED_TO_PURGE_UPDATES'); 297 298 return false; 299 } 300 301 // Reset the last update check timestamp 302 $query = $db->getQuery(true) 303 ->update($db->quoteName('#__update_sites')) 304 ->set($db->quoteName('last_check_timestamp') . ' = ' . $db->quote(0)); 305 $db->setQuery($query); 306 $db->execute(); 307 308 // Clear the administrator cache 309 $this->cleanCache('_system'); 310 311 $this->_message = Text::_('JLIB_INSTALLER_PURGED_UPDATES'); 312 313 return true; 314 } 315 316 /** 317 * Update function. 318 * 319 * Sets the "result" state with the result of the operation. 320 * 321 * @param int[] $uids List of updates to apply 322 * @param int $minimumStability The minimum allowed stability for installed updates {@see Updater} 323 * 324 * @return void 325 * 326 * @since 1.6 327 */ 328 public function update($uids, $minimumStability = Updater::STABILITY_STABLE) 329 { 330 $result = true; 331 332 foreach ($uids as $uid) { 333 $update = new Update(); 334 $instance = new \Joomla\CMS\Table\Update($this->getDatabase()); 335 336 if (!$instance->load($uid)) { 337 // Update no longer available, maybe already updated by a package. 338 continue; 339 } 340 341 $update->loadFromXml($instance->detailsurl, $minimumStability); 342 343 // Find and use extra_query from update_site if available 344 $updateSiteInstance = new \Joomla\CMS\Table\UpdateSite($this->getDatabase()); 345 $updateSiteInstance->load($instance->update_site_id); 346 347 if ($updateSiteInstance->extra_query) { 348 $update->set('extra_query', $updateSiteInstance->extra_query); 349 } 350 351 $this->preparePreUpdate($update, $instance); 352 353 // Install sets state and enqueues messages 354 $res = $this->install($update); 355 356 if ($res) { 357 $instance->delete($uid); 358 } 359 360 $result = $res & $result; 361 } 362 363 // Clear the cached extension data and menu cache 364 $this->cleanCache('_system'); 365 $this->cleanCache('com_modules'); 366 $this->cleanCache('com_plugins'); 367 $this->cleanCache('mod_menu'); 368 369 // Set the final state 370 $this->setState('result', $result); 371 } 372 373 /** 374 * Handles the actual update installation. 375 * 376 * @param Update $update An update definition 377 * 378 * @return boolean Result of install 379 * 380 * @since 1.6 381 */ 382 private function install($update) 383 { 384 // Load overrides plugin. 385 PluginHelper::importPlugin('installer'); 386 387 $app = Factory::getApplication(); 388 389 if (!isset($update->get('downloadurl')->_data)) { 390 Factory::getApplication()->enqueueMessage(Text::_('COM_INSTALLER_INVALID_EXTENSION_UPDATE'), 'error'); 391 392 return false; 393 } 394 395 $url = trim($update->downloadurl->_data); 396 $sources = $update->get('downloadSources', array()); 397 398 if ($extra_query = $update->get('extra_query')) { 399 $url .= (strpos($url, '?') === false) ? '?' : '&'; 400 $url .= $extra_query; 401 } 402 403 $mirror = 0; 404 405 while (!($p_file = InstallerHelper::downloadPackage($url)) && isset($sources[$mirror])) { 406 $name = $sources[$mirror]; 407 $url = trim($name->url); 408 409 if ($extra_query) { 410 $url .= (strpos($url, '?') === false) ? '?' : '&'; 411 $url .= $extra_query; 412 } 413 414 $mirror++; 415 } 416 417 // Was the package downloaded? 418 if (!$p_file) { 419 Factory::getApplication()->enqueueMessage(Text::sprintf('COM_INSTALLER_PACKAGE_DOWNLOAD_FAILED', $url), 'error'); 420 421 return false; 422 } 423 424 $config = $app->getConfig(); 425 $tmp_dest = $config->get('tmp_path'); 426 427 // Unpack the downloaded package file 428 $package = InstallerHelper::unpack($tmp_dest . '/' . $p_file); 429 430 if (empty($package)) { 431 $app->enqueueMessage(Text::sprintf('COM_INSTALLER_UNPACK_ERROR', $p_file), 'error'); 432 433 return false; 434 } 435 436 // Get an installer instance 437 $installer = Installer::getInstance(); 438 $update->set('type', $package['type']); 439 440 // Check the package 441 $check = InstallerHelper::isChecksumValid($package['packagefile'], $update); 442 443 if ($check === InstallerHelper::HASH_NOT_VALIDATED) { 444 $app->enqueueMessage(Text::_('COM_INSTALLER_INSTALL_CHECKSUM_WRONG'), 'error'); 445 446 return false; 447 } 448 449 if ($check === InstallerHelper::HASH_NOT_PROVIDED) { 450 $app->enqueueMessage(Text::_('COM_INSTALLER_INSTALL_CHECKSUM_WARNING'), 'warning'); 451 } 452 453 // Install the package 454 if (!$installer->update($package['dir'])) { 455 // There was an error updating the package 456 $app->enqueueMessage( 457 Text::sprintf( 458 'COM_INSTALLER_MSG_UPDATE_ERROR', 459 Text::_('COM_INSTALLER_TYPE_TYPE_' . strtoupper($package['type'])) 460 ), 461 'error' 462 ); 463 $result = false; 464 } else { 465 // Package updated successfully 466 $app->enqueueMessage( 467 Text::sprintf( 468 'COM_INSTALLER_MSG_UPDATE_SUCCESS', 469 Text::_('COM_INSTALLER_TYPE_TYPE_' . strtoupper($package['type'])) 470 ), 471 'success' 472 ); 473 $result = true; 474 } 475 476 // Quick change 477 $this->type = $package['type']; 478 479 // @todo: Reconfigure this code when you have more battery life left 480 $this->setState('name', $installer->get('name')); 481 $this->setState('result', $result); 482 $app->setUserState('com_installer.message', $installer->message); 483 $app->setUserState('com_installer.extension_message', $installer->get('extension_message')); 484 485 // Cleanup the install files 486 if (!is_file($package['packagefile'])) { 487 $package['packagefile'] = $config->get('tmp_path') . '/' . $package['packagefile']; 488 } 489 490 InstallerHelper::cleanupInstall($package['packagefile'], $package['extractdir']); 491 492 return $result; 493 } 494 495 /** 496 * Method to get the row form. 497 * 498 * @param array $data Data for the form. 499 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 500 * 501 * @return Form|bool A Form object on success, false on failure 502 * 503 * @since 2.5.2 504 */ 505 public function getForm($data = array(), $loadData = true) 506 { 507 // Get the form. 508 Form::addFormPath(JPATH_COMPONENT . '/models/forms'); 509 Form::addFieldPath(JPATH_COMPONENT . '/models/fields'); 510 $form = Form::getInstance('com_installer.update', 'update', array('load_data' => $loadData)); 511 512 // Check for an error. 513 if ($form == false) { 514 $this->setError($form->getMessage()); 515 516 return false; 517 } 518 519 // Check the session for previously entered form data. 520 $data = $this->loadFormData(); 521 522 // Bind the form data if present. 523 if (!empty($data)) { 524 $form->bind($data); 525 } 526 527 return $form; 528 } 529 530 /** 531 * Method to get the data that should be injected in the form. 532 * 533 * @return mixed The data for the form. 534 * 535 * @since 2.5.2 536 */ 537 protected function loadFormData() 538 { 539 // Check the session for previously entered form data. 540 $data = Factory::getApplication()->getUserState($this->context, array()); 541 542 return $data; 543 } 544 545 /** 546 * Method to add parameters to the update 547 * 548 * @param Update $update An update definition 549 * @param \Joomla\CMS\Table\Update $table The update instance from the database 550 * 551 * @return void 552 * 553 * @since 3.7.0 554 */ 555 protected function preparePreUpdate($update, $table) 556 { 557 switch ($table->type) { 558 // Components could have a helper which adds additional data 559 case 'component': 560 $ename = str_replace('com_', '', $table->element); 561 $fname = $ename . '.php'; 562 $cname = ucfirst($ename) . 'Helper'; 563 564 $path = JPATH_ADMINISTRATOR . '/components/' . $table->element . '/helpers/' . $fname; 565 566 if (File::exists($path)) { 567 require_once $path; 568 569 if (class_exists($cname) && is_callable(array($cname, 'prepareUpdate'))) { 570 call_user_func_array(array($cname, 'prepareUpdate'), array(&$update, &$table)); 571 } 572 } 573 574 break; 575 576 // Modules could have a helper which adds additional data 577 case 'module': 578 $cname = str_replace('_', '', $table->element) . 'Helper'; 579 $path = ($table->client_id ? JPATH_ADMINISTRATOR : JPATH_SITE) . '/modules/' . $table->element . '/helper.php'; 580 581 if (File::exists($path)) { 582 require_once $path; 583 584 if (class_exists($cname) && is_callable(array($cname, 'prepareUpdate'))) { 585 call_user_func_array(array($cname, 'prepareUpdate'), array(&$update, &$table)); 586 } 587 } 588 589 break; 590 591 // If we have a plugin, we can use the plugin trigger "onInstallerBeforePackageDownload" 592 // But we should make sure, that our plugin is loaded, so we don't need a second "installer" plugin 593 case 'plugin': 594 $cname = str_replace('plg_', '', $table->element); 595 PluginHelper::importPlugin($table->folder, $cname); 596 break; 597 } 598 } 599 600 /** 601 * Manipulate the query to be used to evaluate if this is an Empty State to provide specific conditions for this extension. 602 * 603 * @return DatabaseQuery 604 * 605 * @since 4.0.0 606 */ 607 protected function getEmptyStateQuery() 608 { 609 $query = parent::getEmptyStateQuery(); 610 611 $query->where($this->getDatabase()->quoteName('extension_id') . ' != 0'); 612 613 return $query; 614 } 615 }
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 |