Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / diffusion / worker / DiffusionUpdateObjectAfterCommitWorker.php
blobca085b2e810cf4867356bb2aca17605412e80186
1 <?php
3 final class DiffusionUpdateObjectAfterCommitWorker
4 extends PhabricatorWorker {
6 private $properties;
8 protected function getViewer() {
9 return PhabricatorUser::getOmnipotentUser();
12 protected function doWork() {
13 $viewer = $this->getViewer();
14 $data = $this->getTaskData();
16 $commit_phid = idx($data, 'commitPHID');
17 if (!$commit_phid) {
18 throw new PhabricatorWorkerPermanentFailureException(
19 pht('No "commitPHID" in task data.'));
22 $commit = id(new DiffusionCommitQuery())
23 ->setViewer($viewer)
24 ->withPHIDs(array($commit_phid))
25 ->needIdentities(true)
26 ->executeOne();
27 if (!$commit) {
28 throw new PhabricatorWorkerPermanentFailureException(
29 pht(
30 'Unable to load commit "%s".',
31 $commit_phid));
34 $object_phid = idx($data, 'objectPHID');
35 if (!$object_phid) {
36 throw new PhabricatorWorkerPermanentFailureException(
37 pht('No "objectPHID" in task data.'));
40 $object = id(new PhabricatorObjectQuery())
41 ->setViewer($viewer)
42 ->withPHIDs(array($object_phid))
43 ->executeOne();
44 if (!$object) {
45 throw new PhabricatorWorkerPermanentFailureException(
46 pht(
47 'Unable to load object "%s".',
48 $object_phid));
51 $properties = idx($data, 'properties', array());
52 $this->properties = $properties;
54 if ($object instanceof ManiphestTask) {
55 $this->updateTask($commit, $object);
56 } else if ($object instanceof DifferentialRevision) {
57 $this->updateRevision($commit, $object);
61 protected function getUpdateProperty($key, $default = null) {
62 return idx($this->properties, $key, $default);
65 protected function getActingPHID(PhabricatorRepositoryCommit $commit) {
66 if ($commit->hasCommitterIdentity()) {
67 return $commit->getCommitterIdentity()->getIdentityDisplayPHID();
70 if ($commit->hasAuthorIdentity()) {
71 return $commit->getAuthorIdentity()->getIdentityDisplayPHID();
74 return id(new PhabricatorDiffusionApplication())->getPHID();
77 protected function loadActingUser($acting_phid) {
78 // If we we were able to identify an author or committer for the commit, we
79 // try to act as that user when affecting other objects, like tasks marked
80 // with "Fixes Txxx".
82 // This helps to prevent mistakes where a user accidentally writes the
83 // wrong task IDs and affects tasks they can't see (and thus can't undo the
84 // status changes for).
86 // This is just a guard rail, not a security measure. An attacker can still
87 // forge another user's identity trivially by forging author or committer
88 // email addresses.
90 // We also let commits with unrecognized authors act on any task to make
91 // behavior less confusing for new installs, and any user can craft a
92 // commit with an unrecognized author and committer.
94 $viewer = $this->getViewer();
96 $user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
97 if (phid_get_type($acting_phid) === $user_type) {
98 $acting_user = id(new PhabricatorPeopleQuery())
99 ->setViewer($viewer)
100 ->withPHIDs(array($acting_phid))
101 ->executeOne();
102 if ($acting_user) {
103 return $acting_user;
107 return $viewer;
110 private function updateTask(
111 PhabricatorRepositoryCommit $commit,
112 ManiphestTask $task) {
114 $acting_phid = $this->getActingPHID($commit);
115 $acting_user = $this->loadActingUser($acting_phid);
117 $commit_phid = $commit->getPHID();
119 $xactions = array();
121 $xactions[] = $this->newEdgeTransaction(
122 $task,
123 $commit,
124 ManiphestTaskHasCommitEdgeType::EDGECONST);
126 $status = $this->getUpdateProperty('status');
127 if ($status) {
128 $xactions[] = $task->getApplicationTransactionTemplate()
129 ->setTransactionType(ManiphestTaskStatusTransaction::TRANSACTIONTYPE)
130 ->setMetadataValue('commitPHID', $commit_phid)
131 ->setNewValue($status);
134 $content_source = $this->newContentSource();
136 $editor = $task->getApplicationTransactionEditor()
137 ->setActor($acting_user)
138 ->setActingAsPHID($acting_phid)
139 ->setContentSource($content_source)
140 ->setContinueOnNoEffect(true)
141 ->setContinueOnMissingFields(true)
142 ->addUnmentionablePHIDs(array($commit_phid));
144 $editor->applyTransactions($task, $xactions);
147 private function updateRevision(
148 PhabricatorRepositoryCommit $commit,
149 DifferentialRevision $revision) {
151 $acting_phid = $this->getActingPHID($commit);
152 $acting_user = $this->loadActingUser($acting_phid);
154 // See T13625. The "Acting User" is the author of the commit based on the
155 // author string, or the Diffusion application PHID if we could not
156 // identify an author.
158 // This user may not be able to view the commit or the revision, and may
159 // also be unable to make API calls. Here, we execute queries and apply
160 // transactions as the omnipotent user.
162 // It would probably be better to use the acting user everywhere here, and
163 // exit gracefully if they can't see the revision (this is how the flow
164 // on tasks works). However, without a positive indicator in the UI
165 // explaining "no revision was updated because the author of this commit
166 // can't see anything", this might be fairly confusing, and break workflows
167 // which have worked historically.
169 // This isn't, per se, a policy violation (you can't get access to anything
170 // you don't already have access to by making commits that reference
171 // revisions, even if you can't see the commits or revisions), so just
172 // leave it for now.
174 $viewer = $this->getViewer();
176 // Reload the revision to get the active diff, which is currently required
177 // by "updateRevisionWithCommit()".
178 $revision = id(new DifferentialRevisionQuery())
179 ->setViewer($viewer)
180 ->withIDs(array($revision->getID()))
181 ->needActiveDiffs(true)
182 ->executeOne();
184 $xactions = array();
186 $xactions[] = $this->newEdgeTransaction(
187 $revision,
188 $commit,
189 DifferentialRevisionHasCommitEdgeType::EDGECONST);
191 $match_data = $this->getUpdateProperty('revisionMatchData');
193 $type_close = DifferentialRevisionCloseTransaction::TRANSACTIONTYPE;
194 $xactions[] = $revision->getApplicationTransactionTemplate()
195 ->setTransactionType($type_close)
196 ->setNewValue(true)
197 ->setMetadataValue('isCommitClose', true)
198 ->setMetadataValue('revisionMatchData', $match_data)
199 ->setMetadataValue('commitPHID', $commit->getPHID());
201 $extraction_engine = id(new DifferentialDiffExtractionEngine())
202 ->setViewer($viewer)
203 ->setAuthorPHID($acting_phid);
205 $content_source = $this->newContentSource();
207 $extraction_engine->updateRevisionWithCommit(
208 $revision,
209 $commit,
210 $xactions,
211 $content_source);
214 private function newEdgeTransaction(
215 $object,
216 PhabricatorRepositoryCommit $commit,
217 $edge_type) {
219 $commit_phid = $commit->getPHID();
221 return $object->getApplicationTransactionTemplate()
222 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
223 ->setMetadataValue('edge:type', $edge_type)
224 ->setNewValue(
225 array(
226 '+' => array(
227 $commit_phid => $commit_phid,