Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20210309.auditors.01.status.sql b/resources/sql/autopatches/20210309.auditors.01.status.sql
new file mode 100644
index 0000000000..731ce3ca44
--- /dev/null
+++ b/resources/sql/autopatches/20210309.auditors.01.status.sql
@@ -0,0 +1,5 @@
+UPDATE {$NAMESPACE}_repository.repository_auditrequest
+ SET auditStatus = 'accepted' WHERE auditStatus = 'closed';
+
+DELETE FROM {$NAMESPACE}_repository.repository_auditrequest
+ WHERE auditStatus IN ('', 'cc', 'audit-not-required');
diff --git a/src/applications/audit/constants/PhabricatorAuditStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditStatusConstants.php
index 920cfd875e..54eb332124 100644
--- a/src/applications/audit/constants/PhabricatorAuditStatusConstants.php
+++ b/src/applications/audit/constants/PhabricatorAuditStatusConstants.php
@@ -1,108 +1,90 @@
<?php
final class PhabricatorAuditStatusConstants extends Phobject {
- const NONE = '';
- const AUDIT_NOT_REQUIRED = 'audit-not-required';
const AUDIT_REQUIRED = 'audit-required';
const CONCERNED = 'concerned';
const ACCEPTED = 'accepted';
const AUDIT_REQUESTED = 'requested';
const RESIGNED = 'resigned';
- const CLOSED = 'closed';
- const CC = 'cc';
public static function getStatusNameMap() {
$map = array(
- self::NONE => pht('Not Applicable'),
- self::AUDIT_NOT_REQUIRED => pht('Audit Not Required'),
self::AUDIT_REQUIRED => pht('Audit Required'),
self::CONCERNED => pht('Concern Raised'),
self::ACCEPTED => pht('Accepted'),
self::AUDIT_REQUESTED => pht('Audit Requested'),
self::RESIGNED => pht('Resigned'),
- self::CLOSED => pht('Closed'),
- self::CC => pht("Was CC'd"),
);
return $map;
}
public static function getActionRequiredStatusConstants() {
return array(
self::AUDIT_REQUIRED,
self::AUDIT_REQUESTED,
);
}
public static function getStatusName($code) {
return idx(self::getStatusNameMap(), $code, pht('Unknown'));
}
public static function getStatusColor($code) {
switch ($code) {
case self::CONCERNED:
$color = 'red';
break;
case self::AUDIT_REQUIRED:
case self::AUDIT_REQUESTED:
$color = 'orange';
break;
case self::ACCEPTED:
$color = 'green';
break;
- case self::AUDIT_NOT_REQUIRED:
- $color = 'blue';
- break;
- case self::CLOSED:
- $color = 'dark';
- break;
case self::RESIGNED:
$color = 'grey';
break;
default:
$color = 'bluegrey';
break;
}
return $color;
}
public static function getStatusIcon($code) {
switch ($code) {
- case self::AUDIT_NOT_REQUIRED:
- $icon = PHUIStatusItemView::ICON_OPEN;
- break;
case self::AUDIT_REQUIRED:
case self::AUDIT_REQUESTED:
$icon = PHUIStatusItemView::ICON_WARNING;
break;
case self::CONCERNED:
$icon = PHUIStatusItemView::ICON_REJECT;
break;
case self::ACCEPTED:
- case self::CLOSED:
$icon = PHUIStatusItemView::ICON_ACCEPT;
break;
case self::RESIGNED:
$icon = 'fa-times';
break;
default:
$icon = PHUIStatusItemView::ICON_QUESTION;
break;
}
return $icon;
}
public static function getOpenStatusConstants() {
return array(
self::AUDIT_REQUIRED,
self::AUDIT_REQUESTED,
self::CONCERNED,
);
}
public static function isOpenStatus($status) {
return in_array($status, self::getOpenStatusConstants());
}
}
diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php
index 98c0d92624..81317fd47e 100644
--- a/src/applications/audit/editor/PhabricatorAuditEditor.php
+++ b/src/applications/audit/editor/PhabricatorAuditEditor.php
@@ -1,882 +1,875 @@
<?php
final class PhabricatorAuditEditor
extends PhabricatorApplicationTransactionEditor {
const MAX_FILES_SHOWN_IN_EMAIL = 1000;
private $affectedFiles;
private $rawPatch;
private $auditorPHIDs = array();
private $didExpandInlineState = false;
private $oldAuditStatus = null;
public function setRawPatch($patch) {
$this->rawPatch = $patch;
return $this;
}
public function getRawPatch() {
return $this->rawPatch;
}
public function getEditorApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
public function getEditorObjectsDescription() {
return pht('Audits');
}
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_INLINESTATE;
$types[] = PhabricatorAuditTransaction::TYPE_COMMIT;
// TODO: These will get modernized eventually, but that can happen one
// at a time later on.
$types[] = PhabricatorAuditActionConstants::INLINE;
return $types;
}
protected function expandTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_INLINESTATE:
$this->didExpandInlineState = true;
break;
}
}
$this->oldAuditStatus = $object->getAuditStatus();
return parent::expandTransactions($object, $xactions);
}
protected function transactionHasEffect(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
return $xaction->hasComment();
}
return parent::transactionHasEffect($object, $xaction);
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
case PhabricatorAuditTransaction::TYPE_COMMIT:
return null;
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
case PhabricatorAuditTransaction::TYPE_COMMIT:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
$comment = $xaction->getComment();
$comment->setAttribute('editing', false);
PhabricatorVersionedDraft::purgeDrafts(
$comment->getPHID(),
$this->getActingAsPHID());
return;
case PhabricatorAuditTransaction::TYPE_COMMIT:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditTransaction::TYPE_COMMIT:
return;
case PhabricatorAuditActionConstants::INLINE:
$reply = $xaction->getComment()->getReplyToComment();
if ($reply && !$reply->getHasReplies()) {
$reply->setHasReplies(1)->save();
}
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function applyBuiltinExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_INLINESTATE:
$table = new PhabricatorAuditTransactionComment();
$conn_w = $table->establishConnection('w');
foreach ($xaction->getNewValue() as $phid => $state) {
queryfx(
$conn_w,
'UPDATE %T SET fixedState = %s WHERE phid = %s',
$table->getTableName(),
$state,
$phid);
}
break;
}
return parent::applyBuiltinExternalTransaction($object, $xaction);
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
// Load auditors explicitly; we may not have them if the caller was a
// generic piece of infrastructure.
$commit = id(new DiffusionCommitQuery())
->setViewer($this->requireActor())
->withIDs(array($object->getID()))
->needAuditRequests(true)
->executeOne();
if (!$commit) {
throw new Exception(
pht('Failed to load commit during transaction finalization!'));
}
$object->attachAudits($commit->getAudits());
$status_concerned = PhabricatorAuditStatusConstants::CONCERNED;
- $status_closed = PhabricatorAuditStatusConstants::CLOSED;
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
$status_accepted = PhabricatorAuditStatusConstants::ACCEPTED;
$status_concerned = PhabricatorAuditStatusConstants::CONCERNED;
$actor_phid = $this->getActingAsPHID();
$actor_is_author = ($object->getAuthorPHID()) &&
($actor_phid == $object->getAuthorPHID());
$import_status_flag = null;
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditTransaction::TYPE_COMMIT:
$import_status_flag = PhabricatorRepositoryCommit::IMPORTED_PUBLISH;
break;
}
}
$old_status = $this->oldAuditStatus;
$requests = $object->getAudits();
$object->updateAuditStatus($requests);
$new_status = $object->getAuditStatus();
$object->save();
if ($import_status_flag) {
$object->writeImportStatusFlag($import_status_flag);
}
// If the commit has changed state after this edit, add an informational
// transaction about the state change.
if ($old_status != $new_status) {
if ($object->isAuditStatusPartiallyAudited()) {
// This state isn't interesting enough to get a transaction. The
// best way we could lead the user forward is something like "This
// commit still requires additional audits." but that's redundant and
// probably not very useful.
} else {
$xaction = $object->getApplicationTransactionTemplate()
->setTransactionType(DiffusionCommitStateTransaction::TRANSACTIONTYPE)
->setOldValue($old_status)
->setNewValue($new_status);
$xaction = $this->populateTransaction($object, $xaction);
$xaction->save();
}
}
// Collect auditor PHIDs for building mail.
$this->auditorPHIDs = mpull($object->getAudits(), 'getAuditorPHID');
return $xactions;
}
protected function expandTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$auditors_type = DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE;
$xactions = parent::expandTransaction($object, $xaction);
switch ($xaction->getTransactionType()) {
case PhabricatorAuditTransaction::TYPE_COMMIT:
$phids = $this->getAuditRequestTransactionPHIDsFromCommitMessage(
$object);
if ($phids) {
$xactions[] = $object->getApplicationTransactionTemplate()
->setTransactionType($auditors_type)
->setNewValue(
array(
'+' => array_fuse($phids),
));
$this->addUnmentionablePHIDs($phids);
}
break;
default:
break;
}
if (!$this->didExpandInlineState) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
$this->didExpandInlineState = true;
$query_template = id(new DiffusionDiffInlineCommentQuery())
->withCommitPHIDs(array($object->getPHID()));
$state_xaction = $this->newInlineStateTransaction(
$object,
$query_template);
if ($state_xaction) {
$xactions[] = $state_xaction;
}
break;
}
}
return $xactions;
}
private function getAuditRequestTransactionPHIDsFromCommitMessage(
PhabricatorRepositoryCommit $commit) {
$actor = $this->getActor();
$data = $commit->getCommitData();
$message = $data->getCommitMessage();
$result = DifferentialCommitMessageParser::newStandardParser($actor)
->setRaiseMissingFieldErrors(false)
->parseFields($message);
$field_key = DifferentialAuditorsCommitMessageField::FIELDKEY;
$phids = idx($result, $field_key, null);
if (!$phids) {
return array();
}
// If a commit lists its author as an auditor, just pretend it does not.
foreach ($phids as $key => $phid) {
if ($phid == $commit->getAuthorPHID()) {
unset($phids[$key]);
}
}
if (!$phids) {
return array();
}
return $phids;
}
protected function sortTransactions(array $xactions) {
$xactions = parent::sortTransactions($xactions);
$head = array();
$tail = array();
foreach ($xactions as $xaction) {
$type = $xaction->getTransactionType();
if ($type == PhabricatorAuditActionConstants::INLINE) {
$tail[] = $xaction;
} else {
$head[] = $xaction;
}
}
return array_values(array_merge($head, $tail));
}
protected function supportsSearch() {
return true;
}
protected function expandCustomRemarkupBlockTransactions(
PhabricatorLiskDAO $object,
array $xactions,
array $changes,
PhutilMarkupEngine $engine) {
$actor = $this->getActor();
$result = array();
// Some interactions (like "Fixes Txxx" interacting with Maniphest) have
// already been processed, so we're only re-parsing them here to avoid
// generating an extra redundant mention. Other interactions are being
// processed for the first time.
// We're only recognizing magic in the commit message itself, not in
// audit comments.
$is_commit = false;
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditTransaction::TYPE_COMMIT:
$is_commit = true;
break;
}
}
if (!$is_commit) {
return $result;
}
$flat_blocks = mpull($changes, 'getNewValue');
$huge_block = implode("\n\n", $flat_blocks);
$phid_map = array();
$monograms = array();
$task_refs = id(new ManiphestCustomFieldStatusParser())
->parseCorpus($huge_block);
foreach ($task_refs as $match) {
foreach ($match['monograms'] as $monogram) {
$monograms[] = $monogram;
}
}
$rev_refs = id(new DifferentialCustomFieldDependsOnParser())
->parseCorpus($huge_block);
foreach ($rev_refs as $match) {
foreach ($match['monograms'] as $monogram) {
$monograms[] = $monogram;
}
}
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getActor())
->withNames($monograms)
->execute();
$phid_map[] = mpull($objects, 'getPHID', 'getPHID');
$reverts_refs = id(new DifferentialCustomFieldRevertsParser())
->parseCorpus($huge_block);
$reverts = array_mergev(ipull($reverts_refs, 'monograms'));
if ($reverts) {
$reverted_objects = DiffusionCommitRevisionQuery::loadRevertedObjects(
$actor,
$object,
$reverts,
$object->getRepository());
$reverted_phids = mpull($reverted_objects, 'getPHID', 'getPHID');
$reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST;
$result[] = id(new PhabricatorAuditTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $reverts_edge)
->setNewValue(array('+' => $reverted_phids));
$phid_map[] = $reverted_phids;
}
// See T13463. Copy "related task" edges from the associated revision, if
// one exists.
$revision = DiffusionCommitRevisionQuery::loadRevisionForCommit(
$actor,
$object);
if ($revision) {
$task_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$revision->getPHID(),
DifferentialRevisionHasTaskEdgeType::EDGECONST);
$task_phids = array_fuse($task_phids);
if ($task_phids) {
$related_edge = DiffusionCommitHasTaskEdgeType::EDGECONST;
$result[] = id(new PhabricatorAuditTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $related_edge)
->setNewValue(array('+' => $task_phids));
}
// Mark these objects as unmentionable, since the explicit relationship
// is stronger and any mentions are redundant.
$phid_map[] = $task_phids;
}
$phid_map = array_mergev($phid_map);
$this->addUnmentionablePHIDs($phid_map);
return $result;
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
$reply_handler = new PhabricatorAuditReplyHandler();
$reply_handler->setMailReceiver($object);
return $reply_handler;
}
protected function getMailSubjectPrefix() {
return pht('[Diffusion]');
}
protected function getMailThreadID(PhabricatorLiskDAO $object) {
// For backward compatibility, use this legacy thread ID.
return 'diffusion-audit-'.$object->getPHID();
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$identifier = $object->getCommitIdentifier();
$repository = $object->getRepository();
$summary = $object->getSummary();
$name = $repository->formatCommitName($identifier);
$subject = "{$name}: {$summary}";
$template = id(new PhabricatorMetaMTAMail())
->setSubject($subject);
$this->attachPatch(
$template,
$object);
return $template;
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$this->requireAuditors($object);
$phids = array();
if ($object->getAuthorPHID()) {
$phids[] = $object->getAuthorPHID();
}
foreach ($object->getAudits() as $audit) {
- if (!$audit->isInteresting()) {
- // Don't send mail to uninteresting auditors, like packages which
- // own this code but which audits have not triggered for.
- continue;
- }
-
if (!$audit->isResigned()) {
$phids[] = $audit->getAuditorPHID();
}
}
$phids[] = $this->getActingAsPHID();
return $phids;
}
protected function newMailUnexpandablePHIDs(PhabricatorLiskDAO $object) {
$this->requireAuditors($object);
$phids = array();
foreach ($object->getAudits() as $auditor) {
if ($auditor->isResigned()) {
$phids[] = $auditor->getAuditorPHID();
}
}
return $phids;
}
protected function getObjectLinkButtonLabelForMail(
PhabricatorLiskDAO $object) {
return pht('View Commit');
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$type_inline = PhabricatorAuditActionConstants::INLINE;
$type_push = PhabricatorAuditTransaction::TYPE_COMMIT;
$is_commit = false;
$inlines = array();
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() == $type_inline) {
$inlines[] = $xaction;
}
if ($xaction->getTransactionType() == $type_push) {
$is_commit = true;
}
}
if ($inlines) {
$body->addTextSection(
pht('INLINE COMMENTS'),
$this->renderInlineCommentsForMail($object, $inlines));
}
if ($is_commit) {
$data = $object->getCommitData();
$body->addTextSection(pht('AFFECTED FILES'), $this->affectedFiles);
$this->inlinePatch(
$body,
$object);
}
$data = $object->getCommitData();
$user_phids = array();
$author_phid = $object->getAuthorPHID();
if ($author_phid) {
$user_phids[$author_phid][] = pht('Author');
}
$committer_phid = $data->getCommitDetail('committerPHID');
if ($committer_phid && ($committer_phid != $author_phid)) {
$user_phids[$committer_phid][] = pht('Committer');
}
foreach ($this->auditorPHIDs as $auditor_phid) {
$user_phids[$auditor_phid][] = pht('Auditor');
}
// TODO: It would be nice to show pusher here too, but that information
// is a little tricky to get at right now.
if ($user_phids) {
$handle_phids = array_keys($user_phids);
$handles = id(new PhabricatorHandleQuery())
->setViewer($this->requireActor())
->withPHIDs($handle_phids)
->execute();
$user_info = array();
foreach ($user_phids as $phid => $roles) {
$user_info[] = pht(
'%s (%s)',
$handles[$phid]->getName(),
implode(', ', $roles));
}
$body->addTextSection(
pht('USERS'),
implode("\n", $user_info));
}
$monogram = $object->getRepository()->formatCommitName(
$object->getCommitIdentifier());
$body->addLinkSection(
pht('COMMIT'),
PhabricatorEnv::getProductionURI('/'.$monogram));
return $body;
}
private function attachPatch(
PhabricatorMetaMTAMail $template,
PhabricatorRepositoryCommit $commit) {
if (!$this->getRawPatch()) {
return;
}
$attach_key = 'metamta.diffusion.attach-patches';
$attach_patches = PhabricatorEnv::getEnvConfig($attach_key);
if (!$attach_patches) {
return;
}
$repository = $commit->getRepository();
$encoding = $repository->getDetail('encoding', 'UTF-8');
$raw_patch = $this->getRawPatch();
$commit_name = $repository->formatCommitName(
$commit->getCommitIdentifier());
$template->addAttachment(
new PhabricatorMailAttachment(
$raw_patch,
$commit_name.'.patch',
'text/x-patch; charset='.$encoding));
}
private function inlinePatch(
PhabricatorMetaMTAMailBody $body,
PhabricatorRepositoryCommit $commit) {
if (!$this->getRawPatch()) {
return;
}
$inline_key = 'metamta.diffusion.inline-patches';
$inline_patches = PhabricatorEnv::getEnvConfig($inline_key);
if (!$inline_patches) {
return;
}
$repository = $commit->getRepository();
$raw_patch = $this->getRawPatch();
$result = null;
$len = substr_count($raw_patch, "\n");
if ($len <= $inline_patches) {
// We send email as utf8, so we need to convert the text to utf8 if
// we can.
$encoding = $repository->getDetail('encoding', 'UTF-8');
if ($encoding) {
$raw_patch = phutil_utf8_convert($raw_patch, 'UTF-8', $encoding);
}
$result = phutil_utf8ize($raw_patch);
}
if ($result) {
$result = "PATCH\n\n{$result}\n";
}
$body->addRawSection($result);
}
private function renderInlineCommentsForMail(
PhabricatorLiskDAO $object,
array $inline_xactions) {
$inlines = mpull($inline_xactions, 'getComment');
$block = array();
$path_map = id(new DiffusionPathQuery())
->withPathIDs(mpull($inlines, 'getPathID'))
->execute();
$path_map = ipull($path_map, 'path', 'id');
foreach ($inlines as $inline) {
$path = idx($path_map, $inline->getPathID());
if ($path === null) {
continue;
}
$start = $inline->getLineNumber();
$len = $inline->getLineLength();
if ($len) {
$range = $start.'-'.($start + $len);
} else {
$range = $start;
}
$content = $inline->getContent();
$block[] = "{$path}:{$range} {$content}";
}
return implode("\n", $block);
}
public function getMailTagsMap() {
return array(
PhabricatorAuditTransaction::MAILTAG_COMMIT =>
pht('A commit is created.'),
PhabricatorAuditTransaction::MAILTAG_ACTION_CONCERN =>
pht('A commit has a concerned raised against it.'),
PhabricatorAuditTransaction::MAILTAG_ACTION_ACCEPT =>
pht('A commit is accepted.'),
PhabricatorAuditTransaction::MAILTAG_ACTION_RESIGN =>
pht('A commit has an auditor resign.'),
PhabricatorAuditTransaction::MAILTAG_ACTION_CLOSE =>
pht('A commit is closed.'),
PhabricatorAuditTransaction::MAILTAG_ADD_AUDITORS =>
pht('A commit has auditors added.'),
PhabricatorAuditTransaction::MAILTAG_ADD_CCS =>
pht("A commit's subscribers change."),
PhabricatorAuditTransaction::MAILTAG_PROJECTS =>
pht("A commit's projects change."),
PhabricatorAuditTransaction::MAILTAG_COMMENT =>
pht('Someone comments on a commit.'),
PhabricatorAuditTransaction::MAILTAG_OTHER =>
pht('Other commit activity not listed above occurs.'),
);
}
protected function shouldApplyHeraldRules(
PhabricatorLiskDAO $object,
array $xactions) {
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditTransaction::TYPE_COMMIT:
$repository = $object->getRepository();
$publisher = $repository->newPublisher();
if (!$publisher->shouldPublishCommit($object)) {
return false;
}
return true;
default:
break;
}
}
return parent::shouldApplyHeraldRules($object, $xactions);
}
protected function buildHeraldAdapter(
PhabricatorLiskDAO $object,
array $xactions) {
return id(new HeraldCommitAdapter())
->setObject($object);
}
protected function didApplyHeraldRules(
PhabricatorLiskDAO $object,
HeraldAdapter $adapter,
HeraldTranscript $transcript) {
$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 array();
}
private function isCommitMostlyImported(PhabricatorLiskDAO $object) {
$has_message = PhabricatorRepositoryCommit::IMPORTED_MESSAGE;
$has_changes = PhabricatorRepositoryCommit::IMPORTED_CHANGE;
// Don't publish feed stories or email about events which occur during
// import. In particular, this affects tasks being attached when they are
// closed by "Fixes Txxxx" in a commit message. See T5851.
$mask = ($has_message | $has_changes);
return $object->isPartiallyImported($mask);
}
private function shouldPublishRepositoryActivity(
PhabricatorLiskDAO $object,
array $xactions) {
// not every code path loads the repository so tread carefully
// TODO: They should, and then we should simplify this.
$repository = $object->getRepository($assert_attached = false);
if ($repository != PhabricatorLiskDAO::ATTACHABLE) {
$publisher = $repository->newPublisher();
if (!$publisher->shouldPublishCommit($object)) {
return false;
}
}
return $this->isCommitMostlyImported($object);
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return $this->shouldPublishRepositoryActivity($object, $xactions);
}
protected function shouldEnableMentions(
PhabricatorLiskDAO $object,
array $xactions) {
return $this->shouldPublishRepositoryActivity($object, $xactions);
}
protected function shouldPublishFeedStory(
PhabricatorLiskDAO $object,
array $xactions) {
return $this->shouldPublishRepositoryActivity($object, $xactions);
}
protected function getCustomWorkerState() {
return array(
'rawPatch' => $this->rawPatch,
'affectedFiles' => $this->affectedFiles,
'auditorPHIDs' => $this->auditorPHIDs,
);
}
protected function getCustomWorkerStateEncoding() {
return array(
'rawPatch' => self::STORAGE_ENCODING_BINARY,
);
}
protected function loadCustomWorkerState(array $state) {
$this->rawPatch = idx($state, 'rawPatch');
$this->affectedFiles = idx($state, 'affectedFiles');
$this->auditorPHIDs = idx($state, 'auditorPHIDs');
return $this;
}
protected function willPublish(PhabricatorLiskDAO $object, array $xactions) {
return id(new DiffusionCommitQuery())
->setViewer($this->requireActor())
->withIDs(array($object->getID()))
->needAuditRequests(true)
->needCommitData(true)
->executeOne();
}
private function requireAuditors(PhabricatorRepositoryCommit $commit) {
if ($commit->hasAttachedAudits()) {
return;
}
$with_auditors = id(new DiffusionCommitQuery())
->setViewer($this->getActor())
->needAuditRequests(true)
->withPHIDs(array($commit->getPHID()))
->executeOne();
if (!$with_auditors) {
throw new Exception(
pht(
'Failed to reload commit ("%s").',
$commit->getPHID()));
}
$commit->attachAudits($with_auditors->getAudits());
}
}
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index 473e8911ac..6037781edc 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -1,1172 +1,1168 @@
<?php
final class DiffusionCommitController extends DiffusionController {
const CHANGES_LIMIT = 100;
private $commitParents;
private $commitRefs;
private $commitMerges;
private $commitErrors;
private $commitExists;
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$drequest = $this->getDiffusionRequest();
$viewer = $request->getUser();
$repository = $drequest->getRepository();
$commit_identifier = $drequest->getCommit();
// If this page is being accessed via "/source/xyz/commit/...", redirect
// to the canonical URI.
$has_callsign = strlen($request->getURIData('repositoryCallsign'));
$has_id = strlen($request->getURIData('repositoryID'));
if (!$has_callsign && !$has_id) {
$canonical_uri = $repository->getCommitURI($commit_identifier);
return id(new AphrontRedirectResponse())
->setURI($canonical_uri);
}
if ($request->getStr('diff')) {
return $this->buildRawDiffResponse($drequest);
}
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers(array($commit_identifier))
->needCommitData(true)
->needAuditRequests(true)
->needAuditAuthority(array($viewer))
->setLimit(100)
->needIdentities(true)
->execute();
$multiple_results = count($commits) > 1;
$crumbs = $this->buildCrumbs(array(
'commit' => !$multiple_results,
));
$crumbs->setBorder(true);
if (!$commits) {
if (!$this->getCommitExists()) {
return new Aphront404Response();
}
$error = id(new PHUIInfoView())
->setTitle(pht('Commit Still Parsing'))
->appendChild(
pht(
'Failed to load the commit because the commit has not been '.
'parsed yet.'));
$title = pht('Commit Still Parsing');
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($error);
} else if ($multiple_results) {
$warning_message =
pht(
'The identifier %s is ambiguous and matches more than one commit.',
phutil_tag(
'strong',
array(),
$commit_identifier));
$error = id(new PHUIInfoView())
->setTitle(pht('Ambiguous Commit'))
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->appendChild($warning_message);
$list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setCommits($commits);
$crumbs->addTextCrumb(pht('Ambiguous Commit'));
$matched_commits = id(new PHUITwoColumnView())
->setFooter(array(
$error,
$list,
));
return $this->newPage()
->setTitle(pht('Ambiguous Commit'))
->setCrumbs($crumbs)
->appendChild($matched_commits);
} else {
$commit = head($commits);
}
$audit_requests = $commit->getAudits();
$commit_data = $commit->getCommitData();
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
$error_panel = null;
$unpublished_panel = null;
$hard_limit = 1000;
if ($commit->isImported()) {
$change_query = DiffusionPathChangeQuery::newFromDiffusionRequest(
$drequest);
$change_query->setLimit($hard_limit + 1);
$changes = $change_query->loadChanges();
} else {
$changes = array();
}
$was_limited = (count($changes) > $hard_limit);
if ($was_limited) {
$changes = array_slice($changes, 0, $hard_limit);
}
$count = count($changes);
$is_unreadable = false;
$hint = null;
if (!$count || $commit->isUnreachable()) {
$hint = id(new DiffusionCommitHintQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withOldCommitIdentifiers(array($commit->getCommitIdentifier()))
->executeOne();
if ($hint) {
$is_unreadable = $hint->isUnreadable();
}
}
if ($is_foreign) {
$subpath = $commit_data->getCommitDetail('svn-subpath');
$error_panel = new PHUIInfoView();
$error_panel->setTitle(pht('Commit Not Tracked'));
$error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING);
$error_panel->appendChild(
pht(
"This Diffusion repository is configured to track only one ".
"subdirectory of the entire Subversion repository, and this commit ".
"didn't affect the tracked subdirectory ('%s'), so no ".
"information is available.",
$subpath));
} else {
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
$engine->setConfig('viewer', $viewer);
$commit_tag = $this->renderCommitHashTag($drequest);
$header = id(new PHUIHeaderView())
->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')))
->setHeaderIcon('fa-code-fork')
->addTag($commit_tag);
if (!$commit->isAuditStatusNoAudit()) {
$status = $commit->getAuditStatusObject();
$icon = $status->getIcon();
$color = $status->getColor();
$status = $status->getName();
$header->setStatus($icon, $color, $status);
}
$curtain = $this->buildCurtain($commit, $repository);
$details = $this->buildPropertyListView(
$commit,
$commit_data,
$audit_requests);
$message = $commit_data->getCommitMessage();
$revision = $commit->getCommitIdentifier();
$message = $this->linkBugtraq($message);
$message = $engine->markupText($message);
$detail_list = new PHUIPropertyListView();
$detail_list->addTextContent(
phutil_tag(
'div',
array(
'class' => 'diffusion-commit-message phabricator-remarkup',
),
$message));
if ($commit->isUnreachable()) {
$did_rewrite = false;
if ($hint) {
if ($hint->isRewritten()) {
$rewritten = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers(array($hint->getNewCommitIdentifier()))
->executeOne();
if ($rewritten) {
$did_rewrite = true;
$rewritten_uri = $rewritten->getURI();
$rewritten_name = $rewritten->getLocalName();
$rewritten_link = phutil_tag(
'a',
array(
'href' => $rewritten_uri,
),
$rewritten_name);
$this->commitErrors[] = pht(
'This commit was rewritten after it was published, which '.
'changed the commit hash. This old version of the commit is '.
'no longer reachable from any branch, tag or ref. The new '.
'version of this commit is %s.',
$rewritten_link);
}
}
}
if (!$did_rewrite) {
$this->commitErrors[] = pht(
'This commit has been deleted in the repository: it is no longer '.
'reachable from any branch, tag, or ref.');
}
}
if (!$commit->isPermanentCommit()) {
$nonpermanent_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setName(pht('Unpublished'))
->setColor(PHUITagView::COLOR_ORANGE);
$header->addTag($nonpermanent_tag);
$holds = $commit_data->newPublisherHoldReasons();
$reasons = array();
foreach ($holds as $hold) {
$reasons[] = array(
phutil_tag('strong', array(), pht('%s:', $hold->getName())),
' ',
$hold->getSummary(),
);
}
if (!$holds) {
$reasons[] = pht('No further details are available.');
}
$doc_href = PhabricatorEnv::getDoclink(
'Diffusion User Guide: Permanent Refs');
$doc_link = phutil_tag(
'a',
array(
'href' => $doc_href,
'target' => '_blank',
),
pht('Learn More'));
$title = array(
pht('Unpublished Commit'),
pht(" \xC2\xB7 "),
$doc_link,
);
$unpublished_panel = id(new PHUIInfoView())
->setTitle($title)
->setErrors($reasons)
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
}
if ($this->getCommitErrors()) {
$error_panel = id(new PHUIInfoView())
->appendChild($this->getCommitErrors())
->setSeverity(PHUIInfoView::SEVERITY_WARNING);
}
}
$timeline = $this->buildComments($commit);
$merge_table = $this->buildMergesTable($commit);
$show_changesets = false;
$info_panel = null;
$change_list = null;
$change_table = null;
if ($is_unreadable) {
$info_panel = $this->renderStatusMessage(
pht('Unreadable Commit'),
pht(
'This commit has been marked as unreadable by an administrator. '.
'It may have been corrupted or created improperly by an external '.
'tool.'));
} else if ($is_foreign) {
// Don't render anything else.
} else if (!$commit->isImported()) {
$info_panel = $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This commit is still importing. Changes will be visible once '.
'the import finishes.'));
} else if (!count($changes)) {
$info_panel = $this->renderStatusMessage(
pht('Empty Commit'),
pht(
'This commit is empty and does not affect any paths.'));
} else if ($was_limited) {
$info_panel = $this->renderStatusMessage(
pht('Very Large Commit'),
pht(
'This commit is very large, and affects more than %d files. '.
'Changes are not shown.',
$hard_limit));
} else if (!$this->getCommitExists()) {
$info_panel = $this->renderStatusMessage(
pht('Commit No Longer Exists'),
pht('This commit no longer exists in the repository.'));
} else {
$show_changesets = true;
// The user has clicked "Show All Changes", and we should show all the
// changes inline even if there are more than the soft limit.
$show_all_details = $request->getBool('show_all');
$change_header = id(new PHUIHeaderView())
->setHeader(pht('Changes (%s)', new PhutilNumber($count)));
$warning_view = null;
if ($count > self::CHANGES_LIMIT && !$show_all_details) {
$button = id(new PHUIButtonView())
->setText(pht('Show All Changes'))
->setHref('?show_all=true')
->setTag('a')
->setIcon('fa-files-o');
$warning_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle(pht('Very Large Commit'))
->appendChild(
pht('This commit is very large. Load each file individually.'));
$change_header->addActionLink($button);
}
$changesets = DiffusionPathChange::convertToDifferentialChangesets(
$viewer,
$changes);
// TODO: This table and panel shouldn't really be separate, but we need
// to clean up the "Load All Files" interaction first.
$change_table = $this->buildTableOfContents(
$changesets,
$change_header,
$warning_view);
$vcs = $repository->getVersionControlSystem();
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$vcs_supports_directory_changes = true;
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$vcs_supports_directory_changes = false;
break;
default:
throw new Exception(pht('Unknown VCS.'));
}
$references = array();
foreach ($changesets as $key => $changeset) {
$file_type = $changeset->getFileType();
if ($file_type == DifferentialChangeType::FILE_DIRECTORY) {
if (!$vcs_supports_directory_changes) {
unset($changesets[$key]);
continue;
}
}
$references[$key] = $drequest->generateURI(
array(
'action' => 'rendering-ref',
'path' => $changeset->getFilename(),
));
}
// TODO: Some parts of the views still rely on properties of the
// DifferentialChangeset. Make the objects ephemeral to make sure we don't
// accidentally save them, and then set their ID to the appropriate ID for
// this application (the path IDs).
$path_ids = array_flip(mpull($changes, 'getPath'));
foreach ($changesets as $changeset) {
$changeset->makeEphemeral();
$changeset->setID($path_ids[$changeset->getFilename()]);
}
if ($count <= self::CHANGES_LIMIT || $show_all_details) {
$visible_changesets = $changesets;
} else {
$visible_changesets = array();
$inlines = id(new DiffusionDiffInlineCommentQuery())
->setViewer($viewer)
->withCommitPHIDs(array($commit->getPHID()))
->withPublishedComments(true)
->withPublishableComments(true)
->execute();
$inlines = mpull($inlines, 'newInlineCommentObject');
$path_ids = mpull($inlines, null, 'getPathID');
foreach ($changesets as $key => $changeset) {
if (array_key_exists($changeset->getID(), $path_ids)) {
$visible_changesets[$key] = $changeset;
}
}
}
$change_list_title = $commit->getDisplayName();
$change_list = new DifferentialChangesetListView();
$change_list->setTitle($change_list_title);
$change_list->setChangesets($changesets);
$change_list->setVisibleChangesets($visible_changesets);
$change_list->setRenderingReferences($references);
$change_list->setRenderURI($repository->getPathURI('diff/'));
$change_list->setRepository($repository);
$change_list->setUser($viewer);
$change_list->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
// TODO: Try to setBranch() to something reasonable here?
$change_list->setStandaloneURI(
$repository->getPathURI('diff/'));
$change_list->setRawFileURIs(
// TODO: Implement this, somewhat tricky if there's an octopus merge
// or whatever?
null,
$repository->getPathURI('diff/?view=r'));
$change_list->setInlineCommentControllerURI(
'/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/');
}
$add_comment = $this->renderAddCommentPanel(
$commit,
$timeline);
$filetree = id(new DifferentialFileTreeEngine())
->setViewer($viewer)
->setDisabled(!$show_changesets);
if ($show_changesets) {
$filetree->setChangesets($changesets);
}
$description_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Description'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($detail_list);
$detail_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Details'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($details);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$unpublished_panel,
$error_panel,
$description_box,
$detail_box,
$timeline,
$merge_table,
$info_panel,
))
->setFooter(
array(
$change_table,
$change_list,
$add_comment,
));
$main_content = array(
$crumbs,
$view,
);
$main_content = $filetree->newView($main_content);
if (!$filetree->getDisabled()) {
$change_list->setFormationView($main_content);
}
$page = $this->newPage()
->setTitle($commit->getDisplayName())
->setPageObjectPHIDS(array($commit->getPHID()))
->appendChild($main_content);
return $page;
}
private function buildPropertyListView(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data,
array $audit_requests) {
$viewer = $this->getViewer();
$commit_phid = $commit->getPHID();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$view = id(new PHUIPropertyListView())
->setUser($this->getRequest()->getUser())
->setObject($commit);
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array($commit_phid))
->withEdgeTypes(array(
DiffusionCommitHasTaskEdgeType::EDGECONST,
DiffusionCommitHasRevisionEdgeType::EDGECONST,
DiffusionCommitRevertsCommitEdgeType::EDGECONST,
DiffusionCommitRevertedByCommitEdgeType::EDGECONST,
));
$edges = $edge_query->execute();
$task_phids = array_keys(
$edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]);
$revision_phid = key(
$edges[$commit_phid][DiffusionCommitHasRevisionEdgeType::EDGECONST]);
$reverts_phids = array_keys(
$edges[$commit_phid][DiffusionCommitRevertsCommitEdgeType::EDGECONST]);
$reverted_by_phids = array_keys(
$edges[$commit_phid][DiffusionCommitRevertedByCommitEdgeType::EDGECONST]);
$phids = $edge_query->getDestinationPHIDs(array($commit_phid));
if ($data->getCommitDetail('reviewerPHID')) {
$phids[] = $data->getCommitDetail('reviewerPHID');
}
$phids[] = $commit->getCommitterDisplayPHID();
$phids[] = $commit->getAuthorDisplayPHID();
// NOTE: We should never normally have more than a single push log, but
// it can occur naturally if a commit is pushed, then the branch it was
// on is deleted, then the commit is pushed again (or through other similar
// chains of events). This should be rare, but does not indicate a bug
// or data issue.
// NOTE: We never query push logs in SVN because the committer is always
// the pusher and the commit time is always the push time; the push log
// is redundant and we save a query by skipping it.
$push_logs = array();
if ($repository->isHosted() && !$repository->isSVN()) {
$push_logs = id(new PhabricatorRepositoryPushLogQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withNewRefs(array($commit->getCommitIdentifier()))
->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT))
->execute();
foreach ($push_logs as $log) {
$phids[] = $log->getPusherPHID();
}
}
$handles = array();
if ($phids) {
$handles = $this->loadViewerHandles($phids);
}
$props = array();
if ($audit_requests) {
$user_requests = array();
$other_requests = array();
foreach ($audit_requests as $audit_request) {
- if (!$audit_request->isInteresting()) {
- continue;
- }
-
if ($audit_request->isUser()) {
$user_requests[] = $audit_request;
} else {
$other_requests[] = $audit_request;
}
}
if ($user_requests) {
$view->addProperty(
pht('Auditors'),
$this->renderAuditStatusView($commit, $user_requests));
}
if ($other_requests) {
$view->addProperty(
pht('Group Auditors'),
$this->renderAuditStatusView($commit, $other_requests));
}
}
$provenance_list = new PHUIStatusListView();
$author_view = $commit->newCommitAuthorView($viewer);
if ($author_view) {
$author_date = $data->getAuthorEpoch();
$author_date = phabricator_datetime($author_date, $viewer);
$provenance_list->addItem(
id(new PHUIStatusItemView())
->setTarget($author_view)
->setNote(pht('Authored on %s', $author_date)));
}
if (!$commit->isAuthorSameAsCommitter()) {
$committer_view = $commit->newCommitCommitterView($viewer);
if ($committer_view) {
$committer_date = $commit->getEpoch();
$committer_date = phabricator_datetime($committer_date, $viewer);
$provenance_list->addItem(
id(new PHUIStatusItemView())
->setTarget($committer_view)
->setNote(pht('Committed on %s', $committer_date)));
}
}
if ($push_logs) {
$pushed_list = new PHUIStatusListView();
foreach ($push_logs as $push_log) {
$pusher_date = $push_log->getEpoch();
$pusher_date = phabricator_datetime($pusher_date, $viewer);
$pusher_view = $handles[$push_log->getPusherPHID()]->renderLink();
$provenance_list->addItem(
id(new PHUIStatusItemView())
->setTarget($pusher_view)
->setNote(pht('Pushed on %s', $pusher_date)));
}
}
$view->addProperty(pht('Provenance'), $provenance_list);
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$view->addProperty(
pht('Reviewer'),
$handles[$reviewer_phid]->renderLink());
}
if ($revision_phid) {
$view->addProperty(
pht('Differential Revision'),
$handles[$revision_phid]->renderLink());
}
$parents = $this->getCommitParents();
if ($parents) {
$view->addProperty(
pht('Parents'),
$viewer->renderHandleList(mpull($parents, 'getPHID')));
}
if ($this->getCommitExists()) {
$view->addProperty(
pht('Branches'),
phutil_tag(
'span',
array(
'id' => 'commit-branches',
),
pht('Unknown')));
$view->addProperty(
pht('Tags'),
phutil_tag(
'span',
array(
'id' => 'commit-tags',
),
pht('Unknown')));
$identifier = $commit->getCommitIdentifier();
$root = $repository->getPathURI("commit/{$identifier}");
Javelin::initBehavior(
'diffusion-commit-branches',
array(
$root.'/branches/' => 'commit-branches',
$root.'/tags/' => 'commit-tags',
));
}
$refs = $this->getCommitRefs();
if ($refs) {
$ref_links = array();
foreach ($refs as $ref_data) {
$ref_links[] = phutil_tag(
'a',
array(
'href' => $ref_data['href'],
),
$ref_data['ref']);
}
$view->addProperty(
pht('References'),
phutil_implode_html(', ', $ref_links));
}
if ($reverts_phids) {
$view->addProperty(
pht('Reverts'),
$viewer->renderHandleList($reverts_phids));
}
if ($reverted_by_phids) {
$view->addProperty(
pht('Reverted By'),
$viewer->renderHandleList($reverted_by_phids));
}
if ($task_phids) {
$task_list = array();
foreach ($task_phids as $phid) {
$task_list[] = $handles[$phid]->renderLink();
}
$task_list = phutil_implode_html(phutil_tag('br'), $task_list);
$view->addProperty(
pht('Tasks'),
$task_list);
}
return $view;
}
private function buildComments(PhabricatorRepositoryCommit $commit) {
$timeline = $this->buildTransactionTimeline(
$commit,
new PhabricatorAuditTransactionQuery());
$timeline->setQuoteRef($commit->getMonogram());
return $timeline;
}
private function renderAddCommentPanel(
PhabricatorRepositoryCommit $commit,
$timeline) {
$request = $this->getRequest();
$viewer = $request->getUser();
// TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better.
require_celerity_resource('differential-core-view-css');
$comment_view = id(new DiffusionCommitEditEngine())
->setViewer($viewer)
->buildEditEngineCommentView($commit);
$comment_view->setTransactionTimeline($timeline);
return $comment_view;
}
private function buildMergesTable(PhabricatorRepositoryCommit $commit) {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$merges = $this->getCommitMerges();
if (!$merges) {
return null;
}
$limit = $this->getMergeDisplayLimit();
$caption = null;
if (count($merges) > $limit) {
$merges = array_slice($merges, 0, $limit);
$caption = new PHUIInfoView();
$caption->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$caption->appendChild(
pht(
'This commit merges a very large number of changes. '.
'Only the first %s are shown.',
new PhutilNumber($limit)));
}
$commit_list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setDiffusionRequest($drequest)
->setHistory($merges);
$panel = id(new PHUIObjectBoxView())
->setHeaderText(pht('Merged Changes'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($commit_list->newObjectItemListView());
if ($caption) {
$panel->setInfoView($caption);
}
return $panel;
}
private function buildCurtain(
PhabricatorRepositoryCommit $commit,
PhabricatorRepository $repository) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$curtain = $this->newCurtainView($commit);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$commit,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $commit->getID();
$edit_uri = $this->getApplicationURI("/commit/edit/{$id}/");
$action = id(new PhabricatorActionView())
->setName(pht('Edit Commit'))
->setHref($edit_uri)
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
$curtain->addAction($action);
$action = id(new PhabricatorActionView())
->setName(pht('Download Raw Diff'))
->setHref($request->getRequestURI()->alter('diff', true))
->setIcon('fa-download');
$curtain->addAction($action);
$relationship_list = PhabricatorObjectRelationshipList::newForObject(
$viewer,
$commit);
$relationship_submenu = $relationship_list->newActionMenu();
if ($relationship_submenu) {
$curtain->addAction($relationship_submenu);
}
return $curtain;
}
private function buildRawDiffResponse(DiffusionRequest $drequest) {
$diff_info = $this->callConduitWithDiffusionRequest(
'diffusion.rawdiffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
));
$file_phid = $diff_info['filePHID'];
$file = id(new PhabricatorFileQuery())
->setViewer($this->getViewer())
->withPHIDs(array($file_phid))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'Failed to load file ("%s") returned by "%s".',
$file_phid,
'diffusion.rawdiffquery'));
}
return $file->getRedirectResponse();
}
private function renderAuditStatusView(
PhabricatorRepositoryCommit $commit,
array $audit_requests) {
assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest');
$viewer = $this->getViewer();
$view = new PHUIStatusListView();
foreach ($audit_requests as $request) {
$code = $request->getAuditStatus();
$item = new PHUIStatusItemView();
$item->setIcon(
PhabricatorAuditStatusConstants::getStatusIcon($code),
PhabricatorAuditStatusConstants::getStatusColor($code),
PhabricatorAuditStatusConstants::getStatusName($code));
$auditor_phid = $request->getAuditorPHID();
$target = $viewer->renderHandle($auditor_phid);
$item->setTarget($target);
if ($commit->hasAuditAuthority($viewer, $request)) {
$item->setHighlighted(true);
}
$view->addItem($item);
}
return $view;
}
private function linkBugtraq($corpus) {
$url = PhabricatorEnv::getEnvConfig('bugtraq.url');
if (!strlen($url)) {
return $corpus;
}
$regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex');
if (!$regexes) {
return $corpus;
}
$parser = id(new PhutilBugtraqParser())
->setBugtraqPattern("[[ {$url} | %BUGID% ]]")
->setBugtraqCaptureExpression(array_shift($regexes));
$select = array_shift($regexes);
if ($select) {
$parser->setBugtraqSelectExpression($select);
}
return $parser->processCorpus($corpus);
}
private function buildTableOfContents(
array $changesets,
$header,
$info_view) {
$drequest = $this->getDiffusionRequest();
$viewer = $this->getViewer();
$toc_view = id(new PHUIDiffTableOfContentsListView())
->setUser($viewer)
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
if ($info_view) {
$toc_view->setInfoView($info_view);
}
// TODO: This is hacky, we just want access to the linkX() methods on
// DiffusionView.
$diffusion_view = id(new DiffusionEmptyResultView())
->setDiffusionRequest($drequest);
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorOwnersApplication',
$viewer);
if (!$changesets) {
$have_owners = false;
}
if ($have_owners) {
if ($viewer->getPHID()) {
$packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
->withAuthorityPHIDs(array($viewer->getPHID()))
->execute();
$toc_view->setAuthorityPackages($packages);
}
$repository = $drequest->getRepository();
$repository_phid = $repository->getPHID();
$control_query = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
->withControl($repository_phid, mpull($changesets, 'getFilename'));
$control_query->execute();
}
foreach ($changesets as $changeset_id => $changeset) {
$path = $changeset->getFilename();
$anchor = $changeset->getAnchorName();
$history_link = $diffusion_view->linkHistory($path);
$browse_link = $diffusion_view->linkBrowse(
$path,
array(
'type' => $changeset->getFileType(),
));
$item = id(new PHUIDiffTableOfContentsItemView())
->setChangeset($changeset)
->setAnchor($anchor)
->setContext(
array(
$history_link,
' ',
$browse_link,
));
if ($have_owners) {
$packages = $control_query->getControllingPackagesForPath(
$repository_phid,
$changeset->getFilename());
$item->setPackages($packages);
}
$toc_view->addItem($item);
}
return $toc_view;
}
private function loadCommitState() {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$commit = $drequest->getCommit();
// TODO: We could use futures here and resolve these calls in parallel.
$exceptions = array();
try {
$parent_refs = $this->callConduitWithDiffusionRequest(
'diffusion.commitparentsquery',
array(
'commit' => $commit,
));
if ($parent_refs) {
$parents = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers($parent_refs)
->execute();
} else {
$parents = array();
}
$this->commitParents = $parents;
} catch (Exception $ex) {
$this->commitParents = false;
$exceptions[] = $ex;
}
$merge_limit = $this->getMergeDisplayLimit();
try {
if ($repository->isSVN()) {
$this->commitMerges = array();
} else {
$merges = $this->callConduitWithDiffusionRequest(
'diffusion.mergedcommitsquery',
array(
'commit' => $commit,
'limit' => $merge_limit + 1,
));
$this->commitMerges = DiffusionPathChange::newFromConduit($merges);
}
} catch (Exception $ex) {
$this->commitMerges = false;
$exceptions[] = $ex;
}
try {
if ($repository->isGit()) {
$refs = $this->callConduitWithDiffusionRequest(
'diffusion.refsquery',
array(
'commit' => $commit,
));
} else {
$refs = array();
}
$this->commitRefs = $refs;
} catch (Exception $ex) {
$this->commitRefs = false;
$exceptions[] = $ex;
}
if ($exceptions) {
$exists = $this->callConduitWithDiffusionRequest(
'diffusion.existsquery',
array(
'commit' => $commit,
));
if ($exists) {
$this->commitExists = true;
foreach ($exceptions as $exception) {
$this->commitErrors[] = $exception->getMessage();
}
} else {
$this->commitExists = false;
$this->commitErrors[] = pht(
'This commit no longer exists in the repository. It may have '.
'been part of a branch which was deleted.');
}
} else {
$this->commitExists = true;
$this->commitErrors = array();
}
}
private function getMergeDisplayLimit() {
return 50;
}
private function getCommitExists() {
if ($this->commitExists === null) {
$this->loadCommitState();
}
return $this->commitExists;
}
private function getCommitParents() {
if ($this->commitParents === null) {
$this->loadCommitState();
}
return $this->commitParents;
}
private function getCommitRefs() {
if ($this->commitRefs === null) {
$this->loadCommitState();
}
return $this->commitRefs;
}
private function getCommitMerges() {
if ($this->commitMerges === null) {
$this->loadCommitState();
}
return $this->commitMerges;
}
private function getCommitErrors() {
if ($this->commitErrors === null) {
$this->loadCommitState();
}
return $this->commitErrors;
}
}
diff --git a/src/applications/diffusion/herald/DiffusionAuditorsHeraldAction.php b/src/applications/diffusion/herald/DiffusionAuditorsHeraldAction.php
index 4ec23b1faa..bd75799f03 100644
--- a/src/applications/diffusion/herald/DiffusionAuditorsHeraldAction.php
+++ b/src/applications/diffusion/herald/DiffusionAuditorsHeraldAction.php
@@ -1,100 +1,98 @@
<?php
abstract class DiffusionAuditorsHeraldAction
extends HeraldAction {
const DO_AUTHORS = 'do.authors';
const DO_ADD_AUDITORS = 'do.add-auditors';
public function getActionGroupKey() {
return HeraldApplicationActionGroup::ACTIONGROUPKEY;
}
public function supportsObject($object) {
return ($object instanceof PhabricatorRepositoryCommit);
}
protected function applyAuditors(array $phids, HeraldRule $rule) {
$adapter = $this->getAdapter();
$object = $adapter->getObject();
$auditors = $object->getAudits();
// Don't try to add commit authors as auditors.
$authors = array();
foreach ($phids as $key => $phid) {
if ($phid == $object->getAuthorPHID()) {
$authors[] = $phid;
unset($phids[$key]);
}
}
if ($authors) {
$this->logEffect(self::DO_AUTHORS, $authors);
if (!$phids) {
return;
}
}
$current = array();
foreach ($auditors as $auditor) {
- if ($auditor->isInteresting()) {
- $current[] = $auditor->getAuditorPHID();
- }
+ $current[] = $auditor->getAuditorPHID();
}
$allowed_types = array(
PhabricatorPeopleUserPHIDType::TYPECONST,
PhabricatorProjectProjectPHIDType::TYPECONST,
PhabricatorOwnersPackagePHIDType::TYPECONST,
);
$targets = $this->loadStandardTargets($phids, $allowed_types, $current);
if (!$targets) {
return;
}
$phids = array_fuse(array_keys($targets));
$xaction = $adapter->newTransaction()
->setTransactionType(DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE)
->setNewValue(
array(
'+' => $phids,
));
$adapter->queueTransaction($xaction);
$this->logEffect(self::DO_ADD_AUDITORS, $phids);
}
protected function getActionEffectMap() {
return array(
self::DO_AUTHORS => array(
'icon' => 'fa-user',
'color' => 'grey',
'name' => pht('Commit Author'),
),
self::DO_ADD_AUDITORS => array(
'icon' => 'fa-user',
'color' => 'green',
'name' => pht('Added Auditors'),
),
);
}
protected function renderActionEffectDescription($type, $data) {
switch ($type) {
case self::DO_AUTHORS:
return pht(
'Declined to add commit author as auditor: %s.',
$this->renderHandleList($data));
case self::DO_ADD_AUDITORS:
return pht(
'Added %s auditor(s): %s.',
phutil_count($data),
$this->renderHandleList($data));
}
}
}
diff --git a/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php b/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php
index 8deee3417e..5f0da133f8 100644
--- a/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php
+++ b/src/applications/diffusion/herald/DiffusionCommitAuditorsHeraldField.php
@@ -1,41 +1,43 @@
<?php
final class DiffusionCommitAuditorsHeraldField
extends DiffusionCommitHeraldField {
const FIELDCONST = 'diffusion.commit.auditors';
public function getHeraldFieldName() {
return pht('Auditors');
}
public function getHeraldFieldValue($object) {
$viewer = PhabricatorUser::getOmnipotentUser();
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withPHIDs(array($object->getPHID()))
->needAuditRequests(true)
->executeOne();
$audits = $commit->getAudits();
$phids = array();
foreach ($audits as $audit) {
- if ($audit->isActiveAudit()) {
- $phids[] = $audit->getAuditorPHID();
+ if ($audit->isResigned()) {
+ continue;
}
+
+ $phids[] = $audit->getAuditorPHID();
}
return $phids;
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_PHID_LIST;
}
protected function getDatasource() {
return new DiffusionAuditorDatasource();
}
}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryAuditRequest.php b/src/applications/repository/storage/PhabricatorRepositoryAuditRequest.php
index e05820c825..1165b03a3f 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryAuditRequest.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryAuditRequest.php
@@ -1,106 +1,83 @@
<?php
final class PhabricatorRepositoryAuditRequest
extends PhabricatorRepositoryDAO
implements PhabricatorPolicyInterface {
protected $auditorPHID;
protected $commitPHID;
protected $auditReasons = array();
protected $auditStatus;
private $commit = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'auditReasons' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'auditStatus' => 'text64',
),
self::CONFIG_KEY_SCHEMA => array(
'commitPHID' => array(
'columns' => array('commitPHID'),
),
'auditorPHID' => array(
'columns' => array('auditorPHID', 'auditStatus'),
),
'key_unique' => array(
'columns' => array('commitPHID', 'auditorPHID'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function isUser() {
$user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
return (phid_get_type($this->getAuditorPHID()) == $user_type);
}
public function attachCommit(PhabricatorRepositoryCommit $commit) {
$this->commit = $commit;
return $this;
}
public function getCommit() {
return $this->assertAttached($this->commit);
}
- public function isActiveAudit() {
- switch ($this->getAuditStatus()) {
- case PhabricatorAuditStatusConstants::NONE:
- case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED:
- case PhabricatorAuditStatusConstants::RESIGNED:
- case PhabricatorAuditStatusConstants::CLOSED:
- case PhabricatorAuditStatusConstants::CC:
- return false;
- }
-
- return true;
- }
-
- public function isInteresting() {
- switch ($this->getAuditStatus()) {
- case PhabricatorAuditStatusConstants::NONE:
- case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED:
- return false;
- }
-
- return true;
- }
-
public function isResigned() {
switch ($this->getAuditStatus()) {
case PhabricatorAuditStatusConstants::RESIGNED:
return true;
}
return false;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getCommit()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getCommit()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'This audit is attached to a commit, and inherits its policies.');
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 9:35 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1073
Default Alt Text
(72 KB)

Event Timeline