[4.4] Upmerge (#41733)

* [4.3] fix localised cli installation (#41706)

* [4.3] Work around a successful upgrade with silent errors (#41367)

* Log all

* Capture errors

* Log messages

* Error collector

* Error collector

* More error collecting

* More error collecting

* Show errors

* Cli command check errors

* phpcs

* phpcs

* Back button and better text

* phpcs

* logs

* Make sure logging is working before continue

* Apply suggestions from code review

---------

Co-authored-by: Richard Fath <richard67@users.noreply.github.com>
Co-authored-by: Harald Leithner <leithner@itronic.at>

* cs

---------

Co-authored-by: heelc29 <66922325+heelc29@users.noreply.github.com>
Co-authored-by: Fedir Zinchuk <getthesite@gmail.com>
Co-authored-by: Richard Fath <richard67@users.noreply.github.com>
This commit is contained in:
Harald Leithner 2023-09-13 12:17:49 +02:00 committed by GitHub
parent 6a83a803aa
commit 295fceefbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 369 additions and 137 deletions

View File

@ -41,6 +41,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
*
@ -83,30 +127,45 @@ class JoomlaInstallerScript
*/
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) {
// Informational log only
$this->uninstallRepeatableFieldsPlugin();
} catch (\Throwable $e) {
$this->collectError('uninstallRepeatableFieldsPlugin', $e);
}
// Uninstall plugins before removing their files and folders
$this->uninstallRepeatableFieldsPlugin();
$this->uninstallEosPlugin();
try {
$this->uninstallEosPlugin();
} catch (\Throwable $e) {
$this->collectError('uninstallEosPlugin', $e);
}
// 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();
// 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();
$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);
}
}
/**
@ -131,7 +190,7 @@ class JoomlaInstallerScript
->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;
}
@ -155,7 +214,7 @@ class JoomlaInstallerScript
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;
}
@ -187,7 +246,7 @@ class JoomlaInstallerScript
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;
}
@ -202,7 +261,7 @@ class JoomlaInstallerScript
try {
$db->execute();
} catch (Exception $e) {
echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>';
$this->collectError(__METHOD__, $e);
return;
}
@ -564,7 +623,7 @@ class JoomlaInstallerScript
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;
}
@ -574,7 +633,10 @@ class JoomlaInstallerScript
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))
);
}
}
}
@ -8100,6 +8162,8 @@ class JoomlaInstallerScript
$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)));
@ -8133,8 +8197,7 @@ class JoomlaInstallerScript
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
@ -8161,8 +8224,7 @@ class JoomlaInstallerScript
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
@ -8194,8 +8256,7 @@ class JoomlaInstallerScript
} catch (Exception $e) {
$converted = $convertedDB;
// Still render the error message from the Exception object
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
$this->collectError(__METHOD__, $e);
}
}
}
@ -8229,8 +8290,7 @@ class JoomlaInstallerScript
} catch (Exception $e) {
$converted = 99;
// Still render the error message from the Exception object
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
$this->collectError(__METHOD__, $e);
}
}
}
@ -8369,6 +8429,9 @@ class JoomlaInstallerScript
}
}
// Refresh versionable assets cache.
Factory::getApplication()->flushAssets();
return true;
}
@ -8838,25 +8901,20 @@ class JoomlaInstallerScript
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
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);
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) {
echo Text::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $e->getCode(), $e->getMessage()) . '<br>';
return;
}
},
['atum', 'cassiopeia']
);
try {
$db->setQuery($query)->execute();
} catch (Exception $e) {
$this->collectError(__METHOD__, $e);
}
}
}
/**
@ -8885,7 +8943,7 @@ class JoomlaInstallerScript
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;
}

View File

@ -65,6 +65,15 @@ class DisplayController extends BaseController
$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();

View File

@ -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 @@ class UpdateController extends BaseController
{
$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 @@ class UpdateController extends BaseController
$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 @@ class UpdateController extends BaseController
$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 @@ class UpdateController extends BaseController
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']);
try {
Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_FINALISE'), Log::INFO, 'Update');
} catch (\RuntimeException $exception) {
// Informational log only
}
/** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */
$model = $this->getModel('Update');
$model->finaliseUpgrade();
try {
$model->finaliseUpgrade();
} catch (\Throwable $e) {
$model->collectError('finaliseUpgrade', $e);
}
// 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);
}
// 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 @@ class UpdateController extends BaseController
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']);
try {
Log::add(Text::_('COM_JOOMLAUPDATE_UPDATE_LOG_CLEANUP'), Log::INFO, 'Update');
} catch (\RuntimeException $exception) {
// Informational log only
}
/** @var \Joomla\Component\Joomlaupdate\Administrator\Model\UpdateModel $model */
$model = $this->getModel('Update');
$model->cleanUp();
try {
$model->cleanUp();
} catch (\Throwable $e) {
$model->collectError('cleanUp', $e);
}
// 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();
}
$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 @@ class UpdateController extends BaseController
/** @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) {

View File

@ -21,6 +21,7 @@ use Joomla\CMS\Http\HttpFactory;
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.
@ -630,6 +653,8 @@ ENDDATA;
*/
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');
@ -658,27 +683,34 @@ ENDDATA;
// 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();
@ -699,6 +731,7 @@ ENDDATA;
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())
@ -721,6 +754,7 @@ ENDDATA;
$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())
@ -744,6 +778,7 @@ ENDDATA;
$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()));
@ -761,6 +796,7 @@ ENDDATA;
$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();
@ -770,12 +806,12 @@ ENDDATA;
// 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(
@ -786,11 +822,14 @@ ENDDATA;
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);
@ -803,24 +842,22 @@ ENDDATA;
}
// 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();
} catch (\Throwable $e) {
$this->collectError('JoomlaInstallerScript::postflight', $e);
return false;
}
// Append messages.
$msg .= ob_get_contents();
ob_end_clean();
if ($msg != '') {
if ($msg) {
$installer->set('extension_message', $msg);
}
// Refresh versionable assets cache.
Factory::getApplication()->flushAssets();
return true;
}
@ -838,6 +875,12 @@ ENDDATA;
*/
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');
@ -880,6 +923,12 @@ ENDDATA;
// 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
}
}
/**
@ -1721,4 +1770,37 @@ ENDDATA;
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');
}
}
}

View File

@ -13,15 +13,45 @@ defined('_JEXEC') or die;
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', '&#x200E;' . 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', '&#x200E;' . 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>

View File

@ -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."

View File

@ -152,7 +152,7 @@ class InstallCommand extends AbstractCommand
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;
}

View File

@ -144,7 +144,7 @@ class InstallationController extends JSONController
$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;

View File

@ -11,9 +11,12 @@ namespace Joomla\CMS\Console;
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 @@ class UpdateCoreCommand extends AbstractCommand
$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 @@ class UpdateCoreCommand extends AbstractCommand
$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 @@ class UpdateCoreCommand extends AbstractCommand
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;