Bene has uploaded a new change for review. https://gerrit.wikimedia.org/r/203527
Change subject: Create a new special page to list properties by data type ...................................................................... Create a new special page to list properties by data type The new special page is called Special:ListProperties. Queries can be done via the form or in the format Special:ListProperties/dataType, eg Special:ListProperties/url. Bug: T55618 Change-Id: I79a08ec87bf76adba869d4b7eb22098b41fbdb72 --- M lib/includes/store/CachingPropertyInfoStore.php M lib/includes/store/DummyPropertyInfoStore.php M lib/includes/store/PropertyInfoStore.php M lib/includes/store/sql/PropertyInfoTable.php M lib/tests/phpunit/store/MockPropertyInfoStore.php M lib/tests/phpunit/store/PropertyInfoStoreTestHelper.php M lib/tests/phpunit/store/Sql/PropertyInfoTableTest.php M repo/Wikibase.i18n.alias.php M repo/Wikibase.php M repo/i18n/en.json M repo/includes/DataTypeSelector.php A repo/includes/specials/SpecialListProperties.php M repo/includes/store/sql/SqlStore.php A repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php 14 files changed, 394 insertions(+), 39 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase refs/changes/27/203527/1 diff --git a/lib/includes/store/CachingPropertyInfoStore.php b/lib/includes/store/CachingPropertyInfoStore.php index 79f9e60..9023c68 100644 --- a/lib/includes/store/CachingPropertyInfoStore.php +++ b/lib/includes/store/CachingPropertyInfoStore.php @@ -90,6 +90,26 @@ } /** + * @see PropertyInfoStore::getPropertyInfoForDataType + * + * @param string $dataType + * + * @return array[] + */ + public function getPropertyInfoForDataType( $dataType ) { + $propertyInfo = $this->getAllPropertyInfo(); + $propertyInfoForDataType = array(); + + foreach ( $propertyInfo as $id => $info ) { + if ( $info[PropertyInfoStore::KEY_DATA_TYPE] === $dataType ) { + $propertyInfoForDataType[$id] = $info; + } + } + + return $propertyInfoForDataType; + } + + /** * @see PropertyInfoStore::getAllPropertyInfo * * @return array[] diff --git a/lib/includes/store/DummyPropertyInfoStore.php b/lib/includes/store/DummyPropertyInfoStore.php index 5da4327..09c6504 100644 --- a/lib/includes/store/DummyPropertyInfoStore.php +++ b/lib/includes/store/DummyPropertyInfoStore.php @@ -27,6 +27,17 @@ } /** + * @see PropertyInfoStore::getPropertyInfoForDataType + * + * @param string $dataType + * + * @return array[] + */ + public function getPropertyInfoForDataType( $dataType ) { + return array(); + } + + /** * @see PropertyInfoStore::getAllPropertyInfo * * @return array[] diff --git a/lib/includes/store/PropertyInfoStore.php b/lib/includes/store/PropertyInfoStore.php index 8c9d01d..5df532c 100644 --- a/lib/includes/store/PropertyInfoStore.php +++ b/lib/includes/store/PropertyInfoStore.php @@ -9,6 +9,7 @@ /** * @license GPL 2+ * @author Daniel Kinzler + * @author Bene* < benestar.wikime...@gmail.com > */ interface PropertyInfoStore { @@ -33,6 +34,21 @@ public function getPropertyInfo( PropertyId $propertyId ); /** + * Returns the property info for all properties with the given data type. + * + * @note: There is no guarantee that an info array is returned for all existing properties. + * Also, it is not guaranteed that the ionfo arrays will contain all well known fields. + * + * @param string $dataType + * + * @return array[] An associative array mapping property IDs to info arrays. + * + * @throws StorageException + * @throws DBError + */ + public function getPropertyInfoForDataType( $dataType ); + + /** * Returns the property info for all properties. * The caller is responsible for avoiding calling this if there are too many properties. * diff --git a/lib/includes/store/sql/PropertyInfoTable.php b/lib/includes/store/sql/PropertyInfoTable.php index 02e8047..c90f4f2 100644 --- a/lib/includes/store/sql/PropertyInfoTable.php +++ b/lib/includes/store/sql/PropertyInfoTable.php @@ -5,6 +5,7 @@ use DBAccessBase; use DBError; use InvalidArgumentException; +use ResultWrapper; use Wikibase\DataModel\Entity\PropertyId; /** @@ -14,6 +15,7 @@ * * @license GPL 2+ * @author Daniel Kinzler + * @author Bene* < benestar.wikime...@gmail.com > */ class PropertyInfoTable extends DBAccessBase implements PropertyInfoStore { @@ -42,9 +44,56 @@ throw new InvalidArgumentException( '$wiki must be a string or false.' ); } + parent::__construct( $wiki ); $this->tableName = 'wb_property_info'; $this->isReadonly = $isReadonly; - $this->wiki = $wiki; + } + + /** + * Decodes an info blob. + * + * @param string|null|bool $blob + * + * @return array|null The decoded blob as an associative array, or null if the blob + * could not be decoded. + */ + private function decodeInfo( $blob ) { + if ( $blob === false || $blob === null ) { + return null; + } + + $info = json_decode( $blob, true ); + + if ( !is_array( $info ) ) { + $info = null; + } + + return $info; + } + + /** + * Decodes a result with info blobs. + * + * @param ResultWrapper $res + * + * @return array[] The array of decoded blobs + */ + private function decodeInfoArray( ResultWrapper $res ) { + $infos = array(); + + while ( $row = $res->fetchObject() ) { + $info = $this->decodeInfo( $row->pi_info ); + + if ( $info === null ) { + wfLogWarning( "failed to decode property info blob for property " + . $row->pi_property_id . ": " . $row->pi_info ); + continue; + } + + $infos[$row->pi_property_id] = $info; + } + + return $infos; } /** @@ -83,29 +132,33 @@ } /** - * Decodes an info blob. + * @see PropertyDataTypeLookup::getPropertyInfoForDataType * - * @param string|null|bool $blob + * @param string $dataType * - * @return array|null The decoded blob as an associative array, or null if the blob - * could not be decoded. + * @return array[] + * + * @throws DBError */ - protected function decodeInfo( $blob ) { - if ( $blob === false || $blob === null ) { - return null; - } + public function getPropertyInfoForDataType( $dataType ) { + $dbw = $this->getConnection( DB_SLAVE ); - $info = json_decode( $blob, true ); + $res = $dbw->select( + $this->tableName, + array( 'pi_property_id', 'pi_info' ), + array( 'pi_type' => $dataType ), + __METHOD__ + ); - if ( !is_array( $info ) ) { - $info = null; - } + $infos = $this->decodeInfoArray( $res ); - return $info; + $this->releaseConnection( $dbw ); + + return $infos; } /** - * @see PropertyInfoStore::getAllPropertyInfo + * @see PropertyInfoStore::getAllPropertyInfo * * @return array[] * @@ -121,19 +174,7 @@ __METHOD__ ); - $infos = array(); - - while ( $row = $res->fetchObject() ) { - $info = $this->decodeInfo( $row->pi_info ); - - if ( $info === null ) { - wfLogWarning( "failed to decode property info blob for property " - . $row->pi_property_id . ": " . $row->pi_info ); - continue; - } - - $infos[$row->pi_property_id] = $info; - } + $infos = $this->decodeInfoArray( $res ); $this->releaseConnection( $dbw ); diff --git a/lib/tests/phpunit/store/MockPropertyInfoStore.php b/lib/tests/phpunit/store/MockPropertyInfoStore.php index 18d59bb..140243b 100644 --- a/lib/tests/phpunit/store/MockPropertyInfoStore.php +++ b/lib/tests/phpunit/store/MockPropertyInfoStore.php @@ -48,7 +48,27 @@ } /** - * @see PropertyInfoStore::getAllPropertyInfo + * @see PropertyInfoStore::getPropertyInfoForDataType + * + * @param string $dataType + * + * @return array[] + */ + public function getPropertyInfoForDataType( $dataType ) { + $propertyInfo = $this->getAllPropertyInfo(); + $propertyInfoForDataType = array(); + + foreach ( $propertyInfo as $id => $info ) { + if ( $info[PropertyInfoStore::KEY_DATA_TYPE] === $dataType ) { + $propertyInfoForDataType[$id] = $info; + } + } + + return $propertyInfoForDataType; + } + + /** + * @see PropertyInfoStore::getAllPropertyInfo * * @return array[] */ diff --git a/lib/tests/phpunit/store/PropertyInfoStoreTestHelper.php b/lib/tests/phpunit/store/PropertyInfoStoreTestHelper.php index 44c59a9..da2a08e 100644 --- a/lib/tests/phpunit/store/PropertyInfoStoreTestHelper.php +++ b/lib/tests/phpunit/store/PropertyInfoStoreTestHelper.php @@ -85,6 +85,28 @@ $this->test->assertEquals( $info23, $table->getPropertyInfo( $p23 ), "should return what was set" ); } + public function testGetPropertyInfoForDataType() { + $table = $this->newPropertyInfoStore(); + $p23 = new PropertyId( 'P23' ); + $p42 = new PropertyId( 'P42' ); + $info23 = array( PropertyInfoStore::KEY_DATA_TYPE => 'string' ); + $info42 = array( PropertyInfoStore::KEY_DATA_TYPE => 'commonsMedia', 'foo' => 'bar' ); + + $this->test->assertCount( 0, $table->getPropertyInfoForDataType( 'commonsMedia' ), "should initially be empty" ); + + $table->setPropertyInfo( $p23, $info23 ); + $this->test->assertCount( 0, $table->getPropertyInfoForDataType( 'commonsMedia' ), "after adding one property" ); + + $table->setPropertyInfo( $p42, $info42 ); + $this->test->assertCount( 1, $table->getPropertyInfoForDataType( 'commonsMedia' ), "after adding the second property" ); + + $table->removePropertyInfo( $p23 ); + $this->test->assertCount( 1, $table->getPropertyInfoForDataType( 'commonsMedia' ), "after removing one property" ); + + $table->removePropertyInfo( $p42 ); + $this->test->assertCount( 0, $table->getPropertyInfoForDataType( 'commonsMedia' ), "after removing the second property" ); + } + public function testGetAllPropertyInfo() { $table = $this->newPropertyInfoStore(); $p23 = new PropertyId( 'P23' ); diff --git a/lib/tests/phpunit/store/Sql/PropertyInfoTableTest.php b/lib/tests/phpunit/store/Sql/PropertyInfoTableTest.php index 4662873..2a03240 100644 --- a/lib/tests/phpunit/store/Sql/PropertyInfoTableTest.php +++ b/lib/tests/phpunit/store/Sql/PropertyInfoTableTest.php @@ -64,6 +64,10 @@ $this->helper->testGetPropertyInfo(); } + public function testGetPropertyInfoForDataType() { + $this->helper->testGetPropertyInfoForDataType(); + } + public function testGetAllPropertyInfo() { $this->helper->testGetAllPropertyInfo(); } diff --git a/repo/Wikibase.i18n.alias.php b/repo/Wikibase.i18n.alias.php index a449b40..be8a5bc 100644 --- a/repo/Wikibase.i18n.alias.php +++ b/repo/Wikibase.i18n.alias.php @@ -22,6 +22,7 @@ 'GoToLinkedPage' => array( 'GoToLinkedPage' ), 'ItemDisambiguation' => array( 'ItemDisambiguation' ), 'ListDatatypes' => array( 'ListDatatypes' ), + 'ListProperties' => array( 'ListProperties' ), 'SetLabel' => array( 'SetLabel' ), 'SetDescription' => array( 'SetDescription' ), 'SetAliases' => array( 'SetAliases' ), diff --git a/repo/Wikibase.php b/repo/Wikibase.php index 5a73d8e..eb89cda 100644 --- a/repo/Wikibase.php +++ b/repo/Wikibase.php @@ -180,6 +180,7 @@ $wgSpecialPages['EntitiesWithoutLabel'] = array( 'Wikibase\Repo\Specials\SpecialEntitiesWithoutPageFactory', 'newSpecialEntitiesWithoutLabel' ); $wgSpecialPages['EntitiesWithoutDescription'] = array( 'Wikibase\Repo\Specials\SpecialEntitiesWithoutPageFactory', 'newSpecialEntitiesWithoutDescription' ); $wgSpecialPages['ListDatatypes'] = 'Wikibase\Repo\Specials\SpecialListDatatypes'; + $wgSpecialPages['ListProperties'] = 'Wikibase\Repo\Specials\SpecialListProperties'; $wgSpecialPages['DispatchStats'] = 'Wikibase\Repo\Specials\SpecialDispatchStats'; $wgSpecialPages['EntityData'] = 'Wikibase\Repo\Specials\SpecialEntityData'; $wgSpecialPages['MyLanguageFallbackChain'] = 'Wikibase\Repo\Specials\SpecialMyLanguageFallbackChain'; diff --git a/repo/i18n/en.json b/repo/i18n/en.json index 71e8bd6..461f15a 100644 --- a/repo/i18n/en.json +++ b/repo/i18n/en.json @@ -245,6 +245,10 @@ "wikibase-listdatatypes-intro": "This is a list of all data types available on this installation:", "wikibase-history-title-with-label": "Revision history of \"$2\" ($1)", "wikibase-history-title-without-label": "Revision history of ($1)", + "special-listproperties": "List of properties by data type", + "wikibase-listproperties-legend": "Get list of properties by data type", + "wikibase-listproperties-label-datatype": "Data type:", + "wikibase-listproperties-submit": "Find", "special-entitieswithoutdescription": "Entities without description", "wikibase-entitieswithoutdescription-legend": "Get list of entities without description", "special-entitieswithoutlabel": "Entities without label", diff --git a/repo/includes/DataTypeSelector.php b/repo/includes/DataTypeSelector.php index f0ae7fa..9fd9a25 100644 --- a/repo/includes/DataTypeSelector.php +++ b/repo/includes/DataTypeSelector.php @@ -55,10 +55,11 @@ * * @param string $id * @param string $name + * @param string $selectedTypeId * * @return string */ - public function getHtml( $id = 'datatype', $name = 'datatype' ) { + public function getHtml( $id = 'datatype', $name = 'datatype', $selectedTypeId = '' ) { $dataTypes = array(); foreach ( $this->dataTypes as $dataType ) { @@ -70,9 +71,15 @@ $html = ''; foreach ( $dataTypes as $typeId => $typeLabel ) { + $attrs = array( 'value' => $typeId ); + + if ( $typeId === $selectedTypeId ) { + $attrs['selected'] = true; + } + $html .= Html::element( 'option', - array( 'value' => $typeId ), + $attrs, $typeLabel ); } diff --git a/repo/includes/specials/SpecialListProperties.php b/repo/includes/specials/SpecialListProperties.php new file mode 100644 index 0000000..00e56b6 --- /dev/null +++ b/repo/includes/specials/SpecialListProperties.php @@ -0,0 +1,159 @@ +<?php + +namespace Wikibase\Repo\Specials; + +use DataTypes\DataTypeFactory; +use Html; +use Linker; +use MWException; +use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\DataTypeSelector; +use Wikibase\PropertyInfoStore; +use Wikibase\Repo\WikibaseRepo; + +/** + * Special page to list properties by data type + * + * @since 0.5 + * @licence GNU GPL v2+ + * @author Bene* < benestar.wikime...@gmail.com > + */ +class SpecialListProperties extends SpecialWikibasePage { + + /** + * @var DataTypeFactory + */ + private $dataTypeFactory; + + /** + * @var PropertyInfoStore + */ + private $propertyInfoStore; + + /** + * @var string + */ + private $dataType; + + /** + * @since 0.5 + */ + public function __construct() { + parent::__construct( 'ListProperties' ); + + $this->dataTypeFactory = WikibaseRepo::getDefaultInstance()->getDataTypeFactory(); + $this->propertyInfoStore = WikibaseRepo::getDefaultInstance()->getStore()->getPropertyInfoStore(); + } + + /** + * @see SpecialWikibasePage::execute + * + * @since 0.5 + * + * @param string|null $subPage + */ + public function execute( $subPage ) { + parent::execute( $subPage ); + + $this->prepareArguments( $subPage ); + $this->showForm(); + + if ( $this->dataType !== null ) { + $this->showQuery(); + } + } + + private function prepareArguments( $subPage ) { + $request = $this->getRequest(); + + $this->dataType = $request->getText( 'datatype', $subPage ); + if ( !in_array( $this->dataType, $this->dataTypeFactory->getTypeIds() ) ) { + $this->dataType = null; + } + } + + private function showForm() { + $dataTypeSelect = new DataTypeSelector( + $this->dataTypeFactory->getTypes(), + $this->getLanguage()->getCode() + ); + + $this->getOutput()->addHTML( + Html::openElement( + 'form', + array( + 'action' => $this->getPageTitle()->getLocalURL(), + 'name' => 'listproperties', + 'id' => 'wb-listproperties-form' + ) + ) . + Html::input ( + 'title', + $this->getPageTitle()->getPrefixedText(), + 'hidden', + array() + ) . + Html::openElement( 'fieldset' ) . + Html::element( + 'legend', + array(), + $this->msg( 'wikibase-listproperties-legend' )->text() + ) . + Html::openElement( 'p' ) . + Html::element( + 'label', + array( + 'for' => 'wb-listproperties-datatype' + ), + $this->msg( 'wikibase-listproperties-label-datatype' )->text() + ) . ' ' . + $dataTypeSelect->getHTML( 'wb-listproperties-datatype', 'datatype', $this->dataType ) . ' ' . + Html::input( + 'submit', + $this->msg( 'wikibase-listproperties-submit' )->text(), + 'submit', + array( + 'id' => 'wikibase-listproperties-submit', + 'class' => 'wb-input-button' + ) + ) . + Html::closeElement( 'p' ) . + Html::closeElement( 'fieldset' ) . + Html::closeElement( 'form' ) + ); + } + + private function showQuery() { + $propertyInfoForDataType = $this->propertyInfoStore->getPropertyInfoForDataType( $this->dataType ); + + if ( empty( $propertyInfoForDataType ) ) { + $this->getOutput()->addWikiMsg( 'specialpage-empty' ); + return; + } + + $html = Html::openElement( 'ul' ); + + foreach ( $propertyInfoForDataType as $numericId => $info ) { + $row = $this->formatRow( PropertyId::newFromNumber( $numericId ) ); + $html .= Html::rawElement( 'li', array(), $row ); + } + + $html .= Html::closeElement( 'ul' ); + $this->getOutput()->addHTML( $html ); + } + + /** + * @see SpecialWikibaseQueryPage::formatRow + */ + private function formatRow( PropertyId $propertyId ) { + try { + $title = WikibaseRepo::getDefaultInstance()->getEntityContentFactory()->getTitleForId( $propertyId ); + return Linker::linkKnown( $title ); + } catch ( MWException $e ) { + wfWarn( "Error formatting result row: " . $e->getMessage() ); + return false; + } + } + +} + diff --git a/repo/includes/store/sql/SqlStore.php b/repo/includes/store/sql/SqlStore.php index 395cbf0..41a820c 100644 --- a/repo/includes/store/sql/SqlStore.php +++ b/repo/includes/store/sql/SqlStore.php @@ -81,9 +81,9 @@ private $entityInfoBuilderFactory = null; /** - * @var PropertyInfoTable|null + * @var PropertyInfoStore|null */ - private $propertyInfoTable = null; + private $propertyInfoStore = null; /** * @var ChangesTable|null @@ -657,19 +657,19 @@ * @return PropertyInfoStore */ public function getPropertyInfoStore() { - if ( !$this->propertyInfoTable ) { - $this->propertyInfoTable = $this->newPropertyInfoTable(); + if ( !$this->propertyInfoStore ) { + $this->propertyInfoStore = $this->newPropertyInfoStore(); } - return $this->propertyInfoTable; + return $this->propertyInfoStore; } /** - * Creates a new PropertyInfoTable + * Creates a new PropertyInfoStore * - * @return PropertyInfoTable + * @return PropertyInfoStore */ - private function newPropertyInfoTable() { + private function newPropertyInfoStore() { $usePropertyInfoTable = WikibaseRepo::getDefaultInstance()-> getSettings()->getSetting( 'usePropertyInfoTable' ); diff --git a/repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php b/repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php new file mode 100644 index 0000000..07fc67f --- /dev/null +++ b/repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php @@ -0,0 +1,49 @@ +<?php + +namespace Wikibase\Test; + +use Language; +use Wikibase\Repo\Specials\SpecialListProperties; +use Wikibase\Test\SpecialPageTestBase; + +/** + * @covers Wikibase\Repo\Specials\SpecialListProperties + * + * @group Wikibase + * @group WikibaseRepo + * @group SpecialPage + * @group WikibaseSpecialPage + * + * @licence GNU GPL v2+ + * @author Bene* < benestar.wikime...@gmail.com > + */ +class SpecialListPropertiesTest extends SpecialPageTestBase { + + protected function setUp() { + parent::setUp(); + + $this->setMwGlobals( array( + 'wgContLang' => Language::factory( 'qqx' ) + ) ); + } + + protected function newSpecialPage() { + return new SpecialListProperties(); + } + + public function testExecute() { + // This also tests that there is no fatal error, that the restriction handling is working + // and doesn't block. That is, the default should let the user execute the page. + list( $output, ) = $this->executeSpecialPage( '' ); + + $this->assertInternalType( 'string', $output ); + $this->assertContains( 'wikibase-listproperties-summary', $output ); + $this->assertContains( 'wikibase-listproperties-legend', $output ); + + list( $output, ) = $this->executeSpecialPage( 'url' ); + + $this->assertContains( 'specialpage-empty', $output ); + } + +} + -- To view, visit https://gerrit.wikimedia.org/r/203527 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I79a08ec87bf76adba869d4b7eb22098b41fbdb72 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Bene <benestar.wikime...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits