Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/audit/editor/PhabricatorAuditCommentEditor.php b/src/applications/audit/editor/PhabricatorAuditCommentEditor.php
index 54f005bbe7..d658a5a6df 100644
--- a/src/applications/audit/editor/PhabricatorAuditCommentEditor.php
+++ b/src/applications/audit/editor/PhabricatorAuditCommentEditor.php
@@ -1,336 +1,287 @@
<?php
final class PhabricatorAuditCommentEditor extends PhabricatorEditor {
private $commit;
private $attachInlineComments;
private $noEmail;
public function __construct(PhabricatorRepositoryCommit $commit) {
$this->commit = $commit;
return $this;
}
public function setAttachInlineComments($attach_inline_comments) {
$this->attachInlineComments = $attach_inline_comments;
return $this;
}
public function setNoEmail($no_email) {
$this->noEmail = $no_email;
return $this;
}
public function addComments(array $comments) {
assert_instances_of($comments, 'PhabricatorAuditComment');
$commit = $this->commit;
$actor = $this->getActor();
$other_comments = PhabricatorAuditComment::loadComments(
$actor,
$commit->getPHID());
$inline_comments = array();
if ($this->attachInlineComments) {
$inline_comments = PhabricatorAuditInlineComment::loadDraftComments(
$actor,
$commit->getPHID());
}
// When an actor submits an audit comment, we update all the audit requests
// they have authority over to reflect the most recent status. The general
// idea here is that if audit has triggered for, e.g., several packages, but
// a user owns all of them, they can clear the audit requirement in one go
// without auditing the commit for each trigger.
$audit_phids = self::loadAuditPHIDsForUser($actor);
$audit_phids = array_fill_keys($audit_phids, true);
$requests = id(new PhabricatorRepositoryAuditRequest())
->loadAllWhere(
'commitPHID = %s',
$commit->getPHID());
// TODO: We should validate the action, currently we allow anyone to, e.g.,
// close an audit if they muck with form parameters. I'll followup with this
// and handle the no-effect cases (e.g., closing and already-closed audit).
$actor_is_author = ($actor->getPHID() == $commit->getAuthorPHID());
// Pick a meaningful action, if we have one.
$action = PhabricatorAuditActionConstants::COMMENT;
foreach ($comments as $comment) {
switch ($comment->getAction()) {
case PhabricatorAuditActionConstants::CLOSE:
case PhabricatorAuditActionConstants::RESIGN:
case PhabricatorAuditActionConstants::ACCEPT:
case PhabricatorAuditActionConstants::CONCERN:
$action = $comment->getAction();
break;
}
}
if ($action == PhabricatorAuditActionConstants::CLOSE) {
if (!PhabricatorEnv::getEnvConfig('audit.can-author-close-audit')) {
throw new Exception('Cannot Close Audit without enabling'.
'audit.can-author-close-audit');
}
// "Close" means wipe out all the concerns.
$concerned_status = PhabricatorAuditStatusConstants::CONCERNED;
foreach ($requests as $request) {
if ($request->getAuditStatus() == $concerned_status) {
$request->setAuditStatus(PhabricatorAuditStatusConstants::CLOSED);
$request->save();
}
}
} else if ($action == PhabricatorAuditActionConstants::RESIGN) {
// "Resign" has unusual rules for writing user rows, only affects the
// user row (never package/project rows), and always affects the user
// row (other actions don't, if they were able to affect a package/project
// row).
$actor_request = null;
foreach ($requests as $request) {
if ($request->getAuditorPHID() == $actor->getPHID()) {
$actor_request = $request;
break;
}
}
if (!$actor_request) {
$actor_request = id(new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($actor->getPHID())
->setAuditReasons(array('Resigned'));
}
$actor_request
->setAuditStatus(PhabricatorAuditStatusConstants::RESIGNED)
->save();
$requests[] = $actor_request;
} else {
$have_any_requests = false;
foreach ($requests as $request) {
if (empty($audit_phids[$request->getAuditorPHID()])) {
continue;
}
$request_is_for_actor =
($request->getAuditorPHID() == $actor->getPHID());
$have_any_requests = true;
$new_status = null;
switch ($action) {
case PhabricatorAuditActionConstants::COMMENT:
case PhabricatorAuditActionConstants::ADD_CCS:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
// Commenting or adding cc's/auditors doesn't change status.
break;
case PhabricatorAuditActionConstants::ACCEPT:
if (!$actor_is_author || $request_is_for_actor) {
// When modifying your own commits, you act only on behalf of
// yourself, not your packages/projects -- the idea being that
// you can't accept your own commits.
$new_status = PhabricatorAuditStatusConstants::ACCEPTED;
}
break;
case PhabricatorAuditActionConstants::CONCERN:
if (!$actor_is_author || $request_is_for_actor) {
// See above.
$new_status = PhabricatorAuditStatusConstants::CONCERNED;
}
break;
default:
throw new Exception("Unknown action '{$action}'!");
}
if ($new_status !== null) {
$request->setAuditStatus($new_status);
$request->save();
}
}
// If the actor has no current authority over any audit trigger, make a
// new one to represent their audit state.
if (!$have_any_requests) {
$new_status = null;
switch ($action) {
case PhabricatorAuditActionConstants::COMMENT:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
case PhabricatorAuditActionConstants::ADD_CCS:
break;
case PhabricatorAuditActionConstants::ACCEPT:
$new_status = PhabricatorAuditStatusConstants::ACCEPTED;
break;
case PhabricatorAuditActionConstants::CONCERN:
$new_status = PhabricatorAuditStatusConstants::CONCERNED;
break;
case PhabricatorAuditActionConstants::CLOSE:
// Impossible to reach this block with 'close'.
default:
throw new Exception("Unknown or invalid action '{$action}'!");
}
if ($new_status !== null) {
$request = id(new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($actor->getPHID())
->setAuditStatus($new_status)
->setAuditReasons(array('Voluntary Participant'))
->save();
$requests[] = $request;
}
}
}
$auditors = array();
foreach ($comments as $comment) {
$meta = $comment->getMetadata();
$auditor_phids = idx(
$meta,
PhabricatorAuditComment::METADATA_ADDED_AUDITORS,
array());
foreach ($auditor_phids as $phid) {
$auditors[] = $phid;
}
}
$requests_by_auditor = mpull($requests, null, 'getAuditorPHID');
$requests_phids = array_keys($requests_by_auditor);
$auditors = array_diff($auditors, $requests_phids);
if ($auditors) {
foreach ($auditors as $auditor_phid) {
$audit_requested = PhabricatorAuditStatusConstants::AUDIT_REQUESTED;
$requests[] = id (new PhabricatorRepositoryAuditRequest())
->setCommitPHID($commit->getPHID())
->setAuditorPHID($auditor_phid)
->setAuditStatus($audit_requested)
->setAuditReasons(
array('Added by '.$actor->getUsername()))
->save();
}
}
$commit->updateAuditStatus($requests);
$commit->save();
$commit->attachAudits($requests);
// Convert old comments into real transactions and apply them with a
// normal editor.
$xactions = array();
foreach ($comments as $comment) {
$xactions[] = $comment->getTransactionForSave();
}
foreach ($inline_comments as $inline) {
$xactions[] = id(new PhabricatorAuditTransaction())
->setTransactionType(PhabricatorAuditActionConstants::INLINE)
->attachComment($inline->getTransactionComment());
}
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_LEGACY,
array());
$editor = id(new PhabricatorAuditEditor())
->setActor($actor)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->setContentSource($content_source)
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
->setDisableEmail($this->noEmail)
->applyTransactions($commit, $xactions);
-
- $feed_dont_publish_phids = array();
- foreach ($requests as $request) {
- $status = $request->getAuditStatus();
- switch ($status) {
- case PhabricatorAuditStatusConstants::RESIGNED:
- case PhabricatorAuditStatusConstants::NONE:
- case PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED:
- $feed_dont_publish_phids[$request->getAuditorPHID()] = 1;
- break;
- default:
- unset($feed_dont_publish_phids[$request->getAuditorPHID()]);
- break;
- }
- }
- $feed_dont_publish_phids = array_keys($feed_dont_publish_phids);
-
- $feed_phids = array_diff($requests_phids, $feed_dont_publish_phids);
- foreach ($comments as $comment) {
- $this->publishFeedStory($comment, $feed_phids);
- }
}
/**
* Load the PHIDs for all objects the user has the authority to act as an
* audit for. This includes themselves, and any packages they are an owner
* of.
*/
public static function loadAuditPHIDsForUser(PhabricatorUser $user) {
$phids = array();
// TODO: This method doesn't really use the right viewer, but in practice we
// never issue this query of this type on behalf of another user and are
// unlikely to do so in the future. This entire method should be refactored
// into a Query class, however, and then we should use a proper viewer.
// The user can audit on their own behalf.
$phids[$user->getPHID()] = true;
$owned_packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($user)
->withOwnerPHIDs(array($user->getPHID()))
->execute();
foreach ($owned_packages as $package) {
$phids[$package->getPHID()] = true;
}
// The user can audit on behalf of all projects they are a member of.
$projects = id(new PhabricatorProjectQuery())
->setViewer($user)
->withMemberPHIDs(array($user->getPHID()))
->execute();
foreach ($projects as $project) {
$phids[$project->getPHID()] = true;
}
return array_keys($phids);
}
- private function publishFeedStory(
- PhabricatorAuditComment $comment,
- array $more_phids) {
-
- $commit = $this->commit;
- $actor = $this->getActor();
-
- $related_phids = array_merge(
- array(
- $actor->getPHID(),
- $commit->getPHID(),
- ),
- $more_phids);
-
- id(new PhabricatorFeedStoryPublisher())
- ->setRelatedPHIDs($related_phids)
- ->setStoryAuthorPHID($actor->getPHID())
- ->setStoryTime(time())
- ->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_AUDIT)
- ->setStoryData(
- array(
- 'commitPHID' => $commit->getPHID(),
- 'action' => $comment->getAction(),
- 'content' => $comment->getContent(),
- ))
- ->publish();
- }
-
}
diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php
index 5aef278637..d016799988 100644
--- a/src/applications/audit/editor/PhabricatorAuditEditor.php
+++ b/src/applications/audit/editor/PhabricatorAuditEditor.php
@@ -1,237 +1,243 @@
<?php
final class PhabricatorAuditEditor
extends PhabricatorApplicationTransactionEditor {
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorTransactions::TYPE_COMMENT;
// TODO: These will get modernized eventually, but that can happen one
// at a time later on.
$types[] = PhabricatorAuditActionConstants::ACTION;
$types[] = PhabricatorAuditActionConstants::INLINE;
$types[] = PhabricatorAuditActionConstants::ADD_AUDITORS;
return $types;
}
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::ACTION:
case PhabricatorAuditActionConstants::INLINE:
return null;
case PhabricatorAuditActionConstants::ADD_AUDITORS:
// TODO: For now, just record the added PHIDs. Eventually, turn these
// into real edge transactions, probably?
return array();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::ACTION:
case PhabricatorAuditActionConstants::INLINE:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
case PhabricatorAuditActionConstants::ACTION:
case PhabricatorAuditActionConstants::INLINE:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
case PhabricatorAuditActionConstants::ACTION:
case PhabricatorAuditActionConstants::INLINE:
return;
case PhabricatorAuditActionConstants::ADD_AUDITORS:
// TODO: For now, these are applied externally.
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
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 shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true;
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
$reply_handler = PhabricatorEnv::newObjectFromConfig(
'metamta.diffusion.reply-handler');
$reply_handler->setMailReceiver($object);
return $reply_handler;
}
protected function getMailSubjectPrefix() {
return PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix');
}
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();
$monogram = $repository->getMonogram();
$summary = $object->getSummary();
$name = $repository->formatCommitName($identifier);
$subject = "{$name}: {$summary}";
$thread_topic = "Commit {$monogram}{$identifier}";
return id(new PhabricatorMetaMTAMail())
->setSubject($subject)
->addHeader('Thread-Topic', $thread_topic);
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$phids = array();
if ($object->getAuthorPHID()) {
$phids[] = $object->getAuthorPHID();
}
$status_resigned = PhabricatorAuditStatusConstants::RESIGNED;
foreach ($object->getAudits() as $audit) {
if ($audit->getAuditStatus() != $status_resigned) {
$phids[] = $audit->getAuditorPHID();
}
}
return $phids;
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
$type_inline = PhabricatorAuditActionConstants::INLINE;
$inlines = array();
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() == $type_inline) {
$inlines[] = $xaction;
}
}
if ($inlines) {
$body->addTextSection(
pht('INLINE COMMENTS'),
$this->renderInlineCommentsForMail($object, $inlines));
}
$monogram = $object->getRepository()->formatCommitName(
$object->getCommitIdentifier());
$body->addTextSection(
pht('COMMIT'),
PhabricatorEnv::getProductionURI('/'.$monogram));
return $body;
}
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);
}
+ protected function shouldPublishFeedStory(
+ PhabricatorLiskDAO $object,
+ array $xactions) {
+ return true;
+ }
+
}
diff --git a/src/applications/audit/storage/PhabricatorAuditTransaction.php b/src/applications/audit/storage/PhabricatorAuditTransaction.php
index 80b1ef6433..94f5884533 100644
--- a/src/applications/audit/storage/PhabricatorAuditTransaction.php
+++ b/src/applications/audit/storage/PhabricatorAuditTransaction.php
@@ -1,209 +1,296 @@
<?php
final class PhabricatorAuditTransaction
extends PhabricatorApplicationTransaction {
public function getApplicationName() {
return 'audit';
}
public function getApplicationTransactionType() {
return PhabricatorRepositoryCommitPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return new PhabricatorAuditTransactionComment();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
$type = $this->getTransactionType();
switch ($type) {
case PhabricatorAuditActionConstants::ADD_CCS:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
$old = $this->getOldValue();
$new = $this->getNewValue();
if (!is_array($old)) {
$old = array();
}
if (!is_array($new)) {
$new = array();
}
foreach (array_keys($old + $new) as $phid) {
$phids[] = $phid;
}
break;
}
return $phids;
}
public function getActionName() {
switch ($this->getTransactionType()) {
case PhabricatorAuditActionConstants::ACTION:
switch ($this->getNewValue()) {
case PhabricatorAuditActionConstants::CONCERN:
return pht('Raised Concern');
case PhabricatorAuditActionConstants::ACCEPT:
return pht('Accepted');
case PhabricatorAuditActionConstants::RESIGN:
return pht('Resigned');
case PhabricatorAuditActionConstants::CLOSE:
return pht('Clsoed');
}
break;
case PhabricatorAuditActionConstants::ADD_AUDITORS:
return pht('Added Auditors');
}
return parent::getActionName();
}
public function getColor() {
$type = $this->getTransactionType();
switch ($type) {
case PhabricatorAuditActionConstants::ACTION:
switch ($this->getNewValue()) {
case PhabricatorAuditActionConstants::CONCERN:
return 'red';
case PhabricatorAuditActionConstants::ACCEPT:
return 'green';
}
}
return parent::getColor();
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$author_handle = $this->renderHandleLink($this->getAuthorPHID());
$type = $this->getTransactionType();
switch ($type) {
case PhabricatorAuditActionConstants::ADD_CCS:
case PhabricatorAuditActionConstants::ADD_AUDITORS:
if (!is_array($old)) {
$old = array();
}
if (!is_array($new)) {
$new = array();
}
$add = array_keys(array_diff_key($new, $old));
$rem = array_keys(array_diff_key($old, $new));
break;
}
switch ($type) {
case PhabricatorAuditActionConstants::INLINE:
return pht(
'%s added inline comments.',
$author_handle);
case PhabricatorAuditActionConstants::ADD_CCS:
if ($add && $rem) {
return pht(
'%s edited subscribers; added: %s, removed: %s.',
$author_handle,
$this->renderHandleList($add),
$this->renderHandleList($rem));
} else if ($add) {
return pht(
'%s added subscribers: %s.',
$author_handle,
$this->renderHandleList($add));
} else if ($rem) {
return pht(
'%s removed subscribers: %s.',
$author_handle,
$this->renderHandleList($rem));
} else {
return pht(
'%s added subscribers...',
$author_handle);
}
case PhabricatorAuditActionConstants::ADD_AUDITORS:
if ($add && $rem) {
return pht(
'%s edited auditors; added: %s, removed: %s.',
$author_handle,
$this->renderHandleList($add),
$this->renderHandleList($rem));
} else if ($add) {
return pht(
'%s added auditors: %s.',
$author_handle,
$this->renderHandleList($add));
} else if ($rem) {
return pht(
'%s removed auditors: %s.',
$author_handle,
$this->renderHandleList($rem));
} else {
return pht(
'%s added auditors...',
$author_handle);
}
case PhabricatorAuditActionConstants::ACTION:
switch ($new) {
case PhabricatorAuditActionConstants::ACCEPT:
return pht(
'%s accepted this commit.',
$author_handle);
case PhabricatorAuditActionConstants::CONCERN:
return pht(
'%s raised a concern with this commit.',
$author_handle);
case PhabricatorAuditActionConstants::RESIGN:
return pht(
'%s resigned from this audit.',
$author_handle);
case PhabricatorAuditActionConstants::CLOSE:
return pht(
'%s closed this audit.',
$author_handle);
}
}
return parent::getTitle();
}
+ public function getTitleForFeed(PhabricatorFeedStory $story) {
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ $author_handle = $this->renderHandleLink($this->getAuthorPHID());
+ $object_handle = $this->renderHandleLink($this->getObjectPHID());
+
+ $type = $this->getTransactionType();
+
+ switch ($type) {
+ case PhabricatorAuditActionConstants::ADD_CCS:
+ case PhabricatorAuditActionConstants::ADD_AUDITORS:
+ if (!is_array($old)) {
+ $old = array();
+ }
+ if (!is_array($new)) {
+ $new = array();
+ }
+ $add = array_keys(array_diff_key($new, $old));
+ $rem = array_keys(array_diff_key($old, $new));
+ break;
+ }
+
+ switch ($type) {
+ case PhabricatorAuditActionConstants::INLINE:
+ return pht(
+ '%s added inline comments to %s.',
+ $author_handle,
+ $object_handle);
+
+ case PhabricatorAuditActionConstants::ADD_AUDITORS:
+ if ($add && $rem) {
+ return pht(
+ '%s edited auditors for %s; added: %s, removed: %s.',
+ $author_handle,
+ $object_handle,
+ $this->renderHandleList($add),
+ $this->renderHandleList($rem));
+ } else if ($add) {
+ return pht(
+ '%s added auditors to %s: %s.',
+ $author_handle,
+ $object_handle,
+ $this->renderHandleList($add));
+ } else if ($rem) {
+ return pht(
+ '%s removed auditors from %s: %s.',
+ $author_handle,
+ $object_handle,
+ $this->renderHandleList($rem));
+ } else {
+ return pht(
+ '%s added auditors to %s...',
+ $author_handle,
+ $object_handle);
+ }
+
+ case PhabricatorAuditActionConstants::ACTION:
+ switch ($new) {
+ case PhabricatorAuditActionConstants::ACCEPT:
+ return pht(
+ '%s accepted %s.',
+ $author_handle,
+ $object_handle);
+ case PhabricatorAuditActionConstants::CONCERN:
+ return pht(
+ '%s raised a concern with %s.',
+ $author_handle,
+ $object_handle);
+ case PhabricatorAuditActionConstants::RESIGN:
+ return pht(
+ '%s resigned from auditing %s.',
+ $author_handle,
+ $object_handle);
+ case PhabricatorAuditActionConstants::CLOSE:
+ return pht(
+ '%s closed the audit of %s.',
+ $author_handle,
+ $object_handle);
+ }
+
+ }
+
+ return parent::getTitleForFeed($story);
+ }
+
+
// TODO: These two mail methods can likely be abstracted by introducing a
// formal concept of "inline comment" transactions.
public function shouldHideForMail(array $xactions) {
$type_inline = PhabricatorAuditActionConstants::INLINE;
switch ($this->getTransactionType()) {
case $type_inline:
foreach ($xactions as $xaction) {
if ($xaction->getTransactionType() != $type_inline) {
return true;
}
}
return ($this !== head($xactions));
}
return parent::shouldHideForMail($xactions);
}
public function getBodyForMail() {
switch ($this->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
return null;
}
return parent::getBodyForMail();
}
}
diff --git a/src/applications/auth/controller/PhabricatorAuthLoginController.php b/src/applications/auth/controller/PhabricatorAuthLoginController.php
index 068be3126c..5658a44c7c 100644
--- a/src/applications/auth/controller/PhabricatorAuthLoginController.php
+++ b/src/applications/auth/controller/PhabricatorAuthLoginController.php
@@ -1,251 +1,251 @@
<?php
final class PhabricatorAuthLoginController
extends PhabricatorAuthController {
private $providerKey;
private $extraURIData;
private $provider;
public function shouldRequireLogin() {
return false;
}
public function shouldAllowRestrictedParameter($parameter_name) {
// Whitelist the OAuth 'code' parameter.
if ($parameter_name == 'code') {
return true;
}
return parent::shouldAllowRestrictedParameter($parameter_name);
}
public function willProcessRequest(array $data) {
$this->providerKey = $data['pkey'];
$this->extraURIData = idx($data, 'extra');
}
public function getExtraURIData() {
return $this->extraURIData;
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$response = $this->loadProvider();
if ($response) {
return $response;
}
$provider = $this->provider;
try {
list($account, $response) = $provider->processLoginRequest($this);
} catch (PhutilAuthUserAbortedException $ex) {
if ($viewer->isLoggedIn()) {
// If a logged-in user cancels, take them back to the external accounts
// panel.
$next_uri = '/settings/panel/external/';
} else {
// If a logged-out user cancels, take them back to the auth start page.
$next_uri = '/';
}
// User explicitly hit "Cancel".
$dialog = id(new AphrontDialogView())
->setUser($viewer)
->setTitle(pht('Authentication Canceled'))
->appendChild(
pht('You canceled authentication.'))
->addCancelButton($next_uri, pht('Continue'));
return id(new AphrontDialogResponse())->setDialog($dialog);
}
if ($response) {
return $response;
}
if (!$account) {
throw new Exception(
'Auth provider failed to load an account from processLoginRequest()!');
}
if ($account->getUserPHID()) {
// The account is already attached to a Phabricator user, so this is
// either a login or a bad account link request.
if (!$viewer->isLoggedIn()) {
if ($provider->shouldAllowLogin()) {
return $this->processLoginUser($account);
} else {
return $this->renderError(
pht(
'The external account ("%s") you just authenticated with is '.
'not configured to allow logins on this Phabricator install. '.
'An administrator may have recently disabled it.',
$provider->getProviderName()));
}
} else if ($viewer->getPHID() == $account->getUserPHID()) {
// This is either an attempt to re-link an existing and already
// linked account (which is silly) or a refresh of an external account
// (e.g., an OAuth account).
return id(new AphrontRedirectResponse())
->setURI('/settings/panel/external/');
} else {
return $this->renderError(
pht(
- 'The external account ("%s") you just used to login is alerady '.
+ 'The external account ("%s") you just used to login is already '.
'associated with another Phabricator user account. Login to the '.
'other Phabricator account and unlink the external account before '.
'linking it to a new Phabricator account.',
$provider->getProviderName()));
}
} else {
// The account is not yet attached to a Phabricator user, so this is
// either a registration or an account link request.
if (!$viewer->isLoggedIn()) {
if ($provider->shouldAllowRegistration()) {
return $this->processRegisterUser($account);
} else {
return $this->renderError(
pht(
'The external account ("%s") you just authenticated with is '.
'not configured to allow registration on this Phabricator '.
'install. An administrator may have recently disabled it.',
$provider->getProviderName()));
}
} else {
if ($provider->shouldAllowAccountLink()) {
return $this->processLinkUser($account);
} else {
return $this->renderError(
pht(
'The external account ("%s") you just authenticated with is '.
'not configured to allow account linking on this Phabricator '.
'install. An administrator may have recently disabled it.',
$provider->getProviderName()));
}
}
}
// This should be unreachable, but fail explicitly if we get here somehow.
return new Aphront400Response();
}
private function processLoginUser(PhabricatorExternalAccount $account) {
$user = id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$account->getUserPHID());
if (!$user) {
return $this->renderError(
pht(
'The external account you just logged in with is not associated '.
'with a valid Phabricator user.'));
}
return $this->loginUser($user);
}
private function processRegisterUser(PhabricatorExternalAccount $account) {
$account_secret = $account->getAccountSecret();
$register_uri = $this->getApplicationURI('register/'.$account_secret.'/');
return $this->setAccountKeyAndContinue($account, $register_uri);
}
private function processLinkUser(PhabricatorExternalAccount $account) {
$account_secret = $account->getAccountSecret();
$confirm_uri = $this->getApplicationURI('confirmlink/'.$account_secret.'/');
return $this->setAccountKeyAndContinue($account, $confirm_uri);
}
private function setAccountKeyAndContinue(
PhabricatorExternalAccount $account,
$next_uri) {
if ($account->getUserPHID()) {
throw new Exception('Account is already registered or linked.');
}
// Regenerate the registration secret key, set it on the external account,
// set a cookie on the user's machine, and redirect them to registration.
// See PhabricatorAuthRegisterController for discussion of the registration
// key.
$registration_key = Filesystem::readRandomCharacters(32);
$account->setProperty(
'registrationKey',
PhabricatorHash::digest($registration_key));
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$account->save();
unset($unguarded);
$this->getRequest()->setTemporaryCookie(
PhabricatorCookies::COOKIE_REGISTRATION,
$registration_key);
return id(new AphrontRedirectResponse())->setURI($next_uri);
}
private function loadProvider() {
$provider = PhabricatorAuthProvider::getEnabledProviderByKey(
$this->providerKey);
if (!$provider) {
return $this->renderError(
pht(
'The account you are attempting to login with uses a nonexistent '.
'or disabled authentication provider (with key "%s"). An '.
'administrator may have recently disabled this provider.',
$this->providerKey));
}
$this->provider = $provider;
return null;
}
protected function renderError($message) {
return $this->renderErrorPage(
pht('Login Failed'),
array($message));
}
public function buildProviderPageResponse(
PhabricatorAuthProvider $provider,
$content) {
$crumbs = $this->buildApplicationCrumbs();
if ($this->getRequest()->getUser()->isLoggedIn()) {
$crumbs->addTextCrumb(pht('Link Account'), $provider->getSettingsURI());
} else {
$crumbs->addTextCrumb(pht('Login'), $this->getApplicationURI('start/'));
}
$crumbs->addTextCrumb($provider->getProviderName());
return $this->buildApplicationPage(
array(
$crumbs,
$content,
),
array(
'title' => pht('Login'),
));
}
public function buildProviderErrorResponse(
PhabricatorAuthProvider $provider,
$message) {
$message = pht(
'Authentication provider ("%s") encountered an error during login. %s',
$provider->getProviderName(),
$message);
return $this->renderError($message);
}
}
diff --git a/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php b/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php
index 3d3f747cd7..a8bab3a3f1 100644
--- a/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php
+++ b/src/applications/diffusion/doorkeeper/DiffusionDoorkeeperCommitFeedStoryPublisher.php
@@ -1,194 +1,200 @@
<?php
final class DiffusionDoorkeeperCommitFeedStoryPublisher
extends DoorkeeperFeedStoryPublisher {
private $auditRequests;
private $activePHIDs;
private $passivePHIDs;
private function getAuditRequests() {
return $this->auditRequests;
}
public function canPublishStory(PhabricatorFeedStory $story, $object) {
- return ($object instanceof PhabricatorRepositoryCommit);
+ return
+ ($story instanceof PhabricatorApplicationTransactionFeedStory) &&
+ ($object instanceof PhabricatorRepositoryCommit);
}
public function isStoryAboutObjectCreation($object) {
// TODO: Although creation stories exist, they currently don't have a
// primary object PHID set, so they'll never make it here because they
// won't pass `canPublishStory()`.
return false;
}
public function isStoryAboutObjectClosure($object) {
// TODO: This isn't quite accurate, but pretty close: check if this story
// is a close (which clearly is about object closure) or is an "Accept" and
// the commit is fully audited (which is almost certainly a closure).
// After ApplicationTransactions, we could annotate feed stories more
// explicitly.
- $story = $this->getFeedStory();
- $action = $story->getStoryData()->getValue('action');
-
- if ($action == PhabricatorAuditActionConstants::CLOSE) {
- return true;
- }
-
$fully_audited = PhabricatorAuditCommitStatusConstants::FULLY_AUDITED;
- if (($action == PhabricatorAuditActionConstants::ACCEPT) &&
- $object->getAuditStatus() == $fully_audited) {
- return true;
+
+ $story = $this->getFeedStory();
+ $xaction = $story->getPrimaryTransaction();
+ switch ($xaction->getTransactionType()) {
+ case PhabricatorAuditActionConstants::ACTION:
+ switch ($xaction->getNewValue()) {
+ case PhabricatorAuditActionConstants::CLOSE:
+ return true;
+ case PhabricatorAuditActionConstants::ACCEPT:
+ if ($object->getAuditStatus() == $fully_audited) {
+ return true;
+ }
+ break;
+ }
}
return false;
}
public function willPublishStory($commit) {
$requests = id(new DiffusionCommitQuery())
->setViewer($this->getViewer())
->withPHIDs(array($commit->getPHID()))
->needAuditRequests(true)
->executeOne()
->getAudits();
// TODO: This is messy and should be generalized, but we don't have a good
// query for it yet. Since we run in the daemons, just do the easiest thing
// we can for the moment. Figure out who all of the "active" (need to
// audit) and "passive" (no action necessary) users are.
$auditor_phids = mpull($requests, 'getAuditorPHID');
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withPHIDs($auditor_phids)
->execute();
$active = array();
$passive = array();
foreach ($requests as $request) {
$status = $request->getAuditStatus();
$object = idx($objects, $request->getAuditorPHID());
if (!$object) {
continue;
}
$request_phids = array();
if ($object instanceof PhabricatorUser) {
$request_phids = array($object->getPHID());
} else if ($object instanceof PhabricatorOwnersPackage) {
$request_phids = PhabricatorOwnersOwner::loadAffiliatedUserPHIDs(
array($object->getID()));
} else if ($object instanceof PhabricatorProject) {
$project = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withIDs(array($object->getID()))
->needMembers(true)
->executeOne();
$request_phids = $project->getMemberPHIDs();
} else {
// Dunno what this is.
$request_phids = array();
}
switch ($status) {
case PhabricatorAuditStatusConstants::AUDIT_REQUIRED:
case PhabricatorAuditStatusConstants::AUDIT_REQUESTED:
case PhabricatorAuditStatusConstants::CONCERNED:
$active += array_fuse($request_phids);
break;
default:
$passive += array_fuse($request_phids);
break;
}
}
// Remove "Active" users from the "Passive" list.
$passive = array_diff_key($passive, $active);
$this->activePHIDs = $active;
$this->passivePHIDs = $passive;
$this->auditRequests = $requests;
return $commit;
}
public function getOwnerPHID($object) {
return $object->getAuthorPHID();
}
public function getActiveUserPHIDs($object) {
return $this->activePHIDs;
}
public function getPassiveUserPHIDs($object) {
return $this->passivePHIDs;
}
public function getCCUserPHIDs($object) {
return PhabricatorSubscribersQuery::loadSubscribersForPHID(
$object->getPHID());
}
public function getObjectTitle($object) {
$prefix = $this->getTitlePrefix($object);
$repository = $object->getRepository();
$name = $repository->formatCommitName($object->getCommitIdentifier());
$title = $object->getSummary();
return ltrim("{$prefix} {$name}: {$title}");
}
public function getObjectURI($object) {
$repository = $object->getRepository();
$name = $repository->formatCommitName($object->getCommitIdentifier());
return PhabricatorEnv::getProductionURI('/'.$name);
}
public function getObjectDescription($object) {
$data = $object->loadCommitData();
if ($data) {
return $data->getCommitMessage();
}
return null;
}
public function isObjectClosed($object) {
switch ($object->getAuditStatus()) {
case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT:
case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED:
case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED:
return false;
default:
return true;
}
}
public function getResponsibilityTitle($object) {
$prefix = $this->getTitlePrefix($object);
return pht('%s Audit', $prefix);
}
public function getStoryText($object) {
$implied_context = $this->getRenderWithImpliedContext();
$story = $this->getFeedStory();
if ($story instanceof PhabricatorFeedStoryAudit) {
$text = $story->renderForAsanaBridge($implied_context);
} else {
$text = $story->renderText();
}
return $text;
}
private function getTitlePrefix(PhabricatorRepositoryCommit $commit) {
$prefix_key = 'metamta.diffusion.subject-prefix';
return PhabricatorEnv::getEnvConfig($prefix_key);
}
}
diff --git a/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php b/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
index 4ccc265450..dc820fb6db 100644
--- a/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
+++ b/src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
@@ -1,78 +1,78 @@
<?php
/**
* @concrete-extensible
*/
class PhabricatorApplicationTransactionFeedStory
extends PhabricatorFeedStory {
public function getPrimaryObjectPHID() {
return $this->getValue('objectPHID');
}
public function getRequiredObjectPHIDs() {
return $this->getValue('transactionPHIDs');
}
public function getRequiredHandlePHIDs() {
$phids = array();
$phids[] = $this->getValue('objectPHID');
foreach ($this->getValue('transactionPHIDs') as $xaction_phid) {
$xaction = $this->getObject($xaction_phid);
foreach ($xaction->getRequiredHandlePHIDs() as $handle_phid) {
$phids[] = $handle_phid;
}
}
return $phids;
}
protected function getPrimaryTransactionPHID() {
return head($this->getValue('transactionPHIDs'));
}
- protected function getPrimaryTransaction() {
+ public function getPrimaryTransaction() {
return $this->getObject($this->getPrimaryTransactionPHID());
}
public function renderView() {
$view = $this->newStoryView();
$handle = $this->getHandle($this->getPrimaryObjectPHID());
$view->setHref($handle->getURI());
$view->setAppIconFromPHID($handle->getPHID());
$xaction_phids = $this->getValue('transactionPHIDs');
$xaction = $this->getPrimaryTransaction();
$xaction->setHandles($this->getHandles());
$view->setTitle($xaction->getTitleForFeed($this));
foreach ($xaction_phids as $xaction_phid) {
$secondary_xaction = $this->getObject($xaction_phid);
$secondary_xaction->setHandles($this->getHandles());
$body = $secondary_xaction->getBodyForFeed($this);
if (nonempty($body)) {
$view->appendChild($body);
}
}
$view->setImage(
$this->getHandle($xaction->getAuthorPHID())->getImageURI());
return $view;
}
public function renderText() {
$xaction = $this->getPrimaryTransaction();
$old_target = $xaction->getRenderingTarget();
$new_target = PhabricatorApplicationTransaction::TARGET_TEXT;
$xaction->setRenderingTarget($new_target);
$xaction->setHandles($this->getHandles());
$text = $xaction->getTitleForFeed($this);
$xaction->setRenderingTarget($old_target);
return $text;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Jul 28, 1:48 PM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
186918
Default Alt Text
(46 KB)

Event Timeline