jenkins-bot has submitted this change and it was merged. 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. Note: Split into Id9775659 Bug: T55618 Change-Id: I79a08ec87bf76adba869d4b7eb22098b41fbdb72 --- M repo/Wikibase.i18n.alias.php M repo/Wikibase.php M repo/i18n/en.json M repo/i18n/qqq.json M repo/includes/DataTypeSelector.php M repo/includes/WikibaseRepo.php A repo/includes/specials/SpecialListProperties.php A repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php 8 files changed, 455 insertions(+), 13 deletions(-) Approvals: Aude: Looks good to me, but someone else must approve Daniel Kinzler: Looks good to me, approved jenkins-bot: Verified 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 6509446..411b17e 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 8ca930e..14c0b70 100644 --- a/repo/i18n/en.json +++ b/repo/i18n/en.json @@ -242,6 +242,12 @@ "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", + "wikibase-listproperties-legend": "Get a list of properties by data type", + "wikibase-listproperties-datatype": "Data type:", + "wikibase-listproperties-all": "All data types", + "wikibase-listproperties-submit": "Find", + "wikibase-listproperties-invalid-datatype": "\"$1\" is not a valid data type.", "special-entitieswithoutdescription": "Entities without description", "wikibase-entitieswithoutdescription-legend": "Get list of entities without description", "special-entitieswithoutlabel": "Entities without label", diff --git a/repo/i18n/qqq.json b/repo/i18n/qqq.json index fb7ee41..d36e48d 100644 --- a/repo/i18n/qqq.json +++ b/repo/i18n/qqq.json @@ -270,6 +270,12 @@ "wikibase-listdatatypes-intro": "Intro text for the ListDatatypes special page.", "wikibase-history-title-with-label": "The title of the history page with label and prefixed id. Parameters:\n* $1 - the prefixed id, the id is usually in parenthesis\n* $2 - the localized label, it is usually in quotes", "wikibase-history-title-without-label": "The title of the history page with only a prefixed id. Parameters:\n* $1 - the prefixed id, the id is usually in parenthesis", + "special-listproperties": "{{doc-special|ListProperties}}\nThis special page returns a list of properties for a given datatype (such as commonsMedia)", + "wikibase-listproperties-legend": "Legend for the ListProperties special page.", + "wikibase-listproperties-datatype": "Label for the datatype dropdown.\n{{Identical|Data type}}", + "wikibase-listproperties-all": "Option to select that properties with all data types should be shown.", + "wikibase-listproperties-submit": "Label for the button that activates the action.\n{{Identical|Find}}", + "wikibase-listproperties-invalid-datatype": "Error message shown when the data type passed in parameter is invalid.\n\nParameters:\n* $1 - data type", "special-entitieswithoutdescription": "{{doc-special|EntitiesWithoutDescription}}\nThis special page returns a list of entities without description for a given language", "wikibase-entitieswithoutdescription-legend": "Legend of the form that allow to change the language.", "special-entitieswithoutlabel": "{{doc-special|EntitiesWithoutLabel}}\nThis special page returns a list of entities without label for a given language", diff --git a/repo/includes/DataTypeSelector.php b/repo/includes/DataTypeSelector.php index f0ae7fa..5e89dad 100644 --- a/repo/includes/DataTypeSelector.php +++ b/repo/includes/DataTypeSelector.php @@ -1,6 +1,7 @@ <?php namespace Wikibase; + use DataTypes\DataType; use Html; use MWException; @@ -55,10 +56,36 @@ * * @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 = '' ) { + $options = $this->getOptionsHtml( $selectedTypeId ); + + $html = Html::rawElement( + 'select', + array( + 'name' => $name, + 'id' => $id, + 'class' => 'wb-select' + ), + $options + ); + + return $html; + } + + /** + * Builds and returns the html for the options of the DataType selector. + * + * @since 0.5 + * + * @param string $selectedTypeId + * + * @return string + */ + public function getOptionsHtml( $selectedTypeId = '' ) { $dataTypes = array(); foreach ( $this->dataTypes as $dataType ) { @@ -72,20 +99,13 @@ foreach ( $dataTypes as $typeId => $typeLabel ) { $html .= Html::element( 'option', - array( 'value' => $typeId ), + array( + 'value' => $typeId, + 'selected' => $typeId === $selectedTypeId + ), $typeLabel ); } - - $html = Html::rawElement( - 'select', - array( - 'name' => $name, - 'id' => $id, - 'class' => 'wb-select' - ), - $html - ); return $html; } diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php index 54574b7..9f37b90 100644 --- a/repo/includes/WikibaseRepo.php +++ b/repo/includes/WikibaseRepo.php @@ -1002,7 +1002,10 @@ return $this->entityNamespaceLookup; } - private function getEntityIdHtmlLinkFormatterFactory() { + /** + * @return EntityIdHtmlLinkFormatterFactory + */ + public function getEntityIdHtmlLinkFormatterFactory() { return new EntityIdHtmlLinkFormatterFactory( $this->getEntityTitleLookup(), new LanguageNameLookup() diff --git a/repo/includes/specials/SpecialListProperties.php b/repo/includes/specials/SpecialListProperties.php new file mode 100644 index 0000000..2732232 --- /dev/null +++ b/repo/includes/specials/SpecialListProperties.php @@ -0,0 +1,246 @@ +<?php + +namespace Wikibase\Repo\Specials; + +use DataTypes\DataTypeFactory; +use Html; +use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\DataTypeSelector; +use Wikibase\LanguageFallbackChainFactory; +use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookup; +use Wikibase\Lib\Store\TermLookup; +use Wikibase\PropertyInfoStore; +use Wikibase\Repo\WikibaseRepo; +use Wikibase\Store\TermBuffer; +use Wikibase\View\EntityIdFormatterFactory; + +/** + * 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 { + + /** + * Max server side caching time in seconds. + * + * @type integer + */ + const CACHE_TTL_IN_SECONDS = 30; + + /** + * @var DataTypeFactory + */ + private $dataTypeFactory; + + /** + * @var PropertyInfoStore + */ + private $propertyInfoStore; + + /** + * @var LanguageFallbackChainFactory + */ + private $languageFallbackChainFactory; + + /** + * @var TermLookup + */ + private $termLookup; + + /** + * @var TermBuffer + */ + private $termBuffer; + + /** + * @var EntityIdFormatterFactory + */ + private $entityIdFormatterFactory; + + /** + * @var string + */ + private $dataType; + + public function __construct() { + parent::__construct( 'ListProperties' ); + + $this->initServices( + WikibaseRepo::getDefaultInstance()->getDataTypeFactory(), + WikibaseRepo::getDefaultInstance()->getStore()->getPropertyInfoStore(), + WikibaseRepo::getDefaultInstance()->getLanguageFallbackChainFactory(), + WikibaseRepo::getDefaultInstance()->getTermLookup(), + WikibaseRepo::getDefaultInstance()->getTermBuffer(), + WikibaseRepo::getDefaultInstance()->getEntityIdHtmlLinkFormatterFactory() + ); + } + + /** + * Set service objects to use. Unit tests may call this to substitute mock + * services. + */ + public function initServices( + DataTypeFactory $dataTypeFactory, + PropertyInfoStore $propertyInfoStore, + LanguageFallbackChainFactory $languageFallbackChainFactory, + TermLookup $termLookup, + TermBuffer $termBuffer, + EntityIdFormatterFactory $entityIdFormatterFactory + ) { + $this->dataTypeFactory = $dataTypeFactory; + $this->propertyInfoStore = $propertyInfoStore; + $this->languageFallbackChainFactory = $languageFallbackChainFactory; + $this->termLookup = $termLookup; + $this->termBuffer = $termBuffer; + $this->entityIdFormatterFactory = $entityIdFormatterFactory; + } + + /** + * @see SpecialWikibasePage::execute + * + * @since 0.5 + * + * @param string|null $subPage + */ + public function execute( $subPage ) { + parent::execute( $subPage ); + + $output = $this->getOutput(); + $output->setSquidMaxage( static::CACHE_TTL_IN_SECONDS ); + + $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 ( $this->dataType !== '' && !in_array( $this->dataType, $this->dataTypeFactory->getTypeIds() ) ) { + $this->showErrorHTML( $this->msg( 'wikibase-listproperties-invalid-datatype', $this->dataType )->escaped() ); + $this->dataType = null; + } + } + + private function showForm() { + $dataTypeSelect = new DataTypeSelector( + $this->dataTypeFactory->getTypes() + array( 'all' ), + $this->getLanguage()->getCode() + ); + + $this->getOutput()->addHTML( + Html::openElement( + 'form', + array( + 'action' => $this->getPageTitle()->getLocalURL(), + 'name' => 'listproperties', + 'id' => 'wb-listproperties-form' + ) + ) . + 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-datatype' )->text() + ) . ' ' . + Html::rawElement( + 'select', + array( + 'name' => 'datatype', + 'id' => 'wb-listproperties-datatype', + 'class' => 'wb-select' + ), + Html::element( + 'option', + array( + 'value' => '', + 'selected' => $this->dataType === '' + ), + $this->msg( 'wikibase-listproperties-all' )->text() + ) . + $dataTypeSelect->getOptionsHtml( $this->dataType ) + ) . ' ' . + Html::input( + '', + $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() { + $propertyIds = $this->getPropertyIds(); + + if ( empty( $propertyIds ) ) { + $this->getOutput()->addWikiMsg( 'specialpage-empty' ); + return; + } + + $languageFallbackChain = $this->languageFallbackChainFactory->newFromLanguage( + $this->getLanguage(), + LanguageFallbackChainFactory::FALLBACK_SELF + | LanguageFallbackChainFactory::FALLBACK_VARIANTS + | LanguageFallbackChainFactory::FALLBACK_OTHERS + ); + $languages = $languageFallbackChain->getFetchLanguageCodes(); + $labelDescriptionLookup = new LanguageFallbackLabelDescriptionLookup( + $this->termLookup, + $languageFallbackChain + ); + $formatter = $this->entityIdFormatterFactory->getEntityIdFormater( $labelDescriptionLookup ); + + $this->termBuffer->prefetchTerms( $propertyIds, array( 'label' ), $languages ); + + $html = Html::openElement( 'ul' ); + + foreach ( $propertyIds as $propertyId ) { + $html .= Html::rawElement( 'li', array(), $formatter->formatEntityId( $propertyId ) ); + } + + $html .= Html::closeElement( 'ul' ); + $this->getOutput()->addHTML( $html ); + } + + /** + * @return PropertyId[] + */ + private function getPropertyIds() { + if ( $this->dataType === '' ) { + $propertyInfoForDataType = $this->propertyInfoStore->getAllPropertyInfo(); + } else { + $propertyInfoForDataType = $this->propertyInfoStore->getPropertyInfoForDataType( $this->dataType ); + } + + $propertyIds = array(); + + foreach ( $propertyInfoForDataType as $numericId => $info ) { + $propertyIds[] = PropertyId::newFromNumber( $numericId ); + } + + return $propertyIds; + } + +} + diff --git a/repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php b/repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php new file mode 100644 index 0000000..4297813 --- /dev/null +++ b/repo/tests/phpunit/includes/specials/SpecialListPropertiesTest.php @@ -0,0 +1,159 @@ +<?php + +namespace Wikibase\Test; + +use DataTypes\DataType; +use DataTypes\DataTypeFactory; +use Language; +use Title; +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\LanguageFallbackChainFactory; +use Wikibase\Lib\LanguageNameLookup; +use Wikibase\PropertyInfoStore; +use Wikibase\Repo\EntityIdHtmlLinkFormatterFactory; +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' ) + ) ); + } + + private function getDataTypeFactory() { + $dataTypeFactory = DataTypeFactory::newFromTypes( array( + new DataType( 'wikibase-item', 'wikibase-item', array() ), + new DataType( 'string', 'string', array() ), + new DataType( 'quantity', 'quantity', array() ) + ) ); + + return $dataTypeFactory; + } + + private function getPropertyInfoStore() { + $propertyInfoStore = new MockPropertyInfoStore(); + + $propertyInfoStore->setPropertyInfo( + new PropertyId( 'P123' ), + array( PropertyInfoStore::KEY_DATA_TYPE => 'wikibase-item' ) + ); + + $propertyInfoStore->setPropertyInfo( + new PropertyId( 'P456' ), + array( PropertyInfoStore::KEY_DATA_TYPE => 'wikibase-item' ) + ); + + $propertyInfoStore->setPropertyInfo( + new PropertyId( 'P789' ), + array( PropertyInfoStore::KEY_DATA_TYPE => 'string' ) + ); + + return $propertyInfoStore; + } + + private function getTermLookup() { + $termLookup = $this->getMock( 'Wikibase\Lib\Store\TermLookup' ); + $termLookup->expects( $this->any() ) + ->method( 'getLabels' ) + ->will( $this->returnCallback( function( PropertyId $propertyId ) { + return array( 'en' => 'Property with label ' . $propertyId->getSerialization() ); + } ) ); + + return $termLookup; + } + + private function getTermBuffer() { + $termBuffer = $this->getMock( 'Wikibase\Store\TermBuffer' ); + $termBuffer->expects( $this->any() ) + ->method( 'prefetchTerms' ); + + return $termBuffer; + } + + private function getEntityTitleLookup() { + $entityTitleLookup = $this->getMock( 'Wikibase\Lib\Store\EntityTitleLookup' ); + $entityTitleLookup->expects( $this->any() ) + ->method( 'getTitleForId' ) + ->will( $this->returnCallback( + function ( EntityId $id ) { + return Title::makeTitle( NS_MAIN, $id->getSerialization() ); + } + ) ); + + return $entityTitleLookup; + } + + protected function newSpecialPage() { + $specialPage = new SpecialListProperties(); + + $specialPage->initServices( + $this->getDataTypeFactory(), + $this->getPropertyInfoStore(), + new LanguageFallbackChainFactory(), + $this->getTermLookup(), + $this->getTermBuffer(), + new EntityIdHtmlLinkFormatterFactory( $this->getEntityTitleLookup(), new LanguageNameLookup() ) + ); + + return $specialPage; + } + + 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 ); + $this->assertNotContains( 'wikibase-listproperties-invalid-datatype', $output ); + } + + public function testExecute_empty() { + list( $output, ) = $this->executeSpecialPage( 'quantity' ); + + $this->assertContains( 'specialpage-empty', $output ); + } + + public function testExecute_error() { + list( $output, ) = $this->executeSpecialPage( 'test<>' ); + + $this->assertContains( 'wikibase-listproperties-invalid-datatype', $output ); + $this->assertContains( 'test<>', $output ); + } + + public function testExecute_wikibase_item() { + // Use en-gb as language to test language fallback + list( $output, ) = $this->executeSpecialPage( 'wikibase-item', null, 'en-gb' ); + + $this->assertContains( 'Property with label P123', $output ); + $this->assertContains( 'Property with label P456', $output ); + $this->assertNotContains( 'P789', $output ); + } + + public function testExecute_string() { + list( $output, ) = $this->executeSpecialPage( 'string', null, 'en-gb' ); + + $this->assertNotContains( 'P123', $output ); + $this->assertNotContains( 'P456', $output ); + $this->assertContains( 'Property with label P789', $output ); + } + +} + -- To view, visit https://gerrit.wikimedia.org/r/203527 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I79a08ec87bf76adba869d4b7eb22098b41fbdb72 Gerrit-PatchSet: 13 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Bene <benestar.wikime...@gmail.com> Gerrit-Reviewer: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Aude <aude.w...@gmail.com> Gerrit-Reviewer: Bene <benestar.wikime...@gmail.com> Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Hoo man <h...@online.de> Gerrit-Reviewer: Jeroen De Dauw <jeroended...@gmail.com> Gerrit-Reviewer: Legoktm <legoktm.wikipe...@gmail.com> Gerrit-Reviewer: Lydia Pintscher <lydia.pintsc...@wikimedia.de> Gerrit-Reviewer: Siebrand <siebr...@kitano.nl> Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits