Daniel Kinzler has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/378786 )
Change subject: EXPERIMENT: infrastructure for hierarchicaly adressable entities ...................................................................... EXPERIMENT: infrastructure for hierarchicaly adressable entities Needs https://github.com/wmde/WikibaseDataModel/pull/760 Bug: T165328 Change-Id: I581a2be249c105a971da2c719d605a812fd48013 --- A lib/includes/Store/HierarchicalEntityRevisionLookup.php A lib/includes/Store/HierarchicalEntityStore.php 2 files changed, 329 insertions(+), 0 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Wikibase refs/changes/86/378786/1 diff --git a/lib/includes/Store/HierarchicalEntityRevisionLookup.php b/lib/includes/Store/HierarchicalEntityRevisionLookup.php new file mode 100644 index 0000000..9086ada --- /dev/null +++ b/lib/includes/Store/HierarchicalEntityRevisionLookup.php @@ -0,0 +1,94 @@ +<?php + +namespace Wikibase\Lib\Store; + +use Wikibase\DataModel\Entity\EntityContainer; +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Entity\HierarchicalEntityId; + +/** + * Generic implementation of EntityRevisionLookup for resolving EntityIds that + * use a hierarchical addressing scheme. + * + * @license GPL-2.0+ + * @author Daniel Kinzler + */ +class HierarchicalEntityRevisionLookup implements EntityRevisionLookup { + + /** + * @var EntityRevisionLookup + */ + private $lookup; + + /** + * @param EntityRevisionLookup $lookup + */ + public function __construct( EntityRevisionLookup $lookup ) { + $this->lookup = $lookup; + } + + /** + * @see EntityRevisionLookup::getEntityRevision. + * + * This implementation recursively resolves HierarchicalEntityIds. + * + * @param EntityId $entityId + * @param int $revisionId + * @param string $mode + * + * @throws RevisionedUnresolvedRedirectException + * @throws StorageException + * @return EntityRevision|null + */ + public function getEntityRevision( + EntityId $entityId, + $revisionId = 0, + $mode = self::LATEST_FROM_REPLICA + ) { + if ( !( $entityId instanceof HierarchicalEntityId ) ) { + return $this->lookup->getEntityRevision( $entityId, $revisionId, $mode ); + } + + $baseId = $entityId->getBaseId(); + $containerRevision = $this->getEntityRevision( $baseId, $revisionId, $mode ); + + if ( $containerRevision === null ) { + return null; + } + + $container = $containerRevision->getEntity(); + + if ( !( $container instanceof EntityContainer) ) { + throw new StorageException( 'Cannot resolve ID ' . $entityId ); + } + + $entity = $container->getEntity( $entityId ); + + return new EntityRevision( + $entity, + $containerRevision->getRevisionId(), + $containerRevision->getTimestamp() + ); + } + + /** + * @see EntityRevisionLookup::getLatestRevisionId. + * + * This implementation finds the root of any hierarchical EntityId before looking up the + * latest revision. + * + * @param EntityId $entityId + * @param string $mode LATEST_FROM_REPLICA, LATEST_FROM_REPLICA_WITH_FALLBACK or LATEST_FROM_MASTER. + * LATEST_FROM_MASTER would force the revision to be determined from the canonical master database. + * + * @return int|false Returns false in case the entity doesn't exist (this includes redirects). + */ + public function getLatestRevisionId( EntityId $entityId, $mode = self::LATEST_FROM_REPLICA ) { + if ( $entityId instanceof HierarchicalEntityId ) { + $entityId = $entityId->getRootId(); + } + + return $this->lookup->getLatestRevisionId( $entityId, $mode ); + } + +} diff --git a/lib/includes/Store/HierarchicalEntityStore.php b/lib/includes/Store/HierarchicalEntityStore.php new file mode 100644 index 0000000..703ab10 --- /dev/null +++ b/lib/includes/Store/HierarchicalEntityStore.php @@ -0,0 +1,235 @@ +<?php + +namespace Wikibase\Lib\Store; + +use MWException; +use PermissionsError; +use Status; +use User; +use Wikibase\DataModel\Entity\EntityContainer; +use Wikibase\DataModel\Entity\EntityDocument; +use Wikibase\DataModel\Entity\EntityId; +use Wikibase\DataModel\Entity\EntityRedirect; +use Wikibase\DataModel\Entity\HierarchicalEntityId; + +/** + * A generic EntityStore implementing storage for entities that use an hierarchical addressing + * scheme. + * + * @license GPL-2.0+ + * @author Daniel Kinzler + */ +class HierarchicalEntityStore implements EntityStore { + + /** + * @var EntityStore + */ + private $store; + + /** + * @var EntityRevisionLookup + */ + private $lookup; + + /** + * @param EntityStore $store + * @param EntityRevisionLookup $lookup + */ + public function __construct( EntityStore $store, EntityRevisionLookup $lookup ) { + $this->store = $store; + $this->lookup = $lookup; + } + + /** + * @param HierarchicalEntityId $id + * + * @throws StorageException + * @return EntityRevision + */ + private function getContainerRevision( + HierarchicalEntityId $id, + $revisionId = 0, + $mode = EntityRevisionLookup::LATEST_FROM_MASTER + ) { + $baseId = $id->getBaseId(); + $rev = $this->lookup->getEntityRevision( $baseId, $revisionId, $mode ); + + if ( !$rev ) { + throw new StorageException( 'Cannot resolve base entity ID ' . $baseId->getSerialization() ); + } + + if ( !( $rev->getEntity() instanceof EntityContainer) ) { + throw new StorageException( 'Cannot resolve ID ' . $id ); + } + + return $rev; + } + + /** + * @param HierarchicalEntityId $id + * + * @throws StorageException + * @return EntityContainer|EntityDocument + */ + private function getContainer( HierarchicalEntityId $id ) { + return $this->getContainerRevision( $id )->getEntity(); + } + + /** + * @see EntityStore::assignFreshId + * + * @param EntityDocument $entity + * + * @throws StorageException + */ + public function assignFreshId( EntityDocument $entity ) { + // FIXME: we will have to know the ID of the parent entity here somehow! + $this->store->assignFreshId( $entity ); + } + + /** + * @see EntityStore::saveEntity + * + * @param EntityDocument $entity the entity to save. + * @param string $summary the edit summary for the new revision. + * @param User $user the user to whom to attribute the edit + * @param int $flags EDIT_XXX flags, as defined for WikiPage::doEditContent. + * Additionally, the EntityContent::EDIT_XXX constants can be used. + * @param int|bool $baseRevId the revision ID $entity is based on. Saving should fail if + * $baseRevId is no longer the current revision. + * + * @see WikiPage::doEditContent + * + * @return EntityRevision + * @throws StorageException + * @throws PermissionsError + */ + public function saveEntity( EntityDocument $entity, $summary, User $user, $flags = 0, $baseRevId = false ) { + if ( !$entity->getId() ) { + if ( $flags & EDIT_NEW ) { + $this->assignFreshId( $entity ); + } else { + throw new StorageException( Status::newFatal( 'edit-gone-missing' ) ); + } + } + + $id = $entity->getId(); + + if ( $id instanceof HierarchicalEntityId ) { + $rev = $this->getContainerRevision( $id, $baseRevId ); + $baseRevId = $rev->getRevisionId(); + $entityToSave = $rev->getEntity(); + $entityToSave->putEntity( $entity ); + } else { + $entityToSave = $entity; + } + + $this->store->saveEntity( $entityToSave, $summary, $user, $flags, $baseRevId ); + } + + /** + * @see EntityStore::saveRedirect + * + * @param EntityRedirect $redirect the redirect to save. + * @param string $summary the edit summary for the new revision. + * @param User $user the user to whom to attribute the edit + * @param int $flags EDIT_XXX flags, as defined for WikiPage::doEditContent. + * @param int|bool $baseRevId the revision ID $entity is based on. Saving should fail if + * $baseRevId is no longer the current revision. + * + * @see WikiPage::doEditContent + * + * @return int The new revision ID + * @throws StorageException + * @throws PermissionsError + */ + public function saveRedirect( EntityRedirect $redirect, $summary, User $user, $flags = 0, $baseRevId = false ) { + // FIXME: does it make sense to support redirects between sub-entities? Implement EntityRedirectContainer + $this->store->saveEntity( $redirect, $summary, $user, $flags, $baseRevId ); + } + + /** + * @see EntityStore::deleteEntity + * + * @param EntityId $entityId + * @param string $reason the reason for deletion + * @param User $user + */ + public function deleteEntity( EntityId $entityId, $reason, User $user ) { + if ( $entityId instanceof HierarchicalEntityId ) { + $container = $this->getContainer( $entityId ); + $container->removeEntity( $entityId ); + $this->saveEntity( $container, $reason, $user, EDIT_UPDATE ); + } else { + $this->store->deleteEntity( $entityId, $reason, $user ); + } + } + + /** + * @see EntityStore::userWasLastToEdit + * + * @param User $user the user + * @param EntityId $id the entity to check + * @param int $lastRevId the revision to check from + * + * @return bool + */ + public function userWasLastToEdit( User $user, EntityId $id, $lastRevId ) { + if ( $id instanceof HierarchicalEntityId ) { + $id = $id->getRootId(); + } + + return $this->store->userWasLastToEdit( $user, $id, $lastRevId ); + } + + /** + * @see EntityStore::updateWatchlist + * + * @param User $user + * @param EntityId $id the entity to watch + * @param bool $watch whether to watch or unwatch the page. + * + * @throws MWException + * @return void + */ + public function updateWatchlist( User $user, EntityId $id, $watch ) { + if ( $id instanceof HierarchicalEntityId ) { + $id = $id->getRootId(); + } + + $this->store->updateWatchlist( $user, $id, $watch ); + } + + /** + * @see EntityStore::isWatching + * + * @param User $user + * @param EntityId $id the entity to watch + * + * @return bool + */ + public function isWatching( User $user, EntityId $id ) { + if ( $id instanceof HierarchicalEntityId ) { + $id = $id->getRootId(); + } + + return $this->store->isWatching( $user, $id ); + } + + /** + * @see EntityStore::canCreateWithCustomId + * + * @param EntityId $id + * + * @return bool + */ + public function canCreateWithCustomId( EntityId $id ) { + if ( $id instanceof HierarchicalEntityId ) { + $container = $this->getContainer( $id ); + return $container->canAddWithCustomId( $id ); + } else { + $this->store->canCreateWithCustomId( $id ); + } + } + +} -- To view, visit https://gerrit.wikimedia.org/r/378786 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I581a2be249c105a971da2c719d605a812fd48013 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits