protected function didApplyHeraldRules(PhabricatorLiskDAO $object, HeraldAdapter $adapter, HeraldTranscript $transcript)
 {
     $xactions = array();
     // Build a transaction to adjust reviewers.
     $reviewers = array(DifferentialReviewerStatus::STATUS_ADDED => array_keys($adapter->getReviewersAddedByHerald()), DifferentialReviewerStatus::STATUS_BLOCKING => array_keys($adapter->getBlockingReviewersAddedByHerald()));
     $old_reviewers = $object->getReviewerStatus();
     $old_reviewers = mpull($old_reviewers, null, 'getReviewerPHID');
     $value = array();
     foreach ($reviewers as $status => $phids) {
         foreach ($phids as $phid) {
             if ($phid == $object->getAuthorPHID()) {
                 // Don't try to add the revision's author as a reviewer, since this
                 // isn't valid and doesn't make sense.
                 continue;
             }
             // If the target is already a reviewer, don't try to change anything
             // if their current status is at least as strong as the new status.
             // For example, don't downgrade an "Accepted" to a "Blocking Reviewer".
             $old_reviewer = idx($old_reviewers, $phid);
             if ($old_reviewer) {
                 $old_status = $old_reviewer->getStatus();
                 $old_strength = DifferentialReviewerStatus::getStatusStrength($old_status);
                 $new_strength = DifferentialReviewerStatus::getStatusStrength($status);
                 if ($new_strength <= $old_strength) {
                     continue;
                 }
             }
             $value['+'][$phid] = array('data' => array('status' => $status));
         }
     }
     if ($value) {
         $edge_reviewer = DifferentialRevisionHasReviewerEdgeType::EDGECONST;
         $xactions[] = id(new DifferentialTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $edge_reviewer)->setNewValue($value);
     }
     // Require legalpad document signatures.
     $legal_phids = $adapter->getRequiredSignatureDocumentPHIDs();
     if ($legal_phids) {
         // We only require signatures of documents which have not already
         // been signed. In general, this reduces the amount of churn that
         // signature rules cause.
         $signatures = id(new LegalpadDocumentSignatureQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withDocumentPHIDs($legal_phids)->withSignerPHIDs(array($object->getAuthorPHID()))->execute();
         $signed_phids = mpull($signatures, 'getDocumentPHID');
         $legal_phids = array_diff($legal_phids, $signed_phids);
         // If we still have something to trigger, add the edges.
         if ($legal_phids) {
             $edge_legal = LegalpadObjectNeedsSignatureEdgeType::EDGECONST;
             $xactions[] = id(new DifferentialTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $edge_legal)->setNewValue(array('+' => array_fuse($legal_phids)));
         }
     }
     // Apply build plans.
     HarbormasterBuildable::applyBuildPlans($adapter->getDiff()->getPHID(), $adapter->getPHID(), $adapter->getBuildPlans());
     return $xactions;
 }
 private function applyHeraldRules(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
 {
     $commit->attachRepository($repository);
     // Don't take any actions on an importing repository. Principally, this
     // avoids generating thousands of audits or emails when you import an
     // established repository on an existing install.
     if ($repository->isImporting()) {
         return;
     }
     if ($repository->getDetail('herald-disabled')) {
         return;
     }
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         throw new PhabricatorWorkerPermanentFailureException(pht('Unable to load commit data. The data for this task is invalid ' . 'or no longer exists.'));
     }
     $adapter = id(new HeraldCommitAdapter())->setCommit($commit);
     $rules = id(new HeraldRuleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withContentTypes(array($adapter->getAdapterContentType()))->withDisabled(false)->needConditionsAndActions(true)->needAppliedToPHIDs(array($adapter->getPHID()))->needValidateAuthors(true)->execute();
     $engine = new HeraldEngine();
     $effects = $engine->applyRules($rules, $adapter);
     $engine->applyEffects($effects, $adapter, $rules);
     $xscript = $engine->getTranscript();
     $audit_phids = $adapter->getAuditMap();
     $cc_phids = $adapter->getAddCCMap();
     if ($audit_phids || $cc_phids) {
         $this->createAudits($commit, $audit_phids, $cc_phids, $rules);
     }
     HarbormasterBuildable::applyBuildPlans($commit->getPHID(), $repository->getPHID(), $adapter->getBuildPlans());
     $explicit_auditors = $this->createAuditsFromCommitMessage($commit, $data);
     $this->publishFeedStory($repository, $commit, $data);
     $herald_targets = $adapter->getEmailPHIDs();
     $email_phids = array_unique(array_merge($explicit_auditors, array_keys($cc_phids), $herald_targets));
     if (!$email_phids) {
         return;
     }
     $revision = $adapter->loadDifferentialRevision();
     if ($revision) {
         $name = $revision->getTitle();
     } else {
         $name = $data->getSummary();
     }
     $author_phid = $data->getCommitDetail('authorPHID');
     $reviewer_phid = $data->getCommitDetail('reviewerPHID');
     $phids = array_filter(array($author_phid, $reviewer_phid, $commit->getPHID()));
     $handles = id(new PhabricatorHandleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($phids)->execute();
     $commit_handle = $handles[$commit->getPHID()];
     $commit_name = $commit_handle->getName();
     if ($author_phid) {
         $author_name = $handles[$author_phid]->getName();
     } else {
         $author_name = $data->getAuthorName();
     }
     if ($reviewer_phid) {
         $reviewer_name = $handles[$reviewer_phid]->getName();
     } else {
         $reviewer_name = null;
     }
     $who = implode(', ', array_filter(array($author_name, $reviewer_name)));
     $description = $data->getCommitMessage();
     $commit_uri = PhabricatorEnv::getProductionURI($commit_handle->getURI());
     $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.';
     $limit = self::MAX_FILES_SHOWN_IN_EMAIL;
     $files = $adapter->loadAffectedPaths();
     sort($files);
     if (count($files) > $limit) {
         array_splice($files, $limit);
         $files[] = '(This commit affected more than ' . $limit . ' files. ' . 'Only ' . $limit . ' are shown here and additional ones are truncated.)';
     }
     $files = implode("\n", $files);
     $xscript_id = $xscript->getID();
     $why_uri = '/herald/transcript/' . $xscript_id . '/';
     $reply_handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit($commit);
     $template = new PhabricatorMetaMTAMail();
     $inline_patch_text = $this->buildPatch($template, $repository, $commit);
     $body = new PhabricatorMetaMTAMailBody();
     $body->addRawSection($description);
     $body->addTextSection(pht('DETAILS'), $commit_uri);
     // TODO: This should be integrated properly once we move to
     // ApplicationTransactions.
     $field_list = PhabricatorCustomField::getObjectFields($commit, PhabricatorCustomField::ROLE_APPLICATIONTRANSACTIONS);
     $field_list->setViewer(PhabricatorUser::getOmnipotentUser())->readFieldsFromStorage($commit);
     foreach ($field_list->getFields() as $field) {
         try {
             $field->buildApplicationTransactionMailBody(new DifferentialTransaction(), $body);
         } catch (Exception $ex) {
             // Log the exception and continue.
             phlog($ex);
         }
     }
     $body->addTextSection(pht('DIFFERENTIAL REVISION'), $differential);
     $body->addTextSection(pht('AFFECTED FILES'), $files);
     $body->addReplySection($reply_handler->getReplyHandlerInstructions());
     $body->addHeraldSection($why_uri);
     $body->addRawSection($inline_patch_text);
     $body = $body->render();
     $prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix');
     $threading = PhabricatorAuditCommentEditor::getMailThreading($repository, $commit);
     list($thread_id, $thread_topic) = $threading;
     $template->setRelatedPHID($commit->getPHID());
     $template->setSubject("{$commit_name}: {$name}");
     $template->setSubjectPrefix($prefix);
     $template->setVarySubjectPrefix('[Commit]');
     $template->setBody($body);
     $template->setThreadID($thread_id, $is_new = true);
     $template->addHeader('Thread-Topic', $thread_topic);
     $template->setIsBulk(true);
     $template->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader());
     if ($author_phid) {
         $template->setFrom($author_phid);
     }
     // TODO: We should verify that each recipient can actually see the
     // commit before sending them email (T603).
     $mails = $reply_handler->multiplexMail($template, id(new PhabricatorHandleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs($email_phids)->execute(), array());
     foreach ($mails as $mail) {
         $mail->saveAndSend();
     }
 }
 private function applyHeraldRules(PhabricatorLiskDAO $object, array $xactions)
 {
     $adapter = $this->buildHeraldAdapter($object, $xactions)->setContentSource($this->getContentSource())->setIsNewObject($this->getIsNewObject())->setAppliedTransactions($xactions);
     if ($this->getApplicationEmail()) {
         $adapter->setApplicationEmail($this->getApplicationEmail());
     }
     $xscript = HeraldEngine::loadAndApplyRules($adapter);
     $this->setHeraldAdapter($adapter);
     $this->setHeraldTranscript($xscript);
     if ($adapter instanceof HarbormasterBuildableAdapterInterface) {
         HarbormasterBuildable::applyBuildPlans($adapter->getHarbormasterBuildablePHID(), $adapter->getHarbormasterContainerPHID(), $adapter->getQueuedHarbormasterBuildRequests());
     }
     return array_merge($this->didApplyHeraldRules($object, $adapter, $xscript), $adapter->getQueuedTransactions());
 }
 protected function didApplyHeraldRules(PhabricatorLiskDAO $object, HeraldAdapter $adapter, HeraldTranscript $transcript)
 {
     $xactions = array();
     $audit_phids = $adapter->getAuditMap();
     foreach ($audit_phids as $phid => $rule_ids) {
         foreach ($rule_ids as $rule_id) {
             $this->addAuditReason($phid, pht('%s Triggered Audit', "H{$rule_id}"));
         }
     }
     if ($audit_phids) {
         $xactions[] = id(new PhabricatorAuditTransaction())->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS)->setNewValue(array_fuse(array_keys($audit_phids)))->setMetadataValue('auditStatus', PhabricatorAuditStatusConstants::AUDIT_REQUIRED)->setMetadataValue('auditReasonMap', $this->auditReasonMap);
     }
     HarbormasterBuildable::applyBuildPlans($object->getPHID(), $object->getRepository()->getPHID(), $adapter->getBuildPlans());
     $limit = self::MAX_FILES_SHOWN_IN_EMAIL;
     $files = $adapter->loadAffectedPaths();
     sort($files);
     if (count($files) > $limit) {
         array_splice($files, $limit);
         $files[] = pht('(This commit affected more than %d files. Only %d are shown here ' . 'and additional ones are truncated.)', $limit, $limit);
     }
     $this->affectedFiles = implode("\n", $files);
     return $xactions;
 }