| Current File : /home/jvzmxxx/wiki/extensions/Wikibase/repo/includes/Api/EditEntity.php |
<?php
namespace Wikibase\Repo\Api;
use ApiMain;
use DataValues\IllegalValueException;
use Deserializers\Deserializer;
use InvalidArgumentException;
use LogicException;
use MWException;
use SiteList;
use Title;
use UsageException;
use Wikibase\ChangeOp\ChangeOp;
use Wikibase\ChangeOp\ChangeOps;
use Wikibase\ChangeOp\FingerprintChangeOpFactory;
use Wikibase\ChangeOp\SiteLinkChangeOpFactory;
use Wikibase\ChangeOp\StatementChangeOpFactory;
use Wikibase\DataModel\Entity\EntityDocument;
use Wikibase\DataModel\Entity\EntityId;
use Wikibase\DataModel\Entity\EntityIdParser;
use Wikibase\DataModel\Entity\Item;
use Wikibase\DataModel\Entity\Property;
use Wikibase\DataModel\Statement\Statement;
use Wikibase\DataModel\Statement\StatementListProvider;
use Wikibase\DataModel\Term\AliasesProvider;
use Wikibase\DataModel\Term\DescriptionsProvider;
use Wikibase\DataModel\Term\LabelsProvider;
use Wikibase\EntityFactory;
use Wikibase\Lib\ContentLanguages;
use Wikibase\Lib\Store\EntityRevisionLookup;
use Wikibase\Repo\WikibaseRepo;
use Wikibase\Summary;
/**
* Derived class for API modules modifying a single entity identified by id xor a combination of
* site and page title.
*
* @since 0.1
*
* @license GPL-2.0+
* @author John Erling Blad < jeblad@gmail.com >
* @author Daniel Kinzler
* @author Tobias Gritschacher < tobias.gritschacher@wikimedia.de >
* @author Addshore
* @author Michał Łazowik
*/
class EditEntity extends ModifyEntity {
/**
* @var ContentLanguages
*/
private $termsLanguages;
/**
* @var FingerprintChangeOpFactory
*/
private $termChangeOpFactory;
/**
* @var StatementChangeOpFactory
*/
private $statementChangeOpFactory;
/**
* @var SiteLinkChangeOpFactory
*/
private $siteLinkChangeOpFactory;
/**
* @var ApiErrorReporter
*/
private $errorReporter;
/**
* @var EntityRevisionLookup
*/
private $revisionLookup;
/**
* @var EntityIdParser
*/
private $idParser;
/**
* @var Deserializer
*/
private $statementDeserializer;
/**
* @var EntityFactory
*/
private $entityFactory;
/**
* @var string[]
*/
private $enabledEntityTypes;
/**
* @see ModifyEntity::__construct
*
* @param ApiMain $mainModule
* @param string $moduleName
* @param string $modulePrefix
*
* @throws MWException
*/
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
parent::__construct( $mainModule, $moduleName, $modulePrefix );
$wikibaseRepo = WikibaseRepo::getDefaultInstance();
$apiHelperFactory = $wikibaseRepo->getApiHelperFactory( $this->getContext() );
$this->termsLanguages = $wikibaseRepo->getTermsLanguages();
$this->errorReporter = $apiHelperFactory->getErrorReporter( $this );
$this->revisionLookup = $wikibaseRepo->getEntityRevisionLookup( 'uncached' );
$this->idParser = $wikibaseRepo->getEntityIdParser();
$this->statementDeserializer = $wikibaseRepo->getExternalFormatStatementDeserializer();
$this->entityFactory = $wikibaseRepo->getEntityFactory();
$this->enabledEntityTypes = $wikibaseRepo->getEnabledEntityTypes();
$changeOpFactoryProvider = $wikibaseRepo->getChangeOpFactoryProvider();
$this->termChangeOpFactory = $changeOpFactoryProvider->getFingerprintChangeOpFactory();
$this->statementChangeOpFactory = $changeOpFactoryProvider->getStatementChangeOpFactory();
$this->siteLinkChangeOpFactory = $changeOpFactoryProvider->getSiteLinkChangeOpFactory();
}
/**
* @see ApiBase::needsToken
*
* @return string
*/
public function needsToken() {
return 'csrf';
}
/**
* @see ApiBase::isWriteMode()
*
* @return bool Always true.
*/
public function isWriteMode() {
return true;
}
/**
* @param EntityDocument $entity
*
* @throws InvalidArgumentException
* @return string[] A list of permissions
*/
protected function getRequiredPermissions( EntityDocument $entity ) {
$permissions = $this->isWriteMode() ? array( 'read', 'edit' ) : array( 'read' );
if ( !$this->entityExists( $entity->getId() ) ) {
$permissions[] = 'createpage';
switch ( $entity->getType() ) {
case 'property':
$permissions[] = $entity->getType() . '-create'; //property-create
break;
}
}
return $permissions;
}
/**
* @param EntityId $entityId
*
* @return bool
*/
private function entityExists( EntityId $entityId ) {
$title = $entityId === null ? null : $this->getTitleLookup()->getTitleForId( $entityId );
return ( $title !== null && $title->exists() );
}
/**
* @see ModifyEntity::createEntity
*
* @param string $entityType
*
* @throws UsageException
* @throws LogicException
* @return EntityDocument
*/
protected function createEntity( $entityType ) {
$this->flags |= EDIT_NEW;
try {
return $this->entityFactory->newEmpty( $entityType );
} catch ( InvalidArgumentException $ex ) {
$this->errorReporter->dieError( "No such entity type: '$entityType'", 'no-such-entity-type' );
}
throw new LogicException( 'ApiErrorReporter::dieError did not throw an exception' );
}
/**
* @see ModifyEntity::validateParameters
*/
protected function validateParameters( array $params ) {
$hasId = isset( $params['id'] );
$hasNew = isset( $params['new'] );
$hasSiteLink = isset( $params['site'] ) && isset( $params['title'] );
$hasSiteLinkPart = isset( $params['site'] ) || isset( $params['title'] );
if ( !( $hasId xor $hasSiteLink xor $hasNew ) ) {
$this->errorReporter->dieError(
'Either provide the item "id" or pairs of "site" and "title" or a "new" type for'
. ' an entity',
'param-missing'
);
}
if ( $hasId && $hasSiteLink ) {
$this->errorReporter->dieError(
'Parameter "id" and "site", "title" combination are not allowed to be both set in'
. ' the same request',
'param-illegal'
);
}
if ( ( $hasId || $hasSiteLinkPart ) && $hasNew ) {
$this->errorReporter->dieError(
'Parameters "id", "site", "title" and "new" are not allowed to be both set in the'
. ' same request',
'param-illegal'
);
}
}
/**
* @see ModifyEntity::modifyEntity
*/
protected function modifyEntity( EntityDocument &$entity, array $params, $baseRevId ) {
$this->validateDataParameter( $params );
$data = json_decode( $params['data'], true );
$this->validateDataProperties( $data, $entity, $baseRevId );
$exists = $this->entityExists( $entity->getId() );
if ( $params['clear'] ) {
if ( $params['baserevid'] && $exists ) {
$latestRevision = $this->revisionLookup->getLatestRevisionId(
$entity->getId(),
EntityRevisionLookup::LATEST_FROM_MASTER
);
if ( !$baseRevId === $latestRevision ) {
$this->errorReporter->dieError(
'Tried to clear entity using baserevid of entity not equal to current revision',
'editconflict'
);
}
}
$entity = $this->clearEntity( $entity );
}
// if we create a new property, make sure we set the datatype
if ( !$exists && $entity instanceof Property ) {
if ( !isset( $data['datatype'] ) ) {
$this->errorReporter->dieError( 'No datatype given', 'param-illegal' );
} else {
$entity->setDataTypeId( $data['datatype'] );
}
}
$changeOps = $this->getChangeOps( $data, $entity );
$this->applyChangeOp( $changeOps, $entity );
$this->buildResult( $entity );
return $this->getSummary( $params );
}
/**
* @param EntityDocument $entity
*
* @return EntityDocument
*/
private function clearEntity( EntityDocument $entity ) {
$newEntity = $this->entityFactory->newEmpty( $entity->getType() );
$newEntity->setId( $entity->getId() );
// FIXME how to avoid special case handling here?
if ( $entity instanceof Property ) {
/** @var Property $newEntity */
$newEntity->setDataTypeId( $entity->getDataTypeId() );
}
return $newEntity;
}
/**
* @param array $params
*
* @return Summary
*/
private function getSummary( array $params ) {
//TODO: Construct a nice and meaningful summary from the changes that get applied!
// Perhaps that could be based on the resulting diff?]
$summary = $this->createSummary( $params );
if ( isset( $params['id'] ) xor ( isset( $params['site'] ) && isset( $params['title'] ) ) ) {
$summary->setAction( $params['clear'] === false ? 'update' : 'override' );
} else {
$summary->setAction( 'create' );
}
return $summary;
}
/**
* @param array $data
* @param EntityDocument $entity
*
* @throws UsageException
* @return ChangeOps
*/
private function getChangeOps( array $data, EntityDocument $entity ) {
$changeOps = new ChangeOps();
//FIXME: Use a ChangeOpBuilder so we can batch fingerprint ops etc,
// for more efficient validation!
if ( array_key_exists( 'labels', $data ) ) {
if ( !( $entity instanceof LabelsProvider ) ) {
$this->errorReporter->dieError( 'The given entity cannot contain labels', 'not-supported' );
}
$this->assertArray( $data['labels'], 'List of labels must be an array' );
$changeOps->add( $this->getLabelChangeOps( $data['labels'] ) );
}
if ( array_key_exists( 'descriptions', $data ) ) {
if ( !( $entity instanceof DescriptionsProvider ) ) {
$this->errorReporter->dieError( 'The given entity cannot contain descriptions', 'not-supported' );
}
$this->assertArray( $data['descriptions'], 'List of descriptions must be an array' );
$changeOps->add( $this->getDescriptionChangeOps( $data['descriptions'] ) );
}
if ( array_key_exists( 'aliases', $data ) ) {
if ( !( $entity instanceof AliasesProvider ) ) {
$this->errorReporter->dieError( 'The given entity cannot contain aliases', 'not-supported' );
}
$this->assertArray( $data['aliases'], 'List of aliases must be an array' );
$changeOps->add( $this->getAliasesChangeOps( $data['aliases'] ) );
}
if ( array_key_exists( 'sitelinks', $data ) ) {
if ( !( $entity instanceof Item ) ) {
$this->errorReporter->dieError( 'Non Items cannot have sitelinks', 'not-supported' );
}
$this->assertArray( $data['sitelinks'], 'List of sitelinks must be an array' );
$changeOps->add( $this->getSiteLinksChangeOps( $data['sitelinks'], $entity ) );
}
if ( array_key_exists( 'claims', $data ) ) {
if ( !( $entity instanceof StatementListProvider ) ) {
$this->errorReporter->dieError( 'The given entity cannot contain statements', 'not-supported' );
}
$this->assertArray( $data['claims'], 'List of claims must be an array' );
$changeOps->add( $this->getClaimsChangeOps( $data['claims'] ) );
}
return $changeOps;
}
/**
* @param array[] $labels
*
* @return ChangeOp[]
*/
private function getLabelChangeOps( array $labels ) {
$labelChangeOps = array();
foreach ( $labels as $langCode => $arg ) {
$this->validateMultilangArgs( $arg, $langCode );
$language = $arg['language'];
$newLabel = ( array_key_exists( 'remove', $arg ) ? '' :
$this->stringNormalizer->trimToNFC( $arg['value'] ) );
if ( $newLabel === "" ) {
$labelChangeOps[] = $this->termChangeOpFactory->newRemoveLabelOp( $language );
} else {
$labelChangeOps[] = $this->termChangeOpFactory->newSetLabelOp( $language, $newLabel );
}
}
return $labelChangeOps;
}
/**
* @param array[] $descriptions
*
* @return ChangeOp[]
*/
private function getDescriptionChangeOps( array $descriptions ) {
$descriptionChangeOps = array();
foreach ( $descriptions as $langCode => $arg ) {
$this->validateMultilangArgs( $arg, $langCode );
$language = $arg['language'];
$newDescription = ( array_key_exists( 'remove', $arg ) ? '' :
$this->stringNormalizer->trimToNFC( $arg['value'] ) );
if ( $newDescription === "" ) {
$descriptionChangeOps[] = $this->termChangeOpFactory->newRemoveDescriptionOp( $language );
} else {
$descriptionChangeOps[] = $this->termChangeOpFactory->newSetDescriptionOp( $language, $newDescription );
}
}
return $descriptionChangeOps;
}
/**
* @param array[] $aliases
*
* @return ChangeOp[]
*/
private function getAliasesChangeOps( array $aliases ) {
$indexedAliases = $this->getIndexedAliases( $aliases );
$aliasesChangeOps = $this->getIndexedAliasesChangeOps( $indexedAliases );
return $aliasesChangeOps;
}
/**
* @param array[] $aliases
*
* @return array[]
*/
private function getIndexedAliases( array $aliases ) {
$indexedAliases = array();
foreach ( $aliases as $langCode => $arg ) {
if ( !is_string( $langCode ) ) {
$indexedAliases[] = ( array_values( $arg ) === $arg ) ? $arg : array( $arg );
} else {
$indexedAliases[$langCode] = ( array_values( $arg ) === $arg ) ? $arg : array( $arg );
}
}
return $indexedAliases;
}
/**
* @param array[] $indexedAliases
*
* @return ChangeOp[]
*/
private function getIndexedAliasesChangeOps( array $indexedAliases ) {
$aliasesChangeOps = array();
foreach ( $indexedAliases as $langCode => $args ) {
$aliasesToSet = array();
$language = '';
foreach ( $args as $arg ) {
$this->validateMultilangArgs( $arg, $langCode );
$alias = array( $this->stringNormalizer->trimToNFC( $arg['value'] ) );
$language = $arg['language'];
if ( array_key_exists( 'remove', $arg ) ) {
$aliasesChangeOps[] = $this->termChangeOpFactory->newRemoveAliasesOp( $language, $alias );
} elseif ( array_key_exists( 'add', $arg ) ) {
$aliasesChangeOps[] = $this->termChangeOpFactory->newAddAliasesOp( $language, $alias );
} else {
$aliasesToSet[] = $alias[0];
}
}
if ( $aliasesToSet !== array() ) {
$aliasesChangeOps[] = $this->termChangeOpFactory->newSetAliasesOp( $language, $aliasesToSet );
}
}
return $aliasesChangeOps;
}
/**
* @param array[] $siteLinks
* @param Item $item
*
* @return ChangeOp[]
*/
private function getSiteLinksChangeOps( array $siteLinks, Item $item ) {
$siteLinksChangeOps = array();
$sites = $this->siteLinkTargetProvider->getSiteList( $this->siteLinkGroups );
foreach ( $siteLinks as $siteId => $arg ) {
$this->checkSiteLinks( $arg, $siteId, $sites );
$globalSiteId = $arg['site'];
if ( !$sites->hasSite( $globalSiteId ) ) {
$this->errorReporter->dieError( "There is no site for global site id '$globalSiteId'", 'no-such-site' );
}
$linkSite = $sites->getSite( $globalSiteId );
$shouldRemove = array_key_exists( 'remove', $arg )
|| ( !isset( $arg['title'] ) && !isset( $arg['badges'] ) )
|| ( isset( $arg['title'] ) && $arg['title'] === '' );
if ( $shouldRemove ) {
$siteLinksChangeOps[] = $this->siteLinkChangeOpFactory->newRemoveSiteLinkOp( $globalSiteId );
} else {
$badges = ( isset( $arg['badges'] ) )
? $this->parseSiteLinkBadges( $arg['badges'] )
: null;
if ( isset( $arg['title'] ) ) {
$linkPage = $linkSite->normalizePageName( $this->stringNormalizer->trimWhitespace( $arg['title'] ) );
if ( $linkPage === false ) {
$this->errorReporter->dieMessage(
'no-external-page',
$globalSiteId,
$arg['title']
);
}
} else {
$linkPage = null;
if ( !$item->getSiteLinkList()->hasLinkWithSiteId( $globalSiteId ) ) {
$this->errorReporter->dieMessage( 'no-such-sitelink', $globalSiteId );
}
}
$siteLinksChangeOps[] = $this->siteLinkChangeOpFactory->newSetSiteLinkOp( $globalSiteId, $linkPage, $badges );
}
}
return $siteLinksChangeOps;
}
/**
* @param array[] $claims
*
* @return ChangeOp[]
*/
private function getClaimsChangeOps( array $claims ) {
$changeOps = array();
//check if the array is associative or in arrays by property
if ( array_keys( $claims ) !== range( 0, count( $claims ) - 1 ) ) {
foreach ( $claims as $subClaims ) {
$changeOps = array_merge( $changeOps,
$this->getRemoveStatementChangeOps( $subClaims ),
$this->getModifyStatementChangeOps( $subClaims ) );
}
} else {
$changeOps = array_merge( $changeOps,
$this->getRemoveStatementChangeOps( $claims ),
$this->getModifyStatementChangeOps( $claims ) );
}
return $changeOps;
}
/**
* @param array[] $statements array of serialized statements
*
* @return ChangeOp[]
*/
private function getModifyStatementChangeOps( array $statements ) {
$opsToReturn = array();
foreach ( $statements as $statementArray ) {
if ( !array_key_exists( 'remove', $statementArray ) ) {
try {
$statement = $this->statementDeserializer->deserialize( $statementArray );
if ( !( $statement instanceof Statement ) ) {
throw new IllegalValueException( 'Statement serialization did not contained a Statement.' );
}
$opsToReturn[] = $this->statementChangeOpFactory->newSetStatementOp( $statement );
} catch ( IllegalValueException $ex ) {
$this->errorReporter->dieException( $ex, 'invalid-claim' );
} catch ( MWException $ex ) {
$this->errorReporter->dieException( $ex, 'invalid-claim' );
}
}
}
return $opsToReturn;
}
/**
* Get changeops that remove all claims that have the 'remove' key in the array
*
* @param array[] $claims array of serialized claims
*
* @return ChangeOp[]
*/
private function getRemoveStatementChangeOps( array $claims ) {
$opsToReturn = array();
foreach ( $claims as $claimArray ) {
if ( array_key_exists( 'remove', $claimArray ) ) {
if ( array_key_exists( 'id', $claimArray ) ) {
$opsToReturn[] = $this->statementChangeOpFactory->newRemoveStatementOp( $claimArray['id'] );
} else {
$this->errorReporter->dieError( 'Cannot remove a claim with no GUID', 'invalid-claim' );
}
}
}
return $opsToReturn;
}
/**
* @param EntityDocument $entity
*/
private function buildResult( EntityDocument $entity ) {
$builder = $this->getResultBuilder();
if ( $entity instanceof LabelsProvider ) {
$builder->addLabels( $entity->getLabels(), 'entity' );
}
if ( $entity instanceof DescriptionsProvider ) {
$builder->addDescriptions( $entity->getDescriptions(), 'entity' );
}
if ( $entity instanceof AliasesProvider ) {
$builder->addAliasGroupList( $entity->getAliasGroups(), 'entity' );
}
if ( $entity instanceof Item ) {
$builder->addSiteLinkList( $entity->getSiteLinkList(), 'entity' );
}
if ( $entity instanceof StatementListProvider ) {
$builder->addStatements( $entity->getStatements(), 'entity' );
}
}
/**
* @param array $params
*/
private function validateDataParameter( array $params ) {
if ( !isset( $params['data'] ) ) {
$this->errorReporter->dieError( 'No data to operate upon', 'no-data' );
}
}
/**
* @param mixed $data
* @param EntityDocument $entity
* @param int $revisionId
*/
private function validateDataProperties( $data, EntityDocument $entity, $revisionId = 0 ) {
$entityId = $entity->getId();
$title = $entityId === null ? null : $this->getTitleLookup()->getTitleForId( $entityId );
$allowedProps = array(
// ignored props
'length',
'count',
'touched',
// checked props
'id',
'type',
'pageid',
'ns',
'title',
'lastrevid',
// useful props
'labels',
'descriptions',
'aliases',
'sitelinks',
'claims',
'datatype'
);
$this->checkValidJson( $data, $allowedProps );
$this->checkEntityId( $data, $entityId );
$this->checkEntityType( $data, $entity );
$this->checkPageIdProp( $data, $title );
$this->checkNamespaceProp( $data, $title );
$this->checkTitleProp( $data, $title );
$this->checkRevisionProp( $data, $revisionId );
}
/**
* @param mixed $data
* @param array $allowedProps
*/
private function checkValidJson( $data, array $allowedProps ) {
if ( is_null( $data ) ) {
$this->errorReporter->dieError( 'Invalid json: The supplied JSON structure could not be parsed or '
. 'recreated as a valid structure', 'invalid-json' );
}
// NOTE: json_decode will decode any JS literal or structure, not just objects!
$this->assertArray( $data, 'Top level structure must be a JSON object' );
foreach ( $data as $prop => $args ) {
// Catch json_decode returning an indexed array (list).
$this->assertString( $prop, 'Top level structure must be a JSON object (no keys found)' );
if ( !in_array( $prop, $allowedProps ) ) {
$this->errorReporter->dieError( "Unknown key in json: $prop", 'not-recognized' );
}
}
}
/**
* @param array $data
* @param Title|null $title
*/
private function checkPageIdProp( array $data, Title $title = null ) {
if ( isset( $data['pageid'] )
&& ( $title === null || $title->getArticleID() !== $data['pageid'] )
) {
$this->errorReporter->dieError(
'Illegal field used in call, "pageid", must either be correct or not given',
'param-illegal'
);
}
}
/**
* @param array $data
* @param Title|null $title
*/
private function checkNamespaceProp( array $data, Title $title = null ) {
// not completely convinced that we can use title to get the namespace in this case
if ( isset( $data['ns'] )
&& ( $title === null || $title->getNamespace() !== $data['ns'] )
) {
$this->errorReporter->dieError(
'Illegal field used in call: "namespace", must either be correct or not given',
'param-illegal'
);
}
}
/**
* @param array $data
* @param Title|null $title
*/
private function checkTitleProp( array $data, Title $title = null ) {
if ( isset( $data['title'] )
&& ( $title === null || $title->getPrefixedText() !== $data['title'] )
) {
$this->errorReporter->dieError(
'Illegal field used in call: "title", must either be correct or not given',
'param-illegal'
);
}
}
/**
* @param array $data
* @param int|null $revisionId
*/
private function checkRevisionProp( array $data, $revisionId ) {
if ( isset( $data['lastrevid'] )
&& ( !is_int( $revisionId ) || $revisionId !== $data['lastrevid'] )
) {
$this->errorReporter->dieError(
'Illegal field used in call: "lastrevid", must either be correct or not given',
'param-illegal'
);
}
}
/**
* @param array $data
* @param EntityId|null $entityId
*/
private function checkEntityId( array $data, EntityId $entityId = null ) {
if ( isset( $data['id'] ) ) {
if ( !$entityId ) {
$this->errorReporter->dieError(
'Illegal field used in call: "id", must not be given when creating a new entity',
'param-illegal'
);
}
$dataId = $this->idParser->parse( $data['id'] );
if ( !$entityId->equals( $dataId ) ) {
$this->errorReporter->dieError(
'Invalid field used in call: "id", must match id parameter',
'param-invalid'
);
}
}
}
/**
* @param array $data
* @param EntityDocument $entity
*/
private function checkEntityType( array $data, EntityDocument $entity ) {
if ( isset( $data['type'] )
&& $entity->getType() !== $data['type']
) {
$this->errorReporter->dieError(
'Invalid field used in call: "type", must match type associated with id',
'param-invalid'
);
}
}
/**
* @see ModifyEntity::getAllowedParams
*/
protected function getAllowedParams() {
return array_merge(
parent::getAllowedParams(),
array(
'data' => array(
self::PARAM_TYPE => 'text',
self::PARAM_REQUIRED => true,
),
'clear' => array(
self::PARAM_TYPE => 'boolean',
self::PARAM_DFLT => false
),
'new' => array(
self::PARAM_TYPE => $this->enabledEntityTypes,
),
)
);
}
/**
* @see ApiBase::getExamplesMessages
*/
protected function getExamplesMessages() {
return array(
// Creating new entites
'action=wbeditentity&new=item&data={}'
=> 'apihelp-wbeditentity-example-1',
'action=wbeditentity&new=item&data={"labels":{'
. '"de":{"language":"de","value":"de-value"},'
. '"en":{"language":"en","value":"en-value"}}}'
=> 'apihelp-wbeditentity-example-2',
'action=wbeditentity&new=property&data={'
. '"labels":{"en-gb":{"language":"en-gb","value":"Propertylabel"}},'
. '"descriptions":{"en-gb":{"language":"en-gb","value":"Propertydescription"}},'
. '"datatype":"string"}'
=> 'apihelp-wbeditentity-example-3',
// Clearing entities
'action=wbeditentity&clear=true&id=Q42&data={}'
=> 'apihelp-wbeditentity-example-4',
'action=wbeditentity&clear=true&id=Q42&data={'
. '"labels":{"en":{"language":"en","value":"en-value"}}}'
=> 'apihelp-wbeditentity-example-5',
// Adding term
'action=wbeditentity&id=Q42&data='
. '{"labels":[{"language":"no","value":"Bar","add":""}]}'
=> 'apihelp-wbeditentity-example-11',
// Removing term
'action=wbeditentity&id=Q42&data='
. '{"labels":[{"language":"en","value":"Foo","remove":""}]}'
=> 'apihelp-wbeditentity-example-12',
// Setting stuff
'action=wbeditentity&id=Q42&data={'
. '"sitelinks":{"nowiki":{"site":"nowiki","title":"København"}}}'
=> 'apihelp-wbeditentity-example-6',
'action=wbeditentity&id=Q42&data={'
. '"descriptions":{"nb":{"language":"nb","value":"nb-Description-Here"}}}'
=> 'apihelp-wbeditentity-example-7',
'action=wbeditentity&id=Q42&data={"claims":[{"mainsnak":{"snaktype":"value",'
. '"property":"P56","datavalue":{"value":"ExampleString","type":"string"}},'
. '"type":"statement","rank":"normal"}]}'
=> 'apihelp-wbeditentity-example-8',
'action=wbeditentity&id=Q42&data={"claims":['
. '{"id":"Q42$D8404CDA-25E4-4334-AF13-A3290BCD9C0F","remove":""},'
. '{"id":"Q42$GH678DSA-01PQ-28XC-HJ90-DDFD9990126X","remove":""}]}'
=> 'apihelp-wbeditentity-example-9',
'action=wbeditentity&id=Q42&data={"claims":[{'
. '"id":"Q42$GH678DSA-01PQ-28XC-HJ90-DDFD9990126X","mainsnak":{"snaktype":"value",'
. '"property":"P56","datavalue":{"value":"ChangedString","type":"string"}},'
. '"type":"statement","rank":"normal"}]}'
=> 'apihelp-wbeditentity-example-10',
);
}
/**
* Check some of the supplied data for multilang arg
*
* @param array $arg The argument array to verify
* @param string $langCode The language code used in the value part
*/
private function validateMultilangArgs( $arg, $langCode ) {
$this->assertArray( $arg, 'An array was expected, but not found in the json for the '
. "langCode $langCode" );
if ( !array_key_exists( 'language', $arg ) ) {
$this->errorReporter->dieError(
"'language' was not found in the label or description json for $langCode",
'missing-language' );
}
$this->assertString( $arg['language'], 'A string was expected, but not found in the json '
. "for the langCode $langCode and argument 'language'" );
if ( !is_numeric( $langCode ) ) {
if ( $langCode !== $arg['language'] ) {
$this->errorReporter->dieError(
"inconsistent language: $langCode is not equal to {$arg['language']}",
'inconsistent-language' );
}
}
if ( !$this->termsLanguages->hasLanguage( $arg['language'] ) ) {
$this->errorReporter->dieError( 'Unknown language: ' . $arg['language'], 'not-recognized-language' );
}
if ( !array_key_exists( 'remove', $arg ) ) {
$this->assertString( $arg['value'], 'A string was expected, but not found in the json '
. "for the langCode $langCode and argument 'value'" );
}
}
/**
* Check some of the supplied data for sitelink arg
*
* @param array $arg The argument array to verify
* @param string $siteCode The site code used in the argument
* @param SiteList|null $sites The valid sites.
*/
private function checkSiteLinks( $arg, $siteCode, SiteList &$sites = null ) {
$this->assertArray( $arg, 'An array was expected, but not found' );
$this->assertString( $arg['site'], 'A string was expected, but not found' );
if ( !is_numeric( $siteCode ) ) {
if ( $siteCode !== $arg['site'] ) {
$this->errorReporter->dieError( "inconsistent site: $siteCode is not equal to {$arg['site']}", 'inconsistent-site' );
}
}
if ( $sites !== null && !$sites->hasSite( $arg['site'] ) ) {
$this->errorReporter->dieError( 'Unknown site: ' . $arg['site'], 'not-recognized-site' );
}
if ( isset( $arg['title'] ) ) {
$this->assertString( $arg['title'], 'A string was expected, but not found' );
}
if ( isset( $arg['badges'] ) ) {
$this->assertArray( $arg['badges'], 'Badges: an array was expected, but not found' );
foreach ( $arg['badges'] as $badge ) {
$this->assertString( $badge, 'Badges: a string was expected, but not found' );
}
}
}
/**
* @param mixed $value
* @param string $message
*/
private function assertArray( $value, $message ) {
$this->assertType( 'array', $value, $message );
}
/**
* @param mixed $value
* @param string $message
*/
private function assertString( $value, $message ) {
$this->assertType( 'string', $value, $message );
}
/**
* @param string $type
* @param mixed $value
* @param string $message
*/
private function assertType( $type, $value, $message ) {
if ( gettype( $value ) !== $type ) {
$this->errorReporter->dieError( $message, 'not-recognized-' . $type );
}
}
}