Robert Vogel has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/374087 )
Change subject: [WiP]BS3: new MediaWiki based config mechansim ...................................................................... [WiP]BS3: new MediaWiki based config mechansim * Introducing new database table for settings (using JSON as serialization format) * Added base config classes * Added migration path from old config * Also removed some unnecessary settings Change-Id: I0deecf4c38a9480a243fdeb008e50fcf466fca37 --- M extension.json M i18n/core/en.json M i18n/core/qqq.json M includes/Core.class.php M includes/DefaultSettings.php A maintenance/BSMigrateSettings.php A maintenance/db/bs_settings3.sql M src/Config.php A src/ExtensionConfig.php A src/Hook/LoadExtensionSchemaUpdates/AddBlueSpice3SettingsAndMigrationMaintenanceScript.php A src/ISetting.php A tests/phpunit/ConfigTest.php 12 files changed, 283 insertions(+), 46 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/BlueSpiceFoundation refs/changes/87/374087/1 diff --git a/extension.json b/extension.json index 0237668..f2a4a10 100644 --- a/extension.json +++ b/extension.json @@ -417,7 +417,8 @@ "ExtensionTypes": "BsCoreHooks::onExtensionTypes", "PageContentSaveComplete": "BsCoreHooks::onPageContentSaveComplete", "UnitTestsList": "BsCoreHooks::onUnitTestsList", - "ResourceLoaderGetConfigVars": "BsExtensionManager::onResourceLoaderGetConfigVars" + "ResourceLoaderGetConfigVars": "BsExtensionManager::onResourceLoaderGetConfigVars", + "LoadExtensionSchemaUpdates": "BlueSpice\\Hook\\LoadExtensionSchemaUpdates\\AddBlueSpice3SettingsAndMigrationMaintenanceScript::callback" }, "config": { "_prefix": "bsg", @@ -582,7 +583,8 @@ "BSApiTasksTestBase": "tests/phpunit/BSApiTasksTestBase.php", "BSTemplateHelper": "includes/TemplateHelper.php", "ResourceLoaderBSTemplateModule": "includes/resourceloader/ResourceLoaderBSTemplateModule.php", - "BSTasksApiSpec": "includes/utility/BSTasksApiSpec.php" + "BSTasksApiSpec": "includes/utility/BSTasksApiSpec.php", + "BSMigrateSettings": "maintenance/BSMigrateSettings.php" }, "load_composer_autoloader": true, "manifest_version": 1, diff --git a/i18n/core/en.json b/i18n/core/en.json index 69431fa..b398205 100644 --- a/i18n/core/en.json +++ b/i18n/core/en.json @@ -37,7 +37,6 @@ "bs-viewtagerrorlist-legend": "$1 - Error", "bs-readonly": "The database is currently locked to new entries and other modifications, probably for routine database maintenance, after which it will be back to normal. The administrator who locked it offered this explanation: $1", "bs-imageofotheruser": "You are not allowed to upload an image for another user.", - "bs-pref-sortalph": "Sort namespaces alphabetically", "bs-permissionerror": "Permission error!", "bs-filesystemhelper-no-directory": "<code>$1</code> is not a valid directory.", "bs-filesystemhelper-has-path-traversal": "Path traversal detected!", diff --git a/i18n/core/qqq.json b/i18n/core/qqq.json index 8710f12..23b8ec5 100644 --- a/i18n/core/qqq.json +++ b/i18n/core/qqq.json @@ -42,7 +42,6 @@ "bs-viewtagerrorlist-legend": "Used in inline error messages produced by tags\n\nParameters:\n* $1 - name of the tag\n{{Identical|Error}}", "bs-readonly": "Shown in various messages when database is in readonly mode and a write operation is requested.\n\nParameters:\n* $1 - explanation as given in $wgReadOnly", "bs-imageofotheruser": "Error message when user tries to upload an avatar for another user", - "bs-pref-sortalph": "Label for checkbox to sort all namespaces alphabetically.", "bs-permissionerror": "Error message sent in ajax requests when user has no permissions to perform that action\n{{Identical|Permission error}}", "bs-filesystemhelper-no-directory": "Error message given when requested directory is not a directory, put $1 in code tags\n\nParameters:\n* $1 - name of the requested directory", "bs-filesystemhelper-has-path-traversal": "Error message given when a file path tries to access files outside mediawiki root.\n\nSee [[w:Directory traversal attack]].", diff --git a/includes/Core.class.php b/includes/Core.class.php index de26310..9656925 100644 --- a/includes/Core.class.php +++ b/includes/Core.class.php @@ -106,42 +106,6 @@ protected static $bHtmlFormClassLoaded = false; - /** - * The constructor is protected because of the singleton pattern. - */ - protected function __construct() { - wfProfileIn('Performance: ' . __METHOD__); - global $wgScriptPath; - $sPath = $wgScriptPath . "/extensions/BlueSpiceFoundation/resources/bluespice/images/"; - - $aFiles = array( - 'txt', 'rtf', - 'doc', 'dot', 'docx', 'dotx', 'dotm', - 'xls', 'xlt', 'xlm', 'xlsx', 'xlsm', 'xltm', 'xltx', - 'ppt', 'pot', 'pps', 'pptx', 'pptm', 'potx', 'potm', 'ppsx', 'ppsm', 'sldx', 'sldm', - 'odt', 'fodt', 'ods', 'fods', 'odp', 'fodp', - 'pdf', - 'zip', 'rar', 'tar', 'tgz', 'gz', 'bzip2', '7zip', - 'xml', 'svg' - ); - $aImages = array( 'png', 'gif', 'jpg', 'jpeg' ); - BsConfig::registerVar( 'MW::FileExtensions', $aFiles, BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_ARRAY_STRING, 'bs-pref-fileextensions', 'multiselectplusadd' ); - BsConfig::registerVar( 'MW::ImageExtensions', $aImages, BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_ARRAY_STRING, 'bs-pref-imageextensions', 'multiselectplusadd' ); - BsConfig::registerVar( 'MW::LogoPath', $sPath . 'bs-logo.png', BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_STRING, 'bs-pref-logopath' ); - BsConfig::registerVar( 'MW::FaviconPath', $sPath . 'favicon.ico', BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_STRING, 'bs-pref-faviconpath' ); - BsConfig::registerVar( 'MW::DefaultUserImage', $sPath . 'bs-user-default-image.png', BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_STRING, 'bs-pref-defaultuserimage' ); - BsConfig::registerVar( 'MW::MiniProfileEnforceHeight', true, BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_BOOL, 'bs-pref-miniprofileenforceheight', 'toggle' ); - BsConfig::registerVar( 'MW::AnonUserImage', $sPath . 'bs-user-anon-image.png', BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_STRING, 'bs-pref-anonuserimage' ); - BsConfig::registerVar( 'MW::DeletedUserImage', $sPath . 'bs-user-deleted-image.png', BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_STRING, 'bs-pref-deleteduserimage' ); - BsConfig::registerVar( 'MW::UserImage', '', BsConfig::LEVEL_USER | BsConfig::TYPE_STRING | BsConfig::NO_DEFAULT, 'bs-authors-profileimage' ); - BsConfig::registerVar( 'MW::PingInterval', 10, BsConfig::LEVEL_PUBLIC | BsConfig::RENDER_AS_JAVASCRIPT | BsConfig::TYPE_INT, 'bs-pref-bspinginterval' ); - BsConfig::registerVar( 'MW::SortAlph', false, BsConfig::LEVEL_PUBLIC | BsConfig::LEVEL_USER | BsConfig::TYPE_BOOL, 'bs-pref-sortalph', 'toggle' ); - BsConfig::registerVar( 'MW::TestMode', false, BsConfig::LEVEL_PUBLIC | BsConfig::TYPE_BOOL, 'bs-pref-testmode', 'toggle' ); - - BsConfig::set( 'MW::ApplicationContext', 'Wiki' ); - wfProfileOut('Performance: ' . __METHOD__); - } - public static function getForbiddenCharsInArticleTitle() { return self::$prForbiddenCharsInArticleTitle; } @@ -357,8 +321,10 @@ //TODO: This does not seem to be the right place for stuff like this. global $wgFileExtensions; - $aFileExtensions = BsConfig::get( 'MW::FileExtensions' ); - $aImageExtensions = BsConfig::get( 'MW::ImageExtensions' ); + $config = MediaWiki\MediaWikiServices::getInstance() + ->getConfigFactory()->makeConfig( 'bsg' ); + $aFileExtensions = $config->get( 'FileExtensions' ); + $aImageExtensions = $config->get( 'ImageExtensions' ); $wgFileExtensions = array_merge( $aFileExtensions, $aImageExtensions ); $wgFileExtensions = array_values( array_unique( $wgFileExtensions ) ); } diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 51ae783..557a945 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -349,4 +349,27 @@ ) ); -$GLOBALS['bsgUserMiniProfileParams'] = [ 'width' => 40, 'height' => 40 ]; \ No newline at end of file +$GLOBALS['bsgUserMiniProfileParams'] = [ 'width' => 40, 'height' => 40 ]; +$GLOBALS['bsgMiniProfileEnforceHeight'] = true; +$GLOBALS['bsgPingInterval'] = 10; + +/** + * ATTENTION: Not to confuse with 'bsgTestSystem'. + * This settings influences e-mail-, export- and other features + * @global string $GLOBALS['bsgTestMode'] + * @name $bsgTestMode + */ +$GLOBALS['bsgTestMode'] = false; +$GLOBALS['bsgFileExtensions'] = [ + 'txt', 'rtf', + 'doc', 'dot', 'docx', 'dotx', 'dotm', + 'xls', 'xlt', 'xlm', 'xlsx', 'xlsm', 'xltm', 'xltx', + 'ppt', 'pot', 'pps', 'pptx', 'pptm', 'potx', 'potm', 'ppsx', 'ppsm', 'sldx', 'sldm', + 'odt', 'fodt', 'ods', 'fods', 'odp', 'fodp', + 'pdf', + 'zip', 'rar', 'tar', 'tgz', 'gz', 'bzip2', '7zip', + 'xml', 'svg' +]; + + +$GLOBALS['bsgImageExtensions'] = [ 'png', 'gif', 'jpg', 'jpeg' ]; \ No newline at end of file diff --git a/maintenance/BSMigrateSettings.php b/maintenance/BSMigrateSettings.php new file mode 100644 index 0000000..fb400a8 --- /dev/null +++ b/maintenance/BSMigrateSettings.php @@ -0,0 +1,92 @@ +<?php + +require_once( 'BSMaintenance.php' ); + +class BSMigrateSettings extends Maintenance { + + public function execute() { + if( $this->noDataToMigrate() ) { + $this->output( "bs_settings -> bs_settings3: No data to migrate" ); + return; + } + + $this->readOldData(); + $this->convertData(); + $this->saveConvertedData(); + } + + protected function noDataToMigrate() { + return $this->getDB( DB_SLAVE )->tableExists( 'bs_settings' ) === false; + } + + protected $oldData = []; + protected function readOldData() { + $res = $this->getDB( DB_SLAVE )->select( 'bs_settings', '*' ); + foreach( $res as $row ) { + $this->oldData[$row->key] = $row->value; + } + } + + protected $newData = []; + protected function convertData() { + foreach( $this->oldData as $oldName => $serializedValue ) { + $newName = $this->makeNewName( $oldName ); + $newValue = $this->convertValue( $serializedValue ); + $this->output( "$oldName => $newName\n" ); + $this->newData[ $newName ] = $newValue; + } + } + + protected function makeNewName( $oldName ) { + if( $oldName === 'MW::LogoPath' ) { + return 'wgLogo'; + } + + //$oldName = "MW::TopMenuBarCustomizer::NumberOfSubEntries" + $nameParts = explode( '::', $oldName ); + array_shift( $nameParts ); //MW + $newName = 'bsg' . implode( '', $nameParts ); + + if( strlen( $newName ) > 255 ) { + throw new Exception( "Variable name '$newName' is too long!" ); + } + + return $newName; + } + + protected function saveConvertedData() { + foreach( $this->newData as $newName => $newValue ) { + $this->getDB( DB_MASTER )->insert( 'bs_settings3', [ + 's_name' => $newName, + 's_value' => $newValue + ] ); + } + } + + protected function convertValue( $serializedValue ) { + $newValue = unserialize( $serializedValue ); + /*if( is_int( $newValue ) ) { + $newValue = (int) $newValue; + } + if( is_array( $newValue ) ) { + $newArray = []; + foreach( $newValue as $key => $element ) { + $newArray[$key] = $this->convertValue( $element ); + } + $newValue = $newArray; + } + if( is_bool( $newValue ) ) { + $newValue = (bool) $newValue; + }*/ + + return FormatJson::encode( $newValue ); + } + +} + +$maintClass = 'BSMigrateSettings'; +if (defined('RUN_MAINTENANCE_IF_AIN')) { + require_once( RUN_MAINTENANCE_IF_MAIN ); +} else { + require_once( DO_MAINTENANCE ); # Make this work on versions before 1.17 +} \ No newline at end of file diff --git a/maintenance/db/bs_settings3.sql b/maintenance/db/bs_settings3.sql new file mode 100644 index 0000000..b911781 --- /dev/null +++ b/maintenance/db/bs_settings3.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS /*_*/bs_settings3 ( + s_name VARCHAR(255) NOT NULL, + s_value mediumblob, + PRIMARY KEY ( s_name ) +) /*$wgDBTableOptions*/; \ No newline at end of file diff --git a/src/Config.php b/src/Config.php index 1ec57d9..56f3a30 100644 --- a/src/Config.php +++ b/src/Config.php @@ -2,8 +2,45 @@ namespace BlueSpice; -class Config extends \GlobalVarConfig { - public static function newInstance() { - return new self( 'bsg' ); +class Config extends \MultiConfig { + + /** + * + * @var \LoadBalancer + */ + protected $loadBalancer = null; + + + /** + * + * @param \LoadBalancer $loadBalancer + */ + public function __construct( $loadBalancer ) { + $this->loadBalancer = $loadBalancer; + parent::__construct( [ + $this->makeDatabaseConfig(), + new \GlobalVarConfig( 'bsg' ) + ] ); } + + public static function newInstance() { + $lb = \LBFactory::singleton()->getMainLB(); + return new self( $lb ); + } + + protected function makeDatabaseConfig() { + $hash = []; + $dbr = $this->loadBalancer->getConnection( DB_SLAVE ); + $res = $dbr->select( 'bs_settings3', '*' ); + + foreach( $res as $row ) { + if( strpos( $row->s_name, 'bsg' ) === 0 ) { + $name = substr( $row->s_name, 3 ); + $hash[$name] = \FormatJson::decode( $row->s_value ); + } + } + + return new \HashConfig( $hash ); + } + } diff --git a/src/ExtensionConfig.php b/src/ExtensionConfig.php new file mode 100644 index 0000000..a4abe2d --- /dev/null +++ b/src/ExtensionConfig.php @@ -0,0 +1,2 @@ +<?php + diff --git a/src/Hook/LoadExtensionSchemaUpdates/AddBlueSpice3SettingsAndMigrationMaintenanceScript.php b/src/Hook/LoadExtensionSchemaUpdates/AddBlueSpice3SettingsAndMigrationMaintenanceScript.php new file mode 100644 index 0000000..ecca3c3 --- /dev/null +++ b/src/Hook/LoadExtensionSchemaUpdates/AddBlueSpice3SettingsAndMigrationMaintenanceScript.php @@ -0,0 +1,24 @@ +<?php + +namespace BlueSpice\Hook\LoadExtensionSchemaUpdates; + +use BlueSpice\Hook\LoadExtensionSchemaUpdates; + +class AddBlueSpice3SettingsAndMigrationMaintenanceScript extends LoadExtensionSchemaUpdates { + protected function doProcess() { + $this->updater->addExtensionTable( + 'bs_settings3', + $this->getExtensionPath() . '/maintenance/db/bs_settings3.sql' + ); + + $this->updater->addPostDatabaseUpdateMaintenance( + 'BSMigrateSettings' + ); + return true; + } + + protected function getExtensionPath() { + return dirname( dirname( dirname( __DIR__ ) ) ); + } + +} \ No newline at end of file diff --git a/src/ISetting.php b/src/ISetting.php new file mode 100644 index 0000000..15a52e2 --- /dev/null +++ b/src/ISetting.php @@ -0,0 +1,29 @@ +<?php + +namespace BlueSpice; + +interface ISetting { + /** + * @return string The variable name like it would be in 'LoalSettings.php'. + * E.g. 'wgLogo' or 'bsgPingInterval' + */ + public function getVariableName(); + + /** + * @return array An array of paths that define where to provide an input + * field within the settings UI. E.g. + * [ 'site/interface', 'site/<extensionX>' ] + * ATTENTION: Path elements need a message key to be available following + * the pattern 'bs-setting-path-<elementName>'. E. g. + * 'bs-setting-path-<extensionX>' + */ + public function getPaths(); + + /** + * + * @param mixed $value Whatever the current value of the setting may be + * @return \HTMLFormField A ready to use HTML-form-field-descriptor, with + * default, labels, descriptions, ... + */ + public function getField( $value ); +} diff --git a/tests/phpunit/ConfigTest.php b/tests/phpunit/ConfigTest.php new file mode 100644 index 0000000..611e9ee --- /dev/null +++ b/tests/phpunit/ConfigTest.php @@ -0,0 +1,59 @@ +<?php + +namespace BlueSpice\Tests; + +use \MediaWiki\MediaWikiServices; + +/** + * @group BlueSpice + * @group BlueSpiceFoundation + * @group Medium + */ +class ConfigTest extends \MediaWikiTestCase { + protected $tablesUsed = [ 'bs_settings3' ]; + + public function setUp() { + parent::setUp(); + + $GLOBALS[ 'bsgUnitTestSetting'] = 5; + $GLOBALS[ 'bsgUnitTestSetting2' ] = [ 'An', 'array' ]; + } + + public function addDBData() { + parent::addDBData(); + $this->db->insert( 'bs_settings3', [ + 's_name' => 'bsgUnitTestSetting', + 's_value' => '"9"' //JSON formatted + ] ); + } + + public function testFactoryReturn() { + $config = MediaWikiServices::getInstance()->getConfigFactory() + ->makeConfig( 'bsg' ); + + $this->assertInstanceOf( + '\\BlueSpice\\Config', //Can be discussed whether just \Config is sufficient to test + $config, + 'MediaWiki ConfigFactory should return propert instance' + ); + } + + public function testDatabasePreval() { + $config = new \BlueSpice\Config( wfGetLB() ); + $this->assertEquals( + 9, + $config->get( 'UnitTestSetting' ), + 'Should return value from database' + ); + } + + public function testGlobalVarDefaulting() { + $config = new \BlueSpice\Config( wfGetLB() ); + + $this->assertArrayEquals( + [ 'An', 'array' ], + $config->get( 'UnitTestSetting2' ), + 'Should fall back to global vars' + ); + } +} -- To view, visit https://gerrit.wikimedia.org/r/374087 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I0deecf4c38a9480a243fdeb008e50fcf466fca37 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation Gerrit-Branch: master Gerrit-Owner: Robert Vogel <vo...@hallowelt.biz> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits