@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 870 lines 25 kB view raw
1<?php 2 3final class PhabricatorAuditEditor 4 extends PhabricatorApplicationTransactionEditor { 5 6 const MAX_FILES_SHOWN_IN_EMAIL = 1000; 7 8 private $affectedFiles; 9 private $rawPatch; 10 private $auditorPHIDs = array(); 11 12 private $didExpandInlineState = false; 13 private $oldAuditStatus = null; 14 15 public function setRawPatch($patch) { 16 $this->rawPatch = $patch; 17 return $this; 18 } 19 20 public function getRawPatch() { 21 return $this->rawPatch; 22 } 23 24 public function getEditorApplicationClass() { 25 return PhabricatorAuditApplication::class; 26 } 27 28 public function getEditorObjectsDescription() { 29 return pht('Audits'); 30 } 31 32 public function getTransactionTypes() { 33 $types = parent::getTransactionTypes(); 34 35 $types[] = PhabricatorTransactions::TYPE_COMMENT; 36 $types[] = PhabricatorTransactions::TYPE_EDGE; 37 $types[] = PhabricatorTransactions::TYPE_INLINESTATE; 38 39 $types[] = PhabricatorAuditTransaction::TYPE_COMMIT; 40 41 // TODO: These will get modernized eventually, but that can happen one 42 // at a time later on. 43 $types[] = PhabricatorAuditActionConstants::INLINE; 44 45 return $types; 46 } 47 48 protected function expandTransactions( 49 PhabricatorLiskDAO $object, 50 array $xactions) { 51 52 foreach ($xactions as $xaction) { 53 switch ($xaction->getTransactionType()) { 54 case PhabricatorTransactions::TYPE_INLINESTATE: 55 $this->didExpandInlineState = true; 56 break; 57 } 58 } 59 60 $this->oldAuditStatus = $object->getAuditStatus(); 61 62 return parent::expandTransactions($object, $xactions); 63 } 64 65 protected function transactionHasEffect( 66 PhabricatorLiskDAO $object, 67 PhabricatorApplicationTransaction $xaction) { 68 69 switch ($xaction->getTransactionType()) { 70 case PhabricatorAuditActionConstants::INLINE: 71 return $xaction->hasComment(); 72 } 73 74 return parent::transactionHasEffect($object, $xaction); 75 } 76 77 protected function getCustomTransactionOldValue( 78 PhabricatorLiskDAO $object, 79 PhabricatorApplicationTransaction $xaction) { 80 switch ($xaction->getTransactionType()) { 81 case PhabricatorAuditActionConstants::INLINE: 82 case PhabricatorAuditTransaction::TYPE_COMMIT: 83 return null; 84 } 85 86 return parent::getCustomTransactionOldValue($object, $xaction); 87 } 88 89 protected function getCustomTransactionNewValue( 90 PhabricatorLiskDAO $object, 91 PhabricatorApplicationTransaction $xaction) { 92 93 switch ($xaction->getTransactionType()) { 94 case PhabricatorAuditActionConstants::INLINE: 95 case PhabricatorAuditTransaction::TYPE_COMMIT: 96 return $xaction->getNewValue(); 97 } 98 99 return parent::getCustomTransactionNewValue($object, $xaction); 100 } 101 102 protected function applyCustomInternalTransaction( 103 PhabricatorLiskDAO $object, 104 PhabricatorApplicationTransaction $xaction) { 105 106 switch ($xaction->getTransactionType()) { 107 case PhabricatorAuditActionConstants::INLINE: 108 $comment = $xaction->getComment(); 109 110 $comment->setAttribute('editing', false); 111 112 PhabricatorVersionedDraft::purgeDrafts( 113 $comment->getPHID(), 114 $this->getActingAsPHID()); 115 return; 116 case PhabricatorAuditTransaction::TYPE_COMMIT: 117 return; 118 } 119 120 return parent::applyCustomInternalTransaction($object, $xaction); 121 } 122 123 protected function applyCustomExternalTransaction( 124 PhabricatorLiskDAO $object, 125 PhabricatorApplicationTransaction $xaction) { 126 127 switch ($xaction->getTransactionType()) { 128 case PhabricatorAuditTransaction::TYPE_COMMIT: 129 return; 130 case PhabricatorAuditActionConstants::INLINE: 131 $reply = $xaction->getComment()->getReplyToComment(); 132 if ($reply && !$reply->getHasReplies()) { 133 $reply->setHasReplies(1)->save(); 134 } 135 return; 136 } 137 138 return parent::applyCustomExternalTransaction($object, $xaction); 139 } 140 141 protected function applyBuiltinExternalTransaction( 142 PhabricatorLiskDAO $object, 143 PhabricatorApplicationTransaction $xaction) { 144 145 switch ($xaction->getTransactionType()) { 146 case PhabricatorTransactions::TYPE_INLINESTATE: 147 $table = new PhabricatorAuditTransactionComment(); 148 $conn_w = $table->establishConnection('w'); 149 foreach ($xaction->getNewValue() as $phid => $state) { 150 queryfx( 151 $conn_w, 152 'UPDATE %T SET fixedState = %s WHERE phid = %s', 153 $table->getTableName(), 154 $state, 155 $phid); 156 } 157 break; 158 } 159 160 return parent::applyBuiltinExternalTransaction($object, $xaction); 161 } 162 163 protected function applyFinalEffects( 164 PhabricatorLiskDAO $object, 165 array $xactions) { 166 167 // Load auditors explicitly; we may not have them if the caller was a 168 // generic piece of infrastructure. 169 170 $commit = id(new DiffusionCommitQuery()) 171 ->setViewer($this->requireActor()) 172 ->withIDs(array($object->getID())) 173 ->needAuditRequests(true) 174 ->executeOne(); 175 if (!$commit) { 176 throw new Exception( 177 pht('Failed to load commit during transaction finalization!')); 178 } 179 $object->attachAudits($commit->getAudits()); 180 181 $actor_phid = $this->getActingAsPHID(); 182 $actor_is_author = ($object->getAuthorPHID()) && 183 ($actor_phid == $object->getAuthorPHID()); 184 185 $import_status_flag = null; 186 foreach ($xactions as $xaction) { 187 switch ($xaction->getTransactionType()) { 188 case PhabricatorAuditTransaction::TYPE_COMMIT: 189 $import_status_flag = PhabricatorRepositoryCommit::IMPORTED_PUBLISH; 190 break; 191 } 192 } 193 194 $old_status = $this->oldAuditStatus; 195 196 $requests = $object->getAudits(); 197 $object->updateAuditStatus($requests); 198 199 $new_status = $object->getAuditStatus(); 200 201 $object->save(); 202 203 if ($import_status_flag) { 204 $object->writeImportStatusFlag($import_status_flag); 205 } 206 207 // If the commit has changed state after this edit, add an informational 208 // transaction about the state change. 209 if ($old_status != $new_status) { 210 if ($object->isAuditStatusPartiallyAudited()) { 211 // This state isn't interesting enough to get a transaction. The 212 // best way we could lead the user forward is something like "This 213 // commit still requires additional audits." but that's redundant and 214 // probably not very useful. 215 } else { 216 $xaction = $object->getApplicationTransactionTemplate() 217 ->setTransactionType(DiffusionCommitStateTransaction::TRANSACTIONTYPE) 218 ->setOldValue($old_status) 219 ->setNewValue($new_status); 220 221 $xaction = $this->populateTransaction($object, $xaction); 222 223 $xaction->save(); 224 } 225 } 226 227 // Collect auditor PHIDs for building mail. 228 $this->auditorPHIDs = mpull($object->getAudits(), 'getAuditorPHID'); 229 230 return $xactions; 231 } 232 233 protected function expandTransaction( 234 PhabricatorLiskDAO $object, 235 PhabricatorApplicationTransaction $xaction) { 236 237 $auditors_type = DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE; 238 239 $xactions = parent::expandTransaction($object, $xaction); 240 241 switch ($xaction->getTransactionType()) { 242 case PhabricatorAuditTransaction::TYPE_COMMIT: 243 $phids = $this->getAuditRequestTransactionPHIDsFromCommitMessage( 244 $object); 245 if ($phids) { 246 $xactions[] = $object->getApplicationTransactionTemplate() 247 ->setTransactionType($auditors_type) 248 ->setNewValue( 249 array( 250 '+' => array_fuse($phids), 251 )); 252 $this->addUnmentionablePHIDs($phids); 253 } 254 break; 255 default: 256 break; 257 } 258 259 if (!$this->didExpandInlineState) { 260 switch ($xaction->getTransactionType()) { 261 case PhabricatorTransactions::TYPE_COMMENT: 262 $this->didExpandInlineState = true; 263 264 $query_template = id(new DiffusionDiffInlineCommentQuery()) 265 ->withCommitPHIDs(array($object->getPHID())); 266 267 $state_xaction = $this->newInlineStateTransaction( 268 $object, 269 $query_template); 270 271 if ($state_xaction) { 272 $xactions[] = $state_xaction; 273 } 274 break; 275 } 276 } 277 278 return $xactions; 279 } 280 281 private function getAuditRequestTransactionPHIDsFromCommitMessage( 282 PhabricatorRepositoryCommit $commit) { 283 284 $actor = $this->getActor(); 285 $data = $commit->getCommitData(); 286 $message = $data->getCommitMessage(); 287 288 $result = DifferentialCommitMessageParser::newStandardParser($actor) 289 ->setRaiseMissingFieldErrors(false) 290 ->parseFields($message); 291 292 $field_key = DifferentialAuditorsCommitMessageField::FIELDKEY; 293 $phids = idx($result, $field_key, null); 294 295 if (!$phids) { 296 return array(); 297 } 298 299 // If a commit lists its author as an auditor, just pretend it does not. 300 foreach ($phids as $key => $phid) { 301 if ($phid == $commit->getAuthorPHID()) { 302 unset($phids[$key]); 303 } 304 } 305 306 if (!$phids) { 307 return array(); 308 } 309 310 return $phids; 311 } 312 313 protected function sortTransactions(array $xactions) { 314 $xactions = parent::sortTransactions($xactions); 315 316 $head = array(); 317 $tail = array(); 318 319 foreach ($xactions as $xaction) { 320 $type = $xaction->getTransactionType(); 321 if ($type == PhabricatorAuditActionConstants::INLINE) { 322 $tail[] = $xaction; 323 } else { 324 $head[] = $xaction; 325 } 326 } 327 328 return array_values(array_merge($head, $tail)); 329 } 330 331 protected function supportsSearch() { 332 return true; 333 } 334 335 protected function expandCustomRemarkupBlockTransactions( 336 PhabricatorLiskDAO $object, 337 array $xactions, 338 array $changes, 339 PhutilMarkupEngine $engine) { 340 341 $actor = $this->getActor(); 342 $result = array(); 343 344 // Some interactions (like "Fixes Txxx" interacting with Maniphest) have 345 // already been processed, so we're only re-parsing them here to avoid 346 // generating an extra redundant mention. Other interactions are being 347 // processed for the first time. 348 349 // We're only recognizing magic in the commit message itself, not in 350 // audit comments. 351 352 $is_commit = false; 353 foreach ($xactions as $xaction) { 354 switch ($xaction->getTransactionType()) { 355 case PhabricatorAuditTransaction::TYPE_COMMIT: 356 $is_commit = true; 357 break; 358 } 359 } 360 361 if (!$is_commit) { 362 return $result; 363 } 364 365 $flat_blocks = mpull($changes, 'getNewValue'); 366 $huge_block = implode("\n\n", $flat_blocks); 367 $phid_map = array(); 368 $monograms = array(); 369 370 $task_refs = id(new ManiphestCustomFieldStatusParser()) 371 ->parseCorpus($huge_block); 372 foreach ($task_refs as $match) { 373 foreach ($match['monograms'] as $monogram) { 374 $monograms[] = $monogram; 375 } 376 } 377 378 $rev_refs = id(new DifferentialCustomFieldDependsOnParser()) 379 ->parseCorpus($huge_block); 380 foreach ($rev_refs as $match) { 381 foreach ($match['monograms'] as $monogram) { 382 $monograms[] = $monogram; 383 } 384 } 385 386 $objects = id(new PhabricatorObjectQuery()) 387 ->setViewer($this->getActor()) 388 ->withNames($monograms) 389 ->execute(); 390 $phid_map[] = mpull($objects, 'getPHID', 'getPHID'); 391 392 $reverts_refs = id(new DifferentialCustomFieldRevertsParser()) 393 ->parseCorpus($huge_block); 394 $reverts = array_mergev(ipull($reverts_refs, 'monograms')); 395 if ($reverts) { 396 $reverted_objects = DiffusionCommitRevisionQuery::loadRevertedObjects( 397 $actor, 398 $object, 399 $reverts, 400 $object->getRepository()); 401 402 $reverted_phids = mpull($reverted_objects, 'getPHID', 'getPHID'); 403 404 $reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST; 405 $result[] = id(new PhabricatorAuditTransaction()) 406 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 407 ->setMetadataValue('edge:type', $reverts_edge) 408 ->setNewValue(array('+' => $reverted_phids)); 409 410 $phid_map[] = $reverted_phids; 411 } 412 413 // See T13463. Copy "related task" edges from the associated revision, if 414 // one exists. 415 416 $revision = DiffusionCommitRevisionQuery::loadRevisionForCommit( 417 $actor, 418 $object); 419 if ($revision) { 420 $task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 421 $revision->getPHID(), 422 DifferentialRevisionHasTaskEdgeType::EDGECONST); 423 $task_phids = array_fuse($task_phids); 424 425 if ($task_phids) { 426 $related_edge = DiffusionCommitHasTaskEdgeType::EDGECONST; 427 $result[] = id(new PhabricatorAuditTransaction()) 428 ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 429 ->setMetadataValue('edge:type', $related_edge) 430 ->setNewValue(array('+' => $task_phids)); 431 } 432 433 // Mark these objects as unmentionable, since the explicit relationship 434 // is stronger and any mentions are redundant. 435 $phid_map[] = $task_phids; 436 } 437 438 $phid_map = array_mergev($phid_map); 439 $this->addUnmentionablePHIDs($phid_map); 440 441 return $result; 442 } 443 444 protected function buildReplyHandler(PhabricatorLiskDAO $object) { 445 $reply_handler = new PhabricatorAuditReplyHandler(); 446 $reply_handler->setMailReceiver($object); 447 return $reply_handler; 448 } 449 450 protected function getMailSubjectPrefix() { 451 return pht('[Diffusion]'); 452 } 453 454 protected function getMailThreadID(PhabricatorLiskDAO $object) { 455 // For backward compatibility, use this legacy thread ID. 456 return 'diffusion-audit-'.$object->getPHID(); 457 } 458 459 protected function buildMailTemplate(PhabricatorLiskDAO $object) { 460 $identifier = $object->getCommitIdentifier(); 461 $repository = $object->getRepository(); 462 463 $summary = $object->getSummary(); 464 $name = $repository->formatCommitName($identifier); 465 466 $subject = "{$name}: {$summary}"; 467 468 $template = id(new PhabricatorMetaMTAMail()) 469 ->setSubject($subject); 470 471 $this->attachPatch( 472 $template, 473 $object); 474 475 return $template; 476 } 477 478 protected function getMailTo(PhabricatorLiskDAO $object) { 479 $this->requireAuditors($object); 480 481 $phids = array(); 482 483 if ($object->getAuthorPHID()) { 484 $phids[] = $object->getAuthorPHID(); 485 } 486 487 foreach ($object->getAudits() as $audit) { 488 if (!$audit->isResigned()) { 489 $phids[] = $audit->getAuditorPHID(); 490 } 491 } 492 493 $phids[] = $this->getActingAsPHID(); 494 495 return $phids; 496 } 497 498 protected function newMailUnexpandablePHIDs(PhabricatorLiskDAO $object) { 499 $this->requireAuditors($object); 500 501 $phids = array(); 502 503 foreach ($object->getAudits() as $auditor) { 504 if ($auditor->isResigned()) { 505 $phids[] = $auditor->getAuditorPHID(); 506 } 507 } 508 509 return $phids; 510 } 511 512 protected function getObjectLinkButtonLabelForMail( 513 PhabricatorLiskDAO $object) { 514 return pht('View Commit'); 515 } 516 517 protected function buildMailBody( 518 PhabricatorLiskDAO $object, 519 array $xactions) { 520 521 $body = parent::buildMailBody($object, $xactions); 522 523 $type_inline = PhabricatorAuditActionConstants::INLINE; 524 $type_push = PhabricatorAuditTransaction::TYPE_COMMIT; 525 526 $is_commit = false; 527 $inlines = array(); 528 foreach ($xactions as $xaction) { 529 if ($xaction->getTransactionType() == $type_inline) { 530 $inlines[] = $xaction; 531 } 532 if ($xaction->getTransactionType() == $type_push) { 533 $is_commit = true; 534 } 535 } 536 537 if ($inlines) { 538 $body->addTextSection( 539 pht('INLINE COMMENTS'), 540 $this->renderInlineCommentsForMail($object, $inlines)); 541 } 542 543 if ($is_commit) { 544 $data = $object->getCommitData(); 545 $body->addTextSection(pht('AFFECTED FILES'), $this->affectedFiles); 546 $this->inlinePatch( 547 $body, 548 $object); 549 } 550 551 $data = $object->getCommitData(); 552 553 $user_phids = array(); 554 555 $author_phid = $object->getAuthorPHID(); 556 if ($author_phid) { 557 $user_phids[$author_phid][] = pht('Author'); 558 } 559 560 $committer_phid = $data->getCommitDetail('committerPHID'); 561 if ($committer_phid && ($committer_phid != $author_phid)) { 562 $user_phids[$committer_phid][] = pht('Committer'); 563 } 564 565 foreach ($this->auditorPHIDs as $auditor_phid) { 566 $user_phids[$auditor_phid][] = pht('Auditor'); 567 } 568 569 // TODO: It would be nice to show pusher here too, but that information 570 // is a little tricky to get at right now. 571 572 if ($user_phids) { 573 $handle_phids = array_keys($user_phids); 574 $handles = id(new PhabricatorHandleQuery()) 575 ->setViewer($this->requireActor()) 576 ->withPHIDs($handle_phids) 577 ->execute(); 578 579 $user_info = array(); 580 foreach ($user_phids as $phid => $roles) { 581 $user_info[] = pht( 582 '%s (%s)', 583 $handles[$phid]->getName(), 584 implode(', ', $roles)); 585 } 586 587 $body->addTextSection( 588 pht('USERS'), 589 implode("\n", $user_info)); 590 } 591 592 $monogram = $object->getRepository()->formatCommitName( 593 $object->getCommitIdentifier()); 594 595 $body->addLinkSection( 596 pht('COMMIT'), 597 PhabricatorEnv::getProductionURI('/'.$monogram)); 598 599 return $body; 600 } 601 602 private function attachPatch( 603 PhabricatorMetaMTAMail $template, 604 PhabricatorRepositoryCommit $commit) { 605 606 if (!$this->getRawPatch()) { 607 return; 608 } 609 610 $attach_key = 'metamta.diffusion.attach-patches'; 611 $attach_patches = PhabricatorEnv::getEnvConfig($attach_key); 612 if (!$attach_patches) { 613 return; 614 } 615 616 $repository = $commit->getRepository(); 617 $encoding = $repository->getDetail('encoding', 'UTF-8'); 618 619 $raw_patch = $this->getRawPatch(); 620 $commit_name = $repository->formatCommitName( 621 $commit->getCommitIdentifier()); 622 623 $template->addAttachment( 624 new PhabricatorMailAttachment( 625 $raw_patch, 626 $commit_name.'.patch', 627 'text/x-patch; charset='.$encoding)); 628 } 629 630 private function inlinePatch( 631 PhabricatorMetaMTAMailBody $body, 632 PhabricatorRepositoryCommit $commit) { 633 634 if (!$this->getRawPatch()) { 635 return; 636 } 637 638 $inline_key = 'metamta.diffusion.inline-patches'; 639 $inline_patches = PhabricatorEnv::getEnvConfig($inline_key); 640 if (!$inline_patches) { 641 return; 642 } 643 644 $repository = $commit->getRepository(); 645 $raw_patch = $this->getRawPatch(); 646 $result = null; 647 $len = substr_count($raw_patch, "\n"); 648 if ($len <= $inline_patches) { 649 // We send email as utf8, so we need to convert the text to utf8 if 650 // we can. 651 $encoding = $repository->getDetail('encoding', 'UTF-8'); 652 if ($encoding) { 653 $raw_patch = phutil_utf8_convert($raw_patch, 'UTF-8', $encoding); 654 } 655 $result = phutil_utf8ize($raw_patch); 656 } 657 658 if ($result) { 659 $result = "PATCH\n\n{$result}\n"; 660 } 661 $body->addRawSection($result); 662 } 663 664 private function renderInlineCommentsForMail( 665 PhabricatorLiskDAO $object, 666 array $inline_xactions) { 667 668 $inlines = mpull($inline_xactions, 'getComment'); 669 670 $block = array(); 671 672 $path_map = id(new DiffusionPathQuery()) 673 ->withPathIDs(mpull($inlines, 'getPathID')) 674 ->execute(); 675 $path_map = ipull($path_map, 'path', 'id'); 676 677 foreach ($inlines as $inline) { 678 $path = idx($path_map, $inline->getPathID()); 679 if ($path === null) { 680 continue; 681 } 682 683 $start = $inline->getLineNumber(); 684 $len = $inline->getLineLength(); 685 if ($len) { 686 $range = $start.'-'.($start + $len); 687 } else { 688 $range = $start; 689 } 690 691 $content = $inline->getContent(); 692 $block[] = "{$path}:{$range} {$content}"; 693 } 694 695 return implode("\n", $block); 696 } 697 698 public function getMailTagsMap() { 699 return array( 700 PhabricatorAuditTransaction::MAILTAG_COMMIT => 701 pht('A commit is created.'), 702 PhabricatorAuditTransaction::MAILTAG_ACTION_CONCERN => 703 pht('A commit has a concern raised against it.'), 704 PhabricatorAuditTransaction::MAILTAG_ACTION_ACCEPT => 705 pht('A commit is accepted.'), 706 PhabricatorAuditTransaction::MAILTAG_ACTION_RESIGN => 707 pht('A commit has an auditor resign.'), 708 PhabricatorAuditTransaction::MAILTAG_ACTION_CLOSE => 709 pht('A commit is closed.'), 710 PhabricatorAuditTransaction::MAILTAG_ADD_AUDITORS => 711 pht('A commit has auditors added.'), 712 PhabricatorAuditTransaction::MAILTAG_ADD_CCS => 713 pht("A commit's subscribers change."), 714 PhabricatorAuditTransaction::MAILTAG_PROJECTS => 715 pht("A commit's associated projects change."), 716 PhabricatorAuditTransaction::MAILTAG_COMMENT => 717 pht('Someone comments on a commit.'), 718 PhabricatorAuditTransaction::MAILTAG_OTHER => 719 pht('Other commit activity not listed above occurs.'), 720 ); 721 } 722 723 protected function shouldApplyHeraldRules( 724 PhabricatorLiskDAO $object, 725 array $xactions) { 726 727 foreach ($xactions as $xaction) { 728 switch ($xaction->getTransactionType()) { 729 case PhabricatorAuditTransaction::TYPE_COMMIT: 730 $repository = $object->getRepository(); 731 $publisher = $repository->newPublisher(); 732 if (!$publisher->shouldPublishCommit($object)) { 733 return false; 734 } 735 return true; 736 default: 737 break; 738 } 739 } 740 return parent::shouldApplyHeraldRules($object, $xactions); 741 } 742 743 protected function buildHeraldAdapter( 744 PhabricatorLiskDAO $object, 745 array $xactions) { 746 return id(new HeraldCommitAdapter()) 747 ->setObject($object); 748 } 749 750 protected function didApplyHeraldRules( 751 PhabricatorLiskDAO $object, 752 HeraldAdapter $adapter, 753 HeraldTranscript $transcript) { 754 755 $limit = self::MAX_FILES_SHOWN_IN_EMAIL; 756 $files = $adapter->loadAffectedPaths(); 757 sort($files); 758 if (count($files) > $limit) { 759 array_splice($files, $limit); 760 $files[] = pht( 761 '(This commit affected more than %d files. Only %d are shown here '. 762 'and additional ones are truncated.)', 763 $limit, 764 $limit); 765 } 766 $this->affectedFiles = implode("\n", $files); 767 768 return array(); 769 } 770 771 private function isCommitMostlyImported(PhabricatorLiskDAO $object) { 772 $has_message = PhabricatorRepositoryCommit::IMPORTED_MESSAGE; 773 $has_changes = PhabricatorRepositoryCommit::IMPORTED_CHANGE; 774 775 // Don't publish feed stories or email about events which occur during 776 // import. In particular, this affects tasks being attached when they are 777 // closed by "Fixes Txxxx" in a commit message. See T5851. 778 779 $mask = ($has_message | $has_changes); 780 781 return $object->isPartiallyImported($mask); 782 } 783 784 785 private function shouldPublishRepositoryActivity( 786 PhabricatorLiskDAO $object, 787 array $xactions) { 788 789 // not every code path loads the repository so tread carefully 790 // TODO: They should, and then we should simplify this. 791 $repository = $object->getRepository($assert_attached = false); 792 if ($repository != PhabricatorLiskDAO::ATTACHABLE) { 793 $publisher = $repository->newPublisher(); 794 if (!$publisher->shouldPublishCommit($object)) { 795 return false; 796 } 797 } 798 799 return $this->isCommitMostlyImported($object); 800 } 801 802 protected function shouldSendMail( 803 PhabricatorLiskDAO $object, 804 array $xactions) { 805 return $this->shouldPublishRepositoryActivity($object, $xactions); 806 } 807 808 protected function shouldEnableMentions( 809 PhabricatorLiskDAO $object, 810 array $xactions) { 811 return $this->shouldPublishRepositoryActivity($object, $xactions); 812 } 813 814 protected function shouldPublishFeedStory( 815 PhabricatorLiskDAO $object, 816 array $xactions) { 817 return $this->shouldPublishRepositoryActivity($object, $xactions); 818 } 819 820 protected function getCustomWorkerState() { 821 return array( 822 'rawPatch' => $this->rawPatch, 823 'affectedFiles' => $this->affectedFiles, 824 'auditorPHIDs' => $this->auditorPHIDs, 825 ); 826 } 827 828 protected function getCustomWorkerStateEncoding() { 829 return array( 830 'rawPatch' => self::STORAGE_ENCODING_BINARY, 831 ); 832 } 833 834 protected function loadCustomWorkerState(array $state) { 835 $this->rawPatch = idx($state, 'rawPatch'); 836 $this->affectedFiles = idx($state, 'affectedFiles'); 837 $this->auditorPHIDs = idx($state, 'auditorPHIDs'); 838 return $this; 839 } 840 841 protected function willPublish(PhabricatorLiskDAO $object, array $xactions) { 842 return id(new DiffusionCommitQuery()) 843 ->setViewer($this->requireActor()) 844 ->withIDs(array($object->getID())) 845 ->needAuditRequests(true) 846 ->needCommitData(true) 847 ->executeOne(); 848 } 849 850 private function requireAuditors(PhabricatorRepositoryCommit $commit) { 851 if ($commit->hasAttachedAudits()) { 852 return; 853 } 854 855 $with_auditors = id(new DiffusionCommitQuery()) 856 ->setViewer($this->getActor()) 857 ->needAuditRequests(true) 858 ->withPHIDs(array($commit->getPHID())) 859 ->executeOne(); 860 if (!$with_auditors) { 861 throw new Exception( 862 pht( 863 'Failed to reload commit ("%s").', 864 $commit->getPHID())); 865 } 866 867 $commit->attachAudits($with_auditors->getAudits()); 868 } 869 870}