\n";
$tag = ($local) ? get_vocab('upgrade_to_local_version') : get_vocab('upgrade_to_version');
echo get_vocab($tag) . ": $ver
\n";
if ($local)
{
$filename = "upgrade/local/$ver/$sql_type.sql";
$php_filename = "upgrade/local/$ver/post.inc";
}
else
{
$filename = "upgrade/$ver/$sql_type.sql";
$php_filename = "upgrade/$ver/post.inc";
}
$handle = fopen($filename, 'r');
if (!$handle)
{
// No need to localise, should never happen!
echo "Fatal error: Failed to open '$filename' for reading.\n";
return false;
}
$file_size = filesize($filename);
$sql = (!empty($file_size)) ? fread($handle, filesize($filename)) : '';
fclose($handle);
// PostgreSQL databases can have multiple schemas and so need a qualified
// table name when referring to the table name. However the table prefix is also
// used to make, for example, constraint, index and trigger names unique. These
// are unique within the schema and do not need to be schema-qualified. Also, when
// the tables were created from tables.pg.sql they would just have had "mrbs_"
// replaced by the unqualified table prefix.
$sql = str_replace('%DB_TBL_PREFIX%', _tbl('', true), $sql);
$sql = str_replace('%DB_TBL_PREFIX_SHORT%', _tbl('', false), $sql);
foreach (explode(";", $sql) as $query)
{
// Skip any empty query (so that last semicolon doesn't run
// an empty query)
if (preg_match("/\S/", $query))
{
$res = $upgrade_handle->query($query);
}
}
if ($ver > 1)
{
$variable_name = ($local) ? "local_db_version" : "db_version";
$upgrade_handle->command("UPDATE " . _tbl('variables') . " SET variable_content = ? ".
"WHERE variable_name = ?", array($ver, $variable_name));
}
// Now execute the PHP file if there is one
if (is_readable($php_filename))
{
include($php_filename);
}
echo get_vocab('ok');
echo "
" . escape_html(get_vocab('no_tables_found')) . "
\n"; } else { $current_db_schema_version = db_schema_version($upgrade_handle); $current_db_schema_version_local = db_schema_version($upgrade_handle, true); // Do any MRBS upgrades first if ($result && ($current_db_schema_version < DB::DB_SCHEMA_VERSION)) { $result = $result && upgrade_tables(false, $current_db_schema_version, DB::DB_SCHEMA_VERSION, $upgrade_handle); } else { echo "" . escape_html(get_vocab('already_at_version', $current_db_schema_version)) . "
\n"; } // Then any local upgrades if ($result && ($current_db_schema_version_local < DB::DB_SCHEMA_VERSION_LOCAL)) { $result = $result && upgrade_tables(true, $current_db_schema_version_local, DB::DB_SCHEMA_VERSION_LOCAL, $upgrade_handle); } } return $result; } function upgrade_site(array &$admin_handles, ?string $config_file=null) : bool { global $dbsys, $db_host, $db_database, $db_port, $db_schema, $db_tbl_prefix, $db_options; global $db_admin_username, $db_admin_password; // Get the site's DB config settings $settings = get_db_config($config_file); foreach ($settings as $var => $value) { $$var = $value; } // Form the new DSN $dsn = DBFactory::createDsn($dbsys, $db_host, $db_database, $db_port ?? null); // Try and get a handle for the site if (!isset($admin_handles[$dsn])) { // Try and get a new connection using the login details we've already been given try { $admin_handle = DBFactory::create( $dbsys, $db_host, $db_admin_username, $db_admin_password, $db_database, false, $db_port, $db_options ); $admin_handles[$dsn] = $admin_handle; } catch (DBException $e) { // Don't do anything: we were only trying to see if we could connect using the same details } } if (isset($admin_handles[$dsn])) { return do_upgrade($admin_handles[$dsn]); } echo "" . escape_html(get_vocab('no_connection')) . "
\n"; return false; } function upgrade_report(bool $main_site_result, array $failed_sites) : void { global $multisite; // Summarise if (!$multisite) { // Just report on success. Failure will be obvious. if ($main_site_result) { echo "" . escape_html(get_vocab('upgrade_completed')) . "
\n"; } } else { echo "" . escape_html(get_vocab('upgrade_completed')) . "
\n"; } else { // Report on the main site if (!$main_site_result) { echo "" . escape_html(get_vocab('main_site_failed')) . "
\n"; } // Report on failed sub-sites if (!empty($failed_sites)) { echo "" . escape_html(get_vocab('failed_sites')) . "
\n"; echo "" . escape_html(get_vocab('retry_from_failing')) . "
\n"; } } } // Get a database username and password function db_get_userpass() : void { print_header(); $form = new Form(Form::METHOD_POST); $form->setAttributes(array('class' => 'standard', 'id' => 'db_logon', 'action' => multisite(this_page()))); $fieldset = new ElementFieldset(); $fieldset->addLegend(get_vocab('database_login')); // The username field $field = new FieldInputText(); $field->setLabel('Database username') ->setControlAttributes(array('id' => 'form_username', 'name' => 'form_username', 'required' => true)); $fieldset->addElement($field); // The password field $field = new FieldInputPassword(); $field->setLabel('Database password') ->setControlAttributes(array('id' => 'form_password', 'name' => 'form_password')); $fieldset->addElement($field); // The submit button $field = new FieldInputSubmit(); $field->setControlAttributes(array('value' => get_vocab('login'))); $fieldset->addElement($field); $form->addElement($fieldset); $form->render(); // Print footer and exit print_footer(true); } // Sanity check: check that we can access the MRBS tables. If we can't, it's // either because they don't exist or we don't have permission. if (!db()->table_exists(_tbl('entry'))) { Errors::fatalError(get_vocab('fatal_no_tables')); } // Check the database schema versions $current_db_schema_version = db_schema_version(db()); $current_db_schema_version_local = db_schema_version(db(), true); // Although we could check whether the database schema version is higher than // that expected by MRBS, we don't. That's because if we do it prevents us // performing backwards compatible database upgrades in the background, because // the production site will stop working unnecessarily once the database has // been upgraded. // If either of the database schema version numbers are out of date, then // upgrade the database - provided of course that the entry table exists. if (($current_db_schema_version < DB::DB_SCHEMA_VERSION) || ($current_db_schema_version_local < DB::DB_SCHEMA_VERSION_LOCAL)) { // Upgrade needed // Upgrades can take some time, so turn on implicit flushing so that status // updates appear as they happen. Also turn off output compression because // that will involve some buffering. // Note that this won't always result in streamed output as (a) the web server // (b) the browser may implement their own buffering. Solution: use Ajax calls? ini_set('zlib.output_compression', '0'); ob_end_flush(); ob_implicit_flush(); // Just use a simple header as the normal header may (a) use features // which are not available until after the database upgrade or (b) use // functions which are not available until after dbsys has run. print_simple_header(); echo '' . get_vocab('upgrade_required') . "
\n"; $admin_handle = null; // We need to open a connection to the database with a database // username that has admin rights. // TODO: The while loop isn't doing anything. Ideally we want to offer the user a // TODO: chance to try a new username and password after a connection failure. while (empty($admin_handle)) { $db_admin_username = get_form_var('form_username', 'string'); $db_admin_password = get_form_var('form_password', 'string'); if (!isset($db_admin_username) || !isset($db_admin_password)) { // Get a username and password if we haven't got them echo '' . get_vocab('supply_userpass') . "
\n"; echo '' . get_vocab('contact_admin', $mrbs_admin) . "
\n"; db_get_userpass(); } else { try { $admin_handle = DBFactory::create( $dbsys, $db_host, $db_admin_username, $db_admin_password, $db_database, false, $db_port, $db_options ); } catch (DBException $e) { trigger_error($e->getMessage(), E_USER_NOTICE); Errors::fatalError(get_vocab('no_connection')); } } } // Check the CSRF token before we make any changes Form::checkToken(); // If we're using multisite then the various sites could be using different databases, or even // different database systems (MySQL or PostgreSQL). In that case we'll need different // handles to access them, so we store the handles indexed by DSN (which will give enough detail // to distinguish them). $dsn = DBFactory::createDsn($dbsys, $db_host, $db_database, $db_port); $admin_handles = [$dsn => $admin_handle]; // Get all the sub-site config files in this installation $site_config_files = get_site_config_files(); // Upgrade each of the sub-sites $failed_sites = []; foreach ($site_config_files as $site_config_file => $dir) { // Do the upgrade for the sub-site echo "