[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorAuditEditor 4 extends PhabricatorApplicationTransactionEditor { 5 6 const MAX_FILES_SHOWN_IN_EMAIL = 1000; 7 8 private $auditReasonMap = array(); 9 private $heraldEmailPHIDs = array(); 10 private $affectedFiles; 11 private $rawPatch; 12 13 public function addAuditReason($phid, $reason) { 14 if (!isset($this->auditReasonMap[$phid])) { 15 $this->auditReasonMap[$phid] = array(); 16 } 17 $this->auditReasonMap[$phid][] = $reason; 18 return $this; 19 } 20 21 private function getAuditReasons($phid) { 22 if (isset($this->auditReasonMap[$phid])) { 23 return $this->auditReasonMap[$phid]; 24 } 25 if ($this->getIsHeraldEditor()) { 26 $name = 'herald'; 27 } else { 28 $name = $this->getActor()->getUsername(); 29 } 30 return array('Added by '.$name.'.'); 31 } 32 33 public function setRawPatch($patch) { 34 $this->rawPatch = $patch; 35 return $this; 36 } 37 38 public function getRawPatch() { 39 return $this->rawPatch; 40 } 41 42 public function getEditorApplicationClass() { 43 return 'PhabricatorAuditApplication'; 44 } 45 46 public function getEditorObjectsDescription() { 47 return pht('Audits'); 48 } 49 50 public function getTransactionTypes() { 51 $types = parent::getTransactionTypes(); 52 53 $types[] = PhabricatorTransactions::TYPE_COMMENT; 54 $types[] = PhabricatorTransactions::TYPE_EDGE; 55 56 $types[] = PhabricatorAuditTransaction::TYPE_COMMIT; 57 58 // TODO: These will get modernized eventually, but that can happen one 59 // at a time later on. 60 $types[] = PhabricatorAuditActionConstants::ACTION; 61 $types[] = PhabricatorAuditActionConstants::INLINE; 62 $types[] = PhabricatorAuditActionConstants::ADD_AUDITORS; 63 64 return $types; 65 } 66 67 protected function transactionHasEffect( 68 PhabricatorLiskDAO $object, 69 PhabricatorApplicationTransaction $xaction) { 70 71 switch ($xaction->getTransactionType()) { 72 case PhabricatorAuditActionConstants::INLINE: 73 return $xaction->hasComment(); 74 } 75 76 return parent::transactionHasEffect($object, $xaction); 77 } 78 79 protected function getCustomTransactionOldValue( 80 PhabricatorLiskDAO $object, 81 PhabricatorApplicationTransaction $xaction) { 82 switch ($xaction->getTransactionType()) { 83 case PhabricatorAuditActionConstants::ACTION: 84 case PhabricatorAuditActionConstants::INLINE: 85 case PhabricatorAuditTransaction::TYPE_COMMIT: 86 return null; 87 case PhabricatorAuditActionConstants::ADD_AUDITORS: 88 // TODO: For now, just record the added PHIDs. Eventually, turn these 89 // into real edge transactions, probably? 90 return array(); 91 } 92 93 return parent::getCustomTransactionOldValue($object, $xaction); 94 } 95 96 protected function getCustomTransactionNewValue( 97 PhabricatorLiskDAO $object, 98 PhabricatorApplicationTransaction $xaction) { 99 100 switch ($xaction->getTransactionType()) { 101 case PhabricatorAuditActionConstants::ACTION: 102 case PhabricatorAuditActionConstants::INLINE: 103 case PhabricatorAuditActionConstants::ADD_AUDITORS: 104 case PhabricatorAuditTransaction::TYPE_COMMIT: 105 return $xaction->getNewValue(); 106 } 107 108 return parent::getCustomTransactionNewValue($object, $xaction); 109 } 110 111 protected function applyCustomInternalTransaction( 112 PhabricatorLiskDAO $object, 113 PhabricatorApplicationTransaction $xaction) { 114 115 switch ($xaction->getTransactionType()) { 116 case PhabricatorTransactions::TYPE_COMMENT: 117 case PhabricatorTransactions::TYPE_SUBSCRIBERS: 118 case PhabricatorTransactions::TYPE_EDGE: 119 case PhabricatorAuditActionConstants::ACTION: 120 case PhabricatorAuditActionConstants::INLINE: 121 case PhabricatorAuditActionConstants::ADD_AUDITORS: 122 case PhabricatorAuditTransaction::TYPE_COMMIT: 123 return; 124 } 125 126 return parent::applyCustomInternalTransaction($object, $xaction); 127 } 128 129 protected function applyCustomExternalTransaction( 130 PhabricatorLiskDAO $object, 131 PhabricatorApplicationTransaction $xaction) { 132 133 switch ($xaction->getTransactionType()) { 134 case PhabricatorTransactions::TYPE_COMMENT: 135 case PhabricatorTransactions::TYPE_SUBSCRIBERS: 136 case PhabricatorTransactions::TYPE_EDGE: 137 case PhabricatorAuditActionConstants::ACTION: 138 case PhabricatorAuditActionConstants::INLINE: 139 case PhabricatorAuditTransaction::TYPE_COMMIT: 140 return; 141 case PhabricatorAuditActionConstants::ADD_AUDITORS: 142 $new = $xaction->getNewValue(); 143 if (!is_array($new)) { 144 $new = array(); 145 } 146 147 $old = $xaction->getOldValue(); 148 if (!is_array($old)) { 149 $old = array(); 150 } 151 152 $add = array_diff_key($new, $old); 153 154 $actor = $this->requireActor(); 155 156 $requests = $object->getAudits(); 157 $requests = mpull($requests, null, 'getAuditorPHID'); 158 foreach ($add as $phid) { 159 if (isset($requests[$phid])) { 160 continue; 161 } 162 163 if ($this->getIsHeraldEditor()) { 164 $audit_requested = $xaction->getMetadataValue('auditStatus'); 165 $audit_reason_map = $xaction->getMetadataValue('auditReasonMap'); 166 $audit_reason = $audit_reason_map[$phid]; 167 } else { 168 $audit_requested = PhabricatorAuditStatusConstants::AUDIT_REQUESTED; 169 $audit_reason = $this->getAuditReasons($phid); 170 } 171 $requests[] = id (new PhabricatorRepositoryAuditRequest()) 172 ->setCommitPHID($object->getPHID()) 173 ->setAuditorPHID($phid) 174 ->setAuditStatus($audit_requested) 175 ->setAuditReasons($audit_reason) 176 ->save(); 177 } 178 179 $object->attachAudits($requests); 180 return; 181 } 182 183 return parent::applyCustomExternalTransaction($object, $xaction); 184 } 185 186 protected function applyFinalEffects( 187 PhabricatorLiskDAO $object, 188 array $xactions) { 189 190 // Load auditors explicitly; we may not have them if the caller was a 191 // generic piece of infrastructure. 192 193 $commit = id(new DiffusionCommitQuery()) 194 ->setViewer($this->requireActor()) 195 ->withIDs(array($object->getID())) 196 ->needAuditRequests(true) 197 ->executeOne(); 198 if (!$commit) { 199 throw new Exception( 200 pht('Failed to load commit during transaction finalization!')); 201 } 202 $object->attachAudits($commit->getAudits()); 203 204 $status_concerned = PhabricatorAuditStatusConstants::CONCERNED; 205 $status_closed = PhabricatorAuditStatusConstants::CLOSED; 206 $status_resigned = PhabricatorAuditStatusConstants::RESIGNED; 207 $status_accepted = PhabricatorAuditStatusConstants::ACCEPTED; 208 $status_concerned = PhabricatorAuditStatusConstants::CONCERNED; 209 210 $actor_phid = $this->getActingAsPHID(); 211 $actor_is_author = ($object->getAuthorPHID()) && 212 ($actor_phid == $object->getAuthorPHID()); 213 214 $import_status_flag = null; 215 foreach ($xactions as $xaction) { 216 switch ($xaction->getTransactionType()) { 217 case PhabricatorAuditTransaction::TYPE_COMMIT: 218 $import_status_flag = PhabricatorRepositoryCommit::IMPORTED_HERALD; 219 break; 220 case PhabricatorAuditActionConstants::ACTION: 221 $new = $xaction->getNewValue(); 222 switch ($new) { 223 case PhabricatorAuditActionConstants::CLOSE: 224 // "Close" means wipe out all the concerns. 225 $requests = $object->getAudits(); 226 foreach ($requests as $request) { 227 if ($request->getAuditStatus() == $status_concerned) { 228 $request 229 ->setAuditStatus($status_closed) 230 ->save(); 231 } 232 } 233 break; 234 case PhabricatorAuditActionConstants::RESIGN: 235 $requests = $object->getAudits(); 236 $requests = mpull($requests, null, 'getAuditorPHID'); 237 $actor_request = idx($requests, $actor_phid); 238 239 // If the actor doesn't currently have a relationship to the 240 // commit, add one explicitly. For example, this allows members 241 // of a project to resign from a commit and have it drop out of 242 // their queue. 243 244 if (!$actor_request) { 245 $actor_request = id(new PhabricatorRepositoryAuditRequest()) 246 ->setCommitPHID($object->getPHID()) 247 ->setAuditorPHID($actor_phid); 248 249 $requests[] = $actor_request; 250 $object->attachAudits($requests); 251 } 252 253 $actor_request 254 ->setAuditStatus($status_resigned) 255 ->save(); 256 break; 257 case PhabricatorAuditActionConstants::ACCEPT: 258 case PhabricatorAuditActionConstants::CONCERN: 259 if ($new == PhabricatorAuditActionConstants::ACCEPT) { 260 $new_status = $status_accepted; 261 } else { 262 $new_status = $status_concerned; 263 } 264 265 $requests = $object->getAudits(); 266 $requests = mpull($requests, null, 'getAuditorPHID'); 267 268 // Figure out which requests the actor has authority over: these 269 // are user requests where they are the auditor, and packages 270 // and projects they are a member of. 271 272 if ($actor_is_author) { 273 // When modifying your own commits, you act only on behalf of 274 // yourself, not your packages/projects -- the idea being that 275 // you can't accept your own commits. 276 $authority_phids = array($actor_phid); 277 } else { 278 $authority_phids = 279 PhabricatorAuditCommentEditor::loadAuditPHIDsForUser( 280 $this->requireActor()); 281 } 282 283 $authority = array_select_keys( 284 $requests, 285 $authority_phids); 286 287 if (!$authority) { 288 // If the actor has no authority over any existing requests, 289 // create a new request for them. 290 291 $actor_request = id(new PhabricatorRepositoryAuditRequest()) 292 ->setCommitPHID($object->getPHID()) 293 ->setAuditorPHID($actor_phid) 294 ->setAuditStatus($new_status) 295 ->save(); 296 297 $requests[$actor_phid] = $actor_request; 298 $object->attachAudits($requests); 299 } else { 300 // Otherwise, update the audit status of the existing requests. 301 foreach ($authority as $request) { 302 $request 303 ->setAuditStatus($new_status) 304 ->save(); 305 } 306 } 307 break; 308 309 } 310 break; 311 } 312 } 313 314 $requests = $object->getAudits(); 315 $object->updateAuditStatus($requests); 316 $object->save(); 317 318 if ($import_status_flag) { 319 $object->writeImportStatusFlag($import_status_flag); 320 } 321 322 return $xactions; 323 } 324 325 protected function expandTransaction( 326 PhabricatorLiskDAO $object, 327 PhabricatorApplicationTransaction $xaction) { 328 329 $xactions = parent::expandTransaction($object, $xaction); 330 switch ($xaction->getTransactionType()) { 331 case PhabricatorAuditTransaction::TYPE_COMMIT: 332 $request = $this->createAuditRequestTransactionFromCommitMessage( 333 $object); 334 if ($request) { 335 $xactions[] = $request; 336 $this->setUnmentionablePHIDMap($request->getNewValue()); 337 } 338 break; 339 default: 340 break; 341 } 342 return $xactions; 343 } 344 345 private function createAuditRequestTransactionFromCommitMessage( 346 PhabricatorRepositoryCommit $commit) { 347 348 $data = $commit->getCommitData(); 349 $message = $data->getCommitMessage(); 350 351 $matches = null; 352 if (!preg_match('/^Auditors:\s*(.*)$/im', $message, $matches)) { 353 return array(); 354 } 355 356 $phids = id(new PhabricatorObjectListQuery()) 357 ->setViewer($this->getActor()) 358 ->setAllowPartialResults(true) 359 ->setAllowedTypes( 360 array( 361 PhabricatorPeopleUserPHIDType::TYPECONST, 362 PhabricatorProjectProjectPHIDType::TYPECONST, 363 )) 364 ->setObjectList($matches[1]) 365 ->execute(); 366 367 if (!$phids) { 368 return array(); 369 } 370 371 foreach ($phids as $phid) { 372 $this->addAuditReason($phid, 'Requested by Author'); 373 } 374 return id(new PhabricatorAuditTransaction()) 375 ->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS) 376 ->setNewValue(array_fuse($phids)); 377 } 378 379 protected function sortTransactions(array $xactions) { 380 $xactions = parent::sortTransactions($xactions); 381 382 $head = array(); 383 $tail = array(); 384 385 foreach ($xactions as $xaction) { 386 $type = $xaction->getTransactionType(); 387 if ($type == PhabricatorAuditActionConstants::INLINE) { 388 $tail[] = $xaction; 389 } else { 390 $head[] = $xaction; 391 } 392 } 393 394 return array_values(array_merge($head, $tail)); 395 } 396 397 protected function validateTransaction( 398 PhabricatorLiskDAO $object, 399 $type, 400 array $xactions) { 401 402 $errors = parent::validateTransaction($object, $type, $xactions); 403 404 foreach ($xactions as $xaction) { 405 switch ($type) { 406 case PhabricatorAuditActionConstants::ACTION: 407 $error = $this->validateAuditAction( 408 $object, 409 $type, 410 $xaction, 411 $xaction->getNewValue()); 412 if ($error) { 413 $errors[] = new PhabricatorApplicationTransactionValidationError( 414 $type, 415 pht('Invalid'), 416 $error, 417 $xaction); 418 } 419 break; 420 } 421 } 422 423 return $errors; 424 } 425 426 private function validateAuditAction( 427 PhabricatorLiskDAO $object, 428 $type, 429 PhabricatorAuditTransaction $xaction, 430 $action) { 431 432 $can_author_close_key = 'audit.can-author-close-audit'; 433 $can_author_close = PhabricatorEnv::getEnvConfig($can_author_close_key); 434 435 $actor_is_author = ($object->getAuthorPHID()) && 436 ($object->getAuthorPHID() == $this->getActingAsPHID()); 437 438 switch ($action) { 439 case PhabricatorAuditActionConstants::CLOSE: 440 if (!$actor_is_author) { 441 return pht( 442 'You can not close this audit because you are not the author '. 443 'of the commit.'); 444 } 445 446 if (!$can_author_close) { 447 return pht( 448 'You can not close this audit because "%s" is disabled in '. 449 'the Phabricator configuration.', 450 $can_author_close_key); 451 } 452 453 break; 454 } 455 456 return null; 457 } 458 459 460 protected function supportsSearch() { 461 return true; 462 } 463 464 protected function expandCustomRemarkupBlockTransactions( 465 PhabricatorLiskDAO $object, 466 array $xactions, 467 $blocks, 468 PhutilMarkupEngine $engine) { 469 470 // we are only really trying to find unmentionable phids here... 471 // don't bother with this outside initial commit (i.e. create) 472 // transaction 473 $is_commit = false; 474 foreach ($xactions as $xaction) { 475 switch ($xaction->getTransactionType()) { 476 case PhabricatorAuditTransaction::TYPE_COMMIT: 477 $is_commit = true; 478 break; 479 } 480 } 481 482 // "result" is always an array.... 483 $result = array(); 484 if (!$is_commit) { 485 return $result; 486 } 487 488 $flat_blocks = array_mergev($blocks); 489 $huge_block = implode("\n\n", $flat_blocks); 490 $phid_map = array(); 491 $phid_map[] = $this->getUnmentionablePHIDMap(); 492 $monograms = array(); 493 494 $task_refs = id(new ManiphestCustomFieldStatusParser()) 495 ->parseCorpus($huge_block); 496 foreach ($task_refs as $match) { 497 foreach ($match['monograms'] as $monogram) { 498 $monograms[] = $monogram; 499 } 500 } 501 502 $rev_refs = id(new DifferentialCustomFieldDependsOnParser()) 503 ->parseCorpus($huge_block); 504 foreach ($rev_refs as $match) { 505 foreach ($match['monograms'] as $monogram) { 506 $monograms[] = $monogram; 507 } 508 } 509 510 $objects = id(new PhabricatorObjectQuery()) 511 ->setViewer($this->getActor()) 512 ->withNames($monograms) 513 ->execute(); 514 $phid_map[] = mpull($objects, 'getPHID', 'getPHID'); 515 $phid_map = array_mergev($phid_map); 516 $this->setUnmentionablePHIDMap($phid_map); 517 518 return $result; 519 } 520 521 522 protected function shouldSendMail( 523 PhabricatorLiskDAO $object, 524 array $xactions) { 525 526 // not every code path loads the repository so tread carefully 527 if ($object->getRepository($assert_attached = false)) { 528 $repository = $object->getRepository(); 529 if ($repository->isImporting()) { 530 return false; 531 } 532 } 533 return $this->isCommitMostlyImported($object); 534 } 535 536 protected function buildReplyHandler(PhabricatorLiskDAO $object) { 537 $reply_handler = PhabricatorEnv::newObjectFromConfig( 538 'metamta.diffusion.reply-handler'); 539 $reply_handler->setMailReceiver($object); 540 return $reply_handler; 541 } 542 543 protected function getMailSubjectPrefix() { 544 return PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix'); 545 } 546 547 protected function getMailThreadID(PhabricatorLiskDAO $object) { 548 // For backward compatibility, use this legacy thread ID. 549 return 'diffusion-audit-'.$object->getPHID(); 550 } 551 552 protected function buildMailTemplate(PhabricatorLiskDAO $object) { 553 $identifier = $object->getCommitIdentifier(); 554 $repository = $object->getRepository(); 555 $monogram = $repository->getMonogram(); 556 557 $summary = $object->getSummary(); 558 $name = $repository->formatCommitName($identifier); 559 560 $subject = "{$name}: {$summary}"; 561 $thread_topic = "Commit {$monogram}{$identifier}"; 562 563 $template = id(new PhabricatorMetaMTAMail()) 564 ->setSubject($subject) 565 ->addHeader('Thread-Topic', $thread_topic); 566 567 $this->attachPatch( 568 $template, 569 $object); 570 571 return $template; 572 } 573 574 protected function getMailTo(PhabricatorLiskDAO $object) { 575 $phids = array(); 576 if ($this->heraldEmailPHIDs) { 577 $phids = $this->heraldEmailPHIDs; 578 } 579 580 if ($object->getAuthorPHID()) { 581 $phids[] = $object->getAuthorPHID(); 582 } 583 584 $status_resigned = PhabricatorAuditStatusConstants::RESIGNED; 585 foreach ($object->getAudits() as $audit) { 586 if ($audit->getAuditStatus() != $status_resigned) { 587 $phids[] = $audit->getAuditorPHID(); 588 } 589 } 590 591 return $phids; 592 } 593 594 protected function buildMailBody( 595 PhabricatorLiskDAO $object, 596 array $xactions) { 597 598 $body = parent::buildMailBody($object, $xactions); 599 600 $type_inline = PhabricatorAuditActionConstants::INLINE; 601 $type_push = PhabricatorAuditTransaction::TYPE_COMMIT; 602 603 $is_commit = false; 604 $inlines = array(); 605 foreach ($xactions as $xaction) { 606 if ($xaction->getTransactionType() == $type_inline) { 607 $inlines[] = $xaction; 608 } 609 if ($xaction->getTransactionType() == $type_push) { 610 $is_commit = true; 611 } 612 } 613 614 if ($inlines) { 615 $body->addTextSection( 616 pht('INLINE COMMENTS'), 617 $this->renderInlineCommentsForMail($object, $inlines)); 618 } 619 620 if ($is_commit) { 621 $data = $object->getCommitData(); 622 $body->addTextSection(pht('AFFECTED FILES'), $this->affectedFiles); 623 $this->inlinePatch( 624 $body, 625 $object); 626 } 627 628 // Reload the commit to pull commit data. 629 $commit = id(new DiffusionCommitQuery()) 630 ->setViewer($this->requireActor()) 631 ->withIDs(array($object->getID())) 632 ->needCommitData(true) 633 ->executeOne(); 634 $data = $commit->getCommitData(); 635 636 $user_phids = array(); 637 638 $author_phid = $commit->getAuthorPHID(); 639 if ($author_phid) { 640 $user_phids[$author_phid][] = pht('Author'); 641 } 642 643 $committer_phid = $data->getCommitDetail('committerPHID'); 644 if ($committer_phid && ($committer_phid != $author_phid)) { 645 $user_phids[$committer_phid][] = pht('Committer'); 646 } 647 648 // we loaded this in applyFinalEffects 649 $audit_requests = $object->getAudits(); 650 $auditor_phids = mpull($audit_requests, 'getAuditorPHID'); 651 foreach ($auditor_phids as $auditor_phid) { 652 $user_phids[$auditor_phid][] = pht('Auditor'); 653 } 654 655 // TODO: It would be nice to show pusher here too, but that information 656 // is a little tricky to get at right now. 657 658 if ($user_phids) { 659 $handle_phids = array_keys($user_phids); 660 $handles = id(new PhabricatorHandleQuery()) 661 ->setViewer($this->requireActor()) 662 ->withPHIDs($handle_phids) 663 ->execute(); 664 665 $user_info = array(); 666 foreach ($user_phids as $phid => $roles) { 667 $user_info[] = pht( 668 '%s (%s)', 669 $handles[$phid]->getName(), 670 implode(', ', $roles)); 671 } 672 673 $body->addTextSection( 674 pht('USERS'), 675 implode("\n", $user_info)); 676 } 677 678 $monogram = $object->getRepository()->formatCommitName( 679 $object->getCommitIdentifier()); 680 681 $body->addLinkSection( 682 pht('COMMIT'), 683 PhabricatorEnv::getProductionURI('/'.$monogram)); 684 685 return $body; 686 } 687 688 private function attachPatch( 689 PhabricatorMetaMTAMail $template, 690 PhabricatorRepositoryCommit $commit) { 691 692 if (!$this->getRawPatch()) { 693 return; 694 } 695 696 $attach_key = 'metamta.diffusion.attach-patches'; 697 $attach_patches = PhabricatorEnv::getEnvConfig($attach_key); 698 if (!$attach_patches) { 699 return; 700 } 701 702 $repository = $commit->getRepository(); 703 $encoding = $repository->getDetail('encoding', 'UTF-8'); 704 705 $raw_patch = $this->getRawPatch(); 706 $commit_name = $repository->formatCommitName( 707 $commit->getCommitIdentifier()); 708 709 $template->addAttachment( 710 new PhabricatorMetaMTAAttachment( 711 $raw_patch, 712 $commit_name.'.patch', 713 'text/x-patch; charset='.$encoding)); 714 } 715 716 private function inlinePatch( 717 PhabricatorMetaMTAMailBody $body, 718 PhabricatorRepositoryCommit $commit) { 719 720 if (!$this->getRawPatch()) { 721 return; 722 } 723 724 $inline_key = 'metamta.diffusion.inline-patches'; 725 $inline_patches = PhabricatorEnv::getEnvConfig($inline_key); 726 if (!$inline_patches) { 727 return; 728 } 729 730 $repository = $commit->getRepository(); 731 $raw_patch = $this->getRawPatch(); 732 $result = null; 733 $len = substr_count($raw_patch, "\n"); 734 if ($len <= $inline_patches) { 735 // We send email as utf8, so we need to convert the text to utf8 if 736 // we can. 737 $encoding = $repository->getDetail('encoding', 'UTF-8'); 738 if ($encoding) { 739 $raw_patch = phutil_utf8_convert($raw_patch, 'UTF-8', $encoding); 740 } 741 $result = phutil_utf8ize($raw_patch); 742 } 743 744 if ($result) { 745 $result = "PATCH\n\n{$result}\n"; 746 } 747 $body->addRawSection($result); 748 } 749 750 private function renderInlineCommentsForMail( 751 PhabricatorLiskDAO $object, 752 array $inline_xactions) { 753 754 $inlines = mpull($inline_xactions, 'getComment'); 755 756 $block = array(); 757 758 $path_map = id(new DiffusionPathQuery()) 759 ->withPathIDs(mpull($inlines, 'getPathID')) 760 ->execute(); 761 $path_map = ipull($path_map, 'path', 'id'); 762 763 foreach ($inlines as $inline) { 764 $path = idx($path_map, $inline->getPathID()); 765 if ($path === null) { 766 continue; 767 } 768 769 $start = $inline->getLineNumber(); 770 $len = $inline->getLineLength(); 771 if ($len) { 772 $range = $start.'-'.($start + $len); 773 } else { 774 $range = $start; 775 } 776 777 $content = $inline->getContent(); 778 $block[] = "{$path}:{$range} {$content}"; 779 } 780 781 return implode("\n", $block); 782 } 783 784 public function getMailTagsMap() { 785 return array( 786 PhabricatorAuditTransaction::MAILTAG_COMMIT => 787 pht('A commit is created.'), 788 PhabricatorAuditTransaction::MAILTAG_ACTION_CONCERN => 789 pht('A commit has a concerned raised against it.'), 790 PhabricatorAuditTransaction::MAILTAG_ACTION_ACCEPT => 791 pht('A commit is accepted.'), 792 PhabricatorAuditTransaction::MAILTAG_ACTION_RESIGN => 793 pht('A commit has an auditor resign.'), 794 PhabricatorAuditTransaction::MAILTAG_ACTION_CLOSE => 795 pht('A commit is closed.'), 796 PhabricatorAuditTransaction::MAILTAG_ADD_AUDITORS => 797 pht('A commit has auditors added.'), 798 PhabricatorAuditTransaction::MAILTAG_ADD_CCS => 799 pht("A commit's subscribers change."), 800 PhabricatorAuditTransaction::MAILTAG_PROJECTS => 801 pht("A commit's projects change."), 802 PhabricatorAuditTransaction::MAILTAG_COMMENT => 803 pht('Someone comments on a commit.'), 804 PhabricatorAuditTransaction::MAILTAG_OTHER => 805 pht('Other commit activity not listed above occurs.'), 806 ); 807 } 808 809 810 811 protected function shouldPublishFeedStory( 812 PhabricatorLiskDAO $object, 813 array $xactions) { 814 return $this->shouldSendMail($object, $xactions); 815 } 816 817 protected function shouldApplyHeraldRules( 818 PhabricatorLiskDAO $object, 819 array $xactions) { 820 821 foreach ($xactions as $xaction) { 822 switch ($xaction->getTransactionType()) { 823 case PhabricatorAuditTransaction::TYPE_COMMIT: 824 $repository = $object->getRepository(); 825 if ($repository->isImporting()) { 826 return false; 827 } 828 if ($repository->getDetail('herald-disabled')) { 829 return false; 830 } 831 return true; 832 default: 833 break; 834 } 835 } 836 return parent::shouldApplyHeraldRules($object, $xactions); 837 } 838 839 protected function buildHeraldAdapter( 840 PhabricatorLiskDAO $object, 841 array $xactions) { 842 843 return id(new HeraldCommitAdapter()) 844 ->setCommit($object); 845 } 846 847 protected function didApplyHeraldRules( 848 PhabricatorLiskDAO $object, 849 HeraldAdapter $adapter, 850 HeraldTranscript $transcript) { 851 852 $xactions = array(); 853 854 $audit_phids = $adapter->getAuditMap(); 855 foreach ($audit_phids as $phid => $rule_ids) { 856 foreach ($rule_ids as $rule_id) { 857 $this->addAuditReason( 858 $phid, 859 pht( 860 '%s Triggered Audit', 861 "H{$rule_id}")); 862 } 863 } 864 if ($audit_phids) { 865 $xactions[] = id(new PhabricatorAuditTransaction()) 866 ->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS) 867 ->setNewValue(array_fuse(array_keys($audit_phids))) 868 ->setMetadataValue( 869 'auditStatus', 870 PhabricatorAuditStatusConstants::AUDIT_REQUIRED) 871 ->setMetadataValue( 872 'auditReasonMap', $this->auditReasonMap); 873 } 874 875 $cc_phids = $adapter->getAddCCMap(); 876 $add_ccs = array('+' => array()); 877 foreach ($cc_phids as $phid => $rule_ids) { 878 $add_ccs['+'][$phid] = $phid; 879 } 880 $xactions[] = id(new PhabricatorAuditTransaction()) 881 ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) 882 ->setNewValue($add_ccs); 883 884 $this->heraldEmailPHIDs = $adapter->getEmailPHIDs(); 885 886 HarbormasterBuildable::applyBuildPlans( 887 $object->getPHID(), 888 $object->getRepository()->getPHID(), 889 $adapter->getBuildPlans()); 890 891 $limit = self::MAX_FILES_SHOWN_IN_EMAIL; 892 $files = $adapter->loadAffectedPaths(); 893 sort($files); 894 if (count($files) > $limit) { 895 array_splice($files, $limit); 896 $files[] = pht( 897 '(This commit affected more than %d files. Only %d are shown here '. 898 'and additional ones are truncated.)', 899 $limit, 900 $limit); 901 } 902 $this->affectedFiles = implode("\n", $files); 903 904 return $xactions; 905 } 906 907 private function isCommitMostlyImported(PhabricatorLiskDAO $object) { 908 $has_message = PhabricatorRepositoryCommit::IMPORTED_MESSAGE; 909 $has_changes = PhabricatorRepositoryCommit::IMPORTED_CHANGE; 910 911 // Don't publish feed stories or email about events which occur during 912 // import. In particular, this affects tasks being attached when they are 913 // closed by "Fixes Txxxx" in a commit message. See T5851. 914 915 $mask = ($has_message | $has_changes); 916 917 return $object->isPartiallyImported($mask); 918 } 919 920 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |