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

Change subject: BSFoundation: Added settings data store
......................................................................


BSFoundation: Added settings data store

TODO: We still need config cache invalidation when written into db

Change-Id: I1776ef3ba3e00963a342c01bcb717249c50b7de9
---
M includes/ExtensionMW.class.php
M includes/GenericTagExtensionHandler.php
M maintenance/BSMigrateSettings.php
M src/Config.php
M src/Context.php
M src/Data/DatabaseWriter.php
M src/Data/FieldType.php
M src/Data/IRecord.php
M src/Data/IWriter.php
M src/Data/LimitOffsetTrimmer.php
M src/Data/Reader.php
M src/Data/ReaderParams.php
M src/Data/Record.php
A src/Data/RecordSet.php
M src/Data/ResultSet.php
A src/Data/Settings/PrimaryDataProvider.php
A src/Data/Settings/Reader.php
A src/Data/Settings/Record.php
A src/Data/Settings/Schema.php
A src/Data/Settings/Store.php
A src/Data/Settings/Writer.php
A src/Data/Writer.php
A src/ITagExtensionDefinitionProvider.php
M tests/phpunit/ConfigTest.php
24 files changed, 649 insertions(+), 63 deletions(-)

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



diff --git a/includes/ExtensionMW.class.php b/includes/ExtensionMW.class.php
index 1a79b04..875bf62 100644
--- a/includes/ExtensionMW.class.php
+++ b/includes/ExtensionMW.class.php
@@ -1,11 +1,12 @@
 <?php
 
 use BlueSpice\Extension;
+use BlueSpice\ITagExtensionDefinitionProvider;
 
 /**
  * @deprecated since version 3.0.0 - Use \BlueSpice\Extension instead
  */
-abstract class BsExtensionMW extends Extension {
+abstract class BsExtensionMW extends Extension implements 
ITagExtensionDefinitionProvider {
 
        protected $mExtensionKey = null;
        protected $mExtensionFile = null;
@@ -236,11 +237,9 @@
 
        /**
         * Returns an array of tag extension definitions
-        * @deprecated since version 3.0.0
         * @return array
         */
        public function makeTagExtensionDefinitions() {
-               wfDeprecated( __METHOD__, '3.0.0' );
                return array();
        }
 }
diff --git a/includes/GenericTagExtensionHandler.php 
b/includes/GenericTagExtensionHandler.php
index f6d9df0..89b1e3e 100644
--- a/includes/GenericTagExtensionHandler.php
+++ b/includes/GenericTagExtensionHandler.php
@@ -56,6 +56,9 @@
         */
        public static function setupHandlers( $aExtensions, $parser ) {
                foreach( $aExtensions as $oExtension ) {
+                       if( !$oExtension instanceof 
BlueSpice\ITagExtensionDefinitionProvider ) {
+                               continue;
+                       }
                        $aExtensionTags = 
$oExtension->makeTagExtensionDefinitions();
                        foreach( $aExtensionTags as $sTagName => $aTagDef ) {
                                $oTagHandler = new self( $sTagName, $aTagDef );
diff --git a/maintenance/BSMigrateSettings.php 
b/maintenance/BSMigrateSettings.php
index 555beea..848db80 100644
--- a/maintenance/BSMigrateSettings.php
+++ b/maintenance/BSMigrateSettings.php
@@ -2,18 +2,7 @@
 
 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();
-       }
+class BSMigrateSettings extends LoggedUpdateMaintenance {
 
        protected function noDataToMigrate() {
                return $this->getDB( DB_REPLICA )->tableExists( 'bs_settings' ) 
=== false;
@@ -38,19 +27,34 @@
        }
 
        protected function makeNewName( $oldName ) {
-               if( $oldName === 'MW::LogoPath' ) {
-                       return 'wgLogo';
+               if( $deviatingName = $this->fromDeviatingNames( $oldName ) ) {
+                       return $deviatingName;
                }
 
                //$oldName = "MW::TopMenuBarCustomizer::NumberOfSubEntries"
                $nameParts = explode( '::', $oldName );
                array_shift( $nameParts ); //MW
-               $newName = 'bsg' . implode( '', $nameParts );
+               $newName = implode( '', $nameParts );
 
                if( strlen( $newName ) > 255 ) {
                        throw new Exception( "Variable name '$newName' is too 
long!" );
                }
 
+               return $newName;
+       }
+
+       protected function fromDeviatingNames( $oldName ) {
+               if( $oldName === 'MW::LogoPath' ) {
+                       return 'Logo';
+               }
+               if( $oldName === 'MW::FaviconPath' ) {
+                       return 'Favicon';
+               }
+               $newName = false;
+               \Hooks::run( 'BSMigrateSettingsFromDeviatingNames', [
+                       $oldName,
+                       &$newName
+               ]);
                return $newName;
        }
 
@@ -82,11 +86,21 @@
                return FormatJson::encode( $newValue );
        }
 
-}
+       protected function doDBUpdates() {
+               if( $this->noDataToMigrate() ) {
+                       $this->output( "bs_settings -> bs_settings3: No data to 
migrate" );
+                       return true;
+               }
 
-$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
+               $this->readOldData();
+               $this->convertData();
+               $this->saveConvertedData();
+
+               return true;
+       }
+
+       protected function getUpdateKey() {
+               return 'bs_settings3-migration';
+       }
+
+}
diff --git a/src/Config.php b/src/Config.php
index e81912b..c94e899 100644
--- a/src/Config.php
+++ b/src/Config.php
@@ -1,6 +1,9 @@
 <?php
 
 namespace BlueSpice;
+use BlueSpice\Data\Settings\Store;
+use BlueSpice\Data\Settings\Record;
+use BlueSpice\Context;
 
 class Config extends \MultiConfig implements \Serializable {
 
@@ -59,25 +62,42 @@
                return new self( $lb );
        }
 
+       /**
+        * //TODO: We need a config chache invalidation when writing to the db!
+        * Invalidates the cache of config stored in the database
+        * @return boolean
+        */
+       public function invalidateCache() {
+               //TODO: We need a config chache invalidation when writing to 
the db!
+               return true;
+       }
+
        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', '*' );
+               $store = $this->getStore();
+               $resultSet = $store->getReader()->read(
+                       new Data\ReaderParams( [
+                               'limit' => Data\ReaderParams::LIMIT_INFINITE
+                       ] )
+               );
 
-               foreach( $res as $row ) {
-                       if( strpos(  $row->s_name, 'bsg' ) === 0 ) {
-                               $name = substr( $row->s_name, 3 );
-                               $hash[$name] = \FormatJson::decode( 
$row->s_value );
-                       }
+               foreach( $resultSet->getRecords() as $record ) {
+                       $name = $record->get( Record::NAME );
+                       $hash[ $name ] = $record->get( Record::VALUE );
                }
 
                return new \HashConfig( $hash );
        }
 
+       protected function getStore() {
+               return new Store(
+                       new Context( \RequestContext::getMain(), $this ),
+                       $this->loadBalancer
+               );
+       }
+
 }
diff --git a/src/Context.php b/src/Context.php
index a369f4e..40510c0 100644
--- a/src/Context.php
+++ b/src/Context.php
@@ -1,7 +1,7 @@
 <?php
 namespace BlueSpice;
 
-abstract class Context implements \IContextSource {
+class Context implements \IContextSource {
 
        /**
         *
diff --git a/src/Data/DatabaseWriter.php b/src/Data/DatabaseWriter.php
index 979b2cf..895a809 100644
--- a/src/Data/DatabaseWriter.php
+++ b/src/Data/DatabaseWriter.php
@@ -1,8 +1,9 @@
 <?php
 
 namespace BlueSpice\Data;
+use BlueSpice\Data\RecordSet;
 
-abstract class DatabaseWriter implements IWriter {
+abstract class DatabaseWriter extends Writer {
 
        /**
         *
@@ -12,9 +13,234 @@
 
        /**
         *
-        * @param \LoadBalancer $loadBalancer
+        * @var IReader
         */
-       public function __construct( $loadBalancer ) {
+       protected $reader = null;
+
+       abstract protected function getIdentifierFields();
+
+       abstract protected function getTableName();
+
+       /**
+        *
+        * @param IReader $reader
+        * @param \Wikimedia\Rdbms\LoadBalancer $loadBalancer
+        * @param \IContextSource $context
+        * @param \Config $config
+        */
+       public function __construct( IReader $reader, $loadBalancer, 
\IContextSource $context = null, \Config $config = null ) {
+               parent::__construct( $context, $config );
+               $this->reader = $reader;
                $this->db = $loadBalancer->getConnection( DB_MASTER );
        }
+
+       /**
+        * Create or Update given records
+        * @param RecordSet $recordSet
+        * @return RecordSet
+        */
+       public function write( $recordSet ) {
+               foreach( $recordSet->getRecords() as $record ) {
+                       if( !$record->getStatus()->isOK() ) {
+                               continue;
+                       }
+                       if( !$existingRecord = $this->getExistingRecord( 
$record ) ) {
+                               $this->insert( $record );
+                               continue;
+                       }
+                       $this->modify( $existingRecord, $record  );
+               }
+               return $recordSet;
+       }
+
+       /**
+        * Remove given records
+        * @param RecordSet $recordSet
+        * @return RecordSet
+        */
+       public function remove( $recordSet ) {
+               foreach( $recordSet->getRecords() as $record ) {
+                       if( !$record->getStatus()->isOK() ) {
+                               continue;
+                       }
+                       if( !$existingRecord = $this->getExistingRecord( 
$record ) ) {
+                               $record->getStatus()->fatal(
+                                       "Record not found in table: 
".$this->getTableName()
+                               );
+                               continue;
+                       }
+                       $this->delete( $existingRecord, $record  );
+               }
+               return $recordSet;
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function insert( $record ) {
+               $record->getData();
+               try {
+                       $success = $this->db->insert(
+                               $this->getTableName(),
+                               $this->makeInsertFields( $record ),
+                               __METHOD__
+                       );
+               } catch ( \Exception $e ) {
+                       $record->getStatus()->fatal( $e );
+                       return;
+               }
+               if( !$success ) {
+                       $record->getStatus()->fatal(
+                               "Error writing into: ".$this->getTableName()
+                       );
+               }
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $existingRecord
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function modify( $existingRecord, $record ) {
+               $record->getData();
+               try {
+                       $success = $this->db->update(
+                               $this->getTableName(),
+                               $this->makeUpdateFields( $existingRecord, 
$record ),
+                               $this->makeUpdateConditions( $existingRecord, 
$record ),
+                               __METHOD__
+                       );
+               } catch ( \Exception $e ) {
+                       $record->getStatus()->fatal( $e );
+                       return;
+               }
+               if( !$success ) {
+                       $record->getStatus()->fatal(
+                               "Error writing into: ".$this->getTableName()
+                       );
+               }
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $existingRecord
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function delete( $existingRecord, $record ) {
+               $record->getData();
+               try {
+                       $success = $this->db->delete(
+                               $this->getTableName(),
+                               $this->makeDeleteConditions( $existingRecord, 
$record ),
+                               __METHOD__
+                       );
+               } catch ( \Exception $e ) {
+                       $record->getStatus()->fatal( $e );
+                       return;
+               }
+               if( !$success ) {
+                       $record->getStatus()->fatal(
+                               "Error deleting from: ".$this->getTableName()
+                       );
+               }
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function makeInsertFields( $record ) {
+               return (array) $record->getData();
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $existingRecord
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function makeUpdateFields( $existingRecord, $record ) {
+               $return = [];
+               foreach( (array) $record->getData() as $fieldName => $mValue ) {
+                       if( in_array( $fieldName, $this->getIdentifierFields() 
) ) {
+                               continue;
+                       }
+                       $return[$fieldName] = $mValue;
+               }
+               return $return;
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $existingRecord
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function makeUpdateConditions( $existingRecord, $record ) {
+               $return = [];
+               foreach( $this->getIdentifierFields() as $fieldName ) {
+                       $return[$fieldName] = $existingRecord->get( $fieldName 
);
+               }
+               return $return;
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $existingRecord
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function makeDeleteConditions( $existingRecord, $record ) {
+               $return = [];
+               foreach( $this->getIdentifierFields() as $fieldName ) {
+                       $return[$fieldName] = $existingRecord->get( $fieldName 
);
+               }
+               return $return;
+       }
+
+       /**
+        *
+        * @param BlueSpice\Data\Record $record
+        */
+       protected function getExistingRecord( $record ) {
+               $recordSet = $this->reader->read( new 
\BlueSpice\Data\ReaderParams( [
+                       'filter' => $this->makeExistingRecordFilters( $record )
+               ]));
+               $records = $recordSet->getRecords();
+               if( count( $records ) < 1 ) {
+                       return false;
+               }
+               return $records[0];
+       }
+
+       /**
+        *
+        * @param BlueSpice\Data\Record $record
+        */
+       protected function makeExistingRecordFilters( $record ) {
+               $filters = [];
+               foreach( $this->getIdentifierFields() as $fieldName ) {
+                       $filters[] = $this->makeExistingRecordFilter(
+                               $record,
+                               $fieldName
+                       );
+               }
+               return $filters;
+       }
+
+       /**
+        *
+        * @param BlueSpice\Data\Record $record
+        */
+       protected function makeExistingRecordFilter( $record, $fieldName ) {
+               return [
+                       Filter::KEY_FIELD => $fieldName,
+                       Filter::KEY_VALUE => $record->get( $fieldName ),
+                       Filter::KEY_TYPE => $this->getFieldType( $fieldName ),
+                       Filter::KEY_COMPARISON => Filter::COMPARISON_EQUALS,
+               ];
+       }
+
+       protected function getFieldType( $fieldName ) {
+               $schema = $this->getSchema();
+               return $schema[$fieldName][Schema::TYPE];
+       }
 }
diff --git a/src/Data/FieldType.php b/src/Data/FieldType.php
index 672a7b6..d419cf2 100644
--- a/src/Data/FieldType.php
+++ b/src/Data/FieldType.php
@@ -13,4 +13,5 @@
        const INT = 'int';
        const STRING = 'string';
        const LISTVALUE = 'list';
+       const MIXED = 'mixed';
 }
diff --git a/src/Data/IRecord.php b/src/Data/IRecord.php
index fa2b839..517665c 100644
--- a/src/Data/IRecord.php
+++ b/src/Data/IRecord.php
@@ -22,4 +22,9 @@
         * @return \stdClass
         */
        public function getData();
+
+       /**
+        * @return \Status
+        */
+       public function getStatus();
 }
diff --git a/src/Data/IWriter.php b/src/Data/IWriter.php
index 5668374..4df58b3 100644
--- a/src/Data/IWriter.php
+++ b/src/Data/IWriter.php
@@ -5,9 +5,22 @@
 interface IWriter {
 
        /**
-        *
-        * @param array $dataSet
-        * @return \Status
+        * Create or Update given records
+        * @param RecordSet $recordSet
+        * @return RecordSet
         */
-       public function write( $dataSet );
+       public function write( $recordSet );
+
+       /**
+        * Delete given records
+        * @param RecordSet $dataSet
+        * @return RecordSet
+        */
+       public function remove( $recordSet );
+
+       /**
+        * @return Schema Column definition compatible to
+        * https://docs.sencha.com/extjs/4.2.1/#!/api/Ext.grid.Panel-cfg-columns
+        */
+       public function getSchema();
 }
\ No newline at end of file
diff --git a/src/Data/LimitOffsetTrimmer.php b/src/Data/LimitOffsetTrimmer.php
index 0b3e1e2..a81bbda 100644
--- a/src/Data/LimitOffsetTrimmer.php
+++ b/src/Data/LimitOffsetTrimmer.php
@@ -28,7 +28,10 @@
         */
        public function trim( $dataSets ) {
                $total = count( $dataSets );
-               $end = $this->limit + $this->offset;
+               $end = $total;
+               if( $this->limit !== ReaderParams::LIMIT_INFINITE ) {
+                       $end = $this->limit + $this->offset;
+               }
 
                if( $end > $total || $end === 0 ) {
                        $end = $total;
diff --git a/src/Data/Reader.php b/src/Data/Reader.php
index 6afc5f3..5e1d519 100644
--- a/src/Data/Reader.php
+++ b/src/Data/Reader.php
@@ -74,7 +74,9 @@
                $dataSets = $trimmer->trim( $dataSets );
 
                $secondaryDataProvider = $this->makeSecondaryDataProvider();
-               $dataSets = $secondaryDataProvider->extend( $dataSets );
+               if( $secondaryDataProvider instanceof ISecondaryDataProvider ) {
+                       $dataSets = $secondaryDataProvider->extend( $dataSets );
+               }
 
                $resultSet = new ResultSet( $dataSets, $total  );
                return $resultSet;
@@ -114,7 +116,7 @@
        }
 
        /**
-        * @return ISecondaryDataProvider
+        * @return ISecondaryDataProvider | null to skip
         */
        protected abstract function makeSecondaryDataProvider();
 }
\ No newline at end of file
diff --git a/src/Data/ReaderParams.php b/src/Data/ReaderParams.php
index b7390ab..05de0a9 100644
--- a/src/Data/ReaderParams.php
+++ b/src/Data/ReaderParams.php
@@ -6,6 +6,7 @@
 use BlueSpice\Data\Filter;
 
 class ReaderParams {
+       const LIMIT_INFINITE = -1;
 
        /**
         * For pre filtering
diff --git a/src/Data/Record.php b/src/Data/Record.php
index bc3bc06..2c753bd 100644
--- a/src/Data/Record.php
+++ b/src/Data/Record.php
@@ -12,10 +12,17 @@
 
        /**
         *
+        * @var \Status
+        */
+       protected $status = null;
+
+       /**
+        *
         * @param \stdClass $dataSet
         */
-       public function __construct( $dataSet ) {
+       public function __construct( $dataSet, \Status $status = null ) {
                $this->dataSet = $dataSet;
+               $this->status = $status;
        }
 
        /**
@@ -56,5 +63,17 @@
                return $this->dataSet;
        }
 
+       /**
+        *
+        * @return \Status
+        */
+       public function getStatus() {
+               if( $this->status ) {
+                       return $this->status;
+               }
+               $this->status = \Status::newGood();
+               return $this->status;
+       }
+
 }
 
diff --git a/src/Data/RecordSet.php b/src/Data/RecordSet.php
new file mode 100644
index 0000000..68433f7
--- /dev/null
+++ b/src/Data/RecordSet.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace BlueSpice\Data;
+
+class RecordSet {
+
+       /**
+        *
+        * @var \BlueSpice\Data\Record[]
+        */
+       protected $records = [];
+
+       /**
+        *
+        * @param \BlueSpice\Data\Record[] $records
+        */
+       public function __construct( $records ) {
+               $this->records = $records;
+       }
+
+       /**
+        *
+        * @return \BlueSpice\Data\Record[]
+        */
+       public function getRecords() {
+               return $this->records;
+       }
+}
\ No newline at end of file
diff --git a/src/Data/ResultSet.php b/src/Data/ResultSet.php
index f4fbf60..54a0569 100644
--- a/src/Data/ResultSet.php
+++ b/src/Data/ResultSet.php
@@ -2,13 +2,7 @@
 
 namespace BlueSpice\Data;
 
-class ResultSet {
-
-       /**
-        *
-        * @var \BlueSpice\Data\Record[]
-        */
-       protected $records = [];
+class ResultSet extends RecordSet {
 
        /**
         *
@@ -22,16 +16,8 @@
         * @param int $total
         */
        public function __construct( $records, $total ) {
-               $this->records = $records;
+               parent::__construct( $records );
                $this->total = $total;
-       }
-
-       /**
-        *
-        * @return \BlueSpice\Data\Record[]
-        */
-       public function getRecords() {
-               return $this->records;
        }
 
        /**
diff --git a/src/Data/Settings/PrimaryDataProvider.php 
b/src/Data/Settings/PrimaryDataProvider.php
new file mode 100644
index 0000000..7e5285f
--- /dev/null
+++ b/src/Data/Settings/PrimaryDataProvider.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace BlueSpice\Data\Settings;
+
+use \BlueSpice\Data\IPrimaryDataProvider;
+
+class PrimaryDataProvider implements IPrimaryDataProvider {
+
+       /**
+        *
+        * @var \BlueSpice\Data\Record[]
+        */
+       protected $data = [];
+
+       /**
+        *
+        * @var \Wikimedia\Rdbms\IDatabase
+        */
+       protected $db = null;
+
+       /**
+        *
+        * @param \Wikimedia\Rdbms\IDatabase $db
+        */
+       public function __construct( $db ) {
+               $this->db = $db;
+       }
+
+       /**
+        *
+        * @param string $query
+        * @param type $preFilters
+        */
+       public function makeData( $query = '', $preFilters = [] ) {
+               $res = $this->db->select( 'bs_settings3', '*' );
+
+               $this->data = [];
+               foreach( $res as $row ) {
+                       $this->appendRowToData( $row );
+               }
+
+               return $this->data;
+       }
+
+       protected function appendRowToData( $row ) {
+               $this->data[] = new Record( (object) [
+                       Record::NAME => $row->s_name,
+                       Record::VALUE => \FormatJson::decode( $row->s_value )
+               ]);
+       }
+}
\ No newline at end of file
diff --git a/src/Data/Settings/Reader.php b/src/Data/Settings/Reader.php
new file mode 100644
index 0000000..9b4899d
--- /dev/null
+++ b/src/Data/Settings/Reader.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace BlueSpice\Data\Settings;
+
+use \BlueSpice\Data\DatabaseReader;
+
+class Reader extends DatabaseReader {
+       /**
+        *
+        * @param \LoadBalancer $loadBalancer
+        * @param \IContextSource $context
+        */
+       public function __construct( $loadBalancer, \IContextSource $context = 
null ) {
+               parent::__construct( $loadBalancer, $context, 
$context->getConfig() );
+       }
+
+       protected function makePrimaryDataProvider( $params ) {
+               return new PrimaryDataProvider( $this->db );
+       }
+
+       protected function makeSecondaryDataProvider() {
+               return null;
+       }
+
+       public function getSchema() {
+               return new Schema();
+       }
+
+}
\ No newline at end of file
diff --git a/src/Data/Settings/Record.php b/src/Data/Settings/Record.php
new file mode 100644
index 0000000..6fd09c2
--- /dev/null
+++ b/src/Data/Settings/Record.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace BlueSpice\Data\Settings;
+
+class Record extends \BlueSpice\Data\Record {
+       const NAME = 's_name';
+       const VALUE = 's_value';
+}
\ No newline at end of file
diff --git a/src/Data/Settings/Schema.php b/src/Data/Settings/Schema.php
new file mode 100644
index 0000000..f73a153
--- /dev/null
+++ b/src/Data/Settings/Schema.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace BlueSpice\Data\Settings;
+
+use BlueSpice\Data\FieldType;
+
+class Schema extends \BlueSpice\Data\Schema {
+       public function __construct() {
+               parent::__construct( [
+                       Record::NAME => [
+                               self::FILTERABLE => true,
+                               self::SORTABLE => true,
+                               self::TYPE => FieldType::STRING
+                       ],
+                       Record::VALUE => [
+                               self::FILTERABLE => false,
+                               self::SORTABLE => false,
+                               self::TYPE => FieldType::MIXED
+                       ],
+               ]);
+       }
+}
\ No newline at end of file
diff --git a/src/Data/Settings/Store.php b/src/Data/Settings/Store.php
new file mode 100644
index 0000000..ec217da
--- /dev/null
+++ b/src/Data/Settings/Store.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace BlueSpice\Data\Settings;
+
+class Store implements \BlueSpice\Data\IStore {
+
+       /**
+        *
+        * @var \IContextSource
+        */
+       protected $context = null;
+
+       /**
+        *
+        * @param \IContextSource $context
+        */
+       public function __construct( $context, $loadBalancer ) {
+               $this->context = $context;
+               $this->loadBalancer = $loadBalancer;
+       }
+
+       public function getReader() {
+               return new Reader( $this->loadBalancer, $this->context );
+       }
+
+       public function getWriter() {
+               return new Writer(
+                       $this->getReader(),
+                       $this->loadBalancer,
+                       $this->context
+               );
+       }
+}
\ No newline at end of file
diff --git a/src/Data/Settings/Writer.php b/src/Data/Settings/Writer.php
new file mode 100644
index 0000000..9b67263
--- /dev/null
+++ b/src/Data/Settings/Writer.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace BlueSpice\Data\Settings;
+use BlueSpice\Data\RecordSet;
+
+class Writer extends \BlueSpice\Data\DatabaseWriter {
+
+       /**
+        *
+        * @param \BlueSpice\Data\IReader $reader
+        * @param \LoadBalancer $loadBalancer
+        * @param \IContextSource $context
+        */
+       public function __construct( \BlueSpice\Data\IReader $reader, 
$loadBalancer, \IContextSource $context = null ) {
+               parent::__construct( $reader, $loadBalancer, $context, 
$context->getConfig() );
+       }
+
+       protected function getTableName() {
+               return 'bs_settings3';
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function makeInsertFields( $record ) {
+               $fields = parent::makeInsertFields( $record );
+               $fields[Record::VALUE] = \FormatJson::encode( 
$fields[Record::VALUE] );
+               return $fields;
+       }
+
+       /**
+        *
+        * @param \BlueSpice\Data\IRecord $existingRecord
+        * @param \BlueSpice\Data\IRecord $record
+        */
+       protected function makeUpdateFields( $existingRecord, $record ) {
+               $fields = parent::makeUpdateFields( $existingRecord, $record );
+               $fields[Record::VALUE] = \FormatJson::encode( 
$fields[Record::VALUE] );
+               return $fields;
+       }
+
+       /**
+        * @return Schema Column definition compatible to
+        * https://docs.sencha.com/extjs/4.2.1/#!/api/Ext.grid.Panel-cfg-columns
+        */
+       public function getSchema() {
+               return new Schema();
+       }
+
+       protected function getIdentifierFields() {
+               return [ Record::NAME ];
+       }
+}
diff --git a/src/Data/Writer.php b/src/Data/Writer.php
new file mode 100644
index 0000000..ffa0180
--- /dev/null
+++ b/src/Data/Writer.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace BlueSpice\Data;
+
+use \BlueSpice\Data\IWriter;
+
+abstract class Writer implements IWriter {
+
+       /**
+        *
+        * @var \IContextSource
+        */
+       protected $context = null;
+
+       /**
+        *
+        * @var \Config
+        */
+       protected $config = null;
+
+       /**
+        *
+        * @param \IContextSource $context
+        * @param \Config $config
+        */
+       public function __construct( \IContextSource $context = null, \Config 
$config = null ) {
+               $this->context = $context;
+               if( $this->context === null ) {
+                       $this->context = \RequestContext::getMain();
+               }
+
+               $this->config = $config;
+               if( $this->config === null ) {
+                       $this->config = 
\MediaWiki\MediaWikiServices::getInstance()->getMainConfig();
+               }
+       }
+
+       /**
+        *
+        * @return \User
+        */
+       protected function getUser() {
+               return $this->context->getUser();
+       }
+
+       /**
+        *
+        * @return \Title
+        */
+       protected function getTitle() {
+               return $this->context->getTitle();
+       }
+}
\ No newline at end of file
diff --git a/src/ITagExtensionDefinitionProvider.php 
b/src/ITagExtensionDefinitionProvider.php
new file mode 100644
index 0000000..ffe73e2
--- /dev/null
+++ b/src/ITagExtensionDefinitionProvider.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace BlueSpice;
+
+interface ITagExtensionDefinitionProvider {
+
+       /**
+        * Returns an array of tag extension definitions
+        * @return array
+        */
+       public function makeTagExtensionDefinitions();
+}
\ No newline at end of file
diff --git a/tests/phpunit/ConfigTest.php b/tests/phpunit/ConfigTest.php
index fb3dcd5..aa28c6b 100644
--- a/tests/phpunit/ConfigTest.php
+++ b/tests/phpunit/ConfigTest.php
@@ -22,9 +22,13 @@
        public function addDBData() {
                parent::addDBData();
                $this->db->insert( 'bs_settings3', [
-                       's_name' => 'bsgUnitTestSetting',
+                       's_name' => 'UnitTestSetting',
                        's_value' => '"9"' //JSON formatted
                ] );
+               $config = new \BlueSpice\Config(
+                       
\MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancer()
+               );
+               $config->invalidateCache();
        }
 
        public function testFactoryReturn() {

-- 
To view, visit https://gerrit.wikimedia.org/r/385375
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I1776ef3ba3e00963a342c01bcb717249c50b7de9
Gerrit-PatchSet: 14
Gerrit-Project: mediawiki/extensions/BlueSpiceFoundation
Gerrit-Branch: master
Gerrit-Owner: Pwirth <[email protected]>
Gerrit-Reviewer: Ljonka <[email protected]>
Gerrit-Reviewer: Mglaser <[email protected]>
Gerrit-Reviewer: Pwirth <[email protected]>
Gerrit-Reviewer: Robert Vogel <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

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

Reply via email to