jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/374087 )

Change subject: New MediaWiki based config mechansim
......................................................................


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

'MW::FileExtensions' --> '$bsgFileExtensions'
'MW::ImageExtensions' --> '$bsgImageExtensions'
'MW::LogoPath' --> '$wgLogo'
'MW::FaviconPath' --> '$wgFavicon'
'MW::DefaultUserImage' --> ''
'MW::MiniProfileEnforceHeight' --> 'bsgMiniProfileEnforceHeight'
'MW::AnonUserImage'
'MW::DeletedUserImage'
'MW::UserImage' --> Will be replaced by a proper user setting
'MW::PingInterval' --> 'bsgPingInterval'
'MW::SortAlph' --> Almost unused, will be removed completely
'MW::TestMode' --> 'bsgTestMode'

PatchSet5:
* Added TODO
* Fixed database error when config is initialized before the table was
created (f.e.: in update.php)

PatechSet9:
* Added workaround for when the config object with load balancer is
serialized in object cache

Change-Id: I0deecf4c38a9480a243fdeb008e50fcf466fca37
---
M extension.json
M i18n/core/en.json
M i18n/core/qqq.json
M includes/Core.class.php
M includes/CoreHooks.php
M includes/DefaultSettings.php
M includes/ExtensionManager.class.php
M includes/html/htmlformfields/HTMLMultiSelectEx.php
M includes/outputhandler/views/view.BaseElement.php
A maintenance/BSMigrateSettings.php
A maintenance/db/bs_settings3.sql
M src/Config.php
A src/ConfigDefinition.php
A src/ConfigDefinition/ArraySetting.php
A src/ConfigDefinition/Favicon.php
A src/ConfigDefinition/FileExtensions.php
A src/ConfigDefinition/ImageExtensions.php
A src/ConfigDefinition/IntSetting.php
A src/ConfigDefinition/Logo.php
A src/ConfigDefinition/StringSetting.php
M src/Hook.php
A 
src/Hook/LoadExtensionSchemaUpdates/AddBlueSpice3SettingsAndMigrationMaintenanceScript.php
A src/Hook/ResourceLoaderGetConfigVars.php
A src/Hook/ResourceLoaderGetConfigVars/AddBSGConfig.php
A src/ISetting.php
A tests/phpunit/ConfigTest.php
26 files changed, 791 insertions(+), 68 deletions(-)

Approvals:
  Robert Vogel: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/extension.json b/extension.json
index 57988e8..51bd54e 100644
--- a/extension.json
+++ b/extension.json
@@ -417,7 +417,10 @@
                "LinkerMakeMediaLinkFile": 
"BsCoreHooks::onLinkerMakeMediaLinkFile",
                "ThumbnailBeforeProduceHTML": 
"BsCoreHooks::onThumbnailBeforeProduceHTML",
                "MakeGlobalVariablesScript": 
"BsCoreHooks::onMakeGlobalVariablesScript",
-               "LoadExtensionSchemaUpdates": 
"BsCoreHooks::onLoadExtensionSchemaUpdates",
+               "LoadExtensionSchemaUpdates": [
+                       "BsCoreHooks::onLoadExtensionSchemaUpdates",
+                       
"BlueSpice\\Hook\\LoadExtensionSchemaUpdates\\AddBlueSpice3SettingsAndMigrationMaintenanceScript::callback"
+               ],
                "ApiCheckCanExecute": "BsCoreHooks::onApiCheckCanExecute",
                "UserGetRights": "BsCoreHooks::onUserGetRights",
                "userCan": "BsCoreHooks::onUserCan",
@@ -431,22 +434,29 @@
                        "BsCacheHelper::onPageContentSaveComplete"
                ],
                "UnitTestsList": "BsCoreHooks::onUnitTestsList",
-               "ResourceLoaderGetConfigVars": 
"BsExtensionManager::onResourceLoaderGetConfigVars"
+               "ResourceLoaderGetConfigVars": [
+                       "BsExtensionManager::onResourceLoaderGetConfigVars",
+                       
"BlueSpice\\Hook\\ResourceLoaderGetConfigVars\\AddBSGConfig::callback"
+               ]
        },
+       "config_prefix": "bsg",
        "config": {
-               "_prefix": "bsg",
                "BlueSpiceExtInfo": {
-                       "name": "BlueSpice",
-                       "version": "2.27.1",
-                       "status": "stable",
-                       "package": "BlueSpice Free",
-                       "url": "http://bluespice.com";,
-                       "desc": "Makes MediaWiki enterprise ready.",
-                       "author": [
-                               "[http://www.hallowelt.com Hallo Welt! GmbH]"
-                       ]
+                       "value": {
+                               "name": "BlueSpice",
+                               "version": "2.27.1",
+                               "status": "stable",
+                               "package": "BlueSpice Free",
+                               "url": "http://bluespice.com";,
+                               "desc": "Makes MediaWiki enterprise ready.",
+                               "author": [
+                                       "[http://www.hallowelt.com Hallo Welt! 
GmbH]"
+                               ]
+                       }
                },
-               "ConfigFiles": []
+               "ConfigFiles": {
+                       "value": []
+               }
        },
        "ConfigRegistry": {
                "bsg": "BlueSpice\\Config::newInstance"
@@ -596,9 +606,10 @@
                "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,
+       "manifest_version": 2,
        "callback": "BsCoreHooks::onRegistry"
 }
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 c8023ee..1120b9b 100644
--- a/includes/Core.class.php
+++ b/includes/Core.class.php
@@ -104,42 +104,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;
        }
