Difference between revisions of "SMWIssue4785"
Jump to navigation
Jump to search
(3 intermediate revisions by the same user not shown) | |||
Line 2: | Line 2: | ||
__TOC__ | __TOC__ | ||
= SetupCheck.php = | = SetupCheck.php = | ||
− | <source lang='php' | + | <source lang='php' highlight="383-396,402-406"> |
<?php | <?php | ||
Line 587: | Line 587: | ||
} | } | ||
</source> | </source> | ||
+ | |||
= SetupFile.php = | = SetupFile.php = | ||
− | <source lang='php' | + | <source lang='php' highlight='104-172,181,516-519'> |
<?php | <?php | ||
Latest revision as of 20:29, 4 December 2022
Modified files for https://github.com/SemanticMediaWiki/SemanticMediaWiki/issues/4785
SetupCheck.php
<?php
namespace SMW;
use SMW\Utils\TemplateEngine;
use SMW\Utils\Logo;
use SMW\Localizer\LocalMessageProvider;
use SMW\Exception\FileNotReadableException;
use SMW\Exception\JSONFileParseException;
use RuntimeException;
/**
* @private
*
* @license GNU GPL v2+
* @since 3.1
*
* @author mwjames
*/
class SetupCheck {
/**
* Semantic MediaWiki was loaded or accessed but not correctly enabled.
*/
const ERROR_EXTENSION_LOAD = 'ERROR_EXTENSION_LOAD';
/**
* Semantic MediaWiki was loaded or accessed but not correctly enabled.
*/
const ERROR_EXTENSION_INVALID_ACCESS = 'ERROR_EXTENSION_INVALID_ACCESS';
/**
* A user tried to use `wfLoadExtension( 'SemanticMediaWiki' )` and
* `enableSemantics` at the same causing the ExtensionRegistry to throw an
* "Uncaught Exception: It was attempted to load SemanticMediaWiki twice ..."
*/
const ERROR_EXTENSION_REGISTRY = 'ERROR_EXTENSION_REGISTRY';
/**
* A dependency (extension, MediaWiki) causes an error
*/
const ERROR_EXTENSION_DEPENDENCY = 'ERROR_EXTENSION_DEPENDENCY';
/**
* Multiple dependencies (extension, MediaWiki) caused an error
*/
const ERROR_EXTENSION_DEPENDENCY_MULTIPLE = 'ERROR_EXTENSION_DEPENDENCY_MULTIPLE';
/**
* Extension doesn't match MediaWiki or the PHP requirement.
*/
const ERROR_EXTENSION_INCOMPATIBLE = 'ERROR_EXTENSION_INCOMPATIBLE';
/**
* Extension doesn't match the DB requirement for Semantic MediaWiki.
*/
const ERROR_DB_REQUIREMENT_INCOMPATIBLE = 'ERROR_DB_REQUIREMENT_INCOMPATIBLE';
/**
* The upgrade key has change causing the schema to be invalid
*/
const ERROR_SCHEMA_INVALID_KEY = 'ERROR_SCHEMA_INVALID_KEY';
/**
* A selected default profile could not be loaded or is unknown.
*/
const ERROR_CONFIG_PROFILE_UNKNOWN = 'ERROR_CONFIG_PROFILE_UNKNOWN';
/**
* The system is currently in a maintenance window
*/
const MAINTENANCE_MODE = 'MAINTENANCE_MODE';
/**
* @var []
*/
private $options = [];
/**
* @var SetupFile
*/
private $setupFile;
/**
* @var TemplateEngine
*/
private $templateEngine;
/**
* @var LocalMessageProvider
*/
private $localMessageProvider;
/**
* @var []
*/
private $definitions = [];
/**
* @var string
*/
private $languageCode = 'en';
/**
* @var string
*/
private $fallbackLanguageCode = 'en';
/**
* @var boolean
*/
private $sentHeader = true;
/**
* @var string
*/
private $errorType = '';
/**
* @var string
*/
private $errorMessage = '';
/**
* @var string
*/
private $traceString = '';
/**
* @since 3.1
*
* @param array $vars
* @param SetupFile|null $setupFile
*/
public function __construct( array $options, SetupFile $setupFile = null ) {
$this->options = $options;
$this->setupFile = $setupFile;
$this->templateEngine = new TemplateEngine();
$this->localMessageProvider = new LocalMessageProvider( '/local/setupcheck.i18n.json' );
if ( $this->setupFile === null ) {
$this->setupFile = new SetupFile();
}
}
/**
* @since 3.2
*
* @param string $file
*
* @return array
* @throws RuntimeException
*/
public static function readFromFile( string $file ) : array {
if ( !is_readable( $file ) ) {
throw new FileNotReadableException( $file );
}
$contents = json_decode(
file_get_contents( $file ),
true
);
if ( json_last_error() === JSON_ERROR_NONE ) {
return $contents;
}
throw new JSONFileParseException( $file );
}
/**
* @since 3.1
*
* @param SetupFile|null $setupFile
*
* @return SetupCheck
*/
public static function newFromDefaults( SetupFile $setupFile = null ) {
if ( !defined( 'SMW_VERSION' ) ) {
$version = self::readFromFile( $GLOBALS['smwgIP'] . 'extension.json' )['version'];
} else {
$version = SMW_VERSION;
}
$setupCheck = new SetupCheck(
[
'SMW_VERSION' => $version,
'MW_VERSION' => $GLOBALS['wgVersion'], // MW_VERSION may not yet be defined!!
'wgLanguageCode' => $GLOBALS['wgLanguageCode'],
'smwgUpgradeKey' => $GLOBALS['smwgUpgradeKey']
],
$setupFile
);
return $setupCheck;
}
/**
* @since 3.2
*/
public function disableHeader() {
$this->sentHeader = false;
}
/**
* @since 3.1
*
* @return boolean
*/
public function isCli() {
return PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg';
}
/**
* @since 3.1
*
* @param string $traceString
*/
public function setTraceString( $traceString ) {
$this->traceString = $traceString;
}
/**
* @since 3.2
*
* @param string $errorMessage
*/
public function setErrorMessage( string $errorMessage ) {
$this->errorMessage = $errorMessage;
}
/**
* @since 3.2
*
* @param string $errorType
*/
public function setErrorType( string $errorType ) {
$this->errorType = $errorType;
}
/**
* @since 3.2
*
* @return boolean
*/
public function isError( string $error ) : bool {
return $this->errorType === $error;
}
/**
* @since 3.1
*
* @return boolean
*/
public function hasError() {
$this->errorType = '';
// When it is not a test run or run from the command line we expect that
// the extension is registered using `enableSemantics`
if ( !defined( 'SMW_EXTENSION_LOADED' ) && !$this->isCli() ) {
$this->errorType = self::ERROR_EXTENSION_LOAD;
} elseif ( $this->setupFile->inMaintenanceMode() ) {
$this->errorType = self::MAINTENANCE_MODE;
} elseif ( !$this->isCli() && !$this->setupFile->hasDatabaseMinRequirement() ) {
$this->errorType = self::ERROR_DB_REQUIREMENT_INCOMPATIBLE;
} elseif ( $this->setupFile->isGoodSchema() === false ) {
$this->errorType = self::ERROR_SCHEMA_INVALID_KEY;
}
return $this->errorType !== '';
}
/**
* @note Adding a new error type requires to:
*
* - Define a constant to clearly identify the type of error
* - Extend the `setupcheck.json` to add a definition for the new type and
* specify which information should be displayed
* - In case the existing HTML elements aren't sufficient, create a new
* zxy.ms file and define the HTML code
*
* The `TemplateEngine` will replace arguments defined in the HTML hereby
* absolving this class from any direct HTML manipulation.
*
* @since 3.1
*
* @param boolean $isCli
*
* @return string
*/
public function getError( $isCli = false ) {
$error = [
'title' => '',
'content' => ''
];
$this->languageCode = $_GET['uselang'] ?? $this->options['wgLanguageCode'] ?? 'en';
// Output forms for different error types are registered with a JSON file.
$this->definitions = $this->readFromFile(
$GLOBALS['smwgDir'] . '/data/template/setupcheck/setupcheck.json'
);
// Error messages are specified in a special i18n JSON file to avoid relying
// on the MW message system especially when SMW isn't fully registered
// and we are unable to access any `smw-...` message keys from the standard
// i18n files.
$this->localMessageProvider->setLanguageCode(
$this->languageCode
);
$this->localMessageProvider->loadMessages();
// HTML specific formatting is contained in the following files where
// a defined group of targets correspond to types used in the JSON
$this->templateEngine->bulkLoad(
[
'/setupcheck/setupcheck.ms' => 'setupcheck-html',
'/setupcheck/setupcheck.progress.ms' => 'setupcheck-progress',
// Target specific elements
'/setupcheck/setupcheck.section.ms' => 'section',
'/setupcheck/setupcheck.version.ms' => 'version',
'/setupcheck/setupcheck.paragraph.ms' => 'paragraph',
'/setupcheck/setupcheck.errorbox.ms' => 'errorbox',
'/setupcheck/setupcheck.db.requirement.ms' => 'db-requirement',
]
);
if ( !isset( $this->definitions['error_types'][$this->errorType] ) ) {
throw new RuntimeException( "The `{$this->errorType}` type is not defined in the `setupcheck.json`!" );
}
$error = $this->createErrorContent( $this->errorType );
if ( $isCli === false ) {
$content = $this->buildHTML( $error );
$this->header( 'Content-Type: text/html; charset=UTF-8' );
$this->header( 'Content-Length: ' . strlen( $content ) );
$this->header( 'Cache-control: none' );
$this->header( 'Pragma: no-cache' );
} else {
$content = $error['title'] . "\n\n" . $error['content'];
$content = str_replace(
[ '<!-- ROW -->', '</h3>', '</h4>', '</p>', ' ' ],
[ "\n", "\n\n", "\n\n", "\n\n", ' ' ],
$content
);
$content = "\n" . wordwrap( strip_tags( trim( $content ) ), 73 );
}
return $content;
}
/**
* @since 3.1
*
* @param boolean $isCli
*/
public function showErrorAndAbort( $isCli = false ) {
echo $this->getError( $isCli );
if ( ob_get_level() ) {
ob_flush();
flush();
ob_end_clean();
}
die();
}
private function header( $text ) {
if ( $this->sentHeader ) {
header( $text );
}
}
private function schemaError() {
// get trace
// https://stackoverflow.com/a/7039409/1497139
//
$e = new \Exception;
$content ='<pre>'.$e->getTraceAsString().'</pre>';
$content .='PHP_SAPI: '.PHP_SAPI."<br>";
$isCli=$this->isCli();
$schemaState=SetupFile::getSchemaState($isCli);
$content.='Schema state: '.$schemaState."<br>";
$upgradeKeyBase=SetupFile::makeKey($GLOBALS);
$content.='upgrade key base: '.$upgradeKeyBase;
return $content;
}
private function createErrorContent( $type ) {
$indicator_title = 'Error';
$template = $this->definitions['error_types'][$type];
$content = '';
if ($type==self::ERROR_SCHEMA_INVALID_KEY) {
$content.=$this->schemaError($this->isCli());
}
/**
* Actual output form
*/
foreach ( $template['output_form'] as $value ) {
$content .= $this->createContent( $value, $type );
}
/**
* Special handling for the progress output
*/
if ( isset( $template['progress'] ) ) {
foreach ( $template['progress'] as $value ) {
$text = $this->createCopy( $value['text'] );
if ( isset( $value['progress_keys'] ) ) {
$content .= $this->createProgressIndicator( $value );
}
$args = [
'text' => $text,
'template' => $value['type']
];
$this->templateEngine->compile(
$value['type'],
$args
);
$content .= $this->templateEngine->publish( $value['type'] );
}
}
/**
* Special handling for the stack trace output
*/
if ( isset( $template['stack_trace'] ) && $this->traceString !== '' ) {
foreach ( $template['stack_trace'] as $value ) {
$content .= $this->createContent( $value, $type );
}
}
if ( isset( $template['indicator_title'] ) ) {
$indicator_title = $this->createCopy( $template['indicator_title'] );
}
$error = [
'title' => 'Semantic MediaWiki',
'indicator_title' => $indicator_title,
'content' => $content,
'borderColor' => $template['indicator_color']
];
return $error;
}
private function createContent( $value, $type ) {
if ( $value['text'] === 'ERROR_TEXT' ) {
$text = str_replace( "\n", '<br>', $this->errorMessage );
} elseif ( $value['text'] === 'ERROR_TEXT_MULTIPLE' ) {
$errors = explode( "\n", $this->errorMessage );
$text = '<ul><li>' . implode( '</li><li>', array_filter( $errors ) ) . '</li></ul>';
} elseif ( $value['text'] === 'TRACE_STRING' ) {
$text = $this->traceString;
} else {
$text = $this->createCopy( $value['text'] );
}
$args = [
'text' => $text,
'template' => $value['type']
];
if ( $value['type'] === 'version' ) {
$args['version-title'] = $text;
$args['smw-title'] = 'Semantic MediaWiki';
$args['smw-version'] = $this->options['SMW_VERSION'] ?? 'n/a';
$args['smw-upgradekey'] = $this->options['smwgUpgradeKey'] ?? 'n/a';
$args['mw-title'] = 'MediaWiki';
$args['mw-version'] = $this->options['MW_VERSION'] ?? 'n/a';
$args['code-title'] = $this->createCopy( 'smw-setupcheck-code' );
$args['code-type'] = $type;
}
if ( $value['type'] === 'db-requirement' ) {
$requirements = $this->setupFile->get( SetupFile::DB_REQUIREMENTS );
$args['version-title'] = $text;
$args['db-title'] = $this->createCopy( 'smw-setupcheck-db-title' );
$args['db-type'] = $requirements['type'] ?? 'N/A';
$args['db-current-title'] = $this->createCopy( 'smw-setupcheck-db-current-title' );
$args['db-minimum-title'] = $this->createCopy( 'smw-setupcheck-db-minimum-title' );
$args['db-current-version'] = $requirements['latest_version'] ?? 'N/A';
$args['db-minimum-version'] = $requirements['minimum_version'] ?? 'N/A';
}
// The type is expected to match a defined target and in an event
// that those don't match an exception will be raised.
$this->templateEngine->compile(
$value['type'],
$args
);
return $this->templateEngine->publish( $value['type'] );
}
private function createProgressIndicator( $value ) {
$maintenanceMode = (array)$this->setupFile->getMaintenanceMode();
$content = '';
foreach ( $maintenanceMode as $key => $v ) {
$args = [
'label' => $key,
'value' => $v
];
if ( isset( $value['progress_keys'][$key] ) ) {
$args['label'] = $this->createCopy( $value['progress_keys'][$key] );
}
$this->templateEngine->compile(
'setupcheck-progress',
$args
);
$content .= $this->templateEngine->publish( 'setupcheck-progress' );
}
return $content;
}
private function createCopy( $value, $default = 'n/a' ) {
if ( is_string( $value ) && $this->localMessageProvider->has( $value ) ) {
return $this->localMessageProvider->msg( $value );
}
return $default;
}
private function buildHTML( array $error ) {
$args = [
'logo' => Logo::get( 'small' ),
'title' => $error['title'] ?? '',
'indicator' => $error['indicator_title'] ?? '',
'content' => $error['content'] ?? '',
'borderColor' => $error['borderColor'] ?? '#fff',
'refresh' => $error['refresh'] ?? '30',
];
$this->templateEngine->compile(
'setupcheck-html',
$args
);
$html = $this->templateEngine->publish( 'setupcheck-html' );
// Minify CSS rules, we keep them readable in the template to allow for
// better adaption
// @see http://manas.tungare.name/software/css-compression-in-php/
$html = preg_replace_callback( "/<style\\b[^>]*>(.*?)<\\/style>/s", function( $matches ) {
// Remove space after colons
$style = str_replace( ': ', ':', $matches[0] );
// Remove whitespace
return str_replace( [ "\r\n", "\r", "\n", "\t", ' ', ' ', ' '], '', $style );
},
$html
);
return $html;
}
}
SetupFile.php
<?php
namespace SMW;
use SMW\Exception\FileNotWritableException;
use SMW\Utils\File;
use SMW\SQLStore\Installer;
/**
* @private
*
* @license GNU GPL v2+
* @since 3.1
*
* @author mwjames
*/
class SetupFile {
/**
* Describes the maintenance mode
*/
const MAINTENANCE_MODE = 'maintenance_mode';
/**
* Describes the upgrade key
*/
const UPGRADE_KEY = 'upgrade_key';
/**
* Describes the database requirements
*/
const DB_REQUIREMENTS = 'db_requirements';
/**
* Describes the entity collection setting
*/
const ENTITY_COLLATION = 'entity_collation';
/**
* Key that describes the date of the last table optimization run.
*/
const LAST_OPTIMIZATION_RUN = 'last_optimization_run';
/**
* Describes the file name
*/
const FILE_NAME = '.smw.json';
/**
* Describes incomplete tasks
*/
const INCOMPLETE_TASKS = 'incomplete_tasks';
/**
* Versions
*/
const LATEST_VERSION = 'latest_version';
const PREVIOUS_VERSION = 'previous_version';
/**
* @var File
*/
private $file;
/**
* @since 3.1
*
* @param File|null $file
*/
public function __construct( File $file = null ) {
$this->file = $file;
if ( $this->file === null ) {
$this->file = new File();
}
}
/**
* @since 3.1
*
* @param array $vars
*/
public function loadSchema( &$vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
if ( isset( $vars['smw.json'] ) ) {
return;
}
// @see #3506
$file = File::dir( $vars['smwgConfigFileDir'] . '/' . self::FILE_NAME );
// Doesn't exist? The `Setup::init` will take care of it by trying to create
// a new file and if it fails or unable to do so wail raise an exception
// as we expect to have access to it.
if ( is_readable( $file ) ) {
$vars['smw.json'] = json_decode( file_get_contents( $file ), true );
}
}
/**
* @since 3.1
*
* @param boolean $isCli
*
* @return boolean
*/
public static function isGoodSchema( $isCli = false ) {
// get the schema State as a human readable description
$schemaState=SetupFile::getSchemaState($isCli);
// check that it starts with "ok:" and not "error:"
$result=SetupFile::strStartsWith($schemaState,"ok:");
return $result;
}
/**
* @since 3.1.7
*
* see https://stackoverflow.com/a/6513929/1497139
*
* @param string $haystack
* @param string $needle
*
* return boolean
*/
public static function strStartsWith($haystack, $needle) {
return (strpos($haystack, $needle) === 0);
}
/**
* @since 3.1.7
*
* @param boolean $isCli
*
* @return string
*/
public static function getSchemaState( $isCli = false ) {
if ( $isCli && defined( 'MW_PHPUNIT_TEST' ) ) {
return "ok: CLI with PHP Unit Test active";
}
if ( $isCli === false && ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) ) {
return "ok: isCli is true and PHP_SAPI cli/phpdbg=".PHP_SAPI;
}
// #3563, Use the specific wiki-id as identifier for the instance in use
$id = Site::id();
if ( !isset( $GLOBALS['smw.json'][$id]['upgrade_key'] ) ) {
global $smwgConfigFileDir;
return "error: smw.json for ".$id." upgrade key missing - you might want to check \$smwgConfigFileDir:".$smwgConfigFileDir;
}
$upgradeKey = self::makeUpgradeKey( $GLOBALS );
$expected =$GLOBALS['smw.json'][$id]['upgrade_key'];
if ( $upgradeKey === $expected )
$schemaState= "ok: found upgradeKey.".$upgradeKey;
else
$schemaState= "error: expected upgradeKey ".$expected." for ".$id." but found ".$upgradeKey;
if (
isset( $GLOBALS['smw.json'][$id][self::MAINTENANCE_MODE] ) &&
$GLOBALS['smw.json'][$id][self::MAINTENANCE_MODE] !== false ) {
$schemaState= "error: upgradeKey ".$upgradeKey." is ok but maintainance is active";
}
return $schemaState;
}
/**
* @since 3.1
*
* @param array $vars
*
* @return string
*/
public static function makeUpgradeKey( $vars ) {
return sha1( self::makeKey( $vars ) );
}
/**
* @since 3.1
*
* @param array $vars
*
* @return boolean
*/
public function inMaintenanceMode( $vars = [] ) {
if ( !defined( 'MW_PHPUNIT_TEST' ) && ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) ) {
return false;
}
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$id = Site::id();
if ( !isset( $vars['smw.json'][$id][self::MAINTENANCE_MODE] ) ) {
return false;
}
return $vars['smw.json'][$id][self::MAINTENANCE_MODE] !== false;
}
/**
* @since 3.1
*
* @param array $vars
*
* @return []
*/
public function getMaintenanceMode( $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$id = Site::id();
if ( !isset( $vars['smw.json'][$id][self::MAINTENANCE_MODE] ) ) {
return [];
}
return $vars['smw.json'][$id][self::MAINTENANCE_MODE];
}
/**
* Tracking the latest and previous version, which allows us to decide whether
* current activties relate to an install (new) or upgrade.
*
* @since 3.2
*
* @param int $version
*/
public function setLatestVersion( $version ) {
$latest = $this->get( SetupFile::LATEST_VERSION );
$previous = $this->get( SetupFile::PREVIOUS_VERSION );
if ( $latest === null && $previous === null ) {
$this->set(
[
SetupFile::LATEST_VERSION => $version
]
);
} elseif ( $latest !== $version ) {
$this->set(
[
SetupFile::LATEST_VERSION => $version,
SetupFile::PREVIOUS_VERSION => $latest
]
);
}
}
/**
* @since 3.2
*
* @param string $key
* @param array $args
*/
public function addIncompleteTask( string $key, array $args = [] ) {
$incomplete_tasks = $this->get( self::INCOMPLETE_TASKS );
if ( $incomplete_tasks === null ) {
$incomplete_tasks = [];
}
$incomplete_tasks[$key] = $args === [] ? true : $args;
$this->set( [ self::INCOMPLETE_TASKS => $incomplete_tasks ] );
}
/**
* @since 3.2
*
* @param string $key
*/
public function removeIncompleteTask( string $key ) {
$incomplete_tasks = $this->get( self::INCOMPLETE_TASKS );
if ( $incomplete_tasks === null ) {
$incomplete_tasks = [];
}
unset( $incomplete_tasks[$key] );
$this->set( [ self::INCOMPLETE_TASKS => $incomplete_tasks ] );
}
/**
* @since 3.2
*
* @param array $vars
*
* @return boolean
*/
public function hasDatabaseMinRequirement( array $vars = [] ) : bool {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$id = Site::id();
// No record means, no issues!
if ( !isset( $vars['smw.json'][$id][self::DB_REQUIREMENTS] ) ) {
return true;
}
$requirements = $vars['smw.json'][$id][self::DB_REQUIREMENTS];
return version_compare( $requirements['latest_version'], $requirements['minimum_version'], 'ge' );
}
/**
* @since 3.1
*
* @param array $vars
*
* @return []
*/
public function findIncompleteTasks( $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$id = Site::id();
$tasks = [];
// Key field => [ value that constitutes the `INCOMPLETE` state, error msg ]
$checks = [
\SMW\SQLStore\Installer::POPULATE_HASH_FIELD_COMPLETE => [ false, 'smw-install-incomplete-populate-hash-field' ],
\SMW\Elastic\ElasticStore::REBUILD_INDEX_RUN_COMPLETE => [ false, 'smw-install-incomplete-elasticstore-indexrebuild' ]
];
foreach ( $checks as $key => $value ) {
if ( !isset( $vars['smw.json'][$id][$key] ) ) {
continue;
}
if ( $vars['smw.json'][$id][$key] === $value[0] ) {
$tasks[] = $value[1];
}
}
if ( isset( $vars['smw.json'][$id][self::INCOMPLETE_TASKS] ) ) {
foreach ( $vars['smw.json'][$id][self::INCOMPLETE_TASKS] as $key => $args ) {
if ( $args === true ) {
$tasks[] = $key;
} else {
$tasks[] = [ $key, $args ];
}
}
}
return $tasks;
}
/**
* @since 3.1
*
* @param mixed $maintenanceMode
*/
public function setMaintenanceMode( $maintenanceMode, $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$this->write(
[
self::UPGRADE_KEY => self::makeUpgradeKey( $vars ),
self::MAINTENANCE_MODE => $maintenanceMode
],
$vars
);
}
/**
* @since 3.1
*
* @param array $vars
*/
public function finalize( $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
// #3563, Use the specific wiki-id as identifier for the instance in use
$key = self::makeUpgradeKey( $vars );
$id = Site::id();
if (
isset( $vars['smw.json'][$id][self::UPGRADE_KEY] ) &&
$key === $vars['smw.json'][$id][self::UPGRADE_KEY] &&
$vars['smw.json'][$id][self::MAINTENANCE_MODE] === false ) {
return false;
}
$this->write(
[
self::UPGRADE_KEY => $key,
self::MAINTENANCE_MODE => false
],
$vars
);
}
/**
* @since 3.1
*
* @param array $vars
*/
public function reset( $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$id = Site::id();
$args = [];
if ( !isset( $vars['smw.json'][$id] ) ) {
return;
}
$vars['smw.json'][$id] = [];
$this->write( [], $vars );
}
/**
* @since 3.1
*
* @param array $args
*/
public function set( array $args, $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$this->write( $args, $vars );
}
/**
* @since 3.1
*
* @param array $args
*/
public function get( $key, $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$id = Site::id();
if ( isset( $vars['smw.json'][$id][$key] ) ) {
return $vars['smw.json'][$id][$key];
}
return null;
}
/**
* @since 3.1
*
* @param string $key
*/
public function remove( $key, $vars = [] ) {
if ( $vars === [] ) {
$vars = $GLOBALS;
}
$this->write( [ $key => null ], $vars );
}
/**
* @since 3.1
*
* @param array $vars
* @param array $args
*/
public function write( array $args, array $vars ) {
$configFile = File::dir( $vars['smwgConfigFileDir'] . '/' . self::FILE_NAME );
$id = Site::id();
if ( !isset( $vars['smw.json'] ) ) {
$vars['smw.json'] = [];
}
foreach ( $args as $key => $value ) {
// NULL means that the key key is removed
if ( $value === null ) {
unset( $vars['smw.json'][$id][$key] );
} else {
$vars['smw.json'][$id][$key] = $value;
}
}
// Log the base elements used for computing the key
$vars['smw.json'][$id]['upgrade_key_base'] = self::makeKey(
$vars
);
// Remove legacy
if ( isset( $vars['smw.json']['upgradeKey'] ) ) {
unset( $vars['smw.json']['upgradeKey'] );
}
if ( isset( $vars['smw.json'][$id]['in.maintenance_mode'] ) ) {
unset( $vars['smw.json'][$id]['in.maintenance_mode'] );
}
try {
$this->file->write(
$configFile,
json_encode( $vars['smw.json'], JSON_PRETTY_PRINT )
);
} catch( FileNotWritableException $e ) {
// Users may not have `wgShowExceptionDetails` enabled and would
// therefore not see the exception error message hence we fail hard
// and die
die(
"\n\nERROR: " . $e->getMessage() . "\n" .
"\n The \"smwgConfigFileDir\" setting should point to a" .
"\n directory that is persistent and writable!\n"
);
}
}
/**
* Listed keys will have a "global" impact of how data are stored, formatted,
* or represented in Semantic MediaWiki. In most cases it will require an action
* from an adminstrator when one of those keys are altered.
*/
public static function makeKey( $vars ) {
// Only recognize those properties that require a fixed table
$pageSpecialProperties = array_intersect(
// Special properties enabled?
$vars['smwgPageSpecialProperties'],
// Any custom fixed properties require their own table?
TypesRegistry::getFixedProperties( 'custom_fixed' )
);
$pageSpecialProperties = array_unique( $pageSpecialProperties );
// Sort to ensure the key contains the same order
sort( $vars['smwgFixedProperties'] );
sort( $pageSpecialProperties );
// The following settings influence the "shape" of the tables required
// therefore use the content to compute a key that reflects any
// changes to them
$components = [
$vars['smwgUpgradeKey'],
$vars['smwgDefaultStore'],
$vars['smwgFixedProperties'],
$vars['smwgEnabledFulltextSearch'],
$pageSpecialProperties
];
// Only add the key when it is different from the default setting
if ( $vars['smwgEntityCollation'] !== 'identity' ) {
$components += [ 'smwgEntityCollation' => $vars['smwgEntityCollation'] ];
}
if ( $vars['smwgFieldTypeFeatures'] !== false ) {
$components += [ 'smwgFieldTypeFeatures' => $vars['smwgFieldTypeFeatures'] ];
}
// Recognize when the version requirements change and force
// an update to be able to check the requirements
$components += Setup::MINIMUM_DB_VERSION;
return json_encode( $components );
}
}