diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php new file mode 100644 index 00000000..ec68ddf4 --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/SQLStore/Installer.php @@ -0,0 +1,463 @@ +<?php + +namespace SMW\SQLStore; + +use Hooks; +use Onoi\MessageReporter\MessageReporter; +use Onoi\MessageReporter\MessageReporterAwareTrait; +use Onoi\MessageReporter\MessageReporterFactory; +use SMW\CompatibilityMode; +use SMW\MediaWiki\Jobs\EntityIdDisposerJob; +use SMW\MediaWiki\Jobs\PropertyStatisticsRebuildJob; +use SMW\Options; +use SMW\Site; +use SMW\Utils\File; +use SMW\Exception\FileNotWritableException; + +/** + * @private + * + * @license GNU GPL v2+ + * @since 2.5 + * + * @author mwjames + */ +class Installer implements MessageReporter { + + use MessageReporterAwareTrait; + + /** + * Optimize option + */ + const OPT_TABLE_OPTIMIZE = 'installer.table.optimize'; + + /** + * Job option + */ + const OPT_SUPPLEMENT_JOBS = 'installer.supplement.jobs'; + + /** + * Import option + */ + const OPT_IMPORT = 'installer.import'; + + /** + * Related to ExtensionSchemaUpdates + */ + const OPT_SCHEMA_UPDATE = 'installer.schema.update'; + + /** + * `smw_hash` field population + */ + const POPULATE_HASH_FIELD_COMPLETE = 'populate.smw_hash_field_complete'; + + /** + * @var TableSchemaManager + */ + private $tableSchemaManager; + + /** + * @var TableBuilder + */ + private $tableBuilder; + + /** + * @var TableIntegrityExaminer + */ + private $tableIntegrityExaminer; + + /** + * @var Options + */ + private $options; + + /** + * @var File + */ + private $file; + + /** + * @since 2.5 + * + * @param TableSchemaManager $tableSchemaManager + * @param TableBuilder $tableBuilder + * @param TableIntegrityExaminer $tableIntegrityExaminer + */ + public function __construct( TableSchemaManager $tableSchemaManager, TableBuilder $tableBuilder, TableIntegrityExaminer $tableIntegrityExaminer ) { + $this->tableSchemaManager = $tableSchemaManager; + $this->tableBuilder = $tableBuilder; + $this->tableIntegrityExaminer = $tableIntegrityExaminer; + $this->options = new Options(); + } + + /** + * @since 3.0 + * + * @param Options|array $options + */ + public function setOptions( $options ) { + + if ( !$options instanceof Options ) { + $options = new Options( $options ); + } + + $this->options = $options; + } + + /** + * @since 3.0 + * + * @param File $file + */ + public function setFile( File $file ) { + $this->file = $file; + } + + /** + * @since 2.5 + * + * @param boolean $verbose + */ + public function install( $verbose = true ) { + + // If for some reason the enableSemantics was not yet enabled + // still allow to run the tables create in order for the + // setup to be completed + if ( CompatibilityMode::extensionNotEnabled() ) { + CompatibilityMode::enableTemporaryCliUpdateMode(); + } + + $messageReporter = $this->newMessageReporter( $verbose ); + + $messageReporter->reportMessage( "\nSelected storage engine: \"SMWSQLStore3\" (or an extension thereof)\n" ); + $messageReporter->reportMessage( "\nSetting up standard database configuration for SMW ...\n\n" ); + + $this->tableBuilder->setMessageReporter( + $messageReporter + ); + + $this->tableIntegrityExaminer->setMessageReporter( + $messageReporter + ); + + foreach ( $this->tableSchemaManager->getTables() as $table ) { + $this->tableBuilder->create( $table ); + } + + $this->tableIntegrityExaminer->checkOnPostCreation( $this->tableBuilder ); + + $messageReporter->reportMessage( "\nDatabase initialized completed.\n" ); + + $this->table_optimization( $messageReporter ); + $this->supplement_jobs( $messageReporter ); + + $file = $this->file !== null ? $this->file : new File(); + + self::setUpgradeKey( $GLOBALS, $messageReporter, $file ); + + Hooks::run( + 'SMW::SQLStore::Installer::AfterCreateTablesComplete', + [ + $this->tableBuilder, + $messageReporter, + $this->options + ] + ); + + if ( $this->options->has( self::OPT_SCHEMA_UPDATE ) ) { + $messageReporter->reportMessage( "\n" ); + } + + return true; + } + + /** + * @since 2.5 + * + * @param boolean $verbose + */ + public function uninstall( $verbose = true ) { + + $messageReporter = $this->newMessageReporter( $verbose ); + + $messageReporter->reportMessage( "\nSelected storage engine: \"SMWSQLStore3\" (or an extension thereof)\n" ); + $messageReporter->reportMessage( "\nDeleting all database content and tables generated by SMW ...\n\n" ); + + $this->tableBuilder->setMessageReporter( + $messageReporter + ); + + foreach ( $this->tableSchemaManager->getTables() as $table ) { + $this->tableBuilder->drop( $table ); + } + + $this->tableIntegrityExaminer->checkOnPostDestruction( $this->tableBuilder ); + + Hooks::run( + 'SMW::SQLStore::Installer::AfterDropTablesComplete', + [ + $this->tableBuilder, + $messageReporter, + $this->options + ] + ); + + $messageReporter->reportMessage( "\nStandard and auxiliary tables with all corresponding data\n" ); + $messageReporter->reportMessage( "have been removed successfully.\n" ); + + return true; + } + + /** + * @since 3.0 + * + * @param array $vars + */ + public static function loadSchema( &$vars ) { + + // @see #3506 + $file = File::dir( $vars['smwgConfigFileDir'] . '/.smw.json' ); + + // 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.0 + * + * @param boolean $isCli + * + * @return boolean + */ + public static function isGoodSchema( $isCli = false ) { + + if ( $isCli && defined( 'MW_PHPUNIT_TEST' ) ) { + return true; + } + + if ( $isCli === false && ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) ) { + return true; + } + + // #3563, Use the specific wiki-id as identifier for the instance in use + $id = Site::id(); + + if ( !isset( $GLOBALS['smw.json'][$id]['upgrade_key'] ) ) { + return false; + } + + return self::makeUpgradeKey( $GLOBALS ) === $GLOBALS['smw.json'][$id]['upgrade_key']; + } + + /** + * @since 3.1 + * + * @param array $vars + * + * @return [] + */ + public static function incompleteTasks( $vars ) { + + $id = Site::id(); + $tasks = []; + + // Key field => [ value that constitutes the `INCOMPLETE` state, error msg ] + $checks = [ + self::POPULATE_HASH_FIELD_COMPLETE => [ false, 'smw-install-incomplete-populate-hash-field' ] + ]; + + foreach ( $checks as $key => $value ) { + if ( isset( $vars['smw.json'][$id][$key] ) && $vars['smw.json'][$id][$key] === $value[0] ) { + $tasks[] = $value[1]; + } + } + + return $tasks; + } + + /** + * @since 3.0 + * + * @param array $vars + * + * @return string + */ + public static function makeUpgradeKey( $vars ) { + + // The following settings influence the "shape" of the tables required + // therefore use the content to compute a key that reflects any + // changes to them + + // Only recognize those properties that require a fixed table + $pageSpecialProperties = array_intersect( + $vars['smwgPageSpecialProperties'], + PropertyTableInfoFetcher::getFixedSpecialPropertyList() + ); + + // Sort to ensure the key contains the same order + sort( $vars['smwgFixedProperties'] ); + sort( $pageSpecialProperties ); + + return sha1( + json_encode( + [ + $vars['smwgUpgradeKey'], + $vars['smwgFixedProperties'], + $pageSpecialProperties + ] + ) + ); + } + + /** + * @since 3.0 + * + * @param array $vars + * @param MessageReporter $messageReporter|null + * @param File $file|null + */ + public static function setUpgradeKey( $vars, MessageReporter $messageReporter = null, File $file = null ) { + + // #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]['upgrade_key'] ) && + $key === $vars['smw.json'][$id]['upgrade_key'] ) { + return false; + } + + if ( $messageReporter !== null ) { + $messageReporter->reportMessage( "\nSetting $id upgrade key ..." ); + } + + self::setUpgradeFile( $vars, [ 'upgrade_key' => $key ], $file ); + + if ( $messageReporter !== null ) { + $messageReporter->reportMessage( "\n ... done.\n" ); + } + } + + /** + * @since 3.0 + * + * @param File $file + * @param array $vars + */ + public static function setUpgradeFile( $vars, $args = [], File $file = null ) { + + $configFile = $vars['smwgConfigFileDir'] . '/.smw.json'; + + if ( $file === null ) { + $file = new File(); + } + + $id = Site::id(); + + if ( !isset( $vars['smw.json'] ) ) { + $vars['smw.json'] = []; + } + + foreach ( $args as $key => $value ) { + $vars['smw.json'][$id][$key] = $value; + } + + try { + $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" + ); + } + } + + /** + * @since 2.5 + * + * @param string $message + */ + public function reportMessage( $message ) { + ob_start(); + print $message; + ob_flush(); + flush(); + ob_end_clean(); + } + + private function newMessageReporter( $verbose = true ) { + + if ( $this->messageReporter !== null && !$this->options->safeGet( self::OPT_SCHEMA_UPDATE, false ) ) { + return $this->messageReporter; + } + + $messageReporterFactory = MessageReporterFactory::getInstance(); + + if ( !$verbose ) { + $messageReporter = $messageReporterFactory->newNullMessageReporter(); + } else { + $messageReporter = $messageReporterFactory->newObservableMessageReporter(); + $messageReporter->registerReporterCallback( [ $this, 'reportMessage' ] ); + } + + return $messageReporter; + } + + private function table_optimization( $messageReporter ) { + + if ( !$this->options->safeGet( self::OPT_TABLE_OPTIMIZE, false ) ) { + return $messageReporter->reportMessage( "\nSkipping the table optimization.\n" ); + } + + $messageReporter->reportMessage( "\nRunning table optimization (this may take a moment) ...\n\n" ); + + foreach ( $this->tableSchemaManager->getTables() as $table ) { + $this->tableBuilder->optimize( $table ); + } + + $messageReporter->reportMessage( "\nOptimization completed.\n" ); + } + + private function supplement_jobs( $messageReporter ) { + + if ( !$this->options->safeGet( self::OPT_SUPPLEMENT_JOBS, false ) ) { + return $messageReporter->reportMessage( "\nSkipping supplement job creation.\n" ); + } + + $messageReporter->reportMessage( "\nAdding property statistics rebuild job ...\n" ); + + $title = \Title::newFromText( 'SMW\SQLStore\Installer' ); + + $job = new PropertyStatisticsRebuildJob( + $title, + PropertyStatisticsRebuildJob::newRootJobParams( 'smw.propertyStatisticsRebuild', $title ) + [ 'waitOnCommandLine' => true ] + ); + + $job->insert(); + + $messageReporter->reportMessage( " ... done.\n" ); + $messageReporter->reportMessage( "\nAdding entity disposer job ...\n" ); + + $job = new EntityIdDisposerJob( + $title, + EntityIdDisposerJob::newRootJobParams( 'smw.entityIdDisposer', $title ) + [ 'waitOnCommandLine' => true ] + ); + + $job->insert(); + + $messageReporter->reportMessage( " ... done.\n" ); + } + +} |