@@ -355,8 +319,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/CoreHooks.php b/includes/CoreHooks.php
index 5748345..0a9489b 100755
--- a/includes/CoreHooks.php
+++ b/includes/CoreHooks.php
@@ -77,16 +77,17 @@
        * @return boolean
        */
        public static function onBeforePageDisplay( $out, $skin ) {
-               global $IP, $wgFavicon, $wgExtensionAssetsPath, $wgLogo;
+               global $wgFavicon, $wgExtensionAssetsPath, $wgLogo;
 
-               //TODO: Change this mechanism to make it overwriteable!
-               $wgLogo = BsConfig::get('MW::LogoPath');
+               $config = \MediaWiki\MediaWikiServices::getInstance()
+                       ->getConfigFactory()->makeConfig( 'bsg' );
 
                $out->addModules( 'ext.bluespice' );
                $out->addModuleStyles( 'ext.bluespice.styles' );
                $out->addModuleStyles( 'ext.bluespice.compat.vector.styles' );
 
-               $wgFavicon = BsConfig::get( 'MW::FaviconPath' );
+               $wgFavicon = $config->get( 'Favicon' );
+               $wgLogo = $config->get( 'Logo' );
 
                //Make some variables available on client side:
                global $wgEnableUploads, $wgMaxUploadSize;
@@ -98,8 +99,12 @@
 
                //We cannot use BsConfig::RENDER_AS_JAVASCRIPT because we want 
to
                //normalize the content to lower case:
-               $aFileExtensions  = self::lcNormalizeArray( BsConfig::get( 
'MW::FileExtensions' ) );
-               $aImageExtensions = self::lcNormalizeArray( BsConfig::get( 
'MW::ImageExtensions' ) );
+               $aFileExtensions  = self::lcNormalizeArray(
+                       $config->get( 'FileExtensions' )
+               );
+               $aImageExtensions = self::lcNormalizeArray(
+                       $config->get( 'ImageExtensions' )
+               );
 
                $out->addJsConfigVars( 'bsMaxUploadSize', $aMaxUploadSize );
                $out->addJsConfigVars( 'bsEnableUploads', $wgEnableUploads ); 
//Was: 'MW::InsertFile::EnableUploads' --> 'bsInsertFileEnableUploads'
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 3a2aa87..c395b4a 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -321,7 +321,16 @@
 /**
  * BsExtensionManager extension registration
  */
-$GLOBALS['bsgExtensions'] = array();
+$GLOBALS['bsgExtensions'] = [
+       "BlueSpiceFoundation" => [
+               "configDefinitions" => [
+                       "Logo" => 
"\\BlueSpice\\ConfigDefinition\\Logo::getInstance",
+                       "FileExtensions" => 
"\\BlueSpice\\ConfigDefinition\\FileExtensions::getInstance",
+                       "ImageExtensions" => 
"\\BlueSpice\\ConfigDefinition\\ImageExtensions::getInstance",
+                       "Favicon" => 
"\\BlueSpice\\ConfigDefinition\\Favicon::getInstance",
+               ]
+       ]
+];
 
 /**
  * BsTemplateHelper template directory overwrite
@@ -355,4 +364,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/includes/ExtensionManager.class.php 
b/includes/ExtensionManager.class.php
index 466c2e2..30ad19b 100644
--- a/includes/ExtensionManager.class.php
+++ b/includes/ExtensionManager.class.php
@@ -192,6 +192,9 @@
                }
 
                foreach( $GLOBALS['bsgExtensions'] as $sExtName => $aConfig ) {
+                       if( $sExtName === "BlueSpiceFoundation" ) {
+                               continue;
+                       }
                        self::$prRegisteredExtensions[$sExtName]
                                = self::makeExtensionConfig( $sExtName, 
$aConfig );
                }
diff --git a/includes/html/htmlformfields/HTMLMultiSelectEx.php 
b/includes/html/htmlformfields/HTMLMultiSelectEx.php
index 927e233..bb68e12 100644
--- a/includes/html/htmlformfields/HTMLMultiSelectEx.php
+++ b/includes/html/htmlformfields/HTMLMultiSelectEx.php
@@ -17,7 +17,7 @@
        }
 
        function getInputHTML( $value ) {
-               $this->mParent->getOutput()->addModules( 
'ext.bluespice.html.formfields.multiselect' );
+               \RequestContext::getMain()->getOutput()->addModules( 
'ext.bluespice.html.formfields.multiselect' );
 
                $aOptions = ( isset( $this->mParams['options'] ) ) ? 
$this->mParams['options'] : array();
                $html = $this->formatOptions( $aOptions, $value );
diff --git a/includes/outputhandler/views/view.BaseElement.php 
b/includes/outputhandler/views/view.BaseElement.php
index 87f47d4..7c478ae 100644
--- a/includes/outputhandler/views/view.BaseElement.php
+++ b/includes/outputhandler/views/view.BaseElement.php
@@ -52,11 +52,18 @@
        protected $mOptions         = array();
 
        /**
+        *
+        * @var \Config
+        */
+       protected $config = null;
+
+       /**
         * Build a new element instance and bind an unique id to it.
         */
        public function __construct() {
                wfProfileIn( 'BS::'.__METHOD__ );
                $this->_mId = 'bs-element-'.self::getAutoId();
+               $this->config = 
\MediaWiki\MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 
'bsg' );
                wfProfileOut( 'BS::'.__METHOD__ );
        }
 
diff --git a/maintenance/BSMigrateSettings.php 
b/maintenance/BSMigrateSettings.php
new file mode 100644
index 0000000..555beea
--- /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_REPLICA )->tableExists( 'bs_settings' ) 
=== false;
+       }
+
+       protected $oldData = [];
+       protected function readOldData() {
+               $res = $this->getDB( DB_REPLICA )->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..e81912b 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -2,8 +2,82 @@
 
 namespace BlueSpice;
 
-class Config extends \GlobalVarConfig {
-       public static function newInstance() {
-               return new self( 'bsg' );
+class Config extends \MultiConfig implements \Serializable {
+
+       /**
+        * This fixes the exception in object cache, when config with 
loadBalancer
+        * is serialized.
+        * "Database serialization may cause problems, since the connection is 
not
+        * restored on wakeup"
+        *
+        * @param string $serialized
+        * @return Config
+        */
+       public function unserialize( $serialized ) {
+               return \MediaWiki\MediaWikiServices::getInstance()
+                       ->getConfigFactory()->makeConfig( 'bsg' );
        }
+
+       /**
+        * This fixes the exception in object cache, when config with 
loadBalancer
+        * is serialized.
+        * "Database serialization may cause problems, since the connection is 
not
+        * restored on wakeup"
+        *
+        * @return string
+        */
+       public function serialize() {
+               return serialize( null );
+       }
+
+       /**
+        *
+        * @var \LoadBalancer
+        */
+       protected $loadBalancer = null;
+
+       /**
+        *
+        * @param \LoadBalancer $loadBalancer
+        */
+       public function __construct( $loadBalancer ) {
+               $this->loadBalancer = $loadBalancer;
+               parent::__construct( [
+                       new \GlobalVarConfig( 'bsgOverride' ),
+                       $this->makeDatabaseConfig(),
+                       new \GlobalVarConfig( 'bsg' ),
+                       new \GlobalVarConfig( 'wg' ),
+               ] );
+       }
+
+       /**
+        * Factory method used by \ConfigFactory
+        * @return \Config
+        */
+       public static function newInstance() {
+               $lb = 
\MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancer();
+               return new self( $lb );
+       }
+
+       protected function makeDatabaseConfig() {
+               $hash = [];
+               $dbr = $this->loadBalancer->getConnection( DB_REPLICA );
+               //when config get initialized before the database is created
+               //(f.e.: in update.php)
+               //TODO: Cache this config
+               if( !$dbr->tableExists( 'bs_settings3' ) ) {
+                       return new \HashConfig( $hash );
+               }
+               $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/ConfigDefinition.php b/src/ConfigDefinition.php
new file mode 100644
index 0000000..7bdd610
--- /dev/null
+++ b/src/ConfigDefinition.php
@@ -0,0 +1,189 @@
+<?php
+
+namespace BlueSpice;
+
+abstract class ConfigDefinition implements ISetting {
+
+       protected static $configDefinitions = null;
+
+       /**
+        *
+        * @var \IContextSource
+        */
+       protected $context = null;
+
+       /**
+        *
+        * @var \Config
+        */
+       protected $config = null;
+
+       /**
+        *
+        * @var string
+        */
+       protected $name = '';
+
+       /**
+        *
+        * @param \Context $context
+        * @param \Config $config
+        * @param string $name
+        */
+       protected function __construct( $context, $config, $name ) {
+               $this->context = $context;
+               $this->config = $config;
+               $this->name = $name;
+       }
+
+       /**
+        *
+        * @param string $name
+        * @param \Config $config
+        * @return ConfigDefinition | false
+        */
+       public static function factory( $name, \Config $config = null ) {
+               if( !$config ) {
+                       $config = \MediaWiki\MediaWikiServices::getInstance()
+                               ->getConfigFactory()->makeConfig( 'bsg' );
+               }
+               if( empty( $name ) || !$config->has( $name ) ) {
+                       return false;
+               }
+               $definitions = static::getConfigDefinitions();
+               if( isset( $definitions[$name] ) ) {
+                       $callback = $definitions[$name];
+               } else {
+                       $callback = static::getDefaultDefinitionCallback(
+                               $name,
+                               $config
+                       );
+                       if( !$callback ) {
+                               return false;
+                       }
+               }
+               if( !is_callable( $callback ) ) {
+                       return false;
+               }
+               return call_user_func_array( $callback, [
+                       \RequestContext::getMain(),
+                       $config,
+                       $name,
+               ]);
+       }
+
+       protected static function getDefaultDefinitionCallback( $name, \Config 
$config = null ) {
+               //TODO: make this configurable
+               if( is_string( $config->get( $name ) ) ) {
+                       return 
"\\BlueSpice\\ConfigDefinition\\StringSetting::getInstance";
+               }
+               if( is_int( $config->get( $name ) ) ) {
+                       return 
"\\BlueSpice\\ConfigDefinition\\IntSetting::getInstance";
+               }
+               if( is_array( $config->get( $name ) ) ) {
+                       return 
"\\BlueSpice\\ConfigDefinition\\ArraySetting::getInstance";
+               }
+               return null;
+       }
+
+       protected static function getConfigDefinitions() {
+               if( static::$configDefinitions ) {
+                       return static::$configDefinitions;
+               }
+               static::$configDefinitions = [];
+               foreach( $GLOBALS['bsgExtensions'] as $extName => 
$extDefinition ) {
+                       if( empty( $extDefinition['configDefinitions'] ) ) {
+                               continue;
+                       }
+                       static::$configDefinitions = array_merge(
+                               static::$configDefinitions,
+                               $extDefinition['configDefinitions']
+                       );
+               }
+               return static::$configDefinitions;
+       }
+
+       /**
+        *
+        * @param \Context $context
+        * @param \Config $config
+        * @param string $name
+        * @return ConfigDefinition
+        */
+       public static function getInstance( $context, $config, $name ) {
+               $callback = static::class;
+               $instance = new $callback(
+                       $context,
+                       $config,
+                       $name
+               );
+               return $instance;
+       }
+
+       /**
+        *
+        * @return \Message
+        */
+       public function getDescripttionMessage() {
+               return null;
+       }
+
+       /**
+        *
+        * @return mixed
+        */
+       public function getValue() {
+               return $this->getConfig()->get( $this->getName() );
+       }
+
+       /**
+        *
+        * @return \Config
+        */
+       public function getConfig() {
+               return $this->config;
+       }
+
+       /**
+        *
+        * @return string
+        */
+       public function getName() {
+               return $this->name;
+       }
+
+       /**
+        *
+        * @return string
+        */
+       public function getVariableName() {
+               return "bsg" . $this->getName();
+       }
+
+       public function getPaths() {
+               return [
+                       static::MAIN_PATH_TYPE . '/' . static::TYPE_SYSTEM,
+                       static::MAIN_PATH_EXTENSION . '/BlueSpiceFoundation',
+                       static::MAIN_PATH_PAKAGE . '/BlueSpice',
+               ];
+       }
+
+       public function isStored() {
+               return false;
+       }
+
+       protected function makeFormFieldParams() {
+               return [
+                       'name' => $this->getName(),
+                       'fieldname' => $this->getName(),
+                       'default' => $this->getValue(),
+                       'id' => $this->makeID(),
+                       'label-message' => $this->getLabelMessageKey(),
+                       'disabled' => !$this->isStored(),
+               ];
+       }
+
+       protected function makeID() {
+               return $this->getVariableName();
+       }
+}
diff --git a/src/ConfigDefinition/ArraySetting.php 
b/src/ConfigDefinition/ArraySetting.php
new file mode 100644
index 0000000..01dd99e
--- /dev/null
+++ b/src/ConfigDefinition/ArraySetting.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class ArraySetting extends \BlueSpice\ConfigDefinition {
+
+       public function getHtmlFormField() {
+               return new \HTMLMultiSelectEx( $this->makeFormFieldParams() );
+       }
+
+       protected function makeFormFieldParams() {
+               $params = parent::makeFormFieldParams();
+               $params['options'] = $this->getValue();
+               return $params;
+       }
+
+       public function getLabelMessageKey() {
+               return $this->getVariableName();
+       }
+}
diff --git a/src/ConfigDefinition/Favicon.php b/src/ConfigDefinition/Favicon.php
new file mode 100644
index 0000000..6cb75c5
--- /dev/null
+++ b/src/ConfigDefinition/Favicon.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class Favicon extends StringSetting {
+
+       public function getLabelMessageKey() {
+               return 'bs-pref-faviconpath';
+       }
+
+       public function getVariableName() {
+               return 'wg' . $this->getName();
+       }
+
+       public function isStored() {
+               return true;
+       }
+}
diff --git a/src/ConfigDefinition/FileExtensions.php 
b/src/ConfigDefinition/FileExtensions.php
new file mode 100644
index 0000000..be65e21
--- /dev/null
+++ b/src/ConfigDefinition/FileExtensions.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class FileExtensions extends ArraySetting {
+
+       public function getLabelMessageKey() {
+               return 'bs-pref-imageextensions';
+       }
+
+       public function isStored() {
+               return true;
+       }
+}
diff --git a/src/ConfigDefinition/ImageExtensions.php 
b/src/ConfigDefinition/ImageExtensions.php
new file mode 100644
index 0000000..0ff151f
--- /dev/null
+++ b/src/ConfigDefinition/ImageExtensions.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class ImageExtensions extends ArraySetting {
+
+       public function getLabelMessageKey() {
+               return 'bs-pref-fileextensions';
+       }
+
+       public function isStored() {
+               return true;
+       }
+}
diff --git a/src/ConfigDefinition/IntSetting.php 
b/src/ConfigDefinition/IntSetting.php
new file mode 100644
index 0000000..2db23c7
--- /dev/null
+++ b/src/ConfigDefinition/IntSetting.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class IntSetting extends \BlueSpice\ConfigDefinition {
+
+       public function getHtmlFormField() {
+               return new \HTMLIntFieldOverride( $this->makeFormFieldParams() 
);
+       }
+
+       protected function makeFormFieldParams() {
+               $params = parent::makeFormFieldParams();
+               return $params;
+       }
+
+       public function getLabelMessageKey() {
+               return $this->getVariableName();
+       }
+}
diff --git a/src/ConfigDefinition/Logo.php b/src/ConfigDefinition/Logo.php
new file mode 100644
index 0000000..05a1e5a
--- /dev/null
+++ b/src/ConfigDefinition/Logo.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class Logo extends StringSetting {
+
+       public function getLabelMessageKey() {
+               return 'bs-pref-logopath';
+       }
+
+       public function getVariableName() {
+               return 'wg' . $this->getName();
+       }
+
+       public function isStored() {
+               return true;
+       }
+}
diff --git a/src/ConfigDefinition/StringSetting.php 
b/src/ConfigDefinition/StringSetting.php
new file mode 100644
index 0000000..d4f75bb
--- /dev/null
+++ b/src/ConfigDefinition/StringSetting.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace BlueSpice\ConfigDefinition;
+
+class StringSetting extends \BlueSpice\ConfigDefinition {
+
+       public function getHtmlFormField() {
+               return new \HTMLTextFieldOverride( $this->makeFormFieldParams() 
);
+       }
+
+       protected function makeFormFieldParams() {
+               $params = parent::makeFormFieldParams();
+               return $params;
+       }
+
+       public function getLabelMessageKey() {
+               return $this->getVariableName();
+       }
+}
diff --git a/src/Hook.php b/src/Hook.php
index f3be181..ac8e961 100644
--- a/src/Hook.php
+++ b/src/Hook.php
@@ -69,7 +69,7 @@
         *
         * @var string
         */
-       protected static $configName = 'main';
+       protected static $configName = 'bsg';
 
        /**
         *
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/Hook/ResourceLoaderGetConfigVars.php 
b/src/Hook/ResourceLoaderGetConfigVars.php
new file mode 100644
index 0000000..567305b
--- /dev/null
+++ b/src/Hook/ResourceLoaderGetConfigVars.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Hook handler base class for MediaWiki hook ResourceLoaderGetConfigVars
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * This file is part of BlueSpice MediaWiki
+ * For further information visit http://bluespice.com
+ *
+ * @author     Robert Vogel <[email protected]>
+ * @package    BlueSpiceFoundation
+ * @copyright  Copyright (C) 2017 Hallo Welt! GmbH, All rights reserved.
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License v2 or 
later
+ * @filesource
+ */
+namespace BlueSpice\Hook;
+use BlueSpice\Hook;
+
+abstract class ResourceLoaderGetConfigVars extends Hook {
+
+       /**
+        *
+        * @var array
+        */
+       protected $vars = null;
+
+       /**
+        *
+        * @param array $parser
+        * @return boolean
+        */
+       public static function callback( &$vars ) {
+               $className = static::class;
+               $hookHandler = new $className(
+                       null,
+                       null,
+                       $vars
+               );
+               return $hookHandler->process();
+       }
+
+       /**
+        *
+        * @param \IContextSource $context
+        * @param \Config $config
+        * @param array $vars
+        */
+       public function __construct( $context, $config, &$vars ) {
+               parent::__construct( $context, $config );
+
+               $this->vars = $vars;
+       }
+}
\ No newline at end of file
diff --git a/src/Hook/ResourceLoaderGetConfigVars/AddBSGConfig.php 
b/src/Hook/ResourceLoaderGetConfigVars/AddBSGConfig.php
new file mode 100644
index 0000000..7675de8
--- /dev/null
+++ b/src/Hook/ResourceLoaderGetConfigVars/AddBSGConfig.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace BlueSpice\Hook\ResourceLoaderGetConfigVars;
+
+use BlueSpice\Hook\ResourceLoaderGetConfigVars;
+
+class AddBSGConfig extends ResourceLoaderGetConfigVars {
+
+       protected function doProcess() {
+               return true;
+       }
+
+       protected function getSettingsToExpose() {
+               return [
+
+               ];
+       }
+}
\ No newline at end of file
diff --git a/src/ISetting.php b/src/ISetting.php
new file mode 100644
index 0000000..570b8ad
--- /dev/null
+++ b/src/ISetting.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace BlueSpice;
+
+interface ISetting {
+       const MAIN_PATH_TYPE = 'type';
+       const MAIN_PATH_EXTENSION = 'extension';
+       const MAIN_PATH_PAKAGE = 'pakage';
+
+       const TYPE_SYSTEM = 'system';
+       const TYPE_INTERFACE = 'interface';
+       const TYPE_EDIT = 'edit';
+       const TYPE_SEARCH = 'search';
+
+       /**
+        * @return string The variable name like it would be in 
'LocalSettings.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.
+        * [ 'type/interface', 'extension/<extensionX>', 'pakage/<pakageX>' ]
+        * 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();
+
+       /**
+        *
+        * @return \HTMLFormField A ready to use HTML-form-field-descriptor, 
with
+        * default, labels, descriptions, ...
+        */
+       public function getHtmlFormField();
+
+       /**
+        *
+        * @return boolean, If this variable is stored in the database
+        */
+       public function isStored();
+
+       /**
+        *
+        * @return string, the message key for the label
+        */
+       public function getLabelMessageKey();
+}
diff --git a/tests/phpunit/ConfigTest.php b/tests/phpunit/ConfigTest.php
new file mode 100644
index 0000000..fb3dcd5
--- /dev/null
+++ b/tests/phpunit/ConfigTest.php
@@ -0,0 +1,63 @@
+<?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(
+                       
\MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancer()
+               );
+               $this->assertEquals(
+                       9,
+                       $config->get( 'UnitTestSetting' ),
+                       'Should return value from database'
+               );
+       }
+
+       public function testGlobalVarDefaulting() {
+               $config = new \BlueSpice\Config(
+                       
\MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancer()
+               );
+
+               $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: merged
Gerrit-Change-Id: I0deecf4c38a9480a243fdeb008e50fcf466fca37
Gerrit-PatchSet: 18
Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation
Gerrit-Branch: master
Gerrit-Owner: Robert Vogel <[email protected]>
Gerrit-Reviewer: Ljonka <[email protected]>
Gerrit-Reviewer: Mglaser <[email protected]>
Gerrit-Reviewer: Pwirth <[email protected]>
Gerrit-Reviewer: Raimond Spekking <[email protected]>
Gerrit-Reviewer: Robert Vogel <[email protected]>
Gerrit-Reviewer: Siebrand <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to