Daniel Werner has uploaded a new change for review. https://gerrit.wikimedia.org/r/89117
Change subject: (bug 55593) use jQuery.wikibase.snakview on Special:SimpleQuery ...................................................................... (bug 55593) use jQuery.wikibase.snakview on Special:SimpleQuery Introduces more convenient input method for defining property and value of the simple query. Results into wikibase.repo.fetchedEntities being filled with the entities used in the simple query when the page gets loaded again after clicking the execution button. REQUIRES Ief559fc, Id6ddb49 and I7e10544 in the Wikibase repo. Change-Id: I5c089e8af1f3d1859e0c2f14d1dfcda4df171cd2 --- M Tests/Unit/Setup/ExtensionSetupTest.php A WikibaseQuery.resources.php M src/Wikibase/Query/Setup/ExtensionSetup.php A src/Wikibase/Query/Specials/SimpleQuery.js M src/Wikibase/Query/Specials/SimpleQuery.php 5 files changed, 255 insertions(+), 3 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/WikibaseQuery refs/changes/17/89117/1 diff --git a/Tests/Unit/Setup/ExtensionSetupTest.php b/Tests/Unit/Setup/ExtensionSetupTest.php index b27e985..eefecb7 100644 --- a/Tests/Unit/Setup/ExtensionSetupTest.php +++ b/Tests/Unit/Setup/ExtensionSetupTest.php @@ -23,7 +23,7 @@ parent::setUp(); $this->globalVars = array( 'wgHooks' => array() ); - $this->rootDir = __DIR__; + $this->rootDir = __DIR__ . '/../../..'; $this->dicRegistrant = function() {}; } @@ -67,4 +67,13 @@ $this->assertArrayHasKey( 'WikibaseQueryAliases', $this->globalVars['wgExtensionMessagesFiles'] ); } + public function testResourceLoaderModulesAreRegistered() { + $this->runSetup(); + $this->assertResourceLoaderModulesAreRegistered(); + } + + protected function assertResourceLoaderModulesAreRegistered() { + $this->assertNotEmpty( $this->globalVars['wgResourceModules'] ); + } + } diff --git a/WikibaseQuery.resources.php b/WikibaseQuery.resources.php new file mode 100644 index 0000000..3cdad1f --- /dev/null +++ b/WikibaseQuery.resources.php @@ -0,0 +1,34 @@ +<?php +/** + * Definition of "WikibaseQuery" resourceloader modules. + * + * @since 0.1 + * @licence GNU GPL v2+ + * @author Daniel Werner < daniel.wer...@wikimedia.de > + * + * @codeCoverageIgnoreStart + */ +return call_user_func( function() { + + $moduleTemplate = array( + 'localBasePath' => __DIR__ . '/src/Wikibase/Query', + 'remoteExtPath' => 'WikibaseQuery/src/Wikibase/Query', + ); + + return array( + 'wikibase.query.special.simpleQuery' => $moduleTemplate + array( + 'scripts' => array( + 'Specials/SimpleQuery.js' + ), + 'dependencies' => array( + 'jquery.ui.suggester', + 'jquery.wikibase.snakview', + 'wikibase.serialization.fetchedcontent', + 'wikibase.serialization.entities', + 'mw.ext.wikibase.repo.fetchedEntitiesFromMwConfig', + ), + ), + ); + +} ); +// @codeCoverageIgnoreEnd diff --git a/src/Wikibase/Query/Setup/ExtensionSetup.php b/src/Wikibase/Query/Setup/ExtensionSetup.php index 4e6ace8..cc44db8 100644 --- a/src/Wikibase/Query/Setup/ExtensionSetup.php +++ b/src/Wikibase/Query/Setup/ExtensionSetup.php @@ -35,6 +35,7 @@ $this->registerInternationalization(); $this->registerWebAPI(); $this->registerSpecialPages(); + $this->registerResourceLoaderModules(); } protected function registerDic() { @@ -80,4 +81,11 @@ $this->globalVars['wgSpecialPageGroups']['SimpleQuery'] = 'wikibaserepo'; } + protected function registerResourceLoaderModules() { + $modules = include( $this->rootDirectory . '/WikibaseQuery.resources.php' ); + + foreach( $modules as $moduleName => $module ) { + $this->globalVars['wgResourceModules'][ $moduleName ] = $module; + } + } } diff --git a/src/Wikibase/Query/Specials/SimpleQuery.js b/src/Wikibase/Query/Specials/SimpleQuery.js new file mode 100644 index 0000000..33c11b8 --- /dev/null +++ b/src/Wikibase/Query/Specials/SimpleQuery.js @@ -0,0 +1,98 @@ +/** + * JavaScript for "SimpleQuery" special page. Initializes widgets for better user experience on + * top of the special page. + * + * @since 0.1 + * @licence GNU GPL v2+ + * @author Daniel Werner < daniel.wer...@wikimedia.de > + */ +( function( $ ) { + 'use strict'; + + $( document ).ready( function() { + // TODO: Split this function up a little. + + var $form = $( '#wb-SimpleQuery-form' ); + var $formMembers = $form.children( 'fieldset' ).children(); + var $legend = $formMembers.filter( 'legend' ); + var $button = $formMembers.filter( '.wb-input-button' ); + + var propertyId = $formMembers.find( '#wb-specialsimplequery-property' ).val() || null; + var valueJsonString = $formMembers.find( '#wb-specialsimplequery-valuejson' ).val(); + var dataValue; + + try{ + var valueJson = $.secureEvalJSON( valueJsonString ) || null; + dataValue = valueJson && dataValues.newDataValue( valueJson.type, valueJson.value ); + } catch( error ) { + dataValue = null; + } + + var $snakview = $( '<div/>' ).snakview( { + value: { + property: propertyId, + // Only supports property-value paris right now, basically PropertyValueSnaks, ... + snaktype: wb.PropertyValueSnak.TYPE, + datavalue: dataValue + }, + locked: { + snaktype: true // ...so prevent user from choosing different snak types. + } + } ); + var snakview = $snakview.data( 'snakview' ); + snakview.startEditing(); + + // TODO: (Bug 54021) Hack to prevent from leaving edit mode on Enter/ESC. + snakview.stopEditing = $.noop; + + function setButtonState() { + $button.prop( 'disabled', !snakview.snak() ); + return true; + } + + $formMembers.not( $legend.add( $button ) ).remove(); + $legend.after( $snakview ); + + // TODO: Implement a snakview.focus which focuses the property input or the variation if + // the property has been chosen already. + snakview.$property.find( 'input' ).focus(); + + $snakview.on( 'snakviewchange', setButtonState ); + setButtonState(); + + $form.on( 'submit', function( event ) { + var snakviewValue = snakview.value(); + var $hiddenFields = + buildHiddenFormFields( snakviewValue.property, snakviewValue.datavalue ); + + $form.append( $hiddenFields ); + } ); + } ); + + /** + * Creates the hidden fields to be injected into the form before being sent so the request + * represents the property and value chosen by the user via the snakview. + */ + function buildHiddenFormFields( property, dataValue ) { + var fullValueJson = { + type: dataValue.getType(), + value: dataValue.toJSON() + }; + + var $propertyField = $( '<input>', { + type: 'hidden', + name: 'property', + value: property, + id: 'wb-specialsimplequery-property' + } ); + var $valueField = $( '<input>', { + type: 'hidden', + name: 'valuejson', + value: $.toJSON( fullValueJson ), + id: 'wb-specialsimplequery-valuejson' + } ); + + return $propertyField.add( $valueField ); + } + +}( jQuery ) ); diff --git a/src/Wikibase/Query/Specials/SimpleQuery.php b/src/Wikibase/Query/Specials/SimpleQuery.php index e4fec08..e142ed8 100644 --- a/src/Wikibase/Query/Specials/SimpleQuery.php +++ b/src/Wikibase/Query/Specials/SimpleQuery.php @@ -2,9 +2,18 @@ namespace Wikibase\Query\Specials; -use Wikibase\Query\DIC\ExtensionAccess; -use Wikibase\Lib\Specials\SpecialWikibaseQueryPage; +use Exception; use Html; +use FormatJson; +use Wikibase\Query\DIC\ExtensionAccess; +use Wikibase\Repo\WikibaseRepo; +use Wikibase\Lib\Specials\SpecialWikibaseQueryPage; +use Wikibase\WikiPageEntityLookup; +use Wikibase\EntityContentFactory; +use Wikibase\ReferencedEntitiesFinder; +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Entity\PropertyId; +use Wikibase\Serializers\EntityRevisionSerializer; /** * Special page that allows for querying for all entities with at least one PropertySnak using a @@ -46,8 +55,80 @@ ) ) ); + $this->addFrontendEntityStoreData(); + $output->addModules( 'wikibase.query.special.simpleQuery' ); + $this->showQuery(); return true; + } + + /** + * Adds the information required for frontend widgets to display labels of used entities + * properly. + * + * @since 0.5 + */ + protected function addFrontendEntityStoreData() { + $usedEntities = $this->getReferencedEntitiesFromDataValueJson( $this->valueJson ); + try{ + $usedEntities[] = new PropertyId( $this->propertyId ); + } catch( Exception $e ) {} + + $basicEntityInfo = $this->getUsedEntitiesSerializations( $usedEntities ); + + $this->getOutput()->addJsConfigVars( + 'wbUsedEntities', + FormatJson::encode( $basicEntityInfo ) + ); + } + + /** + * Gets the referenced entities from a serialized value JSON string. + * + * @since 0.5 + * + * @param string $valueJson + * @return EntityId[] + */ + protected function getReferencedEntitiesFromDataValueJson( $valueJson ) { + $dataValueFactory = WikibaseRepo::getDefaultInstance()->getDataValueFactory(); + + $valueJson = json_decode( $valueJson, true ); + if( !is_array( $valueJson ) ) { + return array(); + } + try { + $dataValue = $dataValueFactory->newFromArray( $valueJson ); + } catch( Exception $e ) { + return array(); + } + + $entityFinder = new ReferencedEntitiesFinder(); + return $entityFinder->findDataValueLinks( $dataValue ); + } + + /** + * @param EntityId[] $entityIds + * + * @return array + */ + protected function getUsedEntitiesSerializations( $entityIds ) { + $entityInfo = array(); + + $entityTitleLookup = WikibaseRepo::getDefaultInstance()->getEntityContentFactory(); + $entityRevisionLookup = new WikiPageEntityLookup( false, false ); + + $langCode = $this->getLanguage()->getCode(); + $serializer = EntityRevisionSerializer::newForFrontendStore( $entityTitleLookup, $langCode ); + + foreach( $entityIds as $entityId ) { + $entityRevision = $entityRevisionLookup->getEntityRevision( $entityId ); + if( $entityRevision !== null ) { + $serialization = $serializer->getSerialized( $entityRevision ); + $entityInfo[ $entityId->getPrefixedId() ] = $serialization; + } + } + return $entityInfo; } /** @@ -69,12 +150,34 @@ // class had to be refactored and more specific exception implementations would be // useful as well. return array(); + } catch( \Exception $e ) { + // Catch whatever could possibly go wrong. + return array(); } return $entityIds; } /** + * @param EntityId[] $ids + * + * @return (EntityRevision|null)[] A map from IDs to the respective entities. + * If no entity is found for a given ID, the respective entry in the map will be null. + */ + private function getEntityRevisions( array $ids ) { + $entityRevisionLookup = new WikiPageEntityLookup( false, false ); + $revisions = array(); + + foreach ( $ids as $id ) { + $key = $id->getPrefixedId(); + $revision = $entityRevisionLookup->getEntityRevision( $id ); + $revisions[$key] = $revision; + } + + return $revisions; + } + + /** * Creates HTML for a search form suitable for the special page's purpose. * * @since 0.1 -- To view, visit https://gerrit.wikimedia.org/r/89117 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I5c089e8af1f3d1859e0c2f14d1dfcda4df171cd2 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/WikibaseQuery Gerrit-Branch: master Gerrit-Owner: Daniel Werner <daniel.wer...@wikimedia.de> Gerrit-Reviewer: Addshore <addshorew...@gmail.com> Gerrit-Reviewer: Aude <aude.w...@gmail.com> Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de> Gerrit-Reviewer: Daniel Werner <daniel.wer...@wikimedia.de> Gerrit-Reviewer: Henning Snater <henning.sna...@wikimedia.de> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits