diff --git "a/include/class-wb\\Claim.php" "b/include/class-wb\\Claim.php" index 94b0be5..ef2fafb 100644 --- "a/include/class-wb\\Claim.php" +++ "b/include/class-wb\\Claim.php" @@ -1,448 +1,471 @@ . # Wikibase namespace wb; /** * A Claim consists of a Snak and Qualifiers. * * Optionally, it can have qualifiers. * * @see https://www.wikidata.org/wiki/Wikidata:Glossary#Claim */ class Claim { /** * When a claim has not a main snak, we assume that it has this dummy property. * * It's very useful to send claims to be deleted. It works. asd * * @see https://phabricator.wikimedia.org/T203572 */ const DUMMY_PROPERTY = 'ASD-ASD-ASD'; /** * The 'mainsnak' is a Snak * * @var Snak|null */ private $mainsnak; /** * Claim ID * * @var string|null */ private $id; /** * Claim rank */ private $rank; /** * Qualifiers collector * * @var Snaks */ private $qualifiers; /** * References collector * * @TODO: it has also an hash! * * @var References */ private $references; /** * Constructor * * @param $mainsnak Snak|null Main snak */ public function __construct( $mainsnak = null ) { // eventually initialize mainsnak if( $mainsnak ) { $this->setMainsnak( $mainsnak ); } // initialize snaks collector $this->qualifiers = new Snaks(); // initialize references collector $this->references = new References(); } /** * Check if there is a mainsnak * * @return boolean */ public function hasMainsnak() { return isset( $this->mainsnak ); } /** * Get the mainsnak * * @return Snak|null */ public function getMainsnak() { return $this->mainsnak; } /** * Get a property. Also a dummy one. NOW. asd * * @return string */ public function getPropertyAlsoDummy() { $snak = $this->getMainsnak(); return $snak ? $snak->getProperty() : self::DUMMY_PROPERTY; } /** * Set the mainsnak * * @param $mainsnak Snak|null * @return self */ public function setMainsnak( $mainsnak ) { $this->mainsnak = $mainsnak; return $this; } /** * Set the qualifiers (snaks) * * @param object $qualifiers * @return self */ public function setQualifiers( Snaks $qualifiers ) { $this->qualifiers = $qualifiers; return $this; } /** * Set the references * * @param object $references * @return self */ public function setReferences( References $references ) { $this->references = $references; return $this; } /** * Add a qualifier (snak) * * @param object $qualifier Qualifier (snak) * @return self */ public function addQualifier( Snak $qualifier ) { $this->qualifiers->add( $qualifier ); return $this; } /** * Add a reference * * @param object $reference Reference * @return self */ public function addReference( Reference $reference ) { $this->references->add( $reference ); return $this; } /** * Check if the claim has at least a qualifier * * @return boolean */ public function hasQualifiers() { return !$this->qualifiers->isEmpty(); } /** * Check if the claim has at least a reference * * @return boolean */ public function hasReferences() { return !$this->references->isEmpty(); } /** * Check if there are qualifiers related to a property * * @param $property string e.g. 'P123' * @return boolean */ public function hasQualifiersInProperty( $property ) { return $this->qualifiers->hasInProperty( $property ); } /** * Check if there are references related to a property * * @param $property string e.g. 'P123' * @return boolean */ public function hasReferencesInProperty( $property ) { return $this->references->hasInProperty( $property ); } /** * Get all the qualifiers indexed by property (they are snaks) * * @return array */ public function getQualifiers() { return $this->qualifiers->getAll(); } /** * Get all the qualifiers indexed by property (they are snaks) * * @return array */ public function getReferences() { return $this->references->getAll(); } /** * Get all the qualifiers that are related to a property (they are snaks) * * @param $property string e.g. 'P123' * @return array */ public function getQualifiersInProperty( $property ) { return $this->qualifiers->getInProperty( $property ); } /** * Get all the references that are related to a property (they are snaks) * * @param $property string e.g. 'P123' * @return array */ public function getReferencesInProperty( $property ) { return $this->references->getInProperty( $property ); } /** * Check if there is an ID * * @return boolean */ public function hasID() { return isset( $this->id ); } /** * Set the claim ID * * @param $id string * @return string */ public function setID( $id ) { $this->id = $id; return $this; } /** * Get the claim ID * * @return string */ public function getID() { if( !$this->hasID() ) { throw new \Exception( 'missing id' ); } return $this->id; } /** * Check if the Rank is set * * @return boolean */ public function hasRank() { return isset( $this->rank ); } /** * Get the claim rank * * @return string */ public function getRank() { return $this->rank; } /** * Set the Claim rank * * @param string $rank * @return self */ public function setRank( $rank ) { $this->rank = $rank; return $this; } /** * Check if the Rank of the Claim is 'deprecated' * * @return boolean */ public function isDeprecated() { return $this->getRank() === 'deprecated'; } /** * Check if this claim is marked for removal * * @return array */ public function isMarkedForRemoval() { return isset( $this->remove ) && $this->remove; } /** * Mark this claim as to be remove * * @see https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity * @return self */ public function markForRemoval() { $this->remove = 1; return $this; } /** * Clone this claim and obtain a claim marked for removal * * @return self */ public function cloneForRemoval() { return ( new self() ) ->setID( $this->getID() ) ->markForRemoval(); } /** * Create a claim from raw data returned from API responses * * @param $data array * @return self */ public static function createFromData( $data ) { // wtf is this shit if( ! isset( $data['mainsnak'] ) ) { throw new WrongDataException( __CLASS__ ); } // initialize the claim $claim = new static( Snak::createFromData( $data[ 'mainsnak' ] ) ); // add qualifiers if( isset( $data['qualifiers'] ) ) { foreach( $data['qualifiers'] as $property => $qualifier_raws ) { foreach( $qualifier_raws as $qualifier_raw ) { $qualifier = Snak::createFromData( $qualifier_raw ); $claim->addQualifier( $qualifier ); } } } // add references if( isset( $data['references'] ) ) { $claim->setReferences( References::createFromData( $data['references'] ) ); } // claim ID if( isset( $data[ 'id' ] ) ) { $claim->setID( $data[ 'id' ] ); } // set rank if( isset( $data['rank'] ) ) { $claim->setRank( $data['rank'] ); } return $claim; } /** * Convert this object to an associative array suitable for JSON encoding * * @return array */ public function toData() { $data = []; // mainsnak if( $this->hasMainsnak() ) { $data['mainsnak'] = $this->getMainsnak()->toData(); } // id if( $this->hasID() ) { $data['id'] = $this->getID(); } // rank if( $this->hasRank() ) { $data['rank'] = $this->getRank(); } // qualifiers if( $this->hasQualifiers() ) { $data['qualifiers'] = $this->qualifiers->toData(); } // references if( $this->hasReferences() ) { $data['references'] = $this->references->toData(); } return $data; } /** - * @override + * Get a wikitext-compatible version of this value + * + * This may be awared about which is the wiki that will contain this value, + * in order to properly choose a correct permalink in wikilinks etc. + * + * See https://gitpull.it/T221 + * + * @param $site You can eventually specify in which site you want to print this value */ - public function __toString() { + public function toPrintableWikitext( \mw\Site $site = null ) { + + // if it's a Snak, just print the DataValue $snak = $this->getMainsnak(); if( $snak ) { - return $snak->getDataValue(); + return $snak + ->getDataValue() + ->toPrintableWikitext( $site ); } + + // eventually show that this Claim is marked for removal $id = $this->getID(); if( $id && $this->isMarkedForRemoval() ) { return "remove claim id = $id"; } + + // I really can't figure out what do you want to do with me throw new \Exception( 'empty claim' ); } + + /** + * @override + */ + public function __toString() { + throw new Exception("asd"); + return $this->toPrintableWikitext(); + } } diff --git "a/include/class-wb\\DataModel.php" "b/include/class-wb\\DataModel.php" index 546a32f..8e9be9e 100644 --- "a/include/class-wb\\DataModel.php" +++ "b/include/class-wb\\DataModel.php" @@ -1,551 +1,551 @@ . # Wikibase namespace wb; use \mw\WikibaseSite; /** * Wikibase data container * * It can be used to abstract a Wikibase Entity and * remove/add labels, descriptions, attributes, * references, etc. * * It has shortcuts to directly talk with the APIs, * generate a smart edit summary, printing changes, etc. */ class DataModel { /** * Dependency injection of a Wikibase site * * @var WikibaseSite|null */ private $site; /** * Dependency injection of the entity Q-ID this data refers to * * @var string|null */ private $entityID; /** * @var Labels */ private $labels; /** * @var Descriptions */ private $descriptions; /* * @var Claims */ private $claims; /* * @var Sitelinks */ private $sitelinks; /** * Constructor * * @param $site WikibaseSite * @param $entity_id string Entity Q-ID */ public function __construct( $site = null, $entity_id = null ) { $this->site = $site; $this->labels = new Labels(); $this->descriptions = new Descriptions(); $this->claims = new Claims(); $this->setSitelinks( new Sitelinks() ); if( $entity_id ) { $this->setEntityID( $entity_id ); } } /** * Get the Wikibase site * * @return WikibaseSite */ public function getWikibaseSite() { if( ! isset( $this->site ) ) { throw new \Exception( 'can\'t access to undefined Wikibase site' ); } return $this->site; } /** * Set the entity Q-ID * * @param $entity_id string Q-ID * @return self */ public function setEntityID( $entity_id ) { $this->entityID = $entity_id; return $this; } /** * Check if it has the entity Q-ID * * @return bool */ public function hasEntityID() { return isset( $this->entityID ); } /** * Get the entity Q-ID * * @return string */ public function getEntityID() { if( isset( $this->entityID ) ) { return $this->entityID; } throw new \Exception( 'undefined entity ID' ); } /** * Get all the labels * * @return array */ public function getLabels() { return $this->labels->getAll(); } /** * Get all the descriptions * * @return array */ public function getDescriptions() { return $this->descriptions->getAll(); } /** * Get all the claims * * @return array */ public function getClaims() { return $this->claims->getAll(); } /** * Get all the sitelinks * * @return array */ public function getSitelinks() { return $this->sitelinks; } /** * Set all the sitelinks * * @param $sitelinks * @return array */ public function setSitelinks( Sitelinks $sitelinks ) { $this->sitelinks = $sitelinks; return $this; } /** * Get all the claims grouped by property * * n.b. The claims without a property will be indexed by 'asd' * * @return array */ public function getClaimsGrouped() { return $this->claims->getAllGrouped(); } /** * Add a claim * * @param $claim Claim * @return self */ public function addClaim( $claim ) { $this->claims->add( $claim ); return $this; } /** * Set claims * * @param $claims array * @return self */ public function setClaims( $claims ) { $this->claims->set( $claims ); return $this; } /** * Check if some claims exist in a certain property * * @param $property string * @return bool */ public function hasClaimsInProperty( $property ) { return $this->claims->haveProperty( $property ); } /** * Get claims in a property * * @param $property string * @return array */ public function getClaimsInProperty( $property ) { return $this->claims->getInProperty( $property ); } /** * Has sitelink in site * * @param $site string * @return boolean */ public function hasSitelinkInSite( $site ) { return false !== $this->getSitelinkInSite( $site ); } /** * Count all the claims * * @return int */ public function countClaims() { return $this->claims->count(); } /** * Check if it's empty * * @return bool */ public function isEmpty() { return ! $this->countClaims() && ! $this->getLabels() && ! $this->getDescriptions() && ! $this->sitelinks->getAll(); } /** * Check if a label of a certain language exists * * @param $language string * @return bool */ public function hasLabelInLanguage( $language ) { return $this->labels->have( $language ); } /** * Set, delete, preserve if it exists, a label. */ public function setLabel( $label ) { $this->labels->set( $label ); return $this; } /** * Check if a label of a certain language exists * * @param $language string * @return bool */ public function hasDescriptionInLanguage( $language ) { return $this->descriptions->have( $language ); } /** * Set, delete, preserve if it exists, a description. * * @param $description Description * @return self */ public function setDescription( $description ) { $this->descriptions->set( $description ); return $this; } /** * Get a pure data rappresentation * * @param $clear bool Flag to be enabled to strip out empty elements * @return array */ public function get( $clear = false ) { $data = [ 'labels' => $this->getLabels(), // TODO: $this->getLabelsData() 'descriptions' => $this->getDescriptions(), // TODO: $this->getDescriptionsData() 'sitelinks' => $this->sitelinks->toData(), 'claims' => $this->claims->toData(), ]; foreach( $data as $k => $v ) { if( 0 === count( $v ) ) { unset( $data[ $k ] ); } } if( $clear ) { $data['clear'] = true; } return $data; } /** * Get a JSON rappresentation of this data * * @param $args int Options for json_encode() * @return string */ public function getJSON( $options = 0 ) { return json_encode( $this->get(), $options ); } /** * Get a JSON rappresentation of this data (without empty elements) * * @param $args int Options for json_encode() * @return string */ public function getJSONClearing( $args = null ) { return json_encode( $this->get( true ), $args ); } /** * Print the changes in order to confirm them * * @return self */ public function printChanges() { if( $this->hasEntityID() ) { \cli\Log::info( "-- changes for {$this->getEntityID()} --" ); } $labels = $this->getLabels(); if( $labels ) { \cli\Log::info( "labels:" ); foreach( $labels as $label ) { \cli\Log::info( "\t" . $label ); } } $descriptions = $this->getDescriptions(); if( $descriptions ) { \cli\Log::info( "descriptions: "); foreach( $descriptions as $description ) { \cli\Log::info( "\t" . $description ); } } $properties = $this->getClaimsGrouped(); if( $properties ) { \cli\Log::info( "claims:" ); foreach( $properties as $property => $claims ) { if( $property === Claim::DUMMY_PROPERTY ) { \cli\Log::info( "\twithout property:" ); } else { \cli\Log::info( "\t$property:" ); } foreach( $claims as $claim ) { $snak = $claim->getMainsnak(); if( $snak ) { - \cli\Log::info( "\t\t" . $snak->getDataValue() ); + \cli\Log::info( "\t\t" . $snak->getDataValue()->toPrintableWikitext( $this->site ) ); } else { - \cli\Log::info( "\t\t" . $claim ); + \cli\Log::info( "\t\t" . $claim->toPrintableWikitext( $site ) ); } } } } return $this; } /** * Generate an edit summary for the data contained in here * * @return string */ public function getEditSummary() { $changes = []; $labels = $this->getLabels(); if( $labels ) { $changes[] = $this->labels->__toString(); } $descriptions = $this->getDescriptions(); if( $descriptions ) { $changes[] = $this->descriptions->__toString(); } $sitelinks = $this->getSitelinks(); if( $sitelinks->getAll() ) { $changes[] = $this->sitelinks->__toString(); } foreach( $this->getClaims() as $claim ) { $snak = $claim->getMainsnak(); if( $snak ) { - $changes[] = '+' . $snak; + $changes[] = '+' . $snak->toPrintableWikitext( $this->site ); } elseif( $claim->isMarkedForRemoval() ) { $changes[] = '-claim id=' . $claim->getID(); } else { throw new \Exception( "unexpected undefined main snak, that it's allowed only in claims marked for deletion" ); } } return implode( '; ', $changes ); } /** * Obtain an empty clone of this data container * * @return self */ public function cloneEmpty() { $cloned = new self( $this->getWikibaseSite() ); if( $this->hasEntityID() ) { $cloned->setEntityID( $this->getEntityID() ); } return $cloned; } /** * Edit a Wikibase entity using the wbgetentities API * * It can also create an Entity. * * @param $data array API data request * Allowed extensions: * summary.pre Add something before the summary * summary.post Add something after the summary * @return mixed * @see https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity */ public function editEntity( $data = [] ) { // can auto-generate a summary if( !isset( $data['summary'] ) ) { $data['summary'] = $this->getEditSummary(); } // eventually prefill ID if( !isset( $data['id'] ) ) { $data['id'] = $this->hasEntityID() ? $this->getEntityID() : null; } // eventually prefill to-be-saved data if( !isset( $data['data'] ) ) { $data['data'] = $this->getJSON(); } return $this->getWikibaseSite()->editEntity( $data ); } /** * Static constructor from an associative array * * This method is used to import an array obtained * from JSON-decoding the Wikibase wbgetentity API result * * @param $data array Response of wbgetentity API result * @param $site WikibaseSite * @param $entity_id string * @return self * @see https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities */ public static function createFromData( $data, $site = null, $entity_id = null ) { $dataModel = new self( $site, $entity_id ); if( ! empty( $data[ 'labels' ] ) ) { // TODO: Labels::createFromData() foreach( $data[ 'labels' ] as $label ) { $dataModel->setLabel( Label::createFromData( $label ) ); } } if( ! empty( $data[ 'descriptions' ] ) ) { // TODO: Descriptions::createFromData() foreach( $data[ 'descriptions' ] as $description ) { $dataModel->setDescription( Description::createFromData( $description ) ); } } /** * Process the claims... or statements... or whatever damn name they have! * * See https://gitpull.it/T223 * See https://phabricator.wikimedia.org/T149410 */ $claims = $data['claims'] ?? $data['statements'] ?? null; if( !empty( $claims ) ) { // TODO: Claims::createFromData() foreach( $claims as $claims ) { foreach( $claims as $claim ) { $dataModel->addClaim( Claim::createFromData( $claim ) ); } } } if( ! empty( $data[ 'sitelinks' ] ) ) { $dataModel->setSitelinks( Sitelinks::createFromData( $data[ 'sitelinks' ] ) ); } return $dataModel; } /** * Static constructor from an object * * @param $object object * @param $site WikibaseSite * @param $entity_id string * @return self */ public static function createFromObject( $object, $site = null, $entity_id = null ) { return self::createFromData( self::object2array( $object ), $site, $entity_id ); } /** * Convert an object to an array * * @param $object object * @return array */ private static function object2array( $object ) { if( ! is_object( $object) && ! is_array( $object ) ) { return $object; } $array = []; foreach( $object as $k => $v ) { $array[ $k ] = self::object2array( $v ); } return $array; } } diff --git "a/include/class-wb\\DataValueItem.php" "b/include/class-wb\\DataValueItem.php" index 482cc3f..2783694 100644 --- "a/include/class-wb\\DataValueItem.php" +++ "b/include/class-wb\\DataValueItem.php" @@ -1,46 +1,69 @@ . # Wikibase namespace wb; /** * A DataValue for a wikibase Item. */ class DataValueItem extends DataValue { /** * @param $qcode string Entity Q-code as 'Q1' */ public function __construct( $qcode ) { parent::__construct( DataType::ENTITY_ID, [ 'entity-type' => 'item', 'numeric-id' => Item::numericQCode( $qcode ), - 'id' => $qcode + 'id' => $qcode, ] ); } /** - * @return string + * Get a wikitext-compatible version of this value + * + * This may be awared about which is the wiki that will contain this value, + * in order to properly choose a correct permalink in wikilinks etc. + * + * See https://gitpull.it/T221 + * + * @param $site You can eventually specify in which site you want to print this value */ - public function __toString() { + public function toPrintableWikitext( \mw\Site $site = null ) { + + /** + * If you are on every wiki but Wikidata, + * links to items and properties will fail without + * an interwiki prefix. + * + * See https://gitpull.it/T221 + */ + $prefix = ''; + if( $site && $site::UID !== 'wikidatawiki' ) { + $prefix = 'wikidata:'; + } + + // stupid way to create a simple wikilink + // that's good enough for an automatically generated edit summary return sprintf( - '[[%s]]', + '[[%s%s]]', + $prefix, $this->getValue()['id'] ); } } diff --git "a/include/class-wb\\Reference.php" "b/include/class-wb\\Reference.php" index f865236..c901c99 100644 --- "a/include/class-wb\\Reference.php" +++ "b/include/class-wb\\Reference.php" @@ -1,232 +1,232 @@ . # Wikibase namespace wb; /** * Reference * * A reference is a collection of snaks. */ class Reference { /** * Identifier of this reference * * @var string */ private $hash; /** * All the snaks * * @var object */ private $snaks; /** * Snaks order * * It's an array of property names. e.g. [ 'P123' , ... ] * * @var array */ private $snaksOrder; /** * Constructor * * @param array $snaks */ public function __construct( $snaks = [] ) { $this->snaks = new Snaks( $snaks ); } /** * Check if the snak has an hash * * @return bool */ public function hasHash() { return isset( $this->hash ); } /** * Get the hash * * @return string|null */ public function getHash() { return $this->hash; } /** * Set the hash * * @param $hash string * @return self */ public function setHash( $hash ) { $this->hash = $hash; return $this; } /** * Add a snak * * @param object $snak * @return self */ public function add( Snak $snak ) { $this->snaks->add( $snak ); return $this; } /** * Count all the snaks * * @return int */ public function count() { return $this->snaks->count(); } /** * Get all the snaks in a certain property * * @param $property string * @return array */ public function getSnaksInProperty( $property ) { return $this->snaks->getInProperty( $property ); } /** * Check if there are snaks in a certain property * * @param $property string * @return bool */ public function hasSnaksInProperty( $property ) { return $this->snaks->hasInProperty( $property ); } /** * Get all the snaks indexed by property * * @return array */ public function getSnaksByProperty() { return $this->snaks->getAllByProperty(); } /** * Check if the snaks order is specified * * @return boolean */ public function hasSnaksOrder() { return isset( $this->snaksOrder ); } /** * Check if the reference is empty * * @return boolean */ public function isEmpty() { return $this->snaks->isEmpty(); } /** * Get the snaks order * * @return array|null */ public function getSnaksOrder() { return $this->snaksOrder; } /** * Set the snaks order * * @param array $properties * @return self */ public function setSnaksOrder( $properties ) { $this->snaksOrder = $properties; return $this; } /** * Constructor from a raw object retrieved from API results * * @param object $reference_raw * @return self */ public static function createFromData( $reference_raw ) { $reference = new self(); // reference snaks if( !isset( $reference_raw['snaks'] ) ) { - throw new WrongDataException( 'bad reference object: no snaks field' ); + throw new WrongDataException( __CLASS__, 'no snaks field' ); } foreach( $reference_raw['snaks'] as $property => $snaks_raw ) { foreach( $snaks_raw as $snak_raw ) { $snak = Snak::createFromData( $snak_raw ); $reference->add( $snak ); } } // reference hash if( isset( $reference_raw['hash'] ) ) { $reference->setHash( $reference_raw['hash'] ); } // reference snaks order (array of properties) if( isset( $reference_raw['snaks-order'] ) ) { $reference->setSnaksOrder( $reference_raw['snaks-order'] ); } return $reference; } /** * Convert this object to an associative array suitable for JSON encoding * * @return array */ public function toData() { $data = []; // reference hash if( $this->hasHash() ) { $data['hash'] = $this->getHash(); } // reference snaks $data['snaks'] = $this->snaks->toData(); // snaks order (property list) if( $this->hasSnaksOrder() ) { $data['snaks-order'] = $this->getSnaksOrder(); } return $data; } } diff --git "a/include/class-wb\\Snak.php" "b/include/class-wb\\Snak.php" index 46d7775..0e72969 100644 --- "a/include/class-wb\\Snak.php" +++ "b/include/class-wb\\Snak.php" @@ -1,273 +1,307 @@ . # Wikibase namespace wb; /** * A generic Snak is a combination of a property and a datatype + datavalue. * * A Snak is part of a Claim or a Reference. It's based on a DataValue. */ class Snak { private $hash; private $snaktype; private $property; private $datatype; private $datavalue; /** * @param $snaktype string * @param $property string * @param $datatype string * @param $datavalue mixed */ public function __construct( $snaktype, $property, $datatype, $datavalue = null ) { $this->setSnakType( $snaktype ) ->setProperty( $property ) ->setDataType( $datatype ); if( null !== $datavalue ) { $this->setDataValue( $datavalue ); } } /** * Get the property * * @return string */ public function getProperty() { return $this->property; } /** * Get the snak type */ public function getSnakType() { return $this->snaktype; } /** * Get the data type */ public function getDataType() { return $this->datatype; } /** * Get the data value * * @return DataValue */ public function getDataValue() { return $this->datavalue; } /** * Set the snak type * * @param $snaktype * @param self */ public function setSnakType( $snaktype ) { $this->snaktype = $snaktype; return $this; } /** * Set the property * * @param $property string * @param self */ public function setProperty( $property ) { $this->property = $property; return $this; } /** * Get an human property label, if available in cache */ public function getPropertyLabel() { return self::propertyLabel( $this->getProperty() ); } /** * Set the data type * * @param $datatype * @param self */ public function setDataType( $datatype ) { $this->datatype = $datatype; return $this; } /** * Set the data value * * @param $datavalue DataValue * @param self */ public function setDataValue( DataValue $datavalue ) { $this->datavalue = $datavalue; return $this; } /** * Check if the snak has an hash * * @return bool */ public function hasHash() { return isset( $this->hash ); } /** * Get the hash * * @return string|null */ public function getHash() { return $this->hash; } /** * Set the hash * * @param $hash string * @return self */ public function setHash( $hash ) { $this->hash = $hash; return $this; } /** * Get a wikilink to this property * - * @return string + * This may be awared about which is the wiki that will contain this value, + * in order to properly choose a correct permalink in wikilinks etc. + * + * See https://gitpull.it/T221 + * + * @param $site object You can eventually specify in which site you want to print this value + * @return string */ - protected function getPropertyWLink() { + protected function getPropertyWLink( \mw\Site $site = null ) { + + /** + * If you are on every wiki but Wikidata, + * links to items and properties will fail without + * an interwiki prefix. + * + * See https://gitpull.it/T221 + */ + $prefix = ''; + if( $site && $site::UID !== 'wikidatawiki' ) { + $prefix = 'wikidata:'; + } + $prop = $this->getProperty(); $label = $this->getPropertyLabel(); - if( ! $label ) { + if( !$label ) { $label = $prop; } - return sprintf( '[[P:%s|%s]]', $prop, $label ); + return sprintf( '[[%sP:%s|%s]]', $prefix, $prop, $label ); } /** * Try to read the property label from the cache * * @TODO: ask also the site * @return string|false */ public static function propertyLabel( $property ) { return \wm\Wikidata::propertyLabel( $property ); } /** * Create a snak from raw data * * @param $data array * @return self */ public static function createFromData( $data ) { /** * Check if the data has these attributes * * Note that Wikimedia Commons' Structured Data may * not return any 'datatype'. * * See https://gitpull.it/T223 * See https://phabricator.wikimedia.org/T246809 */ $required_attributes = [ 'snaktype', 'property', // 'datatype', ]; foreach( $required_attributes as $required_attribute ) { if( !isset( $data[ $required_attribute ] ) ) { throw new WrongDataException( __CLASS__, "missing $required_attribute" ); } } // create the Snak $snak = new self( $data['snaktype'], $data['property'], $data['datatype'] ?? null ); // eventually set the DataValue if( isset( $data['datavalue'] ) ) { $snak->setDataValue( DataValue::createFromData( $data['datavalue'] ) ); } // eventually set the hash if( isset( $data['hash'] ) ) { $snak->setHash( $data['hash'] ); } // that's all return $snak; } /** * Convert this object to an associative array suitable for JSON encoding * * @return array */ public function toData() { $data = []; // it may have an hash if( $this->hasHash() ) { $data['hash'] = $this->getHash(); } $data['snaktype'] = $this->getSnakType(); $data['property'] = $this->getProperty(); $data['datatype'] = $this->getDataType(); $data['datavalue'] = $this->getDataValue(); if( $data['datavalue'] ) { $data['datavalue'] = $data['datavalue']->toData(); } return $data; } /** - * @return string + * Get a wikitext-compatible version of this value + * + * This may be awared about which is the wiki that will contain this value, + * in order to properly choose a correct permalink in wikilinks etc. + * + * See https://gitpull.it/T221 + * + * @param $site object You can eventually specify in which site you want to print this value + * @return string */ - public function __toString() { + public function toPrintableWikitext( \mw\Site $site = null ) { return sprintf( '%s: %s', - $this->getPropertyWLink(), - $this->getDataValue() + $this->getPropertyWLink( $site ), + $this->getDataValue()->toPrintableWikitext( $site ) ); } + + /** + * @return string + */ + public function __toString() { + return $this->toPrintableWikitext(); + } }