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#41367 Here are the upstream changes:
diff --git a/administrator/components/com_admin/script.php b/administrator/components/com_admin/script.php index ce4e0c0c977af..9801c16a3eee7 100644 --- a/administrator/components/com_admin/script.php +++ b/administrator/components/com_admin/script.php @@ -40,6 +40,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,30 +126,45 @@ 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'; + // Uninstall plugins before removing their files and folders + try { + $this->uninstallRepeatableFieldsPlugin(); + } catch (\Throwable $e) { + $this->collectError('uninstallRepeatableFieldsPlugin', $e); + } - Log::addLogger($options, Log::INFO, ['Update', 'databasequery', 'jerror']); + try { + $this->uninstallEosPlugin(); + } catch (\Throwable $e) { + $this->collectError('uninstallEosPlugin', $e); + } + // Remove old files try { Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_DELETE_FILES'), Log::INFO, 'Update'); - } catch (RuntimeException $exception) { - // Informational log only + $this->deleteUnexistingFiles(); + } catch (\Throwable $e) { + $this->collectError('deleteUnexistingFiles', $e); } - // Uninstall plugins before removing their files and folders - $this->uninstallRepeatableFieldsPlugin(); - $this->uninstallEosPlugin(); - - // This needs to stay for 2.5 update compatibility - $this->deleteUnexistingFiles(); - $this->updateManifestCaches(); - $this->updateDatabase(); - $this->updateAssets($installer); - $this->clearStatsCache(); - $this->convertTablesToUtf8mb4(true); - $this->addUserAuthProviderColumn(); - $this->cleanJoomlaCache(); + // Further update + try { + $this->updateManifestCaches(); + $this->updateDatabase(); + $this->updateAssets($installer); + $this->clearStatsCache(); + $this->convertTablesToUtf8mb4(true); + $this->addUserAuthProviderColumn(); + } catch (\Throwable $e) { + $this->collectError('Further update', $e); + } + + // Clean cache + try { + $this->cleanJoomlaCache(); + } catch (\Throwable $e) { + $this->collectError('cleanJoomlaCache', $e); + } } /** @@ -130,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; } @@ -154,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; } @@ -186,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; } @@ -201,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; } @@ -563,7 +622,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; } @@ -573,7 +632,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)) + ); } } } @@ -8018,6 +8080,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))); @@ -8051,8 +8115,7 @@ public function convertTablesToUtf8mb4($doDbFixMsg = false) try { $rows = $db->loadRowList(0); } catch (Exception $e) { - // Render the error message from the Exception object - Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); + $this->collectError(__METHOD__, $e); if ($doDbFixMsg) { // Show an error message telling to check database problems @@ -8079,8 +8142,7 @@ public function convertTablesToUtf8mb4($doDbFixMsg = false) try { $convertedDB = $db->loadResult(); } catch (Exception $e) { - // Render the error message from the Exception object - Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); + $this->collectError(__METHOD__, $e); if ($doDbFixMsg) { // Show an error message telling to check database problems @@ -8112,8 +8174,7 @@ public function convertTablesToUtf8mb4($doDbFixMsg = false) } catch (Exception $e) { $converted = $convertedDB; - // Still render the error message from the Exception object - Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); + $this->collectError(__METHOD__, $e); } } } @@ -8147,8 +8208,7 @@ public function convertTablesToUtf8mb4($doDbFixMsg = false) } catch (Exception $e) { $converted = 99; - // Still render the error message from the Exception object - Factory::getApplication()->enqueueMessage($e->getMessage(), 'error'); + $this->collectError(__METHOD__, $e); } } } @@ -8287,6 +8347,9 @@ public function postflight($action, $installer) } } + // Refresh versionable assets cache. + Factory::getApplication()->flushAssets(); + return true; } @@ -8756,25 +8819,20 @@ protected function fixTemplateMode(): void { $db = Factory::getContainer()->get('DatabaseDriver'); - array_map( - function ($template) use ($db) { - $clientId = $template === 'atum' ? 1 : 0; - $query = $db->getQuery(true) - ->update($db->quoteName('#__template_styles')) - ->set($db->quoteName('inheritable') . ' = 1') - ->where($db->quoteName('template') . ' = ' . $db->quote($template)) - ->where($db->quoteName('client_id') . ' = ' . $clientId); - - try { - $db->setQuery($query)->execute(); - } catch (Exception $e) { - echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>'; - - return; - } - }, - ['atum', 'cassiopeia'] - ); + foreach (['atum', 'cassiopeia'] as $template) { + $clientId = $template === 'atum' ? 1 : 0; + $query = $db->getQuery(true) + ->update($db->quoteName('#__template_styles')) + ->set($db->quoteName('inheritable') . ' = 1') + ->where($db->quoteName('template') . ' = ' . $db->quote($template)) + ->where($db->quoteName('client_id') . ' = ' . $clientId); + + try { + $db->setQuery($query)->execute(); + } catch (Exception $e) { + $this->collectError(__METHOD__, $e); + } + } } /** @@ -8803,7 +8861,7 @@ protected function addUserAuthProviderColumn(): void 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; } diff --git a/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php b/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php index c70e53384fb80..453782289b834 100644 --- a/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php +++ b/administrator/components/com_joomlaupdate/src/Controller/DisplayController.php @@ -65,6 +65,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 0d8a0a09cccba..65581128d4547 100644 --- a/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php +++ b/administrator/components/com_joomlaupdate/src/Controller/UpdateController.php @@ -12,6 +12,7 @@ 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; @@ -41,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']; @@ -82,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'; @@ -109,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); @@ -147,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); @@ -185,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); } /** @@ -249,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 71d3dcb8c007d..f2e62828dbde9 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; @@ -50,6 +51,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. @@ -624,6 +647,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'); @@ -652,27 +677,34 @@ 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_contents(); - ob_end_clean(); + // Append messages. + $msg .= ob_get_contents(); + ob_end_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(); @@ -693,6 +725,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()) @@ -715,6 +748,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()) @@ -738,6 +772,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())); @@ -755,6 +790,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(); @@ -764,12 +800,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( @@ -780,11 +816,14 @@ public function finaliseUpgrade() return false; } - } - // Append messages. - $msg .= ob_get_contents(); - ob_end_clean(); + // Append messages. + $msg .= ob_get_contents(); + ob_end_clean(); + } catch (\Throwable $e) { + $this->collectError('JoomlaInstallerScript::update', $e); + return false; + } // Clobber any possible pending updates. $update = new \Joomla\CMS\Table\Update($db); @@ -797,24 +836,22 @@ 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_contents(); - ob_end_clean(); + // Append messages. + $msg .= ob_get_contents(); + ob_end_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; } @@ -832,6 +869,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'); @@ -874,6 +917,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 + } } /** @@ -1715,4 +1764,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 5fb332d3b8313..815c2f42b26d1 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/libraries/src/Console/UpdateCoreCommand.php b/libraries/src/Console/UpdateCoreCommand.php index 2031567012f48..9f6f8416f2ea6 100644 --- a/libraries/src/Console/UpdateCoreCommand.php +++ b/libraries/src/Console/UpdateCoreCommand.php @@ -10,10 +10,13 @@ namespace Joomla\CMS\Console; use Joomla\Application\Cli\CliInput; +use Joomla\CMS\Factory; use Joomla\CMS\Extension\ExtensionHelper; 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;
The text was updated successfully, but these errors were encountered:
fix joomlagerman#3001
a5e32d8
add translation
f4b8bef
tecpromotion
Successfully merging a pull request may close this issue.
New language relevant PR in upstream repo: joomla/joomla-cms#41367 Here are the upstream changes:
Click to expand the diff!
The text was updated successfully, but these errors were encountered: