We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New language relevant PR in upstream repo: joomla/joomla-cms#41735 Here are the upstream changes:
diff --git a/README.md b/README.md index b0d1ba13783e4..07aad0805c0fa 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ The Joomla! 5.0 branch is under heavy development and not all links in this docu Build Status --------------------- -| Drone-CI | AppVeyor | PHP | Node | npm | -|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------| ------------- | ------------- | -| [![Build Status](https://ci.joomla.org/api/badges/joomla/joomla-cms/status.svg?branch=5.0-dev)](https://ci.joomla.org/joomla/joomla-cms) | [![Build status](https://ci.appveyor.com/api/projects/status/ru6sxal8jmfckvjc/branch/5.0-dev?svg=true)](https://ci.appveyor.com/project/release-joomla/joomla-cms) | [![PHP](https://img.shields.io/badge/PHP-V8.1.0-green)](https://www.php.net/) | [![node-lts](https://img.shields.io/badge/Node-V16.0-green)](https://nodejs.org/en/) | [![npm](https://img.shields.io/badge/npm-v8.6.0-green)](https://nodejs.org/en/) | +| Drone-CI | AppVeyor | PHP | Node | npm | +|------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| +| [![Build Status](https://ci.joomla.org/api/badges/joomla/joomla-cms/status.svg?branch=5.0-dev)](https://ci.joomla.org/joomla/joomla-cms) | [![Build status](https://ci.appveyor.com/api/projects/status/ru6sxal8jmfckvjc/branch/5.0-dev?svg=true)](https://ci.appveyor.com/project/release-joomla/joomla-cms) | [![PHP](https://img.shields.io/badge/PHP-V8.1.0-green)](https://www.php.net/) | [![node-lts](https://img.shields.io/badge/Node-V18.0-green)](https://nodejs.org/en/) | [![npm](https://img.shields.io/badge/npm-v9.6.7-green)](https://nodejs.org/en/) | Overview --------------------- diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index 28eca787ecebc..efc2924d58370 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -43,6 +43,50 @@ class JoomlaInstallerScript */ protected $fromVersion = null; + /** + * Callback for collecting errors. Like function(string $context, \Throwable $error){}; + * + * @var callable + * + * @since __DEPLOY_VERSION__ + */ + protected $errorCollector; + + /** + * Set the callback for collecting errors. + * + * @param callable $callback The callback Like function(string $context, \Throwable $error){}; + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function setErrorCollector(callable $callback) + { + $this->errorCollector = $callback; + } + + /** + * Collect errors. + * + * @param string $context A context/place where error happened + * @param \Throwable $error The error that occurred + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + protected function collectError(string $context, \Throwable $error) + { + // The errorCollector are required + // However when someone already running the script manually the code may fail. + if ($this->errorCollector) { + call_user_func($this->errorCollector, $context, $error); + } else { + Log::add($error->getMessage(), Log::ERROR, 'Update'); + } + } + /** * Function to act prior to installation process begins * @@ -82,11 +126,7 @@ public function preflight($action, $installer) */ public function update($installer) { - $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; - $options['text_file'] = 'joomla_update.php'; - - Log::addLogger($options, Log::INFO, ['Update', 'databasequery', 'jerror']); - + // Uninstall plugins before removing their files and folders try { Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_DELETE_FILES'), Log::INFO, 'Update'); } catch (RuntimeException $exception) { @@ -94,15 +134,37 @@ public function update($installer) } // Uninstall extensions before removing their files and folders - $this->uninstallExtensions(); - - // This needs to stay for 2.5 update compatibility - $this->deleteUnexistingFiles(); - $this->updateManifestCaches(); - $this->updateDatabase(); - $this->updateAssets($installer); - $this->clearStatsCache(); - $this->cleanJoomlaCache(); + try { + Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_UNINSTALL_EXTENSIONS'), Log::INFO, 'Update'); + $this->uninstallExtensions(); + } catch (\Throwable $e) { + $this->collectError('uninstallExtensions', $e); + } + + // Remove old files + try { + Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_DELETE_FILES'), Log::INFO, 'Update'); + $this->deleteUnexistingFiles(); + } catch (\Throwable $e) { + $this->collectError('deleteUnexistingFiles', $e); + } + + // Further update + try { + $this->updateManifestCaches(); + $this->updateDatabase(); + $this->updateAssets($installer); + $this->clearStatsCache(); + } catch (\Throwable $e) { + $this->collectError('Further update', $e); + } + + // Clean cache + try { + $this->cleanJoomlaCache(); + } catch (\Throwable $e) { + $this->collectError('cleanJoomlaCache', $e); + } } /** @@ -127,7 +189,7 @@ protected function clearStatsCache() ->where($db->quoteName('element') . ' = ' . $db->quote('stats')) )->loadResult(); } catch (Exception $e) { - echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>'; + $this->collectError(__METHOD__, $e); return; } @@ -151,7 +213,7 @@ protected function clearStatsCache() try { $db->setQuery($query)->execute(); } catch (Exception $e) { - echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>'; + $this->collectError(__METHOD__, $e); return; } @@ -183,7 +245,7 @@ protected function updateDatabaseMysql() try { $results = $db->loadObjectList(); } catch (Exception $e) { - echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>'; + $this->collectError(__METHOD__, $e); return; } @@ -198,7 +260,7 @@ protected function updateDatabaseMysql() try { $db->execute(); } catch (Exception $e) { - echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>'; + $this->collectError(__METHOD__, $e); return; } @@ -233,12 +295,12 @@ protected function uninstallExtensions() * 'pre_function' => Name of an optional migration function to be called before * uninstalling, `null` if not used. */ - ['type' => 'plugin', 'element' => 'demotasks', 'folder' => 'task', 'client_id' => 0, 'pre_function' => null], - ['type' => 'plugin', 'element' => 'compat', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateCompatPlugin'], - ['type' => 'plugin', 'element' => 'logrotation', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateLogRotationPlugin'], - ['type' => 'plugin', 'element' => 'recaptcha', 'folder' => 'captcha', 'client_id' => 0, 'pre_function' => null], - ['type' => 'plugin', 'element' => 'sessiongc', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateSessionGCPlugin'], - ['type' => 'plugin', 'element' => 'updatenotification', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateUpdatenotificationPlugin'], + ['type' => 'plugin', 'element' => 'demotasks', 'folder' => 'task', 'client_id' => 0, 'pre_function' => null], + ['type' => 'plugin', 'element' => 'compat', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateCompatPlugin'], + ['type' => 'plugin', 'element' => 'logrotation', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateLogRotationPlugin'], + ['type' => 'plugin', 'element' => 'recaptcha', 'folder' => 'captcha', 'client_id' => 0, 'pre_function' => null], + ['type' => 'plugin', 'element' => 'sessiongc', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateSessionGCPlugin'], + ['type' => 'plugin', 'element' => 'updatenotification', 'folder' => 'system', 'client_id' => 0, 'pre_function' => 'migrateUpdatenotificationPlugin'], ]; $db = Factory::getDbo(); @@ -477,7 +539,7 @@ protected function updateManifestCaches() try { $extensions = $db->loadObjectList(); } catch (Exception $e) { - echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>'; + $this->collectError(__METHOD__, $e); return; } @@ -487,7 +549,10 @@ protected function updateManifestCaches() foreach ($extensions as $extension) { if (!$installer->refreshManifestCache($extension->extension_id)) { - echo Text::sprintf('FILES_JOOMLA_ERROR_MANIFEST', $extension->type, $extension->element, $extension->name, $extension->client_id) . '<br>'; + $this->collectError( + __METHOD__, + new \Exception(Text::sprintf('FILES_JOOMLA_ERROR_MANIFEST', $extension->type, $extension->element, $extension->name, $extension->client_id)) + ); } } } @@ -2346,6 +2411,8 @@ public function updateAssets($installer) $asset->setLocation(1, 'last-child'); if (!$asset->store()) { + $this->collectError(__METHOD__, new \Exception($asset->getError(true))); + // Install failed, roll back changes $installer->abort(Text::sprintf('JLIB_INSTALLER_ABORT_COMP_INSTALL_ROLLBACK', $asset->getError(true))); @@ -2552,6 +2619,9 @@ private function migratePrivacyconsentConfiguration(): bool return false; } + // Refresh versionable assets cache. + Factory::getApplication()->flushAssets(); + return true; } @@ -2670,7 +2740,7 @@ private function setGuidedToursUid() { /** @var \Joomla\Component\Cache\Administrator\Model\CacheModel $model */ $model = Factory::getApplication()->bootComponent('com_guidedtours')->getMVCFactory() - ->createModel('Tours', 'Administrator', ['ignore_request' => true]); + ->createModel('Tours', 'Administrator', ['ignore_request' => true]); $items = $model->getItems(); diff --git a/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php b/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php index f3294eb4b9be9..b03b7d6dc31eb 100644 --- a/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php +++ b/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php @@ -66,6 +66,15 @@ public function display($cachable = false, $urlparams = false) $view->setModel($warningsModel, false); } + // Check for update result + if ($lName === 'complete') { + $state = $model->getState(); + $state->set('update_finished_with_error', $this->app->getUserState('com_joomlaupdate.update_finished_with_error')); + $state->set('update_errors', (array) $this->app->getUserState('com_joomlaupdate.update_errors', [])); + $state->set('installer_message', $this->app->getUserState('com_joomlaupdate.installer_message')); + $state->set('log_file', $this->app->get('log_path') . '/joomla_update.php'); + } + // Perform update source preference check and refresh update information. $model->applyUpdateSite(); $model->refreshUpdates(); diff --git a/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php b/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php index d4d148602c822..c40706eba35e7 100644 --- a/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php +++ b/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php @@ -11,6 +11,8 @@ namespace Joomla\Component\Joomlaupdate\Administrator\Controller; use Joomla\CMS\Factory; +use Joomla\CMS\Filesystem\File; +use Joomla\CMS\Installer\Installer; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\MVC\Controller\BaseController; @@ -40,19 +42,21 @@ public function download() { $this->checkToken(); - $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; - $options['text_file'] = 'joomla_update.php'; - Log::addLogger($options, Log::INFO, ['Update', 'databasequery', 'jerror']); - $user = $this->app->getIdentity(); + /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ + $model = $this->getModel('Update'); + $user = $this->app->getIdentity(); + // Make sure logging is working before continue try { - Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_START', $user->id, $user->name, \JVERSION), Log::INFO, 'Update'); - } catch (\RuntimeException $exception) { - // Informational log only + Log::add('Test logging', Log::INFO, 'Update'); + } catch (\Throwable $e) { + $message = Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL', $e->getMessage()); + $this->setRedirect('index.php?option=com_joomlaupdate', $message, 'error'); + return; } - /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ - $model = $this->getModel('Update'); + Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_START', $user->id, $user->name, \JVERSION), Log::INFO, 'Update'); + $result = $model->download(); $file = $result['basename']; @@ -81,11 +85,7 @@ public function download() $this->app->setUserState('com_joomlaupdate.file', $file); $url = 'index.php?option=com_joomlaupdate&task=update.install&' . $this->app->getSession()->getFormToken() . '=1'; - try { - Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_FILE', $file), Log::INFO, 'Update'); - } catch (\RuntimeException $exception) { - // Informational log only - } + Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_FILE', $file), Log::INFO, 'Update'); } else { $this->app->setUserState('com_joomlaupdate.file', null); $url = 'index.php?option=com_joomlaupdate'; @@ -108,19 +108,11 @@ public function install() $this->checkToken('get'); $this->app->setUserState('com_joomlaupdate.oldversion', JVERSION); - $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; - $options['text_file'] = 'joomla_update.php'; - Log::addLogger($options, Log::INFO, ['Update', 'databasequery', 'jerror']); - - try { - Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_INSTALL'), Log::INFO, 'Update'); - } catch (\RuntimeException $exception) { - // Informational log only - } - /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ $model = $this->getModel('Update'); + Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_INSTALL'), Log::INFO, 'Update'); + $file = $this->app->getUserState('com_joomlaupdate.file', null); $model->createRestorationFile($file); @@ -146,20 +138,33 @@ public function finalise() return; } - $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; - $options['text_file'] = 'joomla_update.php'; - Log::addLogger($options, Log::INFO, ['Update', 'databasequery', 'jerror']); + /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ + $model = $this->getModel('Update'); try { - Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_FINALISE'), Log::INFO, 'Update'); - } catch (\RuntimeException $exception) { - // Informational log only + $model->finaliseUpgrade(); + } catch (\Throwable $e) { + $model->collectError('finaliseUpgrade', $e); } - /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ - $model = $this->getModel('Update'); + // Check for update errors + if ($model->getErrors()) { + // The errors already should be logged at this point + // Collect a messages to show them later in the complete page + $errors = []; + foreach ($model->getErrors() as $error) { + $errors[] = $error->getMessage(); + } + + $this->app->setUserState('com_joomlaupdate.update_finished_with_error', true); + $this->app->setUserState('com_joomlaupdate.update_errors', $errors); + } - $model->finaliseUpgrade(); + // Check for captured output messages in the installer + $msg = Installer::getInstance()->get('extension_message'); + if ($msg) { + $this->app->setUserState('com_joomlaupdate.installer_message', $msg); + } $url = 'index.php?option=com_joomlaupdate&task=update.cleanup&' . Session::getFormToken() . '=1'; $this->setRedirect($url); @@ -184,29 +189,36 @@ public function cleanup() return; } - $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; - $options['text_file'] = 'joomla_update.php'; - Log::addLogger($options, Log::INFO, ['Update', 'databasequery', 'jerror']); + /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ + $model = $this->getModel('Update'); try { - Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_CLEANUP'), Log::INFO, 'Update'); - } catch (\RuntimeException $exception) { - // Informational log only + $model->cleanUp(); + } catch (\Throwable $e) { + $model->collectError('cleanUp', $e); } - /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ - $model = $this->getModel('Update'); + // Check for update errors + if ($model->getErrors()) { + // The errors already should be logged at this point + // Collect a messages to show them later in the complete page + $errors = $this->app->getUserState('com_joomlaupdate.update_errors', []); + foreach ($model->getErrors() as $error) { + $errors[] = $error->getMessage(); + } - $model->cleanUp(); + $this->app->setUserState('com_joomlaupdate.update_finished_with_error', true); + $this->app->setUserState('com_joomlaupdate.update_errors', $errors); + } $url = 'index.php?option=com_joomlaupdate&view=joomlaupdate&layout=complete'; - $this->setRedirect($url); - try { - Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_COMPLETE', \JVERSION), Log::INFO, 'Update'); - } catch (\RuntimeException $exception) { - // Informational log only + // In case for errored update, redirect to component view + if ($this->app->getUserState('com_joomlaupdate.update_finished_with_error')) { + $url .= '&tmpl=component'; } + + $this->setRedirect($url); } /** @@ -248,6 +260,17 @@ public function upload() /** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */ $model = $this->getModel('Update'); + // Make sure logging is working before continue + try { + Log::add('Test logging', Log::INFO, 'Update'); + } catch (\Throwable $e) { + $message = Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL', $e->getMessage()); + $this->setRedirect('index.php?option=com_joomlaupdate', $message, 'error'); + return; + } + + Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_UPLOAD'), Log::INFO, 'Update'); + try { $model->upload(); } catch (\RuntimeException $e) { diff --git a/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php b/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php index 6badece76a634..a7573e829d19b 100644 --- a/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php +++ b/administrator/components/com_joomlaupdate/src/Model/UpdateModel.php @@ -21,6 +21,7 @@ use Joomla\CMS\Installer\Installer; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; +use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Updater\Update; @@ -51,6 +52,28 @@ class UpdateModel extends BaseDatabaseModel */ private $updateInformation = null; + /** + * Constructor + * + * @param array $config An array of configuration options. + * @param ?MVCFactoryInterface $factory The factory. + * + * @since __DEPLOY_VERSION__ + * @throws \Exception + */ + public function __construct($config = [], MVCFactoryInterface $factory = null) + { + parent::__construct($config, $factory); + + // Register a logger for update process + $options = [ + 'format' => '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}', + 'text_file' => 'joomla_update.php', + ]; + + Log::addLogger($options, Log::ALL, ['Update', 'databasequery', 'jerror']); + } + /** * Detects if the Joomla! update site currently in use matches the one * configured in this component. If they don't match, it changes it. @@ -623,6 +646,8 @@ public function createUpdateFile($basename = null): bool */ public function finaliseUpgrade() { + Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_FINALISE'), Log::INFO, 'Update'); + $installer = Installer::getInstance(); $manifest = $installer->isManifest(JPATH_MANIFESTS . '/files/joomla.xml'); @@ -651,26 +676,33 @@ public function finaliseUpgrade() // Run the script file. \JLoader::register('JoomlaInstallerScript', JPATH_ADMINISTRATOR . '/components/com_admin/script.php'); + $msg = ''; $manifestClass = new \JoomlaInstallerScript(); + $manifestClass->setErrorCollector(function (string $context, \Throwable $error) { + $this->collectError($context, $error); + }); - ob_start(); - ob_implicit_flush(false); + // Run Installer preflight + try { + ob_start(); - if ($manifestClass && method_exists($manifestClass, 'preflight')) { if ($manifestClass->preflight('update', $installer) === false) { + $this->collectError('JoomlaInstallerScript::preflight', new \Exception('Script::preflight finished with "false" result.')); $installer->abort( Text::sprintf( 'JLIB_INSTALLER_ABORT_INSTALL_CUSTOM_INSTALL_FAILURE', Text::_('JLIB_INSTALLER_INSTALL') ) ); - return false; } - } - // Create msg object; first use here. - $msg = ob_get_clean(); + // Append messages. + $msg .= ob_get_clean(); + } catch (\Throwable $e) { + $this->collectError('JoomlaInstallerScript::preflight', $e); + return false; + } // Get a database connector object. $db = version_compare(JVERSION, '4.2.0', 'lt') ? $this->getDbo() : $this->getDatabase(); @@ -691,6 +723,7 @@ public function finaliseUpgrade() try { $db->execute(); } catch (\RuntimeException $e) { + $this->collectError('Extension check', $e); // Install failed, roll back changes. $installer->abort( Text::sprintf('JLIB_INSTALLER_ABORT_FILE_ROLLBACK', Text::_('JLIB_INSTALLER_UPDATE'), $e->getMessage()) @@ -713,6 +746,7 @@ public function finaliseUpgrade() $row->manifest_cache = $installer->generateManifestCache(); if (!$row->store()) { + $this->collectError('Update the manifest_cache', new \Exception('Update the manifest_cache finished with "false" result.')); // Install failed, roll back changes. $installer->abort( Text::sprintf('JLIB_INSTALLER_ABORT_FILE_ROLLBACK', Text::_('JLIB_INSTALLER_UPDATE'), $row->getError()) @@ -736,6 +770,7 @@ public function finaliseUpgrade() $row->set('manifest_cache', $installer->generateManifestCache()); if (!$row->store()) { + $this->collectError('Write the manifest_cache', new \Exception('Writing the manifest_cache finished with "false" result.')); // Install failed, roll back changes. $installer->abort(Text::sprintf('JLIB_INSTALLER_ABORT_FILE_INSTALL_ROLLBACK', $row->getError())); @@ -753,6 +788,7 @@ public function finaliseUpgrade() $result = $installer->parseSchemaUpdates($manifest->update->schemas, $row->extension_id); if ($result === false) { + $this->collectError('installer::parseSchemaUpdates', new \Exception('installer::parseSchemaUpdates finished with "false" result.')); // Install failed, rollback changes (message already logged by the installer). $installer->abort(); @@ -762,12 +798,12 @@ public function finaliseUpgrade() // Reinitialise the installer's extensions table's properties. $installer->extension->getFields(true); - // Start Joomla! 1.6. - ob_start(); - ob_implicit_flush(false); + try { + ob_start(); - if ($manifestClass && method_exists($manifestClass, 'update')) { if ($manifestClass->update($installer) === false) { + $this->collectError('JoomlaInstallerScript::update', new \Exception('Script::update finished with "false" result.')); + // Install failed, rollback changes. $installer->abort( Text::sprintf( @@ -778,10 +814,13 @@ public function finaliseUpgrade() return false; } - } - // Append messages. - $msg .= ob_get_clean(); + // Append messages. + $msg .= ob_get_clean(); + } catch (\Throwable $e) { + $this->collectError('JoomlaInstallerScript::update', $e); + return false; + } // Clobber any possible pending updates. $update = new \Joomla\CMS\Table\Update($db); @@ -794,23 +833,21 @@ public function finaliseUpgrade() } // And now we run the postflight. - ob_start(); - ob_implicit_flush(false); - - if ($manifestClass && method_exists($manifestClass, 'postflight')) { + try { + ob_start(); $manifestClass->postflight('update', $installer); - } - // Append messages. - $msg .= ob_get_clean(); + // Append messages. + $msg .= ob_get_clean(); + } catch (\Throwable $e) { + $this->collectError('JoomlaInstallerScript::postflight', $e); + return false; + } - if ($msg != '') { + if ($msg) { $installer->set('extension_message', $msg); } - // Refresh versionable assets cache. - Factory::getApplication()->flushAssets(); - return true; } @@ -828,6 +865,12 @@ public function finaliseUpgrade() */ public function cleanUp() { + try { + Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_CLEANUP'), Log::INFO, 'Update'); + } catch (\RuntimeException $exception) { + // Informational log only + } + // Load overrides plugin. PluginHelper::importPlugin('installer'); @@ -860,6 +903,12 @@ public function cleanUp() // Trigger event after joomla update. $app->triggerEvent('onJoomlaAfterUpdate', [$oldVersion]); $app->setUserState('com_joomlaupdate.oldversion', null); + + try { + Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_COMPLETE', \JVERSION), Log::INFO, 'Update'); + } catch (\RuntimeException $exception) { + // Informational log only + } } /** @@ -1694,4 +1743,37 @@ function ($value) { return $home || $menu; } + + /** + * Collect errors that happened during update. + * + * @param string $context A context/place where error happened + * @param \Throwable $error The error that occurred + * + * @return void + * + * @since __DEPLOY_VERSION__ + */ + public function collectError(string $context, \Throwable $error) + { + // Store error for further processing by controller + $this->setError($error); + + // Log it + Log::add( + sprintf( + 'An error has occurred while running "%s". Code: %s. Message: %s.', + $context, + $error->getCode(), + $error->getMessage() + ), + Log::ERROR, + 'Update' + ); + + if (JDEBUG) { + $trace = $error->getFile() . ':' . $error->getLine() . PHP_EOL . $error->getTraceAsString(); + Log::add(sprintf('An error trace: %s.', $trace), Log::DEBUG, 'Update'); + } + } } diff --git a/administrator/components/com_joomlaupdate/tmpl/joomlaupdate/complete.php b/administrator/components/com_joomlaupdate/tmpl/joomlaupdate/complete.php index e168c2697e00c..cf2d9268d73d7 100644 --- a/administrator/components/com_joomlaupdate/tmpl/joomlaupdate/complete.php +++ b/administrator/components/com_joomlaupdate/tmpl/joomlaupdate/complete.php @@ -13,15 +13,45 @@ use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; +use Joomla\CMS\Uri\Uri; + +$hadErrors = $this->state->get('update_finished_with_error'); +$errors = $this->state->get('update_errors'); +$logFile = $this->state->get('log_file'); +$installerMsg = $this->state->get('installer_message'); +$forumLink = '<a href="https://forum.joomla.org/" target="_blank" rel="noopener noreferrer">https://forum.joomla.org/</a>'; ?> <div class="card"> <h2 class="card-header"><?php echo Text::_('COM_JOOMLAUPDATE_VIEW_COMPLETE_HEADING'); ?></h2> <div class="card-body"> - <div class="alert alert-success"> - <span class="icon-check-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('NOTICE'); ?></span> - <?php echo Text::sprintf('COM_JOOMLAUPDATE_VIEW_COMPLETE_MESSAGE', '‎' . JVERSION); ?> + <?php if (!$hadErrors) : ?> + <div class="alert alert-success"> + <span class="icon-check-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('NOTICE'); ?></span> + <?php echo Text::sprintf('COM_JOOMLAUPDATE_VIEW_COMPLETE_MESSAGE', '‎' . JVERSION); ?> + </div> + <?php else : ?> + <div class="alert alert-error"> + <span class="icon-check-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('NOTICE'); ?></span> + <?php echo Text::sprintf('COM_JOOMLAUPDATE_VIEW_COMPLETE_WITH_ERROR_MESSAGE', $logFile, $forumLink); ?> + </div> + <p> + <a href="<?php echo Uri::base(true); ?>/" class="btn btn-primary"><?php echo Text::_('JGLOBAL_TPL_CPANEL_LINK_TEXT') ?></a> + </p> + <?php if ($errors) : ?> + <h3><?php echo Text::_('COM_JOOMLAUPDATE_VIEW_COMPLETE_UPDATE_ERRORS'); ?></h3> + <?php foreach ($errors as $error) : ?> + <div class="alert alert-error"><?php echo $error; ?></div> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> + + <?php if ($installerMsg) : ?> + <div> + <h3><?php echo Text::_('COM_JOOMLAUPDATE_VIEW_COMPLETE_INSTALLER_MESSAGE'); ?></h3> + <div class="alert alert-warning"><?php echo $installerMsg ?></div> </div> + <?php endif; ?> </div> </div> diff --git a/administrator/language/en-GB/com_joomlaupdate.ini b/administrator/language/en-GB/com_joomlaupdate.ini index 1f9b8612b596f..541f19a5e1889 100644 --- a/administrator/language/en-GB/com_joomlaupdate.ini +++ b/administrator/language/en-GB/com_joomlaupdate.ini @@ -75,13 +75,18 @@ COM_JOOMLAUPDATE_UPDATE_LOG_FILE="File %s downloaded." COM_JOOMLAUPDATE_UPDATE_LOG_FINALISE="Finalising installation." COM_JOOMLAUPDATE_UPDATE_LOG_INSTALL="Starting installation of new version." COM_JOOMLAUPDATE_UPDATE_LOG_START="Update started by user %2$s (%1$s). Old version is %3$s." +COM_JOOMLAUPDATE_UPDATE_LOG_UPLOAD="Uploading update file" COM_JOOMLAUPDATE_UPDATE_LOG_URL="Downloading update file from %s." +COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL="Logging does not work. Make sure the log folder is writable, and try again. The logging test failed with message: %s" COM_JOOMLAUPDATE_UPDATING_HEAD="Update in progress" COM_JOOMLAUPDATE_UPDATING_INPROGRESS="Please wait; Joomla is being updated. This may take a while." COM_JOOMLAUPDATE_UPDATING_FAIL="The update has failed." COM_JOOMLAUPDATE_UPDATING_COMPLETE="The update is finalising." COM_JOOMLAUPDATE_VIEW_COMPLETE_HEADING="Joomla Version Update Status" +COM_JOOMLAUPDATE_VIEW_COMPLETE_INSTALLER_MESSAGE="Messages captured by installer" COM_JOOMLAUPDATE_VIEW_COMPLETE_MESSAGE="Your site has been updated. Your Joomla version is now %s." +COM_JOOMLAUPDATE_VIEW_COMPLETE_UPDATE_ERRORS="Brief list of captured errors" +COM_JOOMLAUPDATE_VIEW_COMPLETE_WITH_ERROR_MESSAGE="The update completed with errors. Please examine the update logs %s for more details.<br>It is recommended to restore the site from backup, fix the issues that caused the update failure and try to update again.<br>You always can ask for help on Joomla! forum %s" COM_JOOMLAUPDATE_VIEW_DEFAULT_ACTUAL="Actual" COM_JOOMLAUPDATE_VIEW_DEFAULT_COMPATIBILITY_CHECK="Joomla! %s Compatibility Check" COM_JOOMLAUPDATE_VIEW_DEFAULT_COMPATIBLE_UPDATE_WARNING="Extensions marked with <span class='label label-warning'>X.X.X</span> have an extension update available for the current version of Joomla which is not marked as compatible with the updated version of Joomla. You should contact the extension developer for more information." diff --git a/installation/src/Console/InstallCommand.php b/installation/src/Console/InstallCommand.php index 36be6e4e31ce5..6e27e0821055f 100644 --- a/installation/src/Console/InstallCommand.php +++ b/installation/src/Console/InstallCommand.php @@ -151,7 +151,7 @@ protected function doExecute(InputInterface $input, OutputInterface $output): in foreach ($files as $step => $schema) { $serverType = $db->getServerType(); - if (\in_array($step, ['custom1', 'custom2']) && !is_file('sql/' . $serverType . '/' . $schema . '.sql')) { + if (\in_array($step, ['custom1', 'custom2']) && !is_file(JPATH_INSTALLATION . '/sql/' . $serverType . '/' . $schema . '.sql')) { continue; } diff --git a/installation/src/Controller/InstallationController.php b/installation/src/Controller/InstallationController.php index 8cb068739b1db..22353ea21c0d9 100644 --- a/installation/src/Controller/InstallationController.php +++ b/installation/src/Controller/InstallationController.php @@ -167,7 +167,7 @@ public function populate() $schema = $files[$step]; $serverType = $db->getServerType(); - if (in_array($step, ['custom1', 'custom2']) && !is_file('sql/' . $serverType . '/' . $schema . '.sql')) { + if (in_array($step, ['custom1', 'custom2']) && !is_file(JPATH_INSTALLATION . '/sql/' . $serverType . '/' . $schema . '.sql')) { $this->sendJsonResponse($r); return; diff --git a/libraries/src/Console/UpdateCoreCommand.php b/libraries/src/Console/UpdateCoreCommand.php index 72d000acbd280..7feefd9844ac9 100644 --- a/libraries/src/Console/UpdateCoreCommand.php +++ b/libraries/src/Console/UpdateCoreCommand.php @@ -11,9 +11,12 @@ use Joomla\Application\Cli\CliInput; use Joomla\CMS\Extension\ExtensionHelper; +use Joomla\CMS\Factory; use Joomla\CMS\Filesystem\File; use Joomla\CMS\Filesystem\Folder; use Joomla\CMS\Installer\InstallerHelper; +use Joomla\CMS\Language\Text; +use Joomla\CMS\Log\Log; use Joomla\Console\Command\AbstractCommand; use Joomla\Database\DatabaseInterface; use Symfony\Component\Console\Helper\ProgressBar; @@ -131,6 +134,11 @@ private function configureIO(InputInterface $input, OutputInterface $output) $this->cliInput = $input; $this->ioStyle = new SymfonyStyle($input, $output); + + $language = Factory::getLanguage(); + $language->load('lib_joomla', JPATH_ADMINISTRATOR); + $language->load('', JPATH_ADMINISTRATOR); + $language->load('com_joomlaupdate', JPATH_ADMINISTRATOR); } /** @@ -154,6 +162,17 @@ public function doExecute(InputInterface $input, OutputInterface $output): int $model = $this->getUpdateModel(); + // Make sure logging is working before continue + try { + Log::add('Test logging', Log::INFO, 'Update'); + } catch (\Throwable $e) { + $message = Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOGGING_TEST_FAIL', $e->getMessage()); + $this->ioStyle->error($message); + return self::ERR_UPDATE_FAILED; + } + + Log::add(Text::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_START', 0, 'CLI', \JVERSION), Log::INFO, 'Update'); + $this->setUpdateInfo($model->getUpdateInformation()); $this->progressBar->advance(); @@ -184,6 +203,12 @@ public function doExecute(InputInterface $input, OutputInterface $output): int if ($this->updateJoomlaCore($model)) { $this->progressBar->finish(); + + if ($model->getErrors()) { + $this->ioStyle->error('Update finished with errors. Please check logs for details.'); + return self::ERR_UPDATE_FAILED; + } + $this->ioStyle->success('Joomla core updated successfully!'); return self::UPDATE_SUCCESSFUL; diff --git a/plugins/system/debug/src/Extension/Debug.php b/plugins/system/debug/src/Extension/Debug.php index 92bc83549725a..fb780472bbc9a 100644 --- a/plugins/system/debug/src/Extension/Debug.php +++ b/plugins/system/debug/src/Extension/Debug.php @@ -29,6 +29,7 @@ use Joomla\Database\Event\ConnectionEvent; use Joomla\Event\DispatcherInterface; use Joomla\Event\Event; +use Joomla\Event\Priority; use Joomla\Event\SubscriberInterface; use Joomla\Plugin\System\Debug\DataCollector\InfoCollector; use Joomla\Plugin\System\Debug\DataCollector\LanguageErrorsCollector; @@ -147,11 +148,11 @@ public static function getSubscribedEvents(): array 'onBeforeRespond' => 'onBeforeRespond', 'onAfterRespond' => [ 'onAfterRespond', - PHP_INT_MIN, + Priority::MIN, ], ApplicationEvents::AFTER_RESPOND => [ 'onAfterRespond', - PHP_INT_MIN, + Priority::MIN, ], 'onAfterDisconnect' => 'onAfterDisconnect', ];
The text was updated successfully, but these errors were encountered:
tecpromotion
Successfully merging a pull request may close this issue.
New language relevant PR in upstream repo: joomla/joomla-cms#41735 Here are the upstream changes:
Click to expand the diff!
The text was updated successfully, but these errors were encountered: