/**
  * Constructs an EntityChange from the given old and new Entity.
  *
  * @since 0.5
  *
  * @param string      $action The action name
  * @param EntityDocument|null $oldEntity
  * @param EntityDocument|null $newEntity
  * @param array $fields additional fields to set
  *
  * @return EntityChange
  * @throws MWException
  */
 public function newFromUpdate($action, EntityDocument $oldEntity = null, EntityDocument $newEntity = null, array $fields = array())
 {
     if ($oldEntity === null && $newEntity === null) {
         throw new MWException('Either $oldEntity or $newEntity must be given');
     }
     if ($oldEntity === null) {
         $oldEntity = $this->entityFactory->newEmpty($newEntity->getType());
         $id = $newEntity->getId();
     } elseif ($newEntity === null) {
         $newEntity = $this->entityFactory->newEmpty($oldEntity->getType());
         $id = $oldEntity->getId();
     } elseif ($oldEntity->getType() !== $newEntity->getType()) {
         throw new MWException('Entity type mismatch');
     } else {
         $id = $newEntity->getId();
     }
     // HACK: don't include statements diff, since those are unused and not helpful
     // performance-wise to the dispatcher and change handling.
     // For a better solution, see T113468.
     if ($oldEntity instanceof StatementListHolder) {
         $oldEntity->setStatements(new StatementList());
         $newEntity->setStatements(new StatementList());
     }
     $diff = $this->entityDiffer->diffEntities($oldEntity, $newEntity);
     /** @var EntityChange $instance */
     $instance = self::newForEntity($action, $id, $fields);
     $instance->setDiff($diff);
     return $instance;
 }
Exemplo n.º 2
0
 /**
  * @dataProvider provideConflictDetection
  */
 public function testConflictDetection(Entity $base, Entity $current, Entity $new, $expectedConflicts)
 {
     $differ = new EntityDiffer();
     $patcher = new EntityPatcher();
     $patch = $differ->diffEntities($base, $new);
     $patchedCurrent = unserialize(serialize($current));
     $patcher->patchEntity($patchedCurrent, $patch);
     $cleanPatch = $differ->diffEntities($base, $patchedCurrent);
     $conflicts = $patch->count() - $cleanPatch->count();
     $this->assertEquals($expectedConflicts, $conflicts, 'check number of conflicts detected');
 }
Exemplo n.º 3
0
 /**
  * Attempts to fix an edit conflict by patching the intended change into the latest revision after
  * checking for conflicts. This modifies $this->newEntity but does not write anything to the
  * database. Saving of the new content may still fail.
  *
  * @return bool True if the conflict could be resolved, false otherwise
  */
 public function fixEditConflict()
 {
     $baseRev = $this->getBaseRevision();
     $latestRev = $this->getLatestRevision();
     if (!$latestRev) {
         wfLogWarning('Failed to load latest revision of entity ' . $this->newEntity->getId() . '! ' . 'This may indicate entries missing from thw wb_entities_per_page table.');
         return false;
     }
     $entityDiffer = new EntityDiffer();
     $entityPatcher = new EntityPatcher();
     // calculate patch against base revision
     // NOTE: will fail if $baseRev or $base are null, which they may be if
     // this gets called at an inappropriate time. The data flow in this class
     // should be improved.
     $patch = $entityDiffer->diffEntities($baseRev->getEntity(), $this->newEntity);
     if ($patch->isEmpty()) {
         // we didn't technically fix anything, but if there is nothing to change,
         // so just keep the current content as it is.
         $this->newEntity = $latestRev->getEntity()->copy();
         return true;
     }
     // apply the patch( base -> new ) to the latest revision.
     $patchedLatest = $latestRev->getEntity()->copy();
     $entityPatcher->patchEntity($patchedLatest, $patch);
     // detect conflicts against latest revision
     $cleanPatch = $entityDiffer->diffEntities($latestRev->getEntity(), $patchedLatest);
     $conflicts = $patch->count() - $cleanPatch->count();
     if ($conflicts > 0) {
         // patch doesn't apply cleanly
         if ($this->userWasLastToEdit($this->user, $this->newEntity->getId(), $this->getBaseRevisionId())) {
             // it's a self-conflict
             if ($cleanPatch->count() === 0) {
                 // patch collapsed, possibly because of diff operation change from base to latest
                 return false;
             } else {
                 // we still have a working patch, try to apply
                 $this->status->warning('wikibase-self-conflict-patched');
             }
         } else {
             // there are unresolvable conflicts.
             return false;
         }
     } else {
         // can apply cleanly
         $this->status->warning('wikibase-conflict-patched');
     }
     // remember the patched entity as the actual new entity to save
     $this->newEntity = $patchedLatest;
     return true;
 }
 /**
  * Returns a diff between this EntityContent and the given EntityContent.
  *
  * @param EntityContent $toContent
  *
  * @return EntityContentDiff
  */
 public function getDiff(EntityContent $toContent)
 {
     $fromContent = $this;
     $differ = new MapDiffer();
     $redirectDiffOps = $differ->doDiff($fromContent->getRedirectData(), $toContent->getRedirectData());
     $redirectDiff = new Diff($redirectDiffOps, true);
     $fromEntity = $fromContent->isRedirect() ? $this->makeEmptyEntity() : $fromContent->getEntity();
     $toEntity = $toContent->isRedirect() ? $this->makeEmptyEntity() : $toContent->getEntity();
     $entityDiffer = new EntityDiffer();
     $entityDiff = $entityDiffer->diffEntities($fromEntity, $toEntity);
     return new EntityContentDiff($entityDiff, $redirectDiff);
 }
Exemplo n.º 5
0
 public function testGivenUnknownEntityType_getDestructionDiffThrowsException()
 {
     $differ = new EntityDiffer();
     $this->setExpectedException('RuntimeException');
     $differ->getDestructionDiff(new EntityOfUnknownType());
 }