Path: blob/master/src/applications/differential/xaction/DifferentialRevisionUpdateTransaction.php
12256 views
<?php12final class DifferentialRevisionUpdateTransaction3extends DifferentialRevisionTransactionType {45const TRANSACTIONTYPE = 'differential:update';6const EDITKEY = 'update';78public function generateOldValue($object) {9return $object->getActiveDiffPHID();10}1112public function generateNewValue($object, $value) {13// See T13290. If we're updating the revision in response to a commit but14// the revision is already closed, return the old value so we no-op this15// transaction. We don't want to attach more than one commit-diff to a16// revision.1718// Although we can try to bail out earlier so we don't generate this19// transaction in the first place, we may race another worker and end up20// trying to apply it anyway. Here, we have a lock on the object and can21// be certain about the object state.2223if ($this->isCommitUpdate()) {24if ($object->isClosed()) {25return $this->generateOldValue($object);26}27}2829return $value;30}3132public function applyInternalEffects($object, $value) {33$should_review = $this->shouldRequestReviewAfterUpdate($object);34if ($should_review) {35// If we're updating a non-broadcasting revision, put it back in draft36// rather than moving it directly to "Needs Review".37if ($object->getShouldBroadcast()) {38$new_status = DifferentialRevisionStatus::NEEDS_REVIEW;39} else {40$new_status = DifferentialRevisionStatus::DRAFT;41}42$object->setModernRevisionStatus($new_status);43}4445$editor = $this->getEditor();46$diff = $editor->requireDiff($value);4748$this->updateRevisionLineCounts($object, $diff);4950$object->setRepositoryPHID($diff->getRepositoryPHID());51$object->setActiveDiffPHID($diff->getPHID());52$object->attachActiveDiff($diff);53}5455private function shouldRequestReviewAfterUpdate($object) {56if ($this->isCommitUpdate()) {57return false;58}5960$should_update =61$object->isNeedsRevision() ||62$object->isChangePlanned() ||63$object->isAbandoned();64if ($should_update) {65return true;66}6768return false;69}7071public function applyExternalEffects($object, $value) {72$editor = $this->getEditor();73$diff = $editor->requireDiff($value);7475// TODO: This can race with diff updates, particularly those from76// Harbormaster. See discussion in T8650.77$diff->setRevisionID($object->getID());78$diff->save();79}8081public function didCommitTransaction($object, $value) {82$editor = $this->getEditor();83$diff = $editor->requireDiff($value);84$omnipotent = PhabricatorUser::getOmnipotentUser();8586// If there are any outstanding buildables for this diff, tell87// Harbormaster that their containers need to be updated. This is88// common, because `arc` creates buildables so it can upload lint89// and unit results.9091$buildables = id(new HarbormasterBuildableQuery())92->setViewer($omnipotent)93->withManualBuildables(false)94->withBuildablePHIDs(array($diff->getPHID()))95->execute();96foreach ($buildables as $buildable) {97$buildable->sendMessage(98$this->getActor(),99HarbormasterMessageType::BUILDABLE_CONTAINER,100true);101}102103// See T13455. If users have set view properites on a diff and the diff104// is then attached to a revision, attempt to copy their view preferences105// to the revision.106107DifferentialViewState::copyViewStatesToObject(108$diff->getPHID(),109$object->getPHID());110}111112public function getColor() {113return 'sky';114}115116public function getIcon() {117return 'fa-refresh';118}119120public function getActionName() {121if ($this->isCreateTransaction()) {122return pht('Request');123} else {124return pht('Updated');125}126}127128public function getActionStrength() {129return 200;130}131132public function getTitle() {133$old = $this->getOldValue();134$new = $this->getNewValue();135136if ($this->isCommitUpdate()) {137return pht(138'This revision was automatically updated to reflect the '.139'committed changes.');140}141142// NOTE: Very, very old update transactions did not have a new value or143// did not use a diff PHID as a new value. This was changed years ago,144// but wasn't migrated. We might consider migrating if this causes issues.145146return pht(147'%s updated this revision to %s.',148$this->renderAuthor(),149$this->renderNewHandle());150}151152public function getTitleForFeed() {153return pht(154'%s updated the diff for %s.',155$this->renderAuthor(),156$this->renderObject());157}158159public function validateTransactions($object, array $xactions) {160$errors = array();161162$diff_phid = null;163foreach ($xactions as $xaction) {164$diff_phid = $xaction->getNewValue();165166$diff = id(new DifferentialDiffQuery())167->withPHIDs(array($diff_phid))168->setViewer($this->getActor())169->executeOne();170if (!$diff) {171$errors[] = $this->newInvalidError(172pht(173'Specified diff ("%s") does not exist.',174$diff_phid),175$xaction);176continue;177}178179$is_attached =180($diff->getRevisionID()) &&181($diff->getRevisionID() == $object->getID());182if ($is_attached) {183$is_active = ($diff_phid == $object->getActiveDiffPHID());184} else {185$is_active = false;186}187188if ($is_attached) {189if ($is_active) {190// This is a no-op: we're reattaching the current active diff to the191// revision it is already attached to. This is valid and will just192// be dropped later on in the process.193} else {194// At least for now, there's no support for "undoing" a diff and195// reverting to an older proposed change without just creating a196// new diff from whole cloth.197$errors[] = $this->newInvalidError(198pht(199'You can not update this revision with the specified diff '.200'("%s") because this diff is already attached to the revision '.201'as an older version of the change.',202$diff_phid),203$xaction);204continue;205}206} else if ($diff->getRevisionID()) {207$errors[] = $this->newInvalidError(208pht(209'You can not update this revision with the specified diff ("%s") '.210'because the diff is already attached to another revision.',211$diff_phid),212$xaction);213continue;214}215}216217if (!$diff_phid && !$object->getActiveDiffPHID()) {218$errors[] = $this->newInvalidError(219pht(220'You must specify an initial diff when creating a revision.'));221}222223return $errors;224}225226public function isCommitUpdate() {227return (bool)$this->getMetadataValue('isCommitUpdate');228}229230private function updateRevisionLineCounts(231DifferentialRevision $revision,232DifferentialDiff $diff) {233234$revision->setLineCount($diff->getLineCount());235236$conn = $revision->establishConnection('r');237238$row = queryfx_one(239$conn,240'SELECT SUM(addLines) A, SUM(delLines) D FROM %T241WHERE diffID = %d',242id(new DifferentialChangeset())->getTableName(),243$diff->getID());244245if ($row) {246$revision->setAddedLineCount((int)$row['A']);247$revision->setRemovedLineCount((int)$row['D']);248}249}250251public function getTransactionTypeForConduit($xaction) {252return 'update';253}254255public function getFieldValuesForConduit($xaction, $data) {256$commit_phids = $xaction->getMetadataValue('commitPHIDs', array());257258return array(259'old' => $xaction->getOldValue(),260'new' => $xaction->getNewValue(),261'commitPHIDs' => $commit_phids,262);263}264265}266267268