├── phpstan.php ├── .gitignore ├── .github ├── youtube.png ├── dependabot.yml └── FUNDING.yml ├── tests ├── bootstrap.php ├── Unit │ ├── SMW │ │ ├── SemanticPropertyTest.php │ │ └── SemanticEntityTest.php │ ├── ConfigurationTest.php │ └── Translation │ │ ├── PropertyTypeTranslatorTest.php │ │ ├── PropertyTranslatorTest.php │ │ ├── MonoTextTranslatorTest.php │ │ ├── StatementTranslatorTest.php │ │ ├── DataValueTranslatorTest.php │ │ ├── ContainerValueTranslatorTest.php │ │ └── ItemTranslatorTest.php ├── SWBTestCase.php └── TestFactory.php ├── i18n ├── en.json ├── de.json └── qqq.json ├── phpstan.neon ├── .travis.yml ├── Makefile ├── src ├── Wikibase │ └── TypedDataValue.php ├── Configuration.php ├── Translation │ ├── TermTranslator.php │ ├── MonoTextTranslator.php │ ├── ItemTranslator.php │ ├── PropertyTranslator.php │ ├── PropertyTypeTranslator.php │ ├── StatementTranslator.php │ ├── StatementListTranslator.php │ ├── FingerprintTranslator.php │ ├── UserDefinedProperties.php │ ├── TranslatorFactory.php │ ├── FixedProperties.php │ ├── ContainerValueTranslator.php │ └── DataValueTranslator.php ├── SMW │ ├── SemanticProperty.php │ └── SemanticEntity.php ├── EntryPoints │ └── HookHandlers.php ├── SemanticDataUpdate.php └── SemanticWikibase.php ├── phpunit.xml.dist ├── psalm-mw.xml ├── psalm-full.xml ├── composer.json ├── extension.json ├── .travis.install.sh ├── README.md └── COPYING /phpstan.php: -------------------------------------------------------------------------------- 1 | value = $value; 16 | $this->propertyType = $propertyType; 17 | } 18 | 19 | public function getValue(): DataValue { 20 | return $this->value; 21 | } 22 | 23 | public function getPropertyType(): string { 24 | return $this->propertyType; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: JeroenDeDauw 4 | patreon: 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /src/Configuration.php: -------------------------------------------------------------------------------- 1 | mwGlobals = $mwGlobals; 13 | } 14 | 15 | public static function newFromGlobals( array $mwGlobals ): self { 16 | return new self( $mwGlobals ); 17 | } 18 | 19 | public function getLanguageCode(): string { 20 | if ( $this->mwGlobals['wgSemanticWikibaseLanguage'] === '' ) { 21 | return $this->mwGlobals['wgLanguageCode']; 22 | } 23 | 24 | return $this->mwGlobals['wgSemanticWikibaseLanguage']; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | tests 16 | 17 | 18 | 19 | 20 | src 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/Translation/TermTranslator.php: -------------------------------------------------------------------------------- 1 | monoTextTranslator = $monoTextTranslator; 18 | $this->subject = $subject; 19 | } 20 | 21 | public function translateTerm( Term $term ) { 22 | return $this->monoTextTranslator->valueToDataItem( 23 | new MonolingualTextValue( $term->getLanguageCode(), $term->getText() ), 24 | $this->subject 25 | ); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /psalm-mw.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/Unit/SMW/SemanticPropertyTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 19 | [ 'Kittens' ], 20 | $property->getAliases() 21 | ); 22 | } 23 | 24 | public function testGetAliasesReturnsEmptyArrayWhenThereAreNone() { 25 | $property = new SemanticProperty( 'id', '_txt', 'Cats' ); 26 | 27 | $this->assertSame( 28 | [], 29 | $property->getAliases() 30 | ); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/SMW/SemanticProperty.php: -------------------------------------------------------------------------------- 1 | id = $id; 16 | $this->type = $dvType; 17 | $this->label = $label; 18 | $this->alias = $alias; 19 | } 20 | 21 | public function getId(): string { 22 | return $this->id; 23 | } 24 | 25 | public function getType(): string { 26 | return $this->type; 27 | } 28 | 29 | public function getLabel(): string { 30 | return $this->label; 31 | } 32 | 33 | /** 34 | * @return string[] 35 | */ 36 | public function getAliases(): array { 37 | return $this->alias === null ? [] : [ $this->alias ]; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/Unit/ConfigurationTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 17 | 'nl', 18 | Configuration::newFromGlobals( [ 19 | 'wgLanguageCode' => 'nl', 20 | 'wgSemanticWikibaseLanguage' => '', 21 | ] )->getLanguageCode() 22 | ); 23 | } 24 | 25 | public function testWhenLanguageIsConfiguredItIsUsed() { 26 | $this->assertSame( 27 | 'de', 28 | Configuration::newFromGlobals( [ 29 | 'wgLanguageCode' => 'nl', 30 | 'wgSemanticWikibaseLanguage' => 'de', 31 | ] )->getLanguageCode() 32 | ); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /psalm-full.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/Translation/MonoTextTranslator.php: -------------------------------------------------------------------------------- 1 | dataValueFactory = $dataValueFactory; 18 | } 19 | 20 | public function valueToDataItem( MonolingualTextValue $textValue, DIWikiPage $subject ): SMWDIContainer { 21 | $dataItem = $this->dataValueFactory->newDataValueByType( 22 | \SMW\DataValues\MonolingualTextValue::TYPE_ID, 23 | $textValue->getText() . '@' . $textValue->getLanguageCode(), 24 | false, 25 | null, 26 | $subject 27 | )->getDataItem(); 28 | 29 | if ( $dataItem instanceof SMWDIContainer ) { 30 | return $dataItem; 31 | } 32 | 33 | throw new \RuntimeException( 'dataValueFactory did not return a DV with SMWDIContainer' ); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /tests/Unit/Translation/PropertyTypeTranslatorTest.php: -------------------------------------------------------------------------------- 1 | assertSame( '_txt', $this->translate( 'string' ) ); 18 | $this->assertSame( '_txt', $this->translate( 'commonsMedia' ) ); 19 | $this->assertSame( '_mlt_rec', $this->translate( 'monolingualtext' ) ); 20 | $this->assertSame( '_wpg', $this->translate( 'wikibase-item' ) ); 21 | 22 | if ( class_exists( CoordinateValue::class ) ) { 23 | $this->assertSame( '_geo', $this->translate( 'globe-coordinate' ) ); 24 | } 25 | } 26 | 27 | private function translate( string $wikibasePropertyType ): string { 28 | return ( new PropertyTypeTranslator() )->translate( $wikibasePropertyType ); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "professional-wiki/semantic-wikibase", 3 | "type": "mediawiki-extension", 4 | "description": "MediaWiki extension that makes Wikibase data available in Semantic MediaWiki", 5 | "keywords": [ 6 | "MediaWiki", 7 | "Semantic MediaWiki", 8 | "SMW", 9 | "wiki", 10 | "wikibase", 11 | "structured data", 12 | "semantic web", 13 | "wikidata" 14 | ], 15 | "homepage": "https://professional.wiki/en/extension/semantic-wikibase", 16 | "license": "GPL-2.0-or-later", 17 | "authors": [ 18 | { 19 | "name": "Professional.Wiki", 20 | "email": "info@professional.wiki", 21 | "homepage": "https://professional.wiki", 22 | "role": "Creator" 23 | }, 24 | { 25 | "name": "Jeroen De Dauw", 26 | "email": "jeroendedauw@gmail.com", 27 | "homepage": "https://www.entropywins.wtf/mediawiki", 28 | "role": "Creator and lead developer" 29 | } 30 | ], 31 | "support": { 32 | "issues": "https://github.com/ProfessionalWiki/SemanticWikibase/issues", 33 | "source": "https://github.com/ProfessionalWiki/SemanticWikibase" 34 | }, 35 | "require": { 36 | "php": ">=7.4", 37 | "composer/installers": "^2|^1.0.1" 38 | }, 39 | "require-dev": { 40 | "vimeo/psalm": "dev-master", 41 | "phpstan/phpstan": "dev-master" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/EntryPoints/HookHandlers.php: -------------------------------------------------------------------------------- 1 | registerProperties( $propertyRegistry ); 22 | } 23 | 24 | public static function onSmwAddCustomFixedPropertyTables( array &$customFixedProperties, array &$fixedPropertyTablePrefix ): void { 25 | SemanticWikibase::getGlobalInstance()->getFixedProperties() 26 | ->registerFixedTables( $customFixedProperties, $fixedPropertyTablePrefix ); 27 | } 28 | 29 | public static function onSmwUpdateDataBefore( Store $store, SemanticData $semanticData ): void { 30 | SemanticWikibase::getGlobalInstance()->getSemanticDataUpdate() 31 | ->run( $semanticData ); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/SWBTestCase.php: -------------------------------------------------------------------------------- 1 | assertContainsOnlyInstancesOf( SMWDIContainer::class, $actual ); 23 | $this->assertSameSize( $expected, $actual ); 24 | 25 | foreach ( $actual as $index => $textContainer ) { 26 | if ( $textContainer instanceof SMWDIContainer ) { 27 | $this->assertSame( 28 | $expected[$index]->getText(), 29 | $textContainer->getSemanticData()->getPropertyValues( new DIProperty( '_TEXT' ) )[0]->getSerialization() 30 | ); 31 | $this->assertSame( 32 | $expected[$index]->getLanguageCode(), 33 | $textContainer->getSemanticData()->getPropertyValues( new DIProperty( '_LCODE' ) )[0]->getSerialization() 34 | ); 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/Translation/ItemTranslator.php: -------------------------------------------------------------------------------- 1 | fingerprintTranslator = $fingerprintTranslator; 18 | $this->statementListTranslator = $statementListTranslator; 19 | } 20 | 21 | public function translateItem( Item $item ): SemanticEntity { 22 | $semanticEntity = new SemanticEntity(); 23 | 24 | if ( $item->getId() === null ) { 25 | return $semanticEntity; 26 | } 27 | 28 | $this->addId( $semanticEntity, $item->getId() ); 29 | $semanticEntity->add( $this->fingerprintTranslator->translateFingerprint( $item->getFingerprint() ) ); 30 | $semanticEntity->add( $this->statementListTranslator->translateStatements( $item->getStatements() ) ); 31 | 32 | return $semanticEntity; 33 | } 34 | 35 | private function addId( SemanticEntity $semanticEntity, ItemId $itemId ): void { 36 | $semanticEntity->addPropertyValue( 37 | FixedProperties::ID, 38 | new \SMWDIBlob( $itemId->getSerialization() ) 39 | ); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Translation/PropertyTranslator.php: -------------------------------------------------------------------------------- 1 | fingerprintTranslator = $fingerprintTranslator; 18 | $this->statementListTranslator = $statementListTranslator; 19 | } 20 | 21 | public function translateProperty( Property $property ): SemanticEntity { 22 | $semanticEntity = new SemanticEntity(); 23 | 24 | if ( $property->getId() === null ) { 25 | return $semanticEntity; 26 | } 27 | 28 | $this->addId( $semanticEntity, $property->getId() ); 29 | $semanticEntity->add( $this->fingerprintTranslator->translateFingerprint( $property->getFingerprint() ) ); 30 | $semanticEntity->add( $this->statementListTranslator->translateStatements( $property->getStatements() ) ); 31 | 32 | return $semanticEntity; 33 | } 34 | 35 | private function addId( SemanticEntity $semanticEntity, PropertyId $itemId ): void { 36 | $semanticEntity->addPropertyValue( 37 | FixedProperties::ID, 38 | new \SMWDIBlob( $itemId->getSerialization() ) 39 | ); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Semantic Wikibase", 3 | 4 | "version": "0.1.0", 5 | 6 | "author": [ 7 | "[https://www.entropywins.wtf/mediawiki Jeroen De Dauw]", 8 | "[https://professional.wiki/ Professional.Wiki]" 9 | ], 10 | 11 | "url": "https://professional.wiki/en/extension/semantic-wikibase", 12 | 13 | "descriptionmsg": "semantic-wb-desc", 14 | 15 | "license-name": "GPL-2.0-or-later", 16 | 17 | "type": "semantic", 18 | 19 | "requires": { 20 | "MediaWiki": ">= 1.35.0", 21 | "extensions": { 22 | "SemanticMediaWiki": ">=3.1", 23 | "WikibaseRepository": "*" 24 | } 25 | }, 26 | 27 | "MessagesDirs": { 28 | "SemanticWikibase": [ 29 | "i18n" 30 | ] 31 | }, 32 | 33 | "AutoloadNamespaces": { 34 | "MediaWiki\\Extension\\SemanticWikibase\\": "src/", 35 | "MediaWiki\\Extension\\SemanticWikibase\\Tests\\": "tests/" 36 | }, 37 | 38 | "callback": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onExtensionRegistration", 39 | 40 | "Hooks": { 41 | "SMW::Property::initProperties": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwInitProperties", 42 | "SMW::SQLStore::AddCustomFixedPropertyTables": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwAddCustomFixedPropertyTables", 43 | "SMWStore::updateDataBefore": "MediaWiki\\Extension\\SemanticWikibase\\EntryPoints\\HookHandlers::onSmwUpdateDataBefore" 44 | }, 45 | 46 | "config": { 47 | "SemanticWikibaseLanguage": { 48 | "value": "" 49 | } 50 | }, 51 | 52 | "manifest_version": 2 53 | } 54 | -------------------------------------------------------------------------------- /src/SMW/SemanticEntity.php: -------------------------------------------------------------------------------- 1 | dataItemsPerProperty[$propertyId][] = $dataItem; 18 | } 19 | 20 | /** 21 | * @param string $propertyId 22 | * @return SMWDataItem[] 23 | */ 24 | public function getDataItemsForProperty( string $propertyId ): array { 25 | return $this->dataItemsPerProperty[$propertyId] ?? []; 26 | } 27 | 28 | public function toSemanticData( DIWikiPage $subject ): SemanticData { 29 | $semanticData = new SemanticData( $subject ); 30 | 31 | foreach ( $this->dataItemsPerProperty as $propertyId => $dataItems ) { 32 | $property = new DIProperty( $propertyId ); 33 | 34 | foreach ( $dataItems as $dataItem ) { 35 | $semanticData->addPropertyObjectValue( 36 | $property, 37 | $dataItem 38 | ); 39 | } 40 | } 41 | 42 | return $semanticData; 43 | } 44 | 45 | public function functionalMerge( self $entity ): self { 46 | $merged = new SemanticEntity(); 47 | 48 | $merged->add( $this ); 49 | $merged->add( $entity ); 50 | 51 | return $merged; 52 | } 53 | 54 | public function add( self $entity ): void { 55 | foreach ( $entity->dataItemsPerProperty as $propertyId => $dataItems ) { 56 | foreach ( $dataItems as $dataItem ) { 57 | $this->addPropertyValue( $propertyId, $dataItem ); 58 | } 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/Translation/PropertyTypeTranslator.php: -------------------------------------------------------------------------------- 1 | getMap() ) 17 | && is_string( $this->getMap()[$wikibasePropertyType] ); 18 | } 19 | 20 | /** 21 | * Wikibase property type => SMW DataValue type 22 | * 23 | * @param string $wikibasePropertyType 24 | * 25 | * @return string SMW DataValue type ID 26 | */ 27 | public function translate( string $wikibasePropertyType ): string { 28 | return $this->getMap()[$wikibasePropertyType]; 29 | } 30 | 31 | private function getMap(): array { 32 | $map = [ 33 | 'commonsMedia' => StringValue::TYPE_ID, 34 | 'geo-shape' => StringValue::TYPE_ID, 35 | 'monolingualtext' => MonolingualTextValue::TYPE_ID, 36 | 'quantity' => '_wpg', // Purposefully not using SMWQuantityValue 37 | 'string' => StringValue::TYPE_ID, 38 | 'tabular-data' => null, // TODO 39 | 'entity-schema' => null, // TODO 40 | 'time' => SMWTimeValue::TYPE_ID, 41 | 'url' => '_uri', 42 | 'external-id' => ExternalIdentifierValue::TYPE_ID, 43 | 'wikibase-item' => '_wpg', 44 | 'wikibase-property' => '_wpg', 45 | ]; 46 | 47 | if ( class_exists( CoordinateValue::class ) ) { 48 | $map['globe-coordinate'] = '_geo'; 49 | } 50 | 51 | return $map; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /tests/Unit/Translation/PropertyTranslatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 26 | [ new \SMWDIBlob( self::ID ) ], 27 | $this->translate( $this->newProperty() )->getDataItemsForProperty( FixedProperties::ID ) 28 | ); 29 | } 30 | 31 | private function newProperty(): Property { 32 | return new Property( 33 | new PropertyId( self::ID ), 34 | null, 35 | self::TYPE 36 | ); 37 | } 38 | 39 | private function translate( Property $property ): SemanticEntity { 40 | return TestFactory::newTestInstance()->newPropertyTranslator( 41 | DIWikiPage::newFromTitle( \Title::newFromText( 'Item:Q1' ) ) 42 | )->translateProperty( $property ); 43 | } 44 | 45 | public function testLabelIsTranslated() { 46 | $property = $this->newProperty(); 47 | $property->setLabel( 'en', 'Kittens' ); 48 | 49 | $this->assertHasMonolingualTexts( 50 | [ 51 | new MonolingualTextValue( 'en', 'Kittens' ) 52 | ], 53 | $this->translate( $property )->getDataItemsForProperty( FixedProperties::LABEL ) 54 | ); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/Translation/StatementTranslator.php: -------------------------------------------------------------------------------- 1 | propertyTypeLookup = $propertyTypeLookup; 22 | $this->containerValueTranslator = $containerValueTranslator; 23 | $this->dataValueTranslator = $dataValueTranslator; 24 | } 25 | 26 | public function statementToDataItem( Statement $statement, DIWikiPage $subject ): ?SMWDataItem { 27 | $mainSnak = $statement->getMainSnak(); 28 | 29 | if ( !( $mainSnak instanceof PropertyValueSnak ) ) { 30 | return null; 31 | } 32 | 33 | if ( $this->containerValueTranslator->supportsStatement( $statement ) ) { 34 | return $this->containerValueTranslator->statementToDataItem( $statement, $subject ); 35 | } 36 | 37 | return $this->snakWithSimpleDataValueToDataItem( $mainSnak ); 38 | } 39 | 40 | private function snakWithSimpleDataValueToDataItem( PropertyValueSnak $snak ): SMWDataItem { 41 | return $this->dataValueTranslator->translate( 42 | new TypedDataValue( 43 | $this->propertyTypeLookup->getDataTypeIdForProperty( $snak->getPropertyId() ), 44 | $snak->getDataValue() 45 | ) 46 | ); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Translation/StatementListTranslator.php: -------------------------------------------------------------------------------- 1 | statementTranslator = $statementTranslator; 20 | $this->subject = $subject; 21 | } 22 | 23 | public function translateStatements( StatementList $statements ): SemanticEntity { 24 | $semanticEntity = new SemanticEntity(); 25 | 26 | foreach ( $statements->getBestStatements()->getByRank( [ Statement::RANK_PREFERRED, Statement::RANK_NORMAL ] ) as $statement ) { 27 | $this->addStatement( $semanticEntity, $statement ); 28 | } 29 | 30 | return $semanticEntity; 31 | } 32 | 33 | private function addStatement( SemanticEntity $semanticEntity, Statement $statement ): void { 34 | $dataItem = $this->statementTranslator->statementToDataItem( $statement, $this->subject ); 35 | 36 | if ( $dataItem !== null ) { 37 | // TODO: belongs in statement translator 38 | if ( $dataItem instanceof \SMWDIContainer ) { 39 | $semanticEntity->addPropertyValue( DIProperty::TYPE_SUBOBJECT, $dataItem ); 40 | 41 | $semanticEntity->addPropertyValue( 42 | $this->propertyIdForStatement( $statement ), 43 | $dataItem->getSemanticData()->getSubject() 44 | ); 45 | } 46 | else { 47 | $semanticEntity->addPropertyValue( 48 | $this->propertyIdForStatement( $statement ), 49 | $dataItem 50 | ); 51 | } 52 | } 53 | } 54 | 55 | private function propertyIdForStatement( Statement $statement ): string { 56 | return UserDefinedProperties::idFromWikibaseProperty( $statement->getPropertyId() ); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /tests/Unit/Translation/MonoTextTranslatorTest.php: -------------------------------------------------------------------------------- 1 | getMonoTextTranslator(); 25 | 26 | $subject = new DIWikiPage( self::SUBJECT_TITLE, WB_NS_ITEM ); 27 | 28 | $dataItem = $translator->valueToDataItem( 29 | new MonolingualTextValue( self::LANGUAGE, self::TEXT ), 30 | $subject 31 | ); 32 | 33 | $this->assertIsContainer( $dataItem ); 34 | $this->assertHasText( $dataItem ); 35 | $this->assertHasLanguage( $dataItem ); 36 | $this->assertHasSubject( $subject, $dataItem ); 37 | } 38 | 39 | private function assertIsContainer( \SMWDIContainer $dataItem ): void { 40 | $this->assertSame( SMWDataItem::TYPE_CONTAINER, $dataItem->getDIType() ); 41 | } 42 | 43 | private function assertHasText( \SMWDIContainer $dataItem ): void { 44 | $this->assertEquals( 45 | [ 'fluffy bunnies' ], 46 | $dataItem->getSemanticData()->getPropertyValues( new DIProperty( '_TEXT' ) ) 47 | ); 48 | } 49 | 50 | private function assertHasLanguage( \SMWDIContainer $dataItem ): void { 51 | $this->assertEquals( 52 | [ 'en' ], 53 | $dataItem->getSemanticData()->getPropertyValues( new DIProperty( '_LCODE' ) ) 54 | ); 55 | } 56 | 57 | private function assertHasSubject( DIWikiPage $subject, \SMWDIContainer $dataItem ): void { 58 | $this->assertEquals( 59 | $subject->getDBkey(), 60 | $dataItem->getSemanticData()->getSubject()->getDBkey() 61 | ); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/Translation/FingerprintTranslator.php: -------------------------------------------------------------------------------- 1 | termTranslator = $termTranslator; 20 | } 21 | 22 | public function translateFingerprint( Fingerprint $fingerprint ): SemanticEntity { 23 | $semanticEntity = new SemanticEntity(); 24 | 25 | $this->addLabels( $semanticEntity, $fingerprint->getLabels() ); 26 | $this->addDescriptions( $semanticEntity, $fingerprint->getDescriptions() ); 27 | $this->addAliases( $semanticEntity, $fingerprint->getAliasGroups() ); 28 | 29 | return $semanticEntity; 30 | } 31 | 32 | private function addLabels( SemanticEntity $semanticEntity, TermList $labels ): void { 33 | foreach ( $labels as $label ) { 34 | $semanticEntity->addPropertyValue( 35 | FixedProperties::LABEL, 36 | $this->translateTerm( $label ) 37 | ); 38 | } 39 | } 40 | 41 | private function translateTerm( Term $term ): SMWDataItem { 42 | return $this->termTranslator->translateTerm( $term ); 43 | } 44 | 45 | private function addDescriptions( SemanticEntity $semanticEntity, TermList $descriptions ): void { 46 | foreach ( $descriptions as $description ) { 47 | $semanticEntity->addPropertyValue( 48 | FixedProperties::DESCRIPTION, 49 | $this->translateTerm( $description ) 50 | ); 51 | } 52 | } 53 | 54 | private function addAliases( SemanticEntity $semanticEntity, AliasGroupList $aliasGroups ): void { 55 | foreach ( $aliasGroups as $aliasGroup ) { 56 | foreach ( $aliasGroup->getAliases() as $aliasText ) { 57 | $semanticEntity->addPropertyValue( 58 | FixedProperties::ALIAS, 59 | $this->translateTerm( new Term( $aliasGroup->getLanguageCode(), $aliasText ) ) 60 | ); 61 | } 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/Translation/UserDefinedProperties.php: -------------------------------------------------------------------------------- 1 | propertyInfoLookup = $propertyInfoLookup; 25 | $this->propertyTypeTranslator = $propertyTypeTranslator; 26 | $this->termLookup = $termLookup; 27 | $this->labelLanguageCode = $labelLanguageCode; 28 | } 29 | 30 | /** 31 | * @return SemanticProperty[] 32 | */ 33 | public function getAll(): array { 34 | $properties = []; 35 | 36 | foreach ( $this->getAllPropertyInfo() as $id => $propertyInfo ) { 37 | if ( $this->propertyTypeTranslator->canTranslate( $propertyInfo['type'] ) ) { 38 | $properties[] = new SemanticProperty( 39 | self::idFromWikibaseString( $id ), 40 | $this->propertyTypeTranslator->translate( $propertyInfo['type'] ), 41 | $id, 42 | $this->termLookup->getLabel( 43 | new PropertyId( $id ), 44 | $this->labelLanguageCode 45 | ) 46 | ); 47 | } 48 | } 49 | 50 | return $properties; 51 | } 52 | 53 | public function getAllPropertyInfo(): array { 54 | try { 55 | return $this->propertyInfoLookup->getAllPropertyInfo(); 56 | } 57 | catch ( StorageException | DBError $ex ) { 58 | return []; 59 | } 60 | } 61 | 62 | private static function idFromWikibaseString( string $propertyId ): string { 63 | return '___SWB_' . $propertyId; 64 | } 65 | 66 | public static function idFromWikibaseProperty( PropertyId $id ): string { 67 | return self::idFromWikibaseString( $id->getSerialization() ); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/Translation/TranslatorFactory.php: -------------------------------------------------------------------------------- 1 | dataValueFactory = $dataValueFactory; 18 | $this->propertyTypeLookup = $propertyTypeLookup; 19 | } 20 | 21 | public function newItemTranslator( DIWikiPage $subject ): ItemTranslator { 22 | return new ItemTranslator( 23 | $this->newFingerprintTranslator( $subject ), 24 | $this->newStatementListTranslator( $subject ) 25 | ); 26 | } 27 | 28 | public function newPropertyTranslator( DIWikiPage $subject ): PropertyTranslator { 29 | return new PropertyTranslator( 30 | $this->newFingerprintTranslator( $subject ), 31 | $this->newStatementListTranslator( $subject ) 32 | ); 33 | } 34 | 35 | private function newFingerprintTranslator( DIWikiPage $subject ) { 36 | return new FingerprintTranslator( 37 | new TermTranslator( $this->getMonoTextTranslator(), $subject ) 38 | ); 39 | } 40 | 41 | private function newStatementListTranslator( DIWikiPage $subject ): StatementListTranslator { 42 | return new StatementListTranslator( 43 | $this->getStatementTranslator(), 44 | $subject 45 | ); 46 | } 47 | 48 | public function getStatementTranslator(): StatementTranslator { 49 | return new StatementTranslator( 50 | $this->getDataValueTranslator(), 51 | $this->getContainerValueTranslator(), 52 | $this->propertyTypeLookup 53 | ); 54 | } 55 | 56 | private function getDataValueTranslator(): DataValueTranslator { 57 | return new DataValueTranslator(); 58 | } 59 | 60 | public function getContainerValueTranslator(): ContainerValueTranslator { 61 | return new ContainerValueTranslator( 62 | $this->getDataValueTranslator(), 63 | $this->getMonoTextTranslator() 64 | ); 65 | } 66 | 67 | public function getMonoTextTranslator(): MonoTextTranslator { 68 | return new MonoTextTranslator( 69 | $this->dataValueFactory 70 | ); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /.travis.install.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -x 4 | 5 | originalDirectory=$(pwd) 6 | 7 | cd .. 8 | 9 | wget https://github.com/wikimedia/mediawiki/archive/$MW.tar.gz 10 | tar -zxf $MW.tar.gz 11 | mv mediawiki-$MW phase3 12 | 13 | cd phase3 14 | 15 | composer install --prefer-source 16 | 17 | if [ "$DB" == "postgres" ] 18 | then 19 | psql -c 'create database its_a_mw;' -U postgres 20 | php maintenance/install.php --dbtype $DBTYPE --dbuser postgres --dbname its_a_mw --pass AdminPassword TravisWiki admin --scriptpath /TravisWiki 21 | else 22 | mysql -e 'create database its_a_mw;' 23 | php maintenance/install.php --dbtype $DBTYPE --dbuser root --dbname its_a_mw --dbpath $(pwd) --pass AdminPassword TravisWiki admin --scriptpath /TravisWiki 24 | fi 25 | 26 | cd extensions 27 | cp -r $originalDirectory SemanticWikibase 28 | cd SemanticWikibase 29 | composer install 30 | cd .. 31 | cd .. 32 | 33 | composer require "mediawiki/semantic-media-wiki=$SMW" 34 | composer require "wikibase/wikibase=$WB" 35 | 36 | cat <> composer.local.json 37 | { 38 | "extra": { 39 | "merge-plugin": { 40 | "merge-dev": true, 41 | "include": [ 42 | "extensions/*/composer.json" 43 | ] 44 | } 45 | } 46 | } 47 | EOT 48 | 49 | composer install 50 | 51 | echo '$wgEnableWikibaseRepo = true;' >> LocalSettings.php 52 | echo '$wgEnableWikibaseClient = false;' >> LocalSettings.php 53 | echo 'require_once __DIR__ . "/extensions/Wikibase/repo/Wikibase.php";' >> LocalSettings.php 54 | echo 'require_once __DIR__ . "/extensions/Wikibase/repo/ExampleSettings.php";' >> LocalSettings.php 55 | echo '$wgExtraNamespaces[WB_NS_PROPERTY] = "WikibaseProperty";' >> LocalSettings.php 56 | echo '$wgExtraNamespaces[WB_NS_PROPERTY_TALK] = "WikibaseProperty_talk";' >> LocalSettings.php 57 | 58 | echo 'wfLoadExtension( "SemanticWikibase" );' >> LocalSettings.php 59 | 60 | echo 'wfLoadExtension( "SemanticMediaWiki" );' >> LocalSettings.php 61 | echo 'enableSemantics( "example.org" );' >> LocalSettings.php 62 | 63 | echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php 64 | echo 'ini_set("display_errors", 1);' >> LocalSettings.php 65 | echo '$wgShowExceptionDetails = true;' >> LocalSettings.php 66 | echo '$wgShowDBErrorBacktrace = true;' >> LocalSettings.php 67 | echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php 68 | echo "putenv( 'MW_INSTALL_PATH=$(pwd)' );" >> LocalSettings.php 69 | 70 | php maintenance/update.php --quick 71 | -------------------------------------------------------------------------------- /tests/TestFactory.php: -------------------------------------------------------------------------------- 1 | initialize(); 28 | 29 | return self::$instance; 30 | } 31 | 32 | private function initialize(): void { 33 | $this->propertyDataTypeLookup = $this->newPropertyTypeLookup(); 34 | } 35 | 36 | private function newPropertyTypeLookup(): InMemoryDataTypeLookup { 37 | $lookup = new InMemoryDataTypeLookup(); 38 | 39 | $lookup->setDataTypeForProperty( new PropertyId( 'P1' ), 'string' ); 40 | 41 | return $lookup; 42 | } 43 | 44 | public function newPropertyTranslator( DIWikiPage $subject ) { 45 | return $this->getTranslatorFactory()->newPropertyTranslator( $subject ); 46 | } 47 | 48 | protected function getPropertyTypeLookup(): PropertyDataTypeLookup { 49 | return $this->propertyDataTypeLookup; 50 | } 51 | 52 | public function getStatementTranslator(): StatementTranslator { 53 | return $this->getTranslatorFactory()->getStatementTranslator(); 54 | } 55 | 56 | public function newItemTranslator( DIWikiPage $subject ): ItemTranslator { 57 | return $this->getTranslatorFactory()->newItemTranslator( $subject ); 58 | } 59 | 60 | public function getMonoTextTranslator(): MonoTextTranslator { 61 | return $this->getTranslatorFactory()->getMonoTextTranslator(); 62 | } 63 | 64 | public function getContainerValueTranslator(): ContainerValueTranslator { 65 | return $this->getTranslatorFactory()->getContainerValueTranslator(); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /tests/Unit/Translation/StatementTranslatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 31 | new \SMWDIBlob( 'Kittens' ), 32 | $this->newTranslator()->statementToDataItem( 33 | new Statement( 34 | new PropertyValueSnak( 35 | new PropertyId( 'P1' ), 36 | new StringValue( 'Kittens' ) 37 | ) 38 | ), 39 | $this->getSubject() 40 | ) 41 | ); 42 | } 43 | 44 | public function testTranslateContainerValue() { 45 | $container = $this->newTranslator()->statementToDataItem( 46 | new Statement( 47 | new PropertyValueSnak( 48 | new PropertyId( 'P1' ), 49 | new UnboundedQuantityValue( 50 | new DecimalValue( 42 ), 51 | 'mega awesome' 52 | ) 53 | ) 54 | ), 55 | $this->getSubject() 56 | ); 57 | 58 | $semanticData = $container->getSemanticData(); 59 | 60 | $this->assertEquals( 61 | [ new \SMWDINumber( 42 ) ], 62 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_VALUE ) ) 63 | ); 64 | 65 | $this->assertEquals( 66 | [ new \SMWDIBlob( 'mega awesome' ) ], 67 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_UNIT ) ) 68 | ); 69 | } 70 | 71 | private function newTranslator(): StatementTranslator { 72 | return TestFactory::newTestInstance()->getStatementTranslator(); 73 | } 74 | 75 | private function getSubject(): DIWikiPage { 76 | return new DIWikiPage( self::SUBJECT_TITLE, self::SUBJECT_NAMESPACE ); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/SemanticDataUpdate.php: -------------------------------------------------------------------------------- 1 | translatorFactory = $translatorFactory; 27 | $this->itemLookup = $itemLookup; 28 | $this->propertyLookup = $propertyLookup; 29 | } 30 | 31 | public function run( SemanticData $semanticData ): void { 32 | $title = $semanticData->getSubject()->getTitle(); 33 | 34 | if ( $title === null ) { 35 | return; 36 | } 37 | 38 | $semanticData->importDataFrom( 39 | $this->getSemanticEntityFromTitle( $title )->toSemanticData( $semanticData->getSubject() ) 40 | ); 41 | } 42 | 43 | private function getSemanticEntityFromTitle( Title $title ): SemanticEntity { 44 | if ( $title->getNamespace() === WB_NS_ITEM ) { 45 | return $this->getSemanticEntityForItemTitle( $title ); 46 | } 47 | 48 | if ( $title->getNamespace() === WB_NS_PROPERTY ) { 49 | return $this->getSemanticEntityForPropertyTitle( $title ); 50 | } 51 | 52 | return new SemanticEntity(); 53 | } 54 | 55 | private function getSemanticEntityForItemTitle( Title $title ): SemanticEntity { 56 | return $this->newItemTranslator( $title )->translateItem( 57 | $this->itemLookup->getItemForId( new ItemId( $title->getText() ) ) 58 | ); 59 | } 60 | 61 | private function newItemTranslator( Title $title ): ItemTranslator { 62 | return $this->translatorFactory->newItemTranslator( DIWikiPage::newFromTitle( $title ) ); 63 | } 64 | 65 | private function getSemanticEntityForPropertyTitle( Title $title ): SemanticEntity { 66 | return $this->newPropertyTranslator( $title )->translateProperty( 67 | $this->propertyLookup->getPropertyForId( new PropertyId( $title->getText() ) ) 68 | ); 69 | } 70 | 71 | private function newPropertyTranslator( Title $title ): PropertyTranslator { 72 | return $this->translatorFactory->newPropertyTranslator( DIWikiPage::newFromTitle( $title ) ); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/Translation/FixedProperties.php: -------------------------------------------------------------------------------- 1 | getAll() as $property ) { 25 | $customFixedProperties[$property->getId()] = str_replace( '___', '_', $property->getId() ); 26 | $fixedPropertyTablePrefix[$property->getId()] = 'swb_fpt'; 27 | } 28 | } 29 | 30 | /** 31 | * @return SemanticProperty[] 32 | */ 33 | public function getAll(): array { 34 | return [ 35 | $this->newEntityId(), 36 | $this->newLabel(), 37 | $this->newDescription(), 38 | $this->newAlias(), 39 | 40 | $this->newQuantityValue(), 41 | $this->newQuantityLowerBound(), 42 | $this->newQuantityUpperBound(), 43 | $this->newQuantityUnit(), 44 | ]; 45 | } 46 | 47 | private function newEntityId(): SemanticProperty { 48 | return new SemanticProperty( self::ID, StringValue::TYPE_ID, 'Wikibase ID' ); 49 | } 50 | 51 | private function newLabel(): SemanticProperty { 52 | return new SemanticProperty( self::LABEL, MonolingualTextValue::TYPE_ID, 'Wikibase label' ); 53 | } 54 | 55 | private function newDescription(): SemanticProperty { 56 | return new SemanticProperty( self::DESCRIPTION, MonolingualTextValue::TYPE_ID, 'Wikibase description' ); 57 | } 58 | 59 | private function newAlias(): SemanticProperty { 60 | return new SemanticProperty( self::ALIAS, MonolingualTextValue::TYPE_ID, 'Wikibase alias' ); 61 | } 62 | 63 | private function newQuantityValue(): SemanticProperty { 64 | return new SemanticProperty( self::QUANTITY_VALUE, \SMWNumberValue::TYPE_ID, 'Wikibase quantity' ); 65 | } 66 | 67 | private function newQuantityLowerBound(): SemanticProperty { 68 | return new SemanticProperty( self::QUANTITY_LOWER_BOUND, \SMWNumberValue::TYPE_ID, 'Wikibase lower bound' ); 69 | } 70 | 71 | private function newQuantityUpperBound(): SemanticProperty { 72 | return new SemanticProperty( self::QUANTITY_UPPER_BOUND, \SMWNumberValue::TYPE_ID, 'Wikibase upper bound' ); 73 | } 74 | 75 | private function newQuantityUnit(): SemanticProperty { 76 | return new SemanticProperty( self::QUANTITY_UNIT, StringValue::TYPE_ID, 'Wikibase unit' ); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /tests/Unit/SMW/SemanticEntityTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 18 | [], 19 | ( new SemanticEntity() )->getDataItemsForProperty( FixedProperties::LABEL ) 20 | ); 21 | } 22 | 23 | public function testWhenPropertyHasValues_getDataItemsForPropertyReturnsThem() { 24 | $entity = new SemanticEntity(); 25 | $entity->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'hi' ) ); 26 | $entity->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'there' ) ); 27 | 28 | $this->assertEquals( 29 | [ new \SMWDIBlob( 'hi' ), new \SMWDIBlob( 'there' ) ], 30 | $entity->getDataItemsForProperty( FixedProperties::LABEL ) 31 | ); 32 | } 33 | 34 | public function testWhenOtherValuesExist_getDataItemsForPropertyDoesNotReturnThem() { 35 | $entity = new SemanticEntity(); 36 | $entity->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'hi' ) ); 37 | $entity->addPropertyValue( FixedProperties::DESCRIPTION, new \SMWDIBlob( 'nope' ) ); 38 | $entity->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'there' ) ); 39 | 40 | $this->assertEquals( 41 | [ new \SMWDIBlob( 'hi' ), new \SMWDIBlob( 'there' ) ], 42 | $entity->getDataItemsForProperty( FixedProperties::LABEL ) 43 | ); 44 | } 45 | 46 | public function testMergeWithSameProperties() { 47 | $entity1 = new SemanticEntity(); 48 | $entity1->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'hi' ) ); 49 | 50 | $entity2 = new SemanticEntity(); 51 | $entity2->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'there' ) ); 52 | 53 | $this->assertEquals( 54 | [ new \SMWDIBlob( 'hi' ), new \SMWDIBlob( 'there' ) ], 55 | $entity1->functionalMerge( $entity2 )->getDataItemsForProperty( FixedProperties::LABEL ) 56 | ); 57 | } 58 | 59 | public function testMergeWithDifferentProperties() { 60 | $entity1 = new SemanticEntity(); 61 | $entity1->addPropertyValue( FixedProperties::LABEL, new \SMWDIBlob( 'hi' ) ); 62 | 63 | $entity2 = new SemanticEntity(); 64 | $entity2->addPropertyValue( FixedProperties::DESCRIPTION, new \SMWDIBlob( 'there' ) ); 65 | 66 | $merged = $entity1->functionalMerge( $entity2 ); 67 | 68 | $this->assertEquals( 69 | [ new \SMWDIBlob( 'hi' ) ], 70 | $merged->getDataItemsForProperty( FixedProperties::LABEL ) 71 | ); 72 | 73 | $this->assertEquals( 74 | [ new \SMWDIBlob( 'there' ) ], 75 | $merged->getDataItemsForProperty( FixedProperties::DESCRIPTION ) 76 | ); 77 | } 78 | 79 | public function testMergeIsFunctional() { 80 | $entity1 = new SemanticEntity(); 81 | 82 | $entity2 = new SemanticEntity(); 83 | $entity2->addPropertyValue( FixedProperties::DESCRIPTION, new \SMWDIBlob( 'there' ) ); 84 | 85 | $entity1->functionalMerge( $entity2 ); 86 | 87 | $this->assertSame( 88 | [], 89 | $entity1->getDataItemsForProperty( FixedProperties::DESCRIPTION ) 90 | ); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/SemanticWikibase.php: -------------------------------------------------------------------------------- 1 | config = $config; 38 | } 39 | 40 | public function getSemanticDataUpdate(): SemanticDataUpdate { 41 | $entityLookup = WikibaseRepo::getEntityLookup(); 42 | return new SemanticDataUpdate( 43 | $this->getTranslatorFactory(), 44 | new LegacyAdapterItemLookup( $entityLookup ), 45 | new LegacyAdapterPropertyLookup( $entityLookup ) 46 | ); 47 | } 48 | 49 | protected function getTranslatorFactory(): TranslatorFactory { 50 | return new TranslatorFactory( 51 | $this->getDataValueFactory(), 52 | $this->getPropertyTypeLookup() 53 | ); 54 | } 55 | 56 | private function getDataValueFactory(): DataValueFactory { 57 | return DataValueFactory::getInstance(); 58 | } 59 | 60 | protected function getPropertyTypeLookup(): PropertyDataTypeLookup { 61 | return WikibaseRepo::getPropertyDataTypeLookup(); 62 | } 63 | 64 | public function registerProperties( PropertyRegistry $propertyRegistry ) { 65 | foreach ( $this->getAllProperties() as $property ) { 66 | $propertyRegistry->registerProperty( 67 | $property->getId(), 68 | $property->getType(), 69 | $property->getLabel(), 70 | true, 71 | false 72 | ); 73 | 74 | foreach ( $property->getAliases() as $alias ) { 75 | $propertyRegistry->registerPropertyAlias( $property->getId(), $alias ); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * @return SemanticProperty[] 82 | */ 83 | private function getAllProperties(): array { 84 | return array_merge( 85 | $this->getFixedProperties()->getAll(), 86 | $this->getUserDefinedProperties()->getAll() 87 | ); 88 | } 89 | 90 | public function getFixedProperties(): FixedProperties { 91 | return new FixedProperties(); 92 | } 93 | 94 | public function getUserDefinedProperties(): UserDefinedProperties { 95 | return new UserDefinedProperties( 96 | WikibaseRepo::getWikibaseServices()->getPropertyInfoLookup(), 97 | $this->getPropertyTypeTranslator(), 98 | WikibaseRepo::getTermLookup(), 99 | $this->config->getLanguageCode() 100 | ); 101 | } 102 | 103 | private function getPropertyTypeTranslator(): PropertyTypeTranslator { 104 | return new PropertyTypeTranslator(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/Translation/ContainerValueTranslator.php: -------------------------------------------------------------------------------- 1 | dataValueTranslator = $dataValueTranslator; 24 | $this->monoTextTranslator = $monoTextTranslator; 25 | } 26 | 27 | public function supportsStatement( Statement $statement ): bool { 28 | $mainSnak = $statement->getMainSnak(); 29 | 30 | if ( $mainSnak instanceof PropertyValueSnak ) { 31 | $value = $mainSnak->getDataValue(); 32 | return $value instanceof UnboundedQuantityValue 33 | || $value instanceof MonolingualTextValue; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | public function statementToDataItem( Statement $statement, DIWikiPage $subject ): SMWDIContainer { 40 | $mainSnak = $statement->getMainSnak(); 41 | 42 | if ( !( $mainSnak instanceof PropertyValueSnak ) ) { 43 | throw new \InvalidArgumentException( 'Not a PropertyValueSnak' ); 44 | } 45 | 46 | $dataValue = $mainSnak->getDataValue(); 47 | 48 | if ( $dataValue instanceof MonolingualTextValue ) { 49 | return $this->translateMonolingualTextValue( $dataValue, $subject ); 50 | } 51 | 52 | $container = $this->newContainerSemanticData( $statement, $subject ); 53 | 54 | if ( $dataValue instanceof UnboundedQuantityValue ) { 55 | return $this->translateQuantityValue( $dataValue, $container ); 56 | } 57 | 58 | throw new \InvalidArgumentException( 'DataValue type not supported' ); 59 | } 60 | 61 | private function newContainerSemanticData( Statement $statement, DIWikiPage $subject ): ContainerSemanticData { 62 | return new ContainerSemanticData( new DIWikiPage( 63 | $subject->getDBkey(), 64 | $subject->getNamespace(), 65 | $subject->getInterwiki(), 66 | $statement->getGuid() 67 | ) ); 68 | } 69 | 70 | private function translateQuantityValue( UnboundedQuantityValue $quantityValue, ContainerSemanticData $container ): SMWDIContainer { 71 | $container->addPropertyObjectValue( 72 | new DIProperty( FixedProperties::QUANTITY_VALUE ), 73 | $this->dataValueTranslator->translateDecimalValue( $quantityValue->getAmount() ) 74 | ); 75 | 76 | $container->addPropertyObjectValue( 77 | new DIProperty( FixedProperties::QUANTITY_UNIT ), 78 | new \SMWDIBlob( $quantityValue->getUnit() ) 79 | ); 80 | 81 | if ( $quantityValue instanceof QuantityValue ) { 82 | $container->addPropertyObjectValue( 83 | new DIProperty( FixedProperties::QUANTITY_LOWER_BOUND ), 84 | $this->dataValueTranslator->translateDecimalValue( $quantityValue->getLowerBound() ) 85 | ); 86 | 87 | $container->addPropertyObjectValue( 88 | new DIProperty( FixedProperties::QUANTITY_UPPER_BOUND ), 89 | $this->dataValueTranslator->translateDecimalValue( $quantityValue->getUpperBound() ) 90 | ); 91 | } 92 | 93 | return new SMWDIContainer( $container ); 94 | } 95 | 96 | private function translateMonolingualTextValue( MonolingualTextValue $value, DIWikiPage $subject ): SMWDIContainer { 97 | return $this->monoTextTranslator->valueToDataItem( $value, $subject ); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/Translation/DataValueTranslator.php: -------------------------------------------------------------------------------- 1 | getValue(); 28 | 29 | if ( $value instanceof StringValue ) { 30 | return $this->translateStringValue( $typedValue ); 31 | } 32 | if ( $value instanceof BooleanValue ) { 33 | return new \SMWDIBoolean( $value->getValue() ); 34 | } 35 | if ( $value instanceof NumberValue ) { 36 | return new \SMWDINumber( $value->getValue() ); 37 | } 38 | if ( $value instanceof EntityIdValue ) { 39 | return $this->translateEntityIdValue( $value ); 40 | } 41 | if ( $value instanceof DecimalValue ) { 42 | return $this->translateDecimalValue( $value ); 43 | } 44 | if ( $value instanceof GlobeCoordinateValue ) { 45 | return $this->translateGlobeCoordinateValue( $value ); 46 | } 47 | if ( $value instanceof TimeValue ) { 48 | return $this->translateTimeValue( $value ); 49 | } 50 | 51 | throw new \RuntimeException( 'Support for DataValue type "' . get_class( $value ) . '" not implemented' ); 52 | } 53 | 54 | private function translateStringValue( TypedDataValue $typedValue ): SMWDataItem { 55 | if ( $typedValue->getPropertyType() === 'url' ) { 56 | return SMWDIUri::doUnserialize( $typedValue->getValue()->getValue() ); 57 | } 58 | 59 | return new \SMWDIBlob( $typedValue->getValue()->getValue() ); 60 | } 61 | 62 | private function translateGlobeCoordinateValue( GlobeCoordinateValue $globeValue ): \SMWDIGeoCoord { 63 | return \SMWDIGeoCoord::newFromLatLong( 64 | $globeValue->getLatitude(), 65 | $globeValue->getLongitude() 66 | ); 67 | } 68 | 69 | private function translateEntityIdValue( EntityIdValue $idValue ): SMWDataItem { 70 | return new DIWikiPage( 71 | $idValue->getEntityId()->getSerialization(), 72 | $this->entityIdToNamespaceId( $idValue->getEntityId() ) 73 | ); 74 | } 75 | 76 | private function entityIdToNamespaceId( EntityId $idValue ): int { 77 | if ( $idValue instanceof ItemId ) { 78 | return WB_NS_ITEM; 79 | } 80 | 81 | if ( $idValue instanceof PropertyId ) { 82 | return WB_NS_PROPERTY; 83 | } 84 | 85 | throw new \RuntimeException( 'Support for EntityId type not implemented' ); 86 | } 87 | 88 | public function translateDecimalValue( DecimalValue $value ): SMWDataItem { 89 | return new \SMWDINumber( $value->getValueFloat() ); 90 | } 91 | 92 | private function translateTimeValue( TimeValue $value ): \SMWDITime { 93 | $components = ( new TimeValueParser() )->parse( $value->getTime() ); 94 | 95 | return new \SMWDITime( 96 | $this->wbToSmwCalendarModel( $value->getCalendarModel() ), 97 | $components->get( 'datecomponents' )[0], 98 | $components->get( 'datecomponents' )[2], 99 | $components->get( 'datecomponents' )[4], 100 | $components->get( 'hours' ), 101 | $components->get( 'minutes' ), 102 | $components->get( 'seconds' ), 103 | ); 104 | } 105 | 106 | private function wbToSmwCalendarModel( string $wbCalendarModel ): int { 107 | $julianModel = 'http://www.wikidata.org/entity/Q1985786'; 108 | return $wbCalendarModel === $julianModel ? SMWDITime::CM_JULIAN : SMWDITime::CM_GREGORIAN; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /tests/Unit/Translation/DataValueTranslatorTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 32 | new \SMWDIBlob( 'foo' ), 33 | $this->translate( new StringValue( 'foo' ) ) 34 | ); 35 | } 36 | 37 | private function translate( DataValue $dataValue, string $propertyType = '' ): \SMWDataItem { 38 | return ( new DataValueTranslator() )->translate( new TypedDataValue( $propertyType, $dataValue ) ); 39 | } 40 | 41 | public function testBoolean() { 42 | $this->assertEquals( 43 | new \SMWDIBoolean( false ), 44 | $this->translate( new BooleanValue( false ) ) 45 | ); 46 | } 47 | 48 | public function testNumber() { 49 | $this->assertEquals( 50 | new \SMWDINumber( 42 ), 51 | $this->translate( new NumberValue( 42 ) ) 52 | ); 53 | } 54 | 55 | public function testGlobeCoordinate() { 56 | $this->assertEquals( 57 | SMWDIGeoCoord::newFromLatLong( 1.006, 1.47 ), 58 | $this->translate( new GlobeCoordinateValue( 59 | new LatLongValue( 1.006, 1.47 ), 60 | 0.01, 61 | ) ) 62 | ); 63 | } 64 | 65 | public function testTranslateEntityId() { 66 | $this->assertEquals( 67 | new DIWikiPage( 'Q1', WB_NS_ITEM ), 68 | $this->translate( new EntityIdValue( new ItemId( 'Q1' ) ) ) 69 | ); 70 | 71 | $this->assertEquals( 72 | new DIWikiPage( 'P42', WB_NS_PROPERTY ), 73 | $this->translate( new EntityIdValue( new PropertyId( 'P42' ) ) ) 74 | ); 75 | } 76 | 77 | public function testTranslateDecimalValue() { 78 | $this->assertEquals( 79 | new \SMWDINumber( 1337.42 ), 80 | $this->translate( new DecimalValue( 1337.42 ) ) 81 | ); 82 | } 83 | 84 | public function testTranslateUrlValue() { 85 | $this->assertEquals( 86 | new SMWDIUri( 'https', 'www.EntropyWins.wtf/mediawiki', 'hello', 'there' ), 87 | $this->translate( new StringValue( 'https://www.EntropyWins.wtf/mediawiki?hello#there' ), 'url' ) 88 | ); 89 | } 90 | 91 | public function testTranslateGregorianTimeValue() { 92 | $this->assertEquals( 93 | new \SMWDITime( 94 | \SMWDITime::CM_GREGORIAN, 95 | 2020, 96 | 06, 97 | 27, 98 | 19, 99 | 21, 100 | 42 101 | ), 102 | $this->translate( 103 | new TimeValue( 104 | '+2020-06-27T19:21:42Z', 105 | 0, 106 | 0, 107 | 0, 108 | 11, 109 | 'http://www.wikidata.org/entity/Q1985727' 110 | ), 111 | 'time' 112 | ) 113 | ); 114 | } 115 | 116 | public function testTranslateJulianTimeValue() { 117 | $this->assertEquals( 118 | new \SMWDITime( 119 | \SMWDITime::CM_JULIAN, 120 | 2020, 121 | 06, 122 | 27, 123 | 0, 124 | 0, 125 | 0 126 | ), 127 | $this->translate( 128 | new TimeValue( 129 | '+2020-06-27T00:00:00Z', 130 | 0, 131 | 10, 132 | 20, 133 | 11, 134 | 'http://www.wikidata.org/entity/Q1985786' 135 | ), 136 | 'time' 137 | ) 138 | ); 139 | } 140 | 141 | public function testTranslateExternalId() { 142 | $this->assertEquals( 143 | new \SMWDIBlob( '978-84-339-1247-3' ), 144 | $this->translate( new StringValue( '978-84-339-1247-3' ), 'external-id' ) 145 | ); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /tests/Unit/Translation/ContainerValueTranslatorTest.php: -------------------------------------------------------------------------------- 1 | translate( 33 | new Statement( 34 | new PropertyValueSnak( 35 | new PropertyId( 'P1' ), 36 | new QuantityValue( 37 | new DecimalValue( 42 ), 38 | 'mega awesome', 39 | new DecimalValue( 42.49 ), 40 | new DecimalValue( 41.51 ), 41 | ) 42 | ) 43 | ) 44 | ); 45 | 46 | $semanticData = $container->getSemanticData(); 47 | 48 | $this->assertEquals( 49 | [ new \SMWDINumber( 42 ) ], 50 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_VALUE ) ) 51 | ); 52 | 53 | $this->assertEquals( 54 | [ new \SMWDINumber( 41.51 ) ], 55 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_LOWER_BOUND ) ) 56 | ); 57 | 58 | $this->assertEquals( 59 | [ new \SMWDINumber( 42.49 ) ], 60 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_UPPER_BOUND ) ) 61 | ); 62 | 63 | $this->assertEquals( 64 | [ new \SMWDIBlob( 'mega awesome' ) ], 65 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_UNIT ) ) 66 | ); 67 | } 68 | 69 | public function testTranslateUnboundedQuantity() { 70 | $container = $this->translate( 71 | new Statement( 72 | new PropertyValueSnak( 73 | new PropertyId( 'P1' ), 74 | new UnboundedQuantityValue( 75 | new DecimalValue( 42 ), 76 | 'mega awesome' 77 | ) 78 | ) 79 | ) 80 | ); 81 | 82 | $semanticData = $container->getSemanticData(); 83 | 84 | $this->assertEquals( 85 | [ new \SMWDINumber( 42 ) ], 86 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_VALUE ) ) 87 | ); 88 | 89 | $this->assertEquals( 90 | [ new \SMWDIBlob( 'mega awesome' ) ], 91 | $semanticData->getPropertyValues( new DIProperty( FixedProperties::QUANTITY_UNIT ) ) 92 | ); 93 | } 94 | 95 | private function translate( Statement $statement ): SMWDIContainer { 96 | return $this->newTranslator()->statementToDataItem( 97 | $statement, 98 | $this->getSubject() 99 | ); 100 | } 101 | 102 | private function newTranslator(): ContainerValueTranslator { 103 | return TestFactory::newTestInstance()->getContainerValueTranslator(); 104 | } 105 | 106 | private function getSubject(): DIWikiPage { 107 | return new DIWikiPage( self::SUBJECT_TITLE, self::SUBJECT_NAMESPACE ); 108 | } 109 | 110 | public function testSupportsQuantityStatements() { 111 | $this->assertTrue( $this->newTranslator()->supportsStatement( 112 | new Statement( 113 | new PropertyValueSnak( 114 | new PropertyId( 'P1' ), 115 | new UnboundedQuantityValue( 116 | new DecimalValue( 42 ), 117 | 'mega awesome' 118 | ) 119 | ) 120 | ) 121 | ) ); 122 | } 123 | 124 | public function testDoesNotSupportNonValueStatements() { 125 | $this->assertFalse( $this->newTranslator()->supportsStatement( 126 | new Statement( 127 | new PropertySomeValueSnak( 128 | new PropertyId( 'P1' ) 129 | ) 130 | ) 131 | ) ); 132 | } 133 | 134 | public function testDoesNotSupportBooleanValueStatements() { 135 | $this->assertFalse( $this->newTranslator()->supportsStatement( 136 | new Statement( 137 | new PropertyValueSnak( 138 | new PropertyId( 'P1' ), 139 | new BooleanValue( true ) 140 | ) 141 | ) 142 | ) ); 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /tests/Unit/Translation/ItemTranslatorTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 34 | [], 35 | $this->translate( new Item() )->getDataItemsForProperty( FixedProperties::ID ) 36 | ); 37 | } 38 | 39 | private function translate( Item $item ): SemanticEntity { 40 | return TestFactory::newTestInstance()->newItemTranslator( 41 | DIWikiPage::newFromTitle( \Title::newFromText( 'Item:Q1' ) ) 42 | )->translateItem( $item ); 43 | } 44 | 45 | public function testItemIdIsTranslated() { 46 | $item = new Item( new ItemId( 'Q1' ) ); 47 | 48 | $this->assertEquals( 49 | [ new \SMWDIBlob( 'Q1' ) ], 50 | $this->translate( $item )->getDataItemsForProperty( FixedProperties::ID ) 51 | ); 52 | } 53 | 54 | public function testStatementMainSnakValueIsTranslated() { 55 | $item = new Item( new ItemId( 'Q1' ) ); 56 | $item->getStatements()->addNewStatement( new PropertyValueSnak( 57 | new PropertyId( 'P1' ), 58 | new StringValue( 'Hello there' ) 59 | ) ); 60 | 61 | $this->assertEquals( 62 | [ new \SMWDIBlob( 'Hello there' ) ], 63 | $this->translate( $item )->getDataItemsForProperty( '___SWB_P1' ) 64 | ); 65 | } 66 | 67 | public function testMultipleStatementsForOneProperty() { 68 | $item = new Item( new ItemId( 'Q1' ) ); 69 | 70 | $item->getStatements()->addNewStatement( new PropertyValueSnak( 71 | new PropertyId( 'P1' ), 72 | new StringValue( 'Hello there' ) 73 | ) ); 74 | 75 | $item->getStatements()->addNewStatement( new PropertyValueSnak( 76 | new PropertyId( 'P1' ), 77 | new StringValue( 'fellow sentient' ) 78 | ) ); 79 | 80 | $this->assertEquals( 81 | [ 82 | new \SMWDIBlob( 'Hello there' ), 83 | new \SMWDIBlob( 'fellow sentient' ) 84 | ], 85 | $this->translate( $item )->getDataItemsForProperty( '___SWB_P1' ) 86 | ); 87 | } 88 | 89 | public function testOnlyPropertyValueSnaksGetAdded() { 90 | $item = new Item( new ItemId( 'Q1' ) ); 91 | 92 | $item->getStatements()->addNewStatement( new PropertyNoValueSnak( new PropertyId( 'P1' ) ) ); 93 | $item->getStatements()->addNewStatement( new PropertySomeValueSnak( new PropertyId( 'P1' ) ) ); 94 | 95 | $this->assertSame( 96 | [], 97 | $this->translate( $item )->getDataItemsForProperty( '___SWB_P1' ) 98 | ); 99 | } 100 | 101 | public function testTranslationToSubobject() { 102 | $item = new Item( new ItemId( 'Q1' ) ); 103 | 104 | $item->getStatements()->addNewStatement( 105 | new PropertyValueSnak( 106 | new PropertyId( 'P1' ), 107 | new UnboundedQuantityValue( 108 | new DecimalValue( 42 ), 109 | 'mega awesome' 110 | ) 111 | ), 112 | null, 113 | null, 114 | 'Q1$d40469c7-4586-70f5-7a75-cccef9381c4c' 115 | ); 116 | 117 | $semanticEntity = $this->translate( $item ); 118 | 119 | $this->assertEquals( 120 | [ 121 | new DIWikiPage( 122 | 'Q1', 123 | WB_NS_ITEM, 124 | '', 125 | 'Q1$d40469c7-4586-70f5-7a75-cccef9381c4c' 126 | ) 127 | ], 128 | $semanticEntity->getDataItemsForProperty( '___SWB_P1' ) 129 | ); 130 | 131 | $this->assertHasSubobjectWithPropertyValue( 132 | $semanticEntity, 133 | FixedProperties::QUANTITY_VALUE, 134 | new \SMWDINumber( 42 ) 135 | ); 136 | } 137 | 138 | private function assertHasSubobjectWithPropertyValue( SemanticEntity $semanticEntity, string $propertyId, \SMWDataItem $expectedDataItem ) { 139 | /** 140 | * @var SMWDIContainer $container 141 | */ 142 | $container = $semanticEntity->getDataItemsForProperty( DIProperty::TYPE_SUBOBJECT )[0]; 143 | 144 | $this->assertEquals( 145 | [ $expectedDataItem ], 146 | $container->getSemanticData()->getPropertyValues( new DIProperty( $propertyId ) ) 147 | ); 148 | } 149 | 150 | public function testItemLabelsAreTranslated() { 151 | $item = new Item( new ItemId( 'Q1' ) ); 152 | $item->setLabel( 'en', 'English' ); 153 | $item->setLabel( 'de', 'German' ); 154 | 155 | $this->assertHasMonolingualTexts( 156 | [ 157 | new MonolingualTextValue( 'en', 'English' ), 158 | new MonolingualTextValue( 'de', 'German' ) 159 | ], 160 | $this->translate( $item )->getDataItemsForProperty( FixedProperties::LABEL ) 161 | ); 162 | } 163 | 164 | public function testItemDescriptionsAreTranslated() { 165 | $item = new Item( new ItemId( 'Q1' ) ); 166 | $item->setDescription( 'en', 'English' ); 167 | $item->setDescription( 'de', 'German' ); 168 | 169 | $this->assertHasMonolingualTexts( 170 | [ 171 | new MonolingualTextValue( 'en', 'English' ), 172 | new MonolingualTextValue( 'de', 'German' ) 173 | ], 174 | $this->translate( $item )->getDataItemsForProperty( FixedProperties::DESCRIPTION ) 175 | ); 176 | } 177 | 178 | public function testItemAliasesAreTranslated() { 179 | $item = new Item( new ItemId( 'Q1' ) ); 180 | $item->setAliases( 'en', [ 'Cat', 'Kittens' ] ); 181 | $item->setAliases( 'de', [ 'Katze' ] ); 182 | 183 | $this->assertHasMonolingualTexts( 184 | [ 185 | new MonolingualTextValue( 'en', 'Cat' ), 186 | new MonolingualTextValue( 'en', 'Kittens' ), 187 | new MonolingualTextValue( 'de', 'Katze' ) 188 | ], 189 | $this->translate( $item )->getDataItemsForProperty( FixedProperties::ALIAS ) 190 | ); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Semantic Wikibase 2 | 3 | MediaWiki extension that makes [Wikibase] data available in [Semantic MediaWiki]. 4 | 5 | Semantic Wikibase was created by [Wikibase developers](https://professional.wiki/en/wikibase-software-development) at [Professional Wiki](https://professional.wiki/) 6 | with help from [Frans AL van der Horst](https://www.linkedin.com/in/frans-a-l-van-der-horst-95a3836/). 7 | 8 | ## Demo 9 | 10 | 11 | 12 | ## Platform requirements 13 | 14 | * [PHP] 7.4 or later 15 | * [MediaWiki] 1.35.x 16 | * [Semantic MediaWiki] 3.2.x 17 | * [Wikibase Repository] branch: REL1_35 18 | 19 | ## Installation 20 | 21 | First install MediaWiki, Semantic MediaWiki and Wikibase Repository. 22 | 23 | The recommended way to install Semantic Wikibase is using [Composer](https://getcomposer.org) with 24 | [MediaWiki's built-in support for Composer](https://professional.wiki/en/articles/installing-mediawiki-extensions-with-composer). 25 | 26 | On the commandline, go to your wikis root directory. Then run these two commands: 27 | 28 | ```shell script 29 | COMPOSER=composer.local.json composer require --no-update professional-wiki/semantic-wikibase:* 30 | composer update professional-wiki/semantic-wikibase --no-dev -o 31 | ``` 32 | 33 | Then enable the extension by adding the following to the bottom of your wikis `LocalSettings.php` file: 34 | 35 | ```php 36 | wfLoadExtension( 'SemanticWikibase' ); 37 | ``` 38 | 39 | You can verify the extension was enabled successfully by opening your wikis Special:Version page in your browser. 40 | 41 | As a final step you need to configure the property namespaces. See the configuration section. 42 | 43 | ## Translated data 44 | 45 | Data part of [Wikibase Items and properties] gets translated to Semantic MediaWiki property value pairs. 46 | 47 | ### Labels, descriptions, IDs, etc 48 | 49 | Translated data in the form `Wikibase datamodel element => SMW property name (SMW property type)` 50 | 51 | * EntityId => Wikibase ID (Text) 52 | * Labels => Wikibase label (Monolingual text) 53 | * Descriptions => Wikibase description (Monolingual text) 54 | * Aliases => Wikibase alias (Monolingual text) 55 | 56 | ### Statements 57 | 58 | When a [statement] is translated, only the value of the "main snak" is stored in SMW. 59 | 60 | The [SMW property] name is the ID of the Wikibase property, for instance P42. The label of the Wikibase 61 | property gets added as alias. This means both `[[P42::+]]` and `[[Capital city::+]]` are valid in SMW 62 | queries. 63 | 64 | Deprecated statements are never translated. Normal statements are not translated if there are preferred statements. 65 | The SMW property type is based on the [Wikibase property type]. Only statements with a supported property type are translated. 66 | 67 | ## Supported property types 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 |
Wikibase name (en)SMW name (en)Wikibase idSMW id
Commons media fileTextcommonsMedia_txt
External identifierExternal identifierexternal-id_eid
Geographic coordinatesGeographic coordinatesglobe-coordinate_geo
ItemPagewikibase-item_wpg
Monolingual textMonolingual textmonolingualtext_mlt_rec
Point in timeDatetime_dat
PropertyPagewikibase-property_wpg
QuantitySubobject (Page + Record)quantity_wpg + _rec
StringTextstring_txt
URLURLurl_uri
137 | 138 | Currently not supported types: 139 | 140 | * Entity Schema (entity-schema) 141 | * Geographic shape (geo-shape) 142 | * Tabular Data (tabular-data) 143 | 144 | ## Configuration 145 | 146 | You can configure Semantic Wikibase via [LocalSettings.php]. 147 | 148 | ### Property namespaces 149 | 150 | This is the only required configuration for setting up Semantic Wikibase. 151 | 152 | Wikibase and Semantic MediaWiki both add a Property namespace called "Property". This results in a conflict which 153 | can be resolved by renaming either the Wikibase property namespace or the Semantic MediaWiki property namespace. 154 | 155 | Renaming the Wikibase property namespace: 156 | 157 | ```php 158 | $wgExtraNamespaces[WB_NS_PROPERTY] = 'WikibaseProperty'; 159 | $wgExtraNamespaces[WB_NS_PROPERTY_TALK] = 'WikibaseProperty_talk'; 160 | ``` 161 | 162 | Renaming the SMW property namespace: 163 | 164 | ```php 165 | $wgExtensionFunctions[] = function() { 166 | $GLOBALS['wgExtraNamespaces'][SMW_NS_PROPERTY] = 'SemanticProperty'; 167 | $GLOBALS['wgExtraNamespaces'][SMW_NS_PROPERTY_TALK] = 'SemanticProperty_talk'; 168 | }; 169 | ``` 170 | 171 | You can choose what to rename these namespaces to. They do not need to be `WikibaseProperty` and/or `SemanticProperty`. 172 | As long as they are not the same, Semantic Wikibase will work. 173 | 174 | You will likely need to set these extra permissions to avoid getting errors when creating new items or properties: 175 | 176 | ```php 177 | $wgGroupPermissions['user']['item-create'] = true; 178 | $wgGroupPermissions['user']['property-create'] = true; 179 | ``` 180 | 181 | ### Property label language 182 | 183 | The language used for translation of property labels defaults to the wiki language (`$wgLanguageCode`). 184 | 185 | This means that if your wiki language is English, and you have a property P1 with Dutch label "lokatie" and 186 | English label "location", the name of the property in Semantic MediaWiki will be "location". 187 | 188 | You can specify a language different from your wiki language should be used. This is done with the 189 | `$wgSemanticWikibaseLanguage` setting. With the below example, the label for P1 would be "lokatie": 190 | 191 | ```php 192 | $wgSemanticWikibaseLanguage = 'nl'; 193 | ``` 194 | 195 | ### Disabling translation for an entity type / namespace 196 | 197 | ```php 198 | $smwgNamespacesWithSemanticLinks[WB_NS_ITEM] = false; 199 | $smwgNamespacesWithSemanticLinks[WB_NS_PROPERTY] = false; 200 | ``` 201 | 202 | ## Enhancement ideas 203 | 204 | Data translation: 205 | 206 | * Ability to whitelist or blacklist entities from being translated 207 | * Ability to whitelist or blacklist statements from being translated 208 | * Translation of qualifiers, references, statement rank and other non-main-snak data 209 | * Support for Entities other than Items and Properties 210 | * Translation of Item sitelinks 211 | 212 | Properties: 213 | 214 | * Detection and possibly prevention of property name conflicts between Wikibase and SMW 215 | * (Multilingual) descriptions of Wikibase properties on SMW property pages 216 | * Grouping of Wikibase properties on Special:Browse 217 | 218 | [Professional.Wiki] provides commercial [MediaWiki development], [Wikibase development], and [managed wiki hosting]. 219 | 220 | ## Release notes 221 | 222 | ### Version 0.1 223 | 224 | Released on September 3, 2020 225 | 226 | * [Initial release with support for main snaks and most data types](https://professional.wiki/en/news/semantic-wikibase-released) 227 | 228 | [Professional.Wiki]: https://professional.wiki 229 | [Semantic MediaWiki]: https://www.semantic-mediawiki.org 230 | [Wikibase]: https://wikibase.consulting/what-is-wikibase/ 231 | [MediaWiki]: https://www.mediawiki.org 232 | [PHP]: https://www.php.net 233 | [Wikibase Items and properties]: https://www.mediawiki.org/wiki/Wikibase/DataModel 234 | [statement]: https://www.mediawiki.org/wiki/Wikibase/DataModel#Statements 235 | [Wikibase property type]: https://www.mediawiki.org/wiki/Wikibase/DataModel#Datatypes_and_their_Values 236 | [SMW property]: https://www.semantic-mediawiki.org/wiki/Help:Properties_and_types 237 | [Wikibase Repository]: https://www.mediawiki.org/wiki/Extension:Wikibase_Repository 238 | [LocalSettings.php]: https://www.mediawiki.org/wiki/Manual:LocalSettings.php 239 | [MediaWiki development]: https://professional.wiki/en/mediawiki-development 240 | [Wikibase development]: https://professional.wiki/en/wikibase-software-development 241 | [managed wiki hosting]: https://professional.wiki/en/hosting 242 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The license text below "----" applies to all files within this distribution, other 2 | than those that are in a directory which contains files named "LICENSE" or 3 | "COPYING", or a subdirectory thereof. For those files, the license text contained in 4 | said file overrides any license information contained in directories of smaller depth. 5 | Alternative licenses are typically used for software that is provided by external 6 | parties, and merely packaged with this software for convenience. 7 | ---- 8 | 9 | GNU GENERAL PUBLIC LICENSE 10 | Version 2, June 1991 11 | 12 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 13 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 14 | Everyone is permitted to copy and distribute verbatim copies 15 | of this license document, but changing it is not allowed. 16 | 17 | Preamble 18 | 19 | The licenses for most software are designed to take away your 20 | freedom to share and change it. By contrast, the GNU General Public 21 | License is intended to guarantee your freedom to share and change free 22 | software--to make sure the software is free for all its users. This 23 | General Public License applies to most of the Free Software 24 | Foundation's software and to any other program whose authors commit to 25 | using it. (Some other Free Software Foundation software is covered by 26 | the GNU Lesser General Public License instead.) You can apply it to 27 | your programs, too. 28 | 29 | When we speak of free software, we are referring to freedom, not 30 | price. Our General Public Licenses are designed to make sure that you 31 | have the freedom to distribute copies of free software (and charge for 32 | this service if you wish), that you receive source code or can get it 33 | if you want it, that you can change the software or use pieces of it 34 | in new free programs; and that you know you can do these things. 35 | 36 | To protect your rights, we need to make restrictions that forbid 37 | anyone to deny you these rights or to ask you to surrender the rights. 38 | These restrictions translate to certain responsibilities for you if you 39 | distribute copies of the software, or if you modify it. 40 | 41 | For example, if you distribute copies of such a program, whether 42 | gratis or for a fee, you must give the recipients all the rights that 43 | you have. You must make sure that they, too, receive or can get the 44 | source code. And you must show them these terms so they know their 45 | rights. 46 | 47 | We protect your rights with two steps: (1) copyright the software, and 48 | (2) offer you this license which gives you legal permission to copy, 49 | distribute and/or modify the software. 50 | 51 | Also, for each author's protection and ours, we want to make certain 52 | that everyone understands that there is no warranty for this free 53 | software. If the software is modified by someone else and passed on, we 54 | want its recipients to know that what they have is not the original, so 55 | that any problems introduced by others will not reflect on the original 56 | authors' reputations. 57 | 58 | Finally, any free program is threatened constantly by software 59 | patents. We wish to avoid the danger that redistributors of a free 60 | program will individually obtain patent licenses, in effect making the 61 | program proprietary. To prevent this, we have made it clear that any 62 | patent must be licensed for everyone's free use or not licensed at all. 63 | 64 | The precise terms and conditions for copying, distribution and 65 | modification follow. 66 | 67 | GNU GENERAL PUBLIC LICENSE 68 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 69 | 70 | 0. This License applies to any program or other work which contains 71 | a notice placed by the copyright holder saying it may be distributed 72 | under the terms of this General Public License. The "Program", below, 73 | refers to any such program or work, and a "work based on the Program" 74 | means either the Program or any derivative work under copyright law: 75 | that is to say, a work containing the Program or a portion of it, 76 | either verbatim or with modifications and/or translated into another 77 | language. (Hereinafter, translation is included without limitation in 78 | the term "modification".) Each licensee is addressed as "you". 79 | 80 | Activities other than copying, distribution and modification are not 81 | covered by this License; they are outside its scope. The act of 82 | running the Program is not restricted, and the output from the Program 83 | is covered only if its contents constitute a work based on the 84 | Program (independent of having been made by running the Program). 85 | Whether that is true depends on what the Program does. 86 | 87 | 1. You may copy and distribute verbatim copies of the Program's 88 | source code as you receive it, in any medium, provided that you 89 | conspicuously and appropriately publish on each copy an appropriate 90 | copyright notice and disclaimer of warranty; keep intact all the 91 | notices that refer to this License and to the absence of any warranty; 92 | and give any other recipients of the Program a copy of this License 93 | along with the Program. 94 | 95 | You may charge a fee for the physical act of transferring a copy, and 96 | you may at your option offer warranty protection in exchange for a fee. 97 | 98 | 2. You may modify your copy or copies of the Program or any portion 99 | of it, thus forming a work based on the Program, and copy and 100 | distribute such modifications or work under the terms of Section 1 101 | above, provided that you also meet all of these conditions: 102 | 103 | a) You must cause the modified files to carry prominent notices 104 | stating that you changed the files and the date of any change. 105 | 106 | b) You must cause any work that you distribute or publish, that in 107 | whole or in part contains or is derived from the Program or any 108 | part thereof, to be licensed as a whole at no charge to all third 109 | parties under the terms of this License. 110 | 111 | c) If the modified program normally reads commands interactively 112 | when run, you must cause it, when started running for such 113 | interactive use in the most ordinary way, to print or display an 114 | announcement including an appropriate copyright notice and a 115 | notice that there is no warranty (or else, saying that you provide 116 | a warranty) and that users may redistribute the program under 117 | these conditions, and telling the user how to view a copy of this 118 | License. (Exception: if the Program itself is interactive but 119 | does not normally print such an announcement, your work based on 120 | the Program is not required to print an announcement.) 121 | 122 | These requirements apply to the modified work as a whole. If 123 | identifiable sections of that work are not derived from the Program, 124 | and can be reasonably considered independent and separate works in 125 | themselves, then this License, and its terms, do not apply to those 126 | sections when you distribute them as separate works. But when you 127 | distribute the same sections as part of a whole which is a work based 128 | on the Program, the distribution of the whole must be on the terms of 129 | this License, whose permissions for other licensees extend to the 130 | entire whole, and thus to each and every part regardless of who wrote it. 131 | 132 | Thus, it is not the intent of this section to claim rights or contest 133 | your rights to work written entirely by you; rather, the intent is to 134 | exercise the right to control the distribution of derivative or 135 | collective works based on the Program. 136 | 137 | In addition, mere aggregation of another work not based on the Program 138 | with the Program (or with a work based on the Program) on a volume of 139 | a storage or distribution medium does not bring the other work under 140 | the scope of this License. 141 | 142 | 3. You may copy and distribute the Program (or a work based on it, 143 | under Section 2) in object code or executable form under the terms of 144 | Sections 1 and 2 above provided that you also do one of the following: 145 | 146 | a) Accompany it with the complete corresponding machine-readable 147 | source code, which must be distributed under the terms of Sections 148 | 1 and 2 above on a medium customarily used for software interchange; or, 149 | 150 | b) Accompany it with a written offer, valid for at least three 151 | years, to give any third party, for a charge no more than your 152 | cost of physically performing source distribution, a complete 153 | machine-readable copy of the corresponding source code, to be 154 | distributed under the terms of Sections 1 and 2 above on a medium 155 | customarily used for software interchange; or, 156 | 157 | c) Accompany it with the information you received as to the offer 158 | to distribute corresponding source code. (This alternative is 159 | allowed only for noncommercial distribution and only if you 160 | received the program in object code or executable form with such 161 | an offer, in accord with Subsection b above.) 162 | 163 | The source code for a work means the preferred form of the work for 164 | making modifications to it. For an executable work, complete source 165 | code means all the source code for all modules it contains, plus any 166 | associated interface definition files, plus the scripts used to 167 | control compilation and installation of the executable. However, as a 168 | special exception, the source code distributed need not include 169 | anything that is normally distributed (in either source or binary 170 | form) with the major components (compiler, kernel, and so on) of the 171 | operating system on which the executable runs, unless that component 172 | itself accompanies the executable. 173 | 174 | If distribution of executable or object code is made by offering 175 | access to copy from a designated place, then offering equivalent 176 | access to copy the source code from the same place counts as 177 | distribution of the source code, even though third parties are not 178 | compelled to copy the source along with the object code. 179 | 180 | 4. You may not copy, modify, sublicense, or distribute the Program 181 | except as expressly provided under this License. Any attempt 182 | otherwise to copy, modify, sublicense or distribute the Program is 183 | void, and will automatically terminate your rights under this License. 184 | However, parties who have received copies, or rights, from you under 185 | this License will not have their licenses terminated so long as such 186 | parties remain in full compliance. 187 | 188 | 5. You are not required to accept this License, since you have not 189 | signed it. However, nothing else grants you permission to modify or 190 | distribute the Program or its derivative works. These actions are 191 | prohibited by law if you do not accept this License. Therefore, by 192 | modifying or distributing the Program (or any work based on the 193 | Program), you indicate your acceptance of this License to do so, and 194 | all its terms and conditions for copying, distributing or modifying 195 | the Program or works based on it. 196 | 197 | 6. Each time you redistribute the Program (or any work based on the 198 | Program), the recipient automatically receives a license from the 199 | original licensor to copy, distribute or modify the Program subject to 200 | these terms and conditions. You may not impose any further 201 | restrictions on the recipients' exercise of the rights granted herein. 202 | You are not responsible for enforcing compliance by third parties to 203 | this License. 204 | 205 | 7. If, as a consequence of a court judgment or allegation of patent 206 | infringement or for any other reason (not limited to patent issues), 207 | conditions are imposed on you (whether by court order, agreement or 208 | otherwise) that contradict the conditions of this License, they do not 209 | excuse you from the conditions of this License. If you cannot 210 | distribute so as to satisfy simultaneously your obligations under this 211 | License and any other pertinent obligations, then as a consequence you 212 | may not distribute the Program at all. For example, if a patent 213 | license would not permit royalty-free redistribution of the Program by 214 | all those who receive copies directly or indirectly through you, then 215 | the only way you could satisfy both it and this License would be to 216 | refrain entirely from distribution of the Program. 217 | 218 | If any portion of this section is held invalid or unenforceable under 219 | any particular circumstance, the balance of the section is intended to 220 | apply and the section as a whole is intended to apply in other 221 | circumstances. 222 | 223 | It is not the purpose of this section to induce you to infringe any 224 | patents or other property right claims or to contest validity of any 225 | such claims; this section has the sole purpose of protecting the 226 | integrity of the free software distribution system, which is 227 | implemented by public license practices. Many people have made 228 | generous contributions to the wide range of software distributed 229 | through that system in reliance on consistent application of that 230 | system; it is up to the author/donor to decide if he or she is willing 231 | to distribute software through any other system and a licensee cannot 232 | impose that choice. 233 | 234 | This section is intended to make thoroughly clear what is believed to 235 | be a consequence of the rest of this License. 236 | 237 | 8. If the distribution and/or use of the Program is restricted in 238 | certain countries either by patents or by copyrighted interfaces, the 239 | original copyright holder who places the Program under this License 240 | may add an explicit geographical distribution limitation excluding 241 | those countries, so that distribution is permitted only in or among 242 | countries not thus excluded. In such case, this License incorporates 243 | the limitation as if written in the body of this License. 244 | 245 | 9. The Free Software Foundation may publish revised and/or new versions 246 | of the General Public License from time to time. Such new versions will 247 | be similar in spirit to the present version, but may differ in detail to 248 | address new problems or concerns. 249 | 250 | Each version is given a distinguishing version number. If the Program 251 | specifies a version number of this License which applies to it and "any 252 | later version", you have the option of following the terms and conditions 253 | either of that version or of any later version published by the Free 254 | Software Foundation. If the Program does not specify a version number of 255 | this License, you may choose any version ever published by the Free Software 256 | Foundation. 257 | 258 | 10. If you wish to incorporate parts of the Program into other free 259 | programs whose distribution conditions are different, write to the author 260 | to ask for permission. For software which is copyrighted by the Free 261 | Software Foundation, write to the Free Software Foundation; we sometimes 262 | make exceptions for this. Our decision will be guided by the two goals 263 | of preserving the free status of all derivatives of our free software and 264 | of promoting the sharing and reuse of software generally. 265 | 266 | NO WARRANTY 267 | 268 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 269 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 270 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 271 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 272 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 273 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 274 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 275 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 276 | REPAIR OR CORRECTION. 277 | 278 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 279 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 280 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 281 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 282 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 283 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 284 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 285 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 286 | POSSIBILITY OF SUCH DAMAGES. 287 | 288 | END OF TERMS AND CONDITIONS 289 | 290 | How to Apply These Terms to Your New Programs 291 | 292 | If you develop a new program, and you want it to be of the greatest 293 | possible use to the public, the best way to achieve this is to make it 294 | free software which everyone can redistribute and change under these terms. 295 | 296 | To do so, attach the following notices to the program. It is safest 297 | to attach them to the start of each source file to most effectively 298 | convey the exclusion of warranty; and each file should have at least 299 | the "copyright" line and a pointer to where the full notice is found. 300 | 301 | 302 | Copyright (C) 303 | 304 | This program is free software; you can redistribute it and/or modify 305 | it under the terms of the GNU General Public License as published by 306 | the Free Software Foundation; either version 2 of the License, or 307 | (at your option) any later version. 308 | 309 | This program is distributed in the hope that it will be useful, 310 | but WITHOUT ANY WARRANTY; without even the implied warranty of 311 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 312 | GNU General Public License for more details. 313 | 314 | You should have received a copy of the GNU General Public License along 315 | with this program; if not, write to the Free Software Foundation, Inc., 316 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 317 | 318 | Also add information on how to contact you by electronic and paper mail. 319 | 320 | If the program is interactive, make it output a short notice like this 321 | when it starts in an interactive mode: 322 | 323 | Gnomovision version 69, Copyright (C) year name of author 324 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 325 | This is free software, and you are welcome to redistribute it 326 | under certain conditions; type `show c' for details. 327 | 328 | The hypothetical commands `show w' and `show c' should show the appropriate 329 | parts of the General Public License. Of course, the commands you use may 330 | be called something other than `show w' and `show c'; they could even be 331 | mouse-clicks or menu items--whatever suits your program. 332 | 333 | You should also get your employer (if you work as a programmer) or your 334 | school, if any, to sign a "copyright disclaimer" for the program, if 335 | necessary. Here is a sample; alter the names: 336 | 337 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 338 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 339 | 340 | , 1 April 1989 341 | Ty Coon, President of Vice 342 | 343 | This General Public License does not permit incorporating your program into 344 | proprietary programs. If your program is a subroutine library, you may 345 | consider it more useful to permit linking proprietary applications with the 346 | library. If this is what you want to do, use the GNU Lesser General 347 | Public License instead of this License. 348 | --------------------------------------------------------------------------------