1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-10 14:51:06 +01:00

(stable) Promote 2018 Week 36

This commit is contained in:
epriestley 2018-09-08 04:47:49 -07:00
commit 37a40d8272
25 changed files with 426 additions and 204 deletions

View file

@ -1,53 +0,0 @@
#!/usr/bin/env php
<?php
$root = dirname(dirname(dirname(__FILE__)));
require_once $root.'/scripts/__init_script__.php';
$commit = new PhabricatorRepositoryCommit();
$conn_w = id(new PhabricatorRepository())->establishConnection('w');
$sizes = queryfx_all(
$conn_w,
'SELECT repositoryID, count(*) N FROM %T GROUP BY repositoryID',
$commit->getTableName());
$sizes = ipull($sizes, 'N', 'repositoryID');
$maxes = queryfx_all(
$conn_w,
'SELECT repositoryID, max(epoch) maxEpoch FROM %T GROUP BY repositoryID',
$commit->getTableName());
$maxes = ipull($maxes, 'maxEpoch', 'repositoryID');
$repository_ids = array_keys($sizes + $maxes);
echo pht('Updating %d repositories', count($repository_ids));
foreach ($repository_ids as $repository_id) {
$last_commit = queryfx_one(
$conn_w,
'SELECT id FROM %T WHERE repositoryID = %d AND epoch = %d LIMIT 1',
$commit->getTableName(),
$repository_id,
idx($maxes, $repository_id, 0));
if ($last_commit) {
$last_commit = $last_commit['id'];
} else {
$last_commit = 0;
}
queryfx(
$conn_w,
'INSERT INTO %T (repositoryID, lastCommitID, size, epoch)
VALUES (%d, %d, %d, %d) ON DUPLICATE KEY UPDATE
lastCommitID = VALUES(lastCommitID),
size = VALUES(size),
epoch = VALUES(epoch)',
PhabricatorRepository::TABLE_SUMMARY,
$repository_id,
$last_commit,
idx($sizes, $repository_id, 0),
idx($maxes, $repository_id, 0));
echo '.';
}
echo "\n".pht('Done.')."\n";

View file

@ -313,6 +313,7 @@ phutil_register_library_map(array(
'ConduitCallTestCase' => 'applications/conduit/call/__tests__/ConduitCallTestCase.php', 'ConduitCallTestCase' => 'applications/conduit/call/__tests__/ConduitCallTestCase.php',
'ConduitColumnsParameterType' => 'applications/conduit/parametertype/ConduitColumnsParameterType.php', 'ConduitColumnsParameterType' => 'applications/conduit/parametertype/ConduitColumnsParameterType.php',
'ConduitConnectConduitAPIMethod' => 'applications/conduit/method/ConduitConnectConduitAPIMethod.php', 'ConduitConnectConduitAPIMethod' => 'applications/conduit/method/ConduitConnectConduitAPIMethod.php',
'ConduitConstantDescription' => 'applications/conduit/data/ConduitConstantDescription.php',
'ConduitEpochParameterType' => 'applications/conduit/parametertype/ConduitEpochParameterType.php', 'ConduitEpochParameterType' => 'applications/conduit/parametertype/ConduitEpochParameterType.php',
'ConduitException' => 'applications/conduit/protocol/exception/ConduitException.php', 'ConduitException' => 'applications/conduit/protocol/exception/ConduitException.php',
'ConduitGetCapabilitiesConduitAPIMethod' => 'applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php', 'ConduitGetCapabilitiesConduitAPIMethod' => 'applications/conduit/method/ConduitGetCapabilitiesConduitAPIMethod.php',
@ -5628,6 +5629,7 @@ phutil_register_library_map(array(
'ConduitCallTestCase' => 'PhabricatorTestCase', 'ConduitCallTestCase' => 'PhabricatorTestCase',
'ConduitColumnsParameterType' => 'ConduitParameterType', 'ConduitColumnsParameterType' => 'ConduitParameterType',
'ConduitConnectConduitAPIMethod' => 'ConduitAPIMethod', 'ConduitConnectConduitAPIMethod' => 'ConduitAPIMethod',
'ConduitConstantDescription' => 'Phobject',
'ConduitEpochParameterType' => 'ConduitParameterType', 'ConduitEpochParameterType' => 'ConduitParameterType',
'ConduitException' => 'Exception', 'ConduitException' => 'Exception',
'ConduitGetCapabilitiesConduitAPIMethod' => 'ConduitAPIMethod', 'ConduitGetCapabilitiesConduitAPIMethod' => 'ConduitAPIMethod',

View file

@ -2,6 +2,9 @@
final class PhabricatorAuditCommitStatusConstants extends Phobject { final class PhabricatorAuditCommitStatusConstants extends Phobject {
private $key;
private $spec = array();
const NONE = 0; const NONE = 0;
const NEEDS_AUDIT = 1; const NEEDS_AUDIT = 1;
const CONCERN_RAISED = 2; const CONCERN_RAISED = 2;
@ -9,17 +12,57 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
const FULLY_AUDITED = 4; const FULLY_AUDITED = 4;
const NEEDS_VERIFICATION = 5; const NEEDS_VERIFICATION = 5;
public static function getStatusNameMap() { const MODERN_NONE = 'none';
$map = array( const MODERN_NEEDS_AUDIT = 'needs-audit';
self::NONE => pht('No Audits'), const MODERN_CONCERN_RAISED = 'concern-raised';
self::NEEDS_AUDIT => pht('Audit Required'), const MODERN_PARTIALLY_AUDITED = 'partially-audited';
self::CONCERN_RAISED => pht('Concern Raised'), const MODERN_AUDITED = 'audited';
self::NEEDS_VERIFICATION => pht('Needs Verification'), const MODERN_NEEDS_VERIFICATION = 'needs-verification';
self::PARTIALLY_AUDITED => pht('Partially Audited'),
self::FULLY_AUDITED => pht('Audited'),
);
return $map; public static function newForLegacyStatus($status) {
$map = self::getMap();
foreach ($map as $key => $spec) {
if (idx($spec, 'legacy') == $status) {
return self::newForStatus($key);
}
}
return self::newForStatus($status);
}
public static function newForStatus($status) {
$result = new self();
$result->key = $status;
$map = self::getMap();
if (isset($map[$status])) {
$result->spec = $map[$status];
}
return $result;
}
public function getKey() {
return $this->key;
}
public function getIcon() {
return idx($this->spec, 'icon');
}
public function getColor() {
return idx($this->spec, 'color');
}
public function getName() {
return idx($this->spec, 'name', pht('Unknown ("%s")', $this->key));
}
public static function getStatusNameMap() {
$map = self::getMap();
return ipull($map, 'name', 'legacy');
} }
public static function getStatusName($code) { public static function getStatusName($code) {
@ -27,66 +70,71 @@ final class PhabricatorAuditCommitStatusConstants extends Phobject {
} }
public static function getOpenStatusConstants() { public static function getOpenStatusConstants() {
return array( $constants = array();
self::CONCERN_RAISED, foreach (self::getMap() as $map) {
self::NEEDS_AUDIT, if (!$map['closed']) {
self::NEEDS_VERIFICATION, $constants[] = $map['legacy'];
self::PARTIALLY_AUDITED, }
); }
return $constants;
} }
public static function getStatusColor($code) { public static function getStatusColor($code) {
switch ($code) { $map = self::getMap();
case self::CONCERN_RAISED: $map = ipull($map, 'color', 'legacy');
$color = 'red'; return idx($map, $code);
break;
case self::NEEDS_AUDIT:
$color = 'orange';
break;
case self::PARTIALLY_AUDITED:
$color = 'yellow';
break;
case self::FULLY_AUDITED:
$color = 'green';
break;
case self::NONE:
$color = 'bluegrey';
break;
case self::NEEDS_VERIFICATION:
$color = 'indigo';
break;
default:
$color = null;
break;
}
return $color;
} }
public static function getStatusIcon($code) { public static function getStatusIcon($code) {
switch ($code) { $map = self::getMap();
case self::CONCERN_RAISED: $map = ipull($map, 'icon', 'legacy');
$icon = 'fa-times-circle'; return idx($map, $code);
break;
case self::NEEDS_AUDIT:
$icon = 'fa-exclamation-circle';
break;
case self::PARTIALLY_AUDITED:
$icon = 'fa-check-circle-o';
break;
case self::FULLY_AUDITED:
$icon = 'fa-check-circle';
break;
case self::NONE:
$icon = 'fa-check';
break;
case self::NEEDS_VERIFICATION:
$icon = 'fa-refresh';
break;
default:
$icon = null;
break;
}
return $icon;
} }
private static function getMap() {
return array(
self::MODERN_NONE => array(
'name' => pht('No Audits'),
'legacy' => self::NONE,
'icon' => 'fa-check',
'color' => 'bluegrey',
'closed' => true,
),
self::MODERN_NEEDS_AUDIT => array(
'name' => pht('Audit Required'),
'legacy' => self::NEEDS_AUDIT,
'icon' => 'fa-exclamation-circle',
'color' => 'orange',
'closed' => false,
),
self::MODERN_CONCERN_RAISED => array(
'name' => pht('Concern Raised'),
'legacy' => self::CONCERN_RAISED,
'icon' => 'fa-times-circle',
'color' => 'red',
'closed' => false,
),
self::MODERN_PARTIALLY_AUDITED => array(
'name' => pht('Partially Audited'),
'legacy' => self::PARTIALLY_AUDITED,
'icon' => 'fa-check-circle-o',
'color' => 'yellow',
'closed' => false,
),
self::MODERN_AUDITED => array(
'name' => pht('Audited'),
'legacy' => self::FULLY_AUDITED,
'icon' => 'fa-check-circle',
'color' => 'green',
'closed' => true,
),
self::MODERN_NEEDS_VERIFICATION => array(
'name' => pht('Needs Verification'),
'legacy' => self::NEEDS_VERIFICATION,
'icon' => 'fa-refresh',
'color' => 'indigo',
'closed' => false,
),
);
}
} }

View file

@ -258,19 +258,31 @@ final class PhabricatorAuditEditor
$this->didExpandInlineState = true; $this->didExpandInlineState = true;
$actor_phid = $this->getActingAsPHID(); $actor_phid = $this->getActingAsPHID();
$actor_is_author = ($object->getAuthorPHID() == $actor_phid); $author_phid = $object->getAuthorPHID();
if (!$actor_is_author) { $actor_is_author = ($actor_phid == $author_phid);
break;
}
$state_map = PhabricatorTransactions::getInlineStateMap(); $state_map = PhabricatorTransactions::getInlineStateMap();
$inlines = id(new DiffusionDiffInlineCommentQuery()) $query = id(new DiffusionDiffInlineCommentQuery())
->setViewer($this->getActor()) ->setViewer($this->getActor())
->withCommitPHIDs(array($object->getPHID())) ->withCommitPHIDs(array($object->getPHID()))
->withFixedStates(array_keys($state_map)) ->withFixedStates(array_keys($state_map));
$inlines = array();
$inlines[] = id(clone $query)
->withAuthorPHIDs(array($actor_phid))
->withHasTransaction(false)
->execute(); ->execute();
if ($actor_is_author) {
$inlines[] = id(clone $query)
->withHasTransaciton(true)
->execute();
}
$inlines = array_mergev($inlines);
if (!$inlines) { if (!$inlines) {
break; break;
} }

View file

@ -30,11 +30,11 @@ final class PhabricatorAuditSynchronizeManagementWorkflow
continue; continue;
} }
$old_status = $commit->getAuditStatus(); $old_status = $commit->getAuditStatusObject();
$commit->updateAuditStatus($commit->getAudits()); $commit->updateAuditStatus($commit->getAudits());
$new_status = $commit->getAuditStatus(); $new_status = $commit->getAuditStatusObject();
if ($old_status == $new_status) { if ($old_status->getKey() == $new_status->getKey()) {
echo tsprintf( echo tsprintf(
"%s\n", "%s\n",
pht( pht(
@ -46,10 +46,8 @@ final class PhabricatorAuditSynchronizeManagementWorkflow
pht( pht(
'Updating "%s": "%s" -> "%s".', 'Updating "%s": "%s" -> "%s".',
$commit->getDisplayName(), $commit->getDisplayName(),
PhabricatorAuditCommitStatusConstants::getStatusName( $old_status->getName(),
$old_status), $new_status->getName()));
PhabricatorAuditCommitStatusConstants::getStatusName(
$new_status)));
$commit->save(); $commit->save();
} }

View file

@ -67,35 +67,48 @@ final class PhabricatorCommitSearchEngine
->setKey('responsiblePHIDs') ->setKey('responsiblePHIDs')
->setConduitKey('responsible') ->setConduitKey('responsible')
->setAliases(array('responsible', 'responsibles', 'responsiblePHID')) ->setAliases(array('responsible', 'responsibles', 'responsiblePHID'))
->setDatasource(new DifferentialResponsibleDatasource()), ->setDatasource(new DifferentialResponsibleDatasource())
->setDescription(
pht(
'Find commits where given users, projects, or packages are '.
'responsible for the next steps in the audit workflow.')),
id(new PhabricatorUsersSearchField()) id(new PhabricatorUsersSearchField())
->setLabel(pht('Authors')) ->setLabel(pht('Authors'))
->setKey('authorPHIDs') ->setKey('authorPHIDs')
->setConduitKey('authors') ->setConduitKey('authors')
->setAliases(array('author', 'authors', 'authorPHID')), ->setAliases(array('author', 'authors', 'authorPHID'))
->setDescription(pht('Find commits authored by particular users.')),
id(new PhabricatorSearchDatasourceField()) id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Auditors')) ->setLabel(pht('Auditors'))
->setKey('auditorPHIDs') ->setKey('auditorPHIDs')
->setConduitKey('auditors') ->setConduitKey('auditors')
->setAliases(array('auditor', 'auditors', 'auditorPHID')) ->setAliases(array('auditor', 'auditors', 'auditorPHID'))
->setDatasource(new DiffusionAuditorFunctionDatasource()), ->setDatasource(new DiffusionAuditorFunctionDatasource())
->setDescription(
pht(
'Find commits where given users, projects, or packages are '.
'auditors.')),
id(new PhabricatorSearchCheckboxesField()) id(new PhabricatorSearchCheckboxesField())
->setLabel(pht('Audit Status')) ->setLabel(pht('Audit Status'))
->setKey('statuses') ->setKey('statuses')
->setAliases(array('status')) ->setAliases(array('status'))
->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap()), ->setOptions(PhabricatorAuditCommitStatusConstants::getStatusNameMap())
->setDescription(pht('Find commits with given audit statuses.')),
id(new PhabricatorSearchDatasourceField()) id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Repositories')) ->setLabel(pht('Repositories'))
->setKey('repositoryPHIDs') ->setKey('repositoryPHIDs')
->setConduitKey('repositories') ->setConduitKey('repositories')
->setAliases(array('repository', 'repositories', 'repositoryPHID')) ->setAliases(array('repository', 'repositories', 'repositoryPHID'))
->setDatasource(new DiffusionRepositoryFunctionDatasource()), ->setDatasource(new DiffusionRepositoryFunctionDatasource())
->setDescription(pht('Find commits in particular repositories.')),
id(new PhabricatorSearchDatasourceField()) id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Packages')) ->setLabel(pht('Packages'))
->setKey('packagePHIDs') ->setKey('packagePHIDs')
->setConduitKey('packages') ->setConduitKey('packages')
->setAliases(array('package', 'packages', 'packagePHID')) ->setAliases(array('package', 'packages', 'packagePHID'))
->setDatasource(new PhabricatorOwnersPackageDatasource()), ->setDatasource(new PhabricatorOwnersPackageDatasource())
->setDescription(
pht('Find commits which affect given packages.')),
id(new PhabricatorSearchThreeStateField()) id(new PhabricatorSearchThreeStateField())
->setLabel(pht('Unreachable')) ->setLabel(pht('Unreachable'))
->setKey('unreachable') ->setKey('unreachable')

View file

@ -120,14 +120,11 @@ final class PhabricatorAuditListView extends AphrontView {
$commit_desc = $this->getCommitDescription($commit_phid); $commit_desc = $this->getCommitDescription($commit_phid);
$committed = phabricator_datetime($commit->getEpoch(), $viewer); $committed = phabricator_datetime($commit->getEpoch(), $viewer);
$status = $commit->getAuditStatus(); $status = $commit->getAuditStatusObject();
$status_text = $status_text = $status->getName();
PhabricatorAuditCommitStatusConstants::getStatusName($status); $status_color = $status->getColor();
$status_color = $status_icon = $status->getIcon();
PhabricatorAuditCommitStatusConstants::getStatusColor($status);
$status_icon =
PhabricatorAuditCommitStatusConstants::getStatusIcon($status);
$author_phid = $commit->getAuthorPHID(); $author_phid = $commit->getAuthorPHID();
if ($author_phid) { if ($author_phid) {

View file

@ -0,0 +1,26 @@
<?php
final class ConduitConstantDescription extends Phobject {
private $key;
private $value;
public function setKey($key) {
$this->key = $key;
return $this;
}
public function getKey() {
return $this->key;
}
public function setValue($value) {
$this->value = $value;
return $this;
}
public function getValue() {
return $this->value;
}
}

View file

@ -77,19 +77,17 @@ final class DifferentialInlineCommentEditController
} }
protected function loadCommentForEdit($id) { protected function loadCommentForEdit($id) {
$request = $this->getRequest(); $viewer = $this->getViewer();
$user = $request->getUser();
$inline = $this->loadComment($id); $inline = $this->loadComment($id);
if (!$this->canEditInlineComment($user, $inline)) { if (!$this->canEditInlineComment($viewer, $inline)) {
throw new Exception(pht('That comment is not editable!')); throw new Exception(pht('That comment is not editable!'));
} }
return $inline; return $inline;
} }
protected function loadCommentForDone($id) { protected function loadCommentForDone($id) {
$request = $this->getRequest(); $viewer = $this->getViewer();
$viewer = $request->getUser();
$inline = $this->loadComment($id); $inline = $this->loadComment($id);
if (!$inline) { if (!$inline) {
@ -120,19 +118,32 @@ final class DifferentialInlineCommentEditController
throw new Exception(pht('Unable to load revision.')); throw new Exception(pht('Unable to load revision.'));
} }
if ($revision->getAuthorPHID() !== $viewer->getPHID()) { $viewer_phid = $viewer->getPHID();
throw new Exception(pht('You are not the revision owner.')); $is_owner = ($viewer_phid == $revision->getAuthorPHID());
$is_author = ($viewer_phid == $inline->getAuthorPHID());
$is_draft = ($inline->isDraft());
if ($is_owner) {
// You own the revision, so you can mark the comment as "Done".
} else if ($is_author && $is_draft) {
// You made this comment and it's still a draft, so you can mark
// it as "Done".
} else {
throw new Exception(
pht(
'You are not the revision owner, and this is not a draft comment '.
'you authored.'));
} }
return $inline; return $inline;
} }
private function canEditInlineComment( private function canEditInlineComment(
PhabricatorUser $user, PhabricatorUser $viewer,
DifferentialInlineComment $inline) { DifferentialInlineComment $inline) {
// Only the author may edit a comment. // Only the author may edit a comment.
if ($inline->getAuthorPHID() != $user->getPHID()) { if ($inline->getAuthorPHID() != $viewer->getPHID()) {
return false; return false;
} }

View file

@ -248,19 +248,34 @@ final class DifferentialTransactionEditor
$this->didExpandInlineState = true; $this->didExpandInlineState = true;
$actor_phid = $this->getActingAsPHID(); $actor_phid = $this->getActingAsPHID();
$actor_is_author = ($object->getAuthorPHID() == $actor_phid); $author_phid = $object->getAuthorPHID();
if (!$actor_is_author) { $actor_is_author = ($actor_phid == $author_phid);
break;
}
$state_map = PhabricatorTransactions::getInlineStateMap(); $state_map = PhabricatorTransactions::getInlineStateMap();
$inlines = id(new DifferentialDiffInlineCommentQuery()) $query = id(new DifferentialDiffInlineCommentQuery())
->setViewer($this->getActor()) ->setViewer($this->getActor())
->withRevisionPHIDs(array($object->getPHID())) ->withRevisionPHIDs(array($object->getPHID()))
->withFixedStates(array_keys($state_map)) ->withFixedStates(array_keys($state_map));
$inlines = array();
// We're going to undraft any "done" marks on your own inlines.
$inlines[] = id(clone $query)
->withAuthorPHIDs(array($actor_phid))
->withHasTransaction(false)
->execute(); ->execute();
// If you're the author, we also undraft any "done" marks on other
// inlines.
if ($actor_is_author) {
$inlines[] = id(clone $query)
->withHasTransaction(true)
->execute();
}
$inlines = array_mergev($inlines);
if (!$inlines) { if (!$inlines) {
break; break;
} }
@ -892,6 +907,17 @@ final class DifferentialTransactionEditor
array $inlines, array $inlines,
PhabricatorMetaMTAMailBody $body) { PhabricatorMetaMTAMailBody $body) {
$limit = 100;
$limit_note = null;
if (count($inlines) > $limit) {
$limit_note = pht(
'(Showing first %s of %s inline comments.)',
new PhutilNumber($limit),
phutil_count($inlines));
$inlines = array_slice($inlines, 0, $limit, true);
}
$section = id(new DifferentialInlineCommentMailView()) $section = id(new DifferentialInlineCommentMailView())
->setViewer($this->getActor()) ->setViewer($this->getActor())
->setInlines($inlines) ->setInlines($inlines)
@ -900,6 +926,9 @@ final class DifferentialTransactionEditor
$header = pht('INLINE COMMENTS'); $header = pht('INLINE COMMENTS');
$section_text = "\n".$section->getPlaintext(); $section_text = "\n".$section->getPlaintext();
if ($limit_note) {
$section_text = $limit_note."\n".$section_text;
}
$style = array( $style = array(
'margin: 6px 0 12px 0;', 'margin: 6px 0 12px 0;',
@ -912,6 +941,16 @@ final class DifferentialTransactionEditor
), ),
$section->getHTML()); $section->getHTML());
if ($limit_note) {
$section_html = array(
phutil_tag(
'em',
array(),
$limit_note),
$section_html,
);
}
$body->addPlaintextSection($header, $section_text, false); $body->addPlaintextSection($header, $section_text, false);
$body->addHTMLSection($header, $section_html); $body->addHTMLSection($header, $section_html);
} }

View file

@ -1152,6 +1152,20 @@ final class DifferentialRevision extends DifferentialDAO
->setKey('testPlan') ->setKey('testPlan')
->setType('string') ->setType('string')
->setDescription(pht('Revision test plan.')), ->setDescription(pht('Revision test plan.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('isDraft')
->setType('bool')
->setDescription(
pht(
'True if this revision is in any draft state, and thus not '.
'notifying reviewers and subscribers about changes.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('holdAsDraft')
->setType('bool')
->setDescription(
pht(
'True if this revision is being held as a draft. It will not be '.
'automatically submitted for review even if tests pass.')),
); );
} }
@ -1172,6 +1186,8 @@ final class DifferentialRevision extends DifferentialDAO
'diffPHID' => $this->getActiveDiffPHID(), 'diffPHID' => $this->getActiveDiffPHID(),
'summary' => $this->getSummary(), 'summary' => $this->getSummary(),
'testPlan' => $this->getTestPlan(), 'testPlan' => $this->getTestPlan(),
'isDraft' => !$this->getShouldBroadcast(),
'holdAsDraft' => (bool)$this->getHoldAsDraft(),
); );
} }

View file

@ -50,19 +50,17 @@ final class DiffusionInlineCommentController
} }
protected function loadCommentForEdit($id) { protected function loadCommentForEdit($id) {
$request = $this->getRequest(); $viewer = $this->getViewer();
$user = $request->getUser();
$inline = $this->loadComment($id); $inline = $this->loadComment($id);
if (!$this->canEditInlineComment($user, $inline)) { if (!$this->canEditInlineComment($viewer, $inline)) {
throw new Exception(pht('That comment is not editable!')); throw new Exception(pht('That comment is not editable!'));
} }
return $inline; return $inline;
} }
protected function loadCommentForDone($id) { protected function loadCommentForDone($id) {
$request = $this->getRequest(); $viewer = $this->getViewer();
$viewer = $request->getUser();
$inline = $this->loadComment($id); $inline = $this->loadComment($id);
if (!$inline) { if (!$inline) {
@ -77,20 +75,32 @@ final class DiffusionInlineCommentController
throw new Exception(pht('Failed to load commit.')); throw new Exception(pht('Failed to load commit.'));
} }
if ((!$commit->getAuthorPHID()) || $owner_phid = $commit->getAuthorPHID();
($commit->getAuthorPHID() != $viewer->getPHID())) { $viewer_phid = $viewer->getPHID();
throw new Exception(pht('You can not mark this comment as complete.')); $viewer_is_owner = ($owner_phid && ($owner_phid == $viewer_phid));
$viewer_is_author = ($viewer_phid == $inline->getAuthorPHID());
$is_draft = $inline->isDraft();
if ($viewer_is_owner) {
// You can mark inlines on your own commits as "Done".
} else if ($viewer_is_author && $is_draft) {
// You can mark your own unsubmitted inlines as "Done".
} else {
throw new Exception(
pht(
'You can not mark this comment as complete: you did not author '.
'the commit and the comment is not a draft you wrote.'));
} }
return $inline; return $inline;
} }
private function canEditInlineComment( private function canEditInlineComment(
PhabricatorUser $user, PhabricatorUser $viewer,
PhabricatorAuditInlineComment $inline) { PhabricatorAuditInlineComment $inline) {
// Only the author may edit a comment. // Only the author may edit a comment.
if ($inline->getAuthorPHID() != $user->getPHID()) { if ($inline->getAuthorPHID() != $viewer->getPHID()) {
return false; return false;
} }

View file

@ -114,10 +114,10 @@ final class DiffusionHistoryTableView extends DiffusionHistoryView {
'type' => $history->getFileType(), 'type' => $history->getFileType(),
)); ));
$status = $commit->getAuditStatus(); $status = $commit->getAuditStatusObject();
$icon = PhabricatorAuditCommitStatusConstants::getStatusIcon($status); $icon = $status->getIcon();
$color = PhabricatorAuditCommitStatusConstants::getStatusColor($status); $color = $status->getColor();
$name = PhabricatorAuditCommitStatusConstants::getStatusName($status); $name = $status->getName();
$audit_view = id(new PHUIIconView()) $audit_view = id(new PHUIIconView())
->setIcon($icon, $color) ->setIcon($icon, $color)

View file

@ -3,6 +3,8 @@
abstract class DrydockRepositoryOperationType extends Phobject { abstract class DrydockRepositoryOperationType extends Phobject {
private $viewer; private $viewer;
private $operation;
private $interface;
abstract public function applyOperation( abstract public function applyOperation(
DrydockRepositoryOperation $operation, DrydockRepositoryOperation $operation,
@ -29,6 +31,27 @@ abstract class DrydockRepositoryOperationType extends Phobject {
return $this->viewer; return $this->viewer;
} }
final public function setOperation(DrydockRepositoryOperation $operation) {
$this->operation = $operation;
return $this;
}
final public function getOperation() {
return $this->operation;
}
final public function setInterface(DrydockInterface $interface) {
$this->interface = $interface;
return $this;
}
final public function getInterface() {
if (!$this->interface) {
throw new PhutilInvalidStateException('setInterface');
}
return $this->interface;
}
final public function getOperationConstant() { final public function getOperationConstant() {
return $this->getPhobjectClassConstant('OPCONST', 32); return $this->getPhobjectClassConstant('OPCONST', 32);
} }

View file

@ -62,6 +62,8 @@ final class DrydockRepositoryOperationQuery extends DrydockQuery {
protected function willFilterPage(array $operations) { protected function willFilterPage(array $operations) {
$implementations = DrydockRepositoryOperationType::getAllOperationTypes(); $implementations = DrydockRepositoryOperationType::getAllOperationTypes();
$viewer = $this->getViewer();
foreach ($operations as $key => $operation) { foreach ($operations as $key => $operation) {
$impl = idx($implementations, $operation->getOperationType()); $impl = idx($implementations, $operation->getOperationType());
if (!$impl) { if (!$impl) {
@ -69,7 +71,10 @@ final class DrydockRepositoryOperationQuery extends DrydockQuery {
unset($operations[$key]); unset($operations[$key]);
continue; continue;
} }
$impl = clone $impl; $impl = id(clone $impl)
->setViewer($viewer)
->setOperation($operation);
$operation->attachImplementation($impl); $operation->attachImplementation($impl);
} }

View file

@ -137,9 +137,9 @@ final class DrydockRepositoryOperation extends DrydockDAO
} }
public function applyOperation(DrydockInterface $interface) { public function applyOperation(DrydockInterface $interface) {
return $this->getImplementation()->applyOperation( $impl = $this->getImplementation();
$this, $impl->setInterface($interface);
$interface); return $impl->applyOperation($this, $interface);
} }
public function getOperationDescription(PhabricatorUser $viewer) { public function getOperationDescription(PhabricatorUser $viewer) {

View file

@ -25,8 +25,6 @@ final class DrydockRepositoryOperationUpdateWorker
private function handleUpdate(DrydockRepositoryOperation $operation) { private function handleUpdate(DrydockRepositoryOperation $operation) {
$viewer = $this->getViewer();
$operation_state = $operation->getOperationState(); $operation_state = $operation->getOperationState();
switch ($operation_state) { switch ($operation_state) {
@ -53,9 +51,6 @@ final class DrydockRepositoryOperationUpdateWorker
// waiting for a lease we're holding. // waiting for a lease we're holding.
try { try {
$operation->getImplementation()
->setViewer($viewer);
$lease = $this->loadWorkingCopyLease($operation); $lease = $this->loadWorkingCopyLease($operation);
$interface = $lease->getInterface( $interface = $lease->getInterface(

View file

@ -193,7 +193,7 @@ abstract class HarbormasterBuildStepImplementation extends Phobject {
* @return string String with variables replaced safely into it. * @return string String with variables replaced safely into it.
*/ */
protected function mergeVariables($function, $pattern, array $variables) { protected function mergeVariables($function, $pattern, array $variables) {
$regexp = '@\\$\\{(?P<name>[a-z\\./-]+)\\}@'; $regexp = '@\\$\\{(?P<name>[a-z\\./_-]+)\\}@';
$matches = null; $matches = null;
preg_match_all($regexp, $pattern, $matches); preg_match_all($regexp, $pattern, $matches);

View file

@ -82,10 +82,10 @@ final class PhabricatorRepositoryCommitPHIDType extends PhabricatorPHIDType {
$handle->setURI($commit->getURI()); $handle->setURI($commit->getURI());
$handle->setTimestamp($commit->getEpoch()); $handle->setTimestamp($commit->getEpoch());
$status = $commit->getAuditStatus(); $status = $commit->getAuditStatusObject();
$icon = PhabricatorAuditCommitStatusConstants::getStatusIcon($status); $icon = $status->getIcon();
$color = PhabricatorAuditCommitStatusConstants::getStatusColor($status); $color = $status->getColor();
$name = PhabricatorAuditCommitStatusConstants::getStatusName($status); $name = $status->getName();
$handle $handle
->setStateIcon($icon) ->setStateIcon($icon)

View file

@ -530,6 +530,11 @@ final class PhabricatorRepositoryCommit
return $data->getCommitDetail('authorPHID'); return $data->getCommitDetail('authorPHID');
} }
public function getAuditStatusObject() {
$status = $this->getAuditStatus();
return PhabricatorAuditCommitStatusConstants::newForLegacyStatus($status);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() { public function getCapabilities() {

View file

@ -161,7 +161,9 @@ If you specify both a `queryKey` and `constraints`, the builtin or saved query
will be applied first as a starting point, then any additional values in will be applied first as a starting point, then any additional values in
`constraints` will be applied, overwriting the defaults from the original query. `constraints` will be applied, overwriting the defaults from the original query.
Specify constraints like this: Different endpoints support different constraints. The constraints this method
supports are detailed below. As an example, you might specify constraints like
this:
```lang=json, name="Example Custom Constraints" ```lang=json, name="Example Custom Constraints"
{ {
@ -188,15 +190,26 @@ EOTEXT
$fields, $fields,
array('ids', 'phids')) + $fields; array('ids', 'phids')) + $fields;
$constant_lists = array();
$rows = array(); $rows = array();
foreach ($fields as $field) { foreach ($fields as $field) {
$key = $field->getConduitKey(); $key = $field->getConduitKey();
$label = $field->getLabel(); $label = $field->getLabel();
$constants = $field->newConduitConstants();
$type_object = $field->getConduitParameterType(); $type_object = $field->getConduitParameterType();
if ($type_object) { if ($type_object) {
$type = $type_object->getTypeName(); $type = $type_object->getTypeName();
$description = $field->getDescription(); $description = $field->getDescription();
if ($constants) {
$description = array(
$description,
' ',
phutil_tag('em', array(), pht('(See table below.)')),
);
}
} else { } else {
$type = null; $type = null;
$description = phutil_tag('em', array(), pht('Not supported.')); $description = phutil_tag('em', array(), pht('Not supported.'));
@ -208,6 +221,35 @@ EOTEXT
$type, $type,
$description, $description,
); );
if ($constants) {
$constant_lists[] = $this->buildRemarkup(
pht(
'Constants supported by the `%s` constraint:',
'statuses'));
$constants_rows = array();
foreach ($constants as $constant) {
$constants_rows[] = array(
$constant->getKey(),
$constant->getValue(),
);
}
$constants_table = id(new AphrontTableView($constants_rows))
->setHeaders(
array(
pht('Key'),
pht('Value'),
))
->setColumnClasses(
array(
'pre',
'wide',
));
$constant_lists[] = $constants_table;
}
} }
$table = id(new AphrontTableView($rows)) $table = id(new AphrontTableView($rows))
@ -231,7 +273,8 @@ EOTEXT
->setCollapsed(true) ->setCollapsed(true)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($this->buildRemarkup($info)) ->appendChild($this->buildRemarkup($info))
->appendChild($table); ->appendChild($table)
->appendChild($constant_lists);
} }
private function buildOrderBox( private function buildOrderBox(

View file

@ -49,4 +49,16 @@ final class PhabricatorSearchCheckboxesField
return new ConduitStringListParameterType(); return new ConduitStringListParameterType();
} }
public function newConduitConstants() {
$list = array();
foreach ($this->getOptions() as $key => $option) {
$list[] = id(new ConduitConstantDescription())
->setKey($key)
->setValue($option);
}
return $list;
}
} }

View file

@ -382,6 +382,10 @@ abstract class PhabricatorSearchField extends Phobject {
return $this->enableForConduit; return $this->enableForConduit;
} }
public function newConduitConstants() {
return array();
}
/* -( Utility Methods )----------------------------------------------------- */ /* -( Utility Methods )----------------------------------------------------- */

View file

@ -88,7 +88,7 @@ abstract class PhabricatorInlineCommentController
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $viewer = $this->getViewer();
$this->readRequestParameters(); $this->readRequestParameters();
@ -221,7 +221,7 @@ abstract class PhabricatorInlineCommentController
$inline = $this->createComment() $inline = $this->createComment()
->setChangesetID($this->getChangesetID()) ->setChangesetID($this->getChangesetID())
->setAuthorPHID($user->getPHID()) ->setAuthorPHID($viewer->getPHID())
->setLineNumber($this->getLineNumber()) ->setLineNumber($this->getLineNumber())
->setLineLength($this->getLineLength()) ->setLineLength($this->getLineLength())
->setIsNewFile($this->getIsNewFile()) ->setIsNewFile($this->getIsNewFile())
@ -231,6 +231,15 @@ abstract class PhabricatorInlineCommentController
$inline->setReplyToCommentPHID($this->getReplyToCommentPHID()); $inline->setReplyToCommentPHID($this->getReplyToCommentPHID());
} }
// If you own this object, mark your own inlines as "Done" by default.
$owner_phid = $this->loadObjectOwnerPHID($inline);
if ($owner_phid) {
if ($viewer->getPHID() == $owner_phid) {
$fixed_state = PhabricatorInlineCommentInterface::STATE_DRAFT;
$inline->setFixedState($fixed_state);
}
}
$this->saveComment($inline); $this->saveComment($inline);
return $this->buildRenderedCommentResponse( return $this->buildRenderedCommentResponse(
@ -313,10 +322,10 @@ abstract class PhabricatorInlineCommentController
private function buildEditDialog() { private function buildEditDialog() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $viewer = $this->getViewer();
$edit_dialog = id(new PHUIDiffInlineCommentEditView()) $edit_dialog = id(new PHUIDiffInlineCommentEditView())
->setUser($user) ->setUser($viewer)
->setSubmitURI($request->getRequestURI()) ->setSubmitURI($request->getRequestURI())
->setIsOnRight($this->getIsOnRight()) ->setIsOnRight($this->getIsOnRight())
->setIsNewFile($this->getIsNewFile()) ->setIsNewFile($this->getIsNewFile())
@ -342,22 +351,22 @@ abstract class PhabricatorInlineCommentController
$on_right) { $on_right) {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $viewer = $this->getViewer();
$engine = new PhabricatorMarkupEngine(); $engine = new PhabricatorMarkupEngine();
$engine->setViewer($user); $engine->setViewer($viewer);
$engine->addObject( $engine->addObject(
$inline, $inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY); PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
$engine->process(); $engine->process();
$phids = array($user->getPHID()); $phids = array($viewer->getPHID());
$handles = $this->loadViewerHandles($phids); $handles = $this->loadViewerHandles($phids);
$object_owner_phid = $this->loadObjectOwnerPHID($inline); $object_owner_phid = $this->loadObjectOwnerPHID($inline);
$view = id(new PHUIDiffInlineCommentDetailView()) $view = id(new PHUIDiffInlineCommentDetailView())
->setUser($user) ->setUser($viewer)
->setInlineComment($inline) ->setInlineComment($inline)
->setIsOnRight($on_right) ->setIsOnRight($on_right)
->setMarkupEngine($engine) ->setMarkupEngine($engine)
@ -378,7 +387,7 @@ abstract class PhabricatorInlineCommentController
private function renderTextArea($text) { private function renderTextArea($text) {
return id(new PhabricatorRemarkupControl()) return id(new PhabricatorRemarkupControl())
->setUser($this->getRequest()->getUser()) ->setViewer($this->getViewer())
->setSigil('differential-inline-comment-edit-textarea') ->setSigil('differential-inline-comment-edit-textarea')
->setName('text') ->setName('text')
->setValue($text) ->setValue($text)

View file

@ -287,15 +287,24 @@ final class PHUIDiffInlineCommentDetailView
$done_button = null; $done_button = null;
$mark_done = $this->getCanMarkDone();
// Allow users to mark their own draft inlines as "Done".
if ($viewer_phid == $inline->getAuthorPHID()) {
if ($inline->isDraft()) {
$mark_done = true;
}
}
if (!$is_synthetic) { if (!$is_synthetic) {
$draft_state = false; $draft_state = false;
switch ($inline->getFixedState()) { switch ($inline->getFixedState()) {
case PhabricatorInlineCommentInterface::STATE_DRAFT: case PhabricatorInlineCommentInterface::STATE_DRAFT:
$is_done = ($this->getCanMarkDone()); $is_done = $mark_done;
$draft_state = true; $draft_state = true;
break; break;
case PhabricatorInlineCommentInterface::STATE_UNDRAFT: case PhabricatorInlineCommentInterface::STATE_UNDRAFT:
$is_done = !($this->getCanMarkDone()); $is_done = !$mark_done;
$draft_state = true; $draft_state = true;
break; break;
case PhabricatorInlineCommentInterface::STATE_DONE: case PhabricatorInlineCommentInterface::STATE_DONE:
@ -309,7 +318,7 @@ final class PHUIDiffInlineCommentDetailView
// If you don't have permission to mark the comment as "Done", you also // If you don't have permission to mark the comment as "Done", you also
// can not see the draft state. // can not see the draft state.
if (!$this->getCanMarkDone()) { if (!$mark_done) {
$draft_state = false; $draft_state = false;
} }
@ -321,21 +330,19 @@ final class PHUIDiffInlineCommentDetailView
$classes[] = 'inline-state-is-draft'; $classes[] = 'inline-state-is-draft';
} }
if ($this->getCanMarkDone()) { if ($mark_done && !$this->preview) {
$done_input = javelin_tag( $done_input = javelin_tag(
'input', 'input',
array( array(
'type' => 'checkbox', 'type' => 'checkbox',
'checked' => ($is_done ? 'checked' : null), 'checked' => ($is_done ? 'checked' : null),
'disabled' => ($this->getCanMarkDone() ? null : 'disabled'),
'class' => 'differential-inline-done', 'class' => 'differential-inline-done',
'sigil' => 'differential-inline-done', 'sigil' => 'differential-inline-done',
)); ));
$done_button = phutil_tag( $done_button = phutil_tag(
'label', 'label',
array( array(
'class' => 'differential-inline-done-label '. 'class' => 'differential-inline-done-label ',
($this->getCanMarkDone() ? null : 'done-is-disabled'),
), ),
array( array(
$done_input, $done_input,