Soeren.oldag has submitted this change and it was merged. Change subject: Implemented basic API for violations ......................................................................
Implemented basic API for violations wdqagetviolations returns all violations for an entity or null wdqamodifyviolation modifies the status of a present violation and returns whether it was a succes Bug: T97428 Change-Id: Ia3fa6ee23706d23e1f1f6ce51deea6538f0874b8 --- M WikidataQuality.php A api/GetViolations.php A api/ModifyViolation.php M composer.json A includes/Serializer/ViolationSerializer.php M includes/Violations/Violation.php M includes/Violations/ViolationStore.php M phpunit.xml.dist A tests/phpunit/Api/GetViolationsTest.php A tests/phpunit/Api/ModifyViolationTest.php A tests/phpunit/Serializer/ViolationSerializerTest.php M tests/phpunit/Violations/ViolationStoreTest.php 12 files changed, 596 insertions(+), 8 deletions(-) Approvals: Soeren.oldag: Checked; Looks good to me, approved diff --git a/WikidataQuality.php b/WikidataQuality.php index bec172e..798c8e7 100644 --- a/WikidataQuality.php +++ b/WikidataQuality.php @@ -40,6 +40,10 @@ 'localBasePath' => __DIR__, 'remoteExtPath' => 'WikidataQuality' ); + + // Define API modules + $GLOBALS['wgAPIModules']['wdqagetviolations'] = 'WikidataQuality\Api\GetViolations'; + $GLOBALS['wgAPIModules']['wdqamodifyviolation'] = 'WikidataQuality\Api\ModifyViolation'; }); // Define database table names diff --git a/api/GetViolations.php b/api/GetViolations.php new file mode 100644 index 0000000..c3b03d7 --- /dev/null +++ b/api/GetViolations.php @@ -0,0 +1,116 @@ +<?php + +namespace WikidataQuality\Api; + +use ApiMain; +use DataValues\Serializers; +use DataValues\Serializers\DataValueSerializer; +use Wikibase\Api\ApiWikibase; +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Statement\StatementList; +use WikidataQuality\Violations\ViolationLookup; +use WikidataQuality\Violations\ViolationQuery; +use WikidataQuality\Serializer\ViolationSerializer; + + +/** + * Class GetViolations + * + * API module to access found violations + * + * @package WikidataQuality\Api + * @author BP2014N1 + * @license GNU GPL v2+ + */ +class GetViolations extends ApiWikibase { + + /** + * @var ViolationLookup + */ + private $violationLookup; + + /** + * @var ViolationSerializer + */ + private $violationSerializer; + + /** + * @param ApiMain $main + * @param string $name + * @param string $prefix + */ + public function __construct( ApiMain $main, $name, $prefix = '' ) { + parent::__construct( $main, $name, $prefix ); + + $this->violationLookup = new ViolationLookup(); + $this->violationSerializer = new ViolationSerializer(); + } + + /** + * Gets violations from the violations table belonging to given entity and outputs them in the form of: + * result + * { claimGuidOfViolation { violation } + * { claimGuidOfAnotherViolation { anotherViolation }} + */ + public function execute() { + $params = $this->extractRequestParams(); + $result = $this->getViolationsOfEntity( $params['entityId'] ); + $this->writeResultOutput( $result ); + } + + /** + * @param string $entityId + * + * @return array + */ + private function getViolationsOfEntity( $entityId ) { + $query = new ViolationQuery(); + $query->setEntityId( $entityId ); + $result = $this->violationLookup->getWhere( $query ); + return $result; + } + + private function writeResultOutput( $result ) { + $output = array(); + if ( $result ) { + foreach ( $result as $violation ) { + $serializedViolation = $this->violationSerializer->serialize( $violation ); + $output[ $violation->getClaimGuid() ] = $serializedViolation; + } + + $this->getResult()->setIndexedTagName( $output, 'violatedClaims' ); + $this->getResult()->addValue( null, 'results', $output ); + } else { + $this->getResult()->addValue( null, 'results', null ); + } + $this->getResultBuilder()->markSuccess( 1 ); + } + + /** + * Returns an array of allowed parameters + * + * @return array + * @codeCoverageIgnore + */ + public function getAllowedParams() { + return array_merge( parent::getAllowedParams(), array( + 'entityId' => array( + ApiWikibase::PARAM_TYPE => 'string', + ApiWikibase::PARAM_ISMULTI => false, + ApiWikibase::PARAM_REQUIRED => true + ) + ) ); + } + + /** + * Returns usage examples for this module + * + * @return array + * @codeCoverageIgnore + */ + public function getExamplesMessages() { + return array( + 'action=wdqagetviolations&entityId=Q76' => 'apihelp-wdqagetviolations-examples-1' + ); + } +} \ No newline at end of file diff --git a/api/ModifyViolation.php b/api/ModifyViolation.php new file mode 100644 index 0000000..a820671 --- /dev/null +++ b/api/ModifyViolation.php @@ -0,0 +1,144 @@ +<?php + +namespace WikidataQuality\Api; + +use ApiMain; +use DataValues\Serializers; +use DataValues\Serializers\DataValueSerializer; +use Wikibase\Api\ApiWikibase; +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Statement\StatementList; +use Wikibase\Repo\WikibaseRepo; +use WikidataQuality\Violations\ViolationLookup; +use WikidataQuality\Violations\ViolationQuery; +use WikidataQuality\Serializer\ViolationSerializer; +use WikidataQuality\Violations\ViolationStore; + + +/** + * Class ModifyViolation + * + * API module to access found violations + * + * @package WikidataQuality\Api + * @author BP2014N1 + * @license GNU GPL v2+ + */ +class ModifyViolation extends ApiWikibase { + + /** + * @var ViolationLookup + */ + private $violationLookup; + + /** + * @var ViolationStore + */ + private $violationStore; + + /** + * @var ViolationSerializer + */ + private $violationSerializer; + + /** + * @param ApiMain $main + * @param string $name + * @param string $prefix + */ + public function __construct( ApiMain $main, $name, $prefix = '' ) { + parent::__construct( $main, $name, $prefix ); + + $this->violationLookup = new ViolationLookup(); + $this->violationStore = new ViolationStore(); + $this->violationSerializer = new ViolationSerializer(); + } + + /** + * + */ + public function execute() { + $params = $this->extractRequestParams(); + + $repo = WikibaseRepo::getDefaultInstance(); + $claimGuidValidator = $repo->getClaimGuidValidator(); + $validGuid = $claimGuidValidator->validate( $params['claimGuid'] ); + if (!$validGuid) { + $this->dieError( 'Invalid claim guid.', 'guid-invalid' ); + } + + $result = $this->modifyViolationStatus( $params['claimGuid'], $params['constraintId'], $params['status'] ); + $this->writeResultOutput( $result ); + } + + /** + * @param string $claimGuid + * @param string $constraintId + * @param string $newStatus + * + * @return bool + * + */ + private function modifyViolationStatus( $claimGuid, $constraintId, $newStatus ) { + $query = new ViolationQuery(); + $query->setClaimGuid( $claimGuid ); + $query->setConstraintClaimGuid( $constraintId ); + + $violationArray = $this->violationLookup->getWhere( $query ); + + $result = false; + + if ( $violationArray && count( $violationArray ) === 1 ) { + $violation = $violationArray[0]; + $violation->setStatus( $newStatus ); + $result = $this->violationStore->saveViolation( $violation ); + } + return $result; + } + + private function writeResultOutput( $result ) { + if ( $result ) { + $this->getResultBuilder()->markSuccess( 1 ); + } else { + $this->getResultBuilder()->markSuccess( 0 ); + } + } + + /** + * Returns an array of allowed parameters + * + * @return array + * @codeCoverageIgnore + */ + public function getAllowedParams() { + return array_merge( parent::getAllowedParams(), array( + 'claimGuid' => array( + ApiWikibase::PARAM_TYPE => 'string', + ApiWikibase::PARAM_REQUIRED => true, + ApiWikibase::PARAM_ISMULTI => false + ), + 'constraintId' => array( + ApiWikibase::PARAM_TYPE => 'string', + ApiWikibase::PARAM_REQUIRED => true, + ApiWikibase::PARAM_ISMULTI => false + ), + 'status' => array( + ApiWikibase::PARAM_TYPE => 'string', + ApiWikibase::PARAM_ISMULTI => false, + ApiWikibase::PARAM_REQUIRED => true + ) + ) ); + } + + /** + * Returns usage examples for this module + * + * @return array + * @codeCoverageIgnore + */ + public function getExamplesMessages() { + return array( + 'action=wdqamodifyviolation&...' => 'apihelp-wdqamodifyviolation-examples-1' + ); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 628ad99..f7a7deb 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "autoload": { "psr-4": { "WikidataQuality\\": "includes/", + "WikidataQuality\\Api\\": "api/", "WikidataQuality\\Specials\\": "specials/", "WikidataQuality\\Tests\\": "tests/phpunit/" }, diff --git a/includes/Serializer/ViolationSerializer.php b/includes/Serializer/ViolationSerializer.php new file mode 100644 index 0000000..8d368bc --- /dev/null +++ b/includes/Serializer/ViolationSerializer.php @@ -0,0 +1,58 @@ +<?php + +namespace WikidataQuality\Serializer; + + +use Serializers\DispatchableSerializer; +use WikidataQuality\Violations\Violation; +use Serializers\Exceptions\UnsupportedObjectException; + + +class ViolationSerializer implements DispatchableSerializer { + + /** + * @see DispatchableSerializer::isSerializerFor + * + * @param mixed $object + * + * @return bool + */ + public function isSerializerFor( $object ) { + return $object instanceof Violation; + } + + /** + * @see Serializer::serialize + * + * @param mixed $object + * + * @return array + * @throws UnsupportedObjectException + */ + public function serialize( $object ) { + if ( !$this->isSerializerFor( $object ) ) { + throw new UnsupportedObjectException( + $object, + 'ViolationSerializer can only serialize Violation objects.' + ); + } + + return $this->getSerialized( $object ); + } + + private function getSerialized( Violation $violation ) { + return array( + 'entityId' => $violation->getEntityId()->getSerialization(), + 'propertyId' => $violation->getPropertyId()->getSerialization(), + 'claimGuid' => $violation->getClaimGuid(), + 'constraintId' => $violation->getConstraintId(), + // TODO: ->getSerialization (when it really is an EntityId and not just a string) + 'constraintTypeEntityId' => $violation->getConstraintTypeEntityId(), + 'additionalInfo' => $violation->getAdditionalInfo(), + 'updatedAt' => $violation->getUpdatedAt(), + 'revisionId' => $violation->getRevisionId(), + 'status' => $violation->getStatus() + ); + } + +} \ No newline at end of file diff --git a/includes/Violations/Violation.php b/includes/Violations/Violation.php index d64ec7f..415299e 100644 --- a/includes/Violations/Violation.php +++ b/includes/Violations/Violation.php @@ -145,7 +145,7 @@ /** * @param string $status */ - private function setStatus( $status ) { + public function setStatus( $status ) { if ( !is_string( $status ) ) { throw new InvalidArgumentException( '$status must be string.' ); } diff --git a/includes/Violations/ViolationStore.php b/includes/Violations/ViolationStore.php index c270a6f..47145e4 100644 --- a/includes/Violations/ViolationStore.php +++ b/includes/Violations/ViolationStore.php @@ -20,8 +20,10 @@ /** * @param Violation $violation + * + * @return mixed */ - public function insertViolation( Violation $violation ) { + public function saveViolation( Violation $violation ) { $this->getDBConnection(); $updatedAt = wfTimestamp( TS_MW ); @@ -39,11 +41,11 @@ ); if ( !$this->violationExists( $violation ) ) { - $this->insertNewViolation( $accumulator ); + $success = $this->insertNewViolation( $accumulator ); } else { - $this->updateViolation( $accumulator ); + $success = $this->updateViolation( $accumulator ); } - + return $success; } /** @@ -60,7 +62,7 @@ } foreach ( $violations as $violation ) { - $this->insertViolation( $violation ); + $this->saveViolation( $violation ); } } @@ -108,7 +110,7 @@ /** * @param array $accumulator * - * @return mixed + * @return bool */ private function insertNewViolation( array $accumulator ) { $db = $this->getDBConnection(); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 582bb0b..cfaf556 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,6 +19,7 @@ </testsuites> <filter> <whitelist addUncoveredFilesFromWhitelist="true"> + <directory suffix=".php">api</directory> <directory suffix=".php">includes</directory> </whitelist> </filter> diff --git a/tests/phpunit/Api/GetViolationsTest.php b/tests/phpunit/Api/GetViolationsTest.php new file mode 100644 index 0000000..c1fdf92 --- /dev/null +++ b/tests/phpunit/Api/GetViolationsTest.php @@ -0,0 +1,97 @@ +<?php + +namespace WikidataQuality\Tests\Api; + +use Wikibase\Test\Api\WikibaseApiTestCase; + + +/** + * @covers WikidataQuality\Api\GetViolations + * + * @group Database + * @group API + * @group medium + * + * @author BP2014N1 + * @license GNU GPL v2+ + */ +class GetViolationsTest extends WikibaseApiTestCase { + + protected function setup() { + parent::setup(); + $this->tablesUsed[] = VIOLATION_TABLE; + } + + public function addDBData() { + $this->db->delete( + VIOLATION_TABLE, + '*' + ); + + $this->db->insert( + VIOLATION_TABLE, + array( + array( + 'entity_id' => 'Q1', + 'pid' => 'P1', + 'claim_guid' => 'P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_id' => 'P666$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_type_entity_id' => 'Q666', + 'additional_info' => '{"type":"JSON", "mandatory":false}', + 'updated_at' => wfTimestamp( TS_MW, '2014-10-15T15:00:00Z' ), + 'revision_id' => 1234, + 'status' => 'compliance' + ), + array( + 'entity_id' => 'Q1', + 'pid' => 'P2', + 'claim_guid' => 'P2$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_id' => 'P667$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_type_entity_id' => 'Q667', + 'additional_info' => '{"type":"JSON", "mandatory":false}', + 'updated_at' => wfTimestamp( TS_MW, '2014-10-15T15:00:00Z' ), + 'revision_id' => 2345, + 'status' => 'exception' + ), + array( + 'entity_id' => 'Q2', + 'pid' => 'P3', + 'claim_guid' => 'P3$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_id' => 'P668$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_type_entity_id' => 'Q668', + 'additional_info' => '{"type":"JSON", "mandatory":false}', + 'updated_at' => wfTimestamp( TS_MW, '2014-10-15T15:00:00Z' ), + 'revision_id' => 3456, + 'status' => 'unverified' + ) + ) + ); + } + + public function testExecuteWithNoResult() { + $params = array( + 'action' => 'wdqagetviolations', + 'entityId' => 'Q3' + ); + $result = $this->doApiRequest( $params ); + $this->assertNull( $result[0]['results'] ); + } + + public function testExecuteWithEntity() { + $params = array( + 'action' => 'wdqagetviolations', + 'entityId' => 'Q1' + ); + $result = $this->doApiRequest( $params ); + + $claimGuidP1 = 'P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; + $claimGuidP2 = 'P2$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; + $claimGuidP3 = 'P3$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; + + $this->assertArrayHasKey( $claimGuidP1, $result[0]['results'] ); + $this->assertEquals( 9, count( $result[0]['results'][ $claimGuidP1 ] ) ); + + $this->assertArrayHasKey( $claimGuidP2, $result[0]['results'] ); + $this->assertArrayNotHasKey( $claimGuidP3, $result[0]['results'] ); + } +} diff --git a/tests/phpunit/Api/ModifyViolationTest.php b/tests/phpunit/Api/ModifyViolationTest.php new file mode 100644 index 0000000..731d58d --- /dev/null +++ b/tests/phpunit/Api/ModifyViolationTest.php @@ -0,0 +1,97 @@ +<?php + +namespace WikidataQuality\Tests\Api; + +use ApiTestCase; + +/** + * @covers WikidataQuality\Api\ModifyViolation + * + * @group Database + * @group API + * @group medium + * + * @author BP2014N1 + * @license GNU GPL v2+ + */ +class ModifyViolationTest extends ApiTestCase { + + protected function setup() { + parent::setup(); + $this->tablesUsed[] = VIOLATION_TABLE; + } + + public function addDBData() { + $this->db->delete( + VIOLATION_TABLE, + '*' + ); + + $this->db->insert( + VIOLATION_TABLE, + array( + array( + 'entity_id' => 'Q1', + 'pid' => 'P1', + 'claim_guid' => 'P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_id' => 'P666$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_type_entity_id' => 'Q666', + 'additional_info' => '{"type":"JSON", "mandatory":false}', + 'updated_at' => wfTimestamp( TS_MW, '2014-10-15T15:00:00Z' ), + 'revision_id' => 1234, + 'status' => 'compliance' + ), + array( + 'entity_id' => 'Q1', + 'pid' => 'P2', + 'claim_guid' => 'P2$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_id' => 'P667$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraint_type_entity_id' => 'Q667', + 'additional_info' => '{"type":"JSON", "mandatory":false}', + 'updated_at' => wfTimestamp( TS_MW, '2014-10-15T15:00:00Z' ), + 'revision_id' => 2345, + 'status' => 'exception' + ) + ) + ); + } + + public function testInvalidExecute() { + $params = array( + 'action' => 'wdqamodifyviolation', + 'claimGuid' => 'P1$1-2-3-4', + 'constraintId' => 'P667$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'status' => 'exception' + ); + $this->setExpectedException( 'UsageException' ); + $this->doApiRequest( $params ); + } + + public function testExecuteWithNoResult() { + $params = array( + 'action' => 'wdqamodifyviolation', + 'claimGuid' => 'P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraintId' => 'P667$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'status' => 'exception' + ); + $result = $this->doApiRequest( $params ); + $this->assertEquals( 0, $result[0]['success'] ); + } + + public function testModifyStatus() { + $params = array( + 'action' => 'wdqamodifyviolation', + 'claimGuid' => 'P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'constraintId' => 'P666$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'status' => 'unverified' + ); + $result = $this->doApiRequest( $params ); + $actualViolation = $this->db->selectRow( + VIOLATION_TABLE, + 'status', + array( 'claim_guid="P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"', + 'constraint_id="P666$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"' ) ); + $this->assertEquals( 1, $result[0]['success'] ); + $this->assertEquals( 'unverified', $actualViolation->status ); + } +} diff --git a/tests/phpunit/Serializer/ViolationSerializerTest.php b/tests/phpunit/Serializer/ViolationSerializerTest.php new file mode 100644 index 0000000..7e13887 --- /dev/null +++ b/tests/phpunit/Serializer/ViolationSerializerTest.php @@ -0,0 +1,68 @@ +<?php + +namespace WikidataQuality\Tests\Serializer; + +use Wikibase\DataModel\Entity\ItemId; +use Wikibase\DataModel\Entity\PropertyId; +use WikidataQuality\Serializer\ViolationSerializer; +use WikidataQuality\Violations\Violation; + + +/** + * @covers WikidataQuality\Serializer\ViolationSerializer + * + * @group WikidataQuality + * + * @uses WikidataQuality\Violations\Violation + * + * @author BP2014N1 + * @license GNU GPL v2+ + */ +class ViolationSerializerTest extends \MediaWikiTestCase { + + /** + * @var ViolationSerializer + */ + private $violationSerializer; + + protected function setUp() { + parent::setUp(); + $this->violationSerializer = new ViolationSerializer(); + } + + protected function tearDown() { + parent::tearDown(); + unset( $this->violationSerializer ); + } + + public function testSerialize() { + $violation = new Violation( + new ItemId( 'Q1' ), + new PropertyId( 'P1' ), + 'P1$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + 'Q666$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', + new ItemId( 'Q666' ), + 1234, + 'exception', + 'foobar', + '2014-10-15T15:00:00Z' + ); + + $serializedViolation = $this->violationSerializer->serialize( $violation ); + + $this->assertEquals( $violation->getEntityId()->getSerialization(), $serializedViolation['entityId'] ); + $this->assertEquals( $violation->getPropertyId()->getSerialization(), $serializedViolation['propertyId'] ); + $this->assertEquals( $violation->getClaimGuid(), $serializedViolation['claimGuid'] ); + $this->assertEquals( $violation->getConstraintId(), $serializedViolation['constraintId'] ); + $this->assertEquals( $violation->getConstraintTypeEntityId(), $serializedViolation['constraintTypeEntityId'] ); + $this->assertEquals( $violation->getRevisionId(), $serializedViolation['revisionId'] ); + $this->assertEquals( $violation->getStatus(), $serializedViolation['status'] ); + $this->assertEquals( $violation->getAdditionalInfo(), $serializedViolation['additionalInfo'] ); + $this->assertEquals( $violation->getUpdatedAt(), $serializedViolation['updatedAt'] ); + } + + public function testSerializeWithInvalidParam() { + $this->setExpectedException( 'Serializers\Exceptions\UnsupportedObjectException' ); + $this->violationSerializer->serialize( 'foo' ); + } +} diff --git a/tests/phpunit/Violations/ViolationStoreTest.php b/tests/phpunit/Violations/ViolationStoreTest.php index c5d20c9..52271c9 100644 --- a/tests/phpunit/Violations/ViolationStoreTest.php +++ b/tests/phpunit/Violations/ViolationStoreTest.php @@ -80,7 +80,7 @@ $claimGuid = 'P13$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'; $violation = new Violation( new ItemId( 'Q42' ), $pid, $claimGuid, 'P666$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', new ItemId( 'Q666' ), 1234, 'unverified' ); - $violationStore->insertViolation( $violation ); + $violationStore->saveViolation( $violation ); $updated = $this->db->selectRow( VIOLATION_TABLE, array ( 'status', 'updated_at' ), array ( 'claim_guid="P13$aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"', -- To view, visit https://gerrit.wikimedia.org/r/207464 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ia3fa6ee23706d23e1f1f6ce51deea6538f0874b8 Gerrit-PatchSet: 8 Gerrit-Project: mediawiki/extensions/WikidataQuality Gerrit-Branch: master Gerrit-Owner: Tamslo <tamaraslosa...@gmail.com> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: Soeren.oldag <soeren_ol...@freenet.de> Gerrit-Reviewer: Tamslo <tamaraslosa...@gmail.com> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits