Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php
index f3858afbf8..6d29d2345d 100644
--- a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php
+++ b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php
@@ -1,124 +1,124 @@
<?php
final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod {
const AUDIT_LEGACYSTATUS_ANY = 'audit-status-any';
const AUDIT_LEGACYSTATUS_OPEN = 'audit-status-open';
const AUDIT_LEGACYSTATUS_CONCERN = 'audit-status-concern';
const AUDIT_LEGACYSTATUS_ACCEPTED = 'audit-status-accepted';
const AUDIT_LEGACYSTATUS_PARTIAL = 'audit-status-partial';
public function getAPIMethodName() {
return 'audit.query';
}
public function getMethodDescription() {
return pht('Query audit requests.');
}
public function getMethodStatus() {
return self::METHOD_STATUS_FROZEN;
}
public function getMethodStatusDescription() {
return pht(
'This method is frozen and will eventually be deprecated. New code '.
'should use "diffusion.commit.search" instead.');
}
protected function defineParamTypes() {
$statuses = array(
self::AUDIT_LEGACYSTATUS_ANY,
self::AUDIT_LEGACYSTATUS_OPEN,
self::AUDIT_LEGACYSTATUS_CONCERN,
self::AUDIT_LEGACYSTATUS_ACCEPTED,
self::AUDIT_LEGACYSTATUS_PARTIAL,
);
$status_const = $this->formatStringConstants($statuses);
return array(
'auditorPHIDs' => 'optional list<phid>',
'commitPHIDs' => 'optional list<phid>',
'status' => ('optional '.$status_const.
' (default = "audit-status-any")'),
'offset' => 'optional int',
'limit' => 'optional int (default = 100)',
);
}
protected function defineReturnType() {
return 'list<dict>';
}
protected function execute(ConduitAPIRequest $request) {
$query = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->needAuditRequests(true);
$auditor_phids = $request->getValue('auditorPHIDs', array());
if ($auditor_phids) {
$query->withAuditorPHIDs($auditor_phids);
}
$commit_phids = $request->getValue('commitPHIDs', array());
if ($commit_phids) {
$query->withPHIDs($commit_phids);
}
$status_map = array(
self::AUDIT_LEGACYSTATUS_OPEN => array(
- PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT,
- PhabricatorAuditCommitStatusConstants::CONCERN_RAISED,
+ PhabricatorAuditCommitStatusConstants::MODERN_NEEDS_AUDIT,
+ PhabricatorAuditCommitStatusConstants::MODERN_CONCERN_RAISED,
),
self::AUDIT_LEGACYSTATUS_CONCERN => array(
- PhabricatorAuditCommitStatusConstants::CONCERN_RAISED,
+ PhabricatorAuditCommitStatusConstants::MODERN_CONCERN_RAISED,
),
self::AUDIT_LEGACYSTATUS_ACCEPTED => array(
- PhabricatorAuditCommitStatusConstants::FULLY_AUDITED,
+ PhabricatorAuditCommitStatusConstants::MODERN_AUDITED,
),
self::AUDIT_LEGACYSTATUS_PARTIAL => array(
- PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED,
+ PhabricatorAuditCommitStatusConstants::MODERN_PARTIALLY_AUDITED,
),
);
$status = $request->getValue('status');
if (isset($status_map[$status])) {
$query->withStatuses($status_map[$status]);
}
// NOTE: These affect the number of commits identified, which is sort of
// reasonable but means the method may return an arbitrary number of
// actual audit requests.
$query->setOffset($request->getValue('offset', 0));
$query->setLimit($request->getValue('limit', 100));
$commits = $query->execute();
$auditor_map = array_fuse($auditor_phids);
$results = array();
foreach ($commits as $commit) {
$requests = $commit->getAudits();
foreach ($requests as $request) {
// If this audit isn't triggered for one of the requested PHIDs,
// skip it.
if ($auditor_map && empty($auditor_map[$request->getAuditorPHID()])) {
continue;
}
$results[] = array(
'id' => $request->getID(),
'commitPHID' => $request->getCommitPHID(),
'auditorPHID' => $request->getAuditorPHID(),
'reasons' => array(),
'status' => $request->getAuditStatus(),
);
}
}
return $results;
}
}
diff --git a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php
index 50e4180074..2196940545 100644
--- a/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php
+++ b/src/applications/audit/constants/PhabricatorAuditCommitStatusConstants.php
@@ -1,164 +1,174 @@
<?php
final class PhabricatorAuditCommitStatusConstants extends Phobject {
private $key;
private $spec = array();
const NONE = 0;
const NEEDS_AUDIT = 1;
const CONCERN_RAISED = 2;
const PARTIALLY_AUDITED = 3;
const FULLY_AUDITED = 4;
const NEEDS_VERIFICATION = 5;
const MODERN_NONE = 'none';
const MODERN_NEEDS_AUDIT = 'needs-audit';
const MODERN_CONCERN_RAISED = 'concern-raised';
const MODERN_PARTIALLY_AUDITED = 'partially-audited';
const MODERN_AUDITED = 'audited';
const MODERN_NEEDS_VERIFICATION = 'needs-verification';
public static function newForLegacyStatus($status) {
$map = self::getMap();
- foreach ($map as $key => $spec) {
- if (idx($spec, 'legacy') == $status) {
- return self::newForStatus($key);
+ if (is_int($status) || ctype_digit($status)) {
+ foreach ($map as $key => $spec) {
+ if ((int)idx($spec, 'legacy') === (int)$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 getLegacyKey() {
+ return idx($this->spec, 'legacy');
+ }
+
public function getName() {
return idx($this->spec, 'name', pht('Unknown ("%s")', $this->key));
}
public function isNoAudit() {
return ($this->key == self::MODERN_NONE);
}
+ public function isNeedsAudit() {
+ return ($this->key == self::MODERN_NEEDS_AUDIT);
+ }
+
public function isConcernRaised() {
return ($this->key == self::MODERN_CONCERN_RAISED);
}
public function isNeedsVerification() {
return ($this->key == self::MODERN_NEEDS_VERIFICATION);
}
public function isPartiallyAudited() {
return ($this->key == self::MODERN_PARTIALLY_AUDITED);
}
public function isAudited() {
return ($this->key == self::MODERN_AUDITED);
}
public function getIsClosed() {
return idx($this->spec, 'closed');
}
public static function getStatusNameMap() {
$map = self::getMap();
return ipull($map, 'name', 'legacy');
}
public static function getStatusName($code) {
return idx(self::getStatusNameMap(), $code, pht('Unknown'));
}
public static function getOpenStatusConstants() {
$constants = array();
foreach (self::getMap() as $map) {
if (!$map['closed']) {
$constants[] = $map['legacy'];
}
}
return $constants;
}
public static function getStatusColor($code) {
$map = self::getMap();
$map = ipull($map, 'color', 'legacy');
return idx($map, $code);
}
public static function getStatusIcon($code) {
$map = self::getMap();
$map = ipull($map, 'icon', 'legacy');
return idx($map, $code);
}
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,
),
);
}
}
diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php
index 0ca9cd97d4..9a7e21b0af 100644
--- a/src/applications/diffusion/query/DiffusionCommitQuery.php
+++ b/src/applications/diffusion/query/DiffusionCommitQuery.php
@@ -1,849 +1,856 @@
<?php
final class DiffusionCommitQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $authorPHIDs;
private $defaultRepository;
private $identifiers;
private $repositoryIDs;
private $repositoryPHIDs;
private $identifierMap;
private $responsiblePHIDs;
private $statuses;
private $packagePHIDs;
private $unreachable;
private $needAuditRequests;
private $auditIDs;
private $auditorPHIDs;
private $epochMin;
private $epochMax;
private $importing;
private $ancestorsOf;
private $needCommitData;
private $needDrafts;
private $needIdentities;
private $mustFilterRefs = false;
private $refRepository;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withAuthorPHIDs(array $phids) {
$this->authorPHIDs = $phids;
return $this;
}
/**
* Load commits by partial or full identifiers, e.g. "rXab82393", "rX1234",
* or "a9caf12". When an identifier matches multiple commits, they will all
* be returned; callers should be prepared to deal with more results than
* they queried for.
*/
public function withIdentifiers(array $identifiers) {
// Some workflows (like blame lookups) can pass in large numbers of
// duplicate identifiers. We only care about unique identifiers, so
// get rid of duplicates immediately.
$identifiers = array_fuse($identifiers);
$this->identifiers = $identifiers;
return $this;
}
/**
* Look up commits in a specific repository. This is a shorthand for calling
* @{method:withDefaultRepository} and @{method:withRepositoryIDs}.
*/
public function withRepository(PhabricatorRepository $repository) {
$this->withDefaultRepository($repository);
$this->withRepositoryIDs(array($repository->getID()));
return $this;
}
/**
* Look up commits in a specific repository. Prefer
* @{method:withRepositoryIDs}; the underlying table is keyed by ID such
* that this method requires a separate initial query to map PHID to ID.
*/
public function withRepositoryPHIDs(array $phids) {
$this->repositoryPHIDs = $phids;
return $this;
}
/**
* If a default repository is provided, ambiguous commit identifiers will
* be assumed to belong to the default repository.
*
* For example, "r123" appearing in a commit message in repository X is
* likely to be unambiguously "rX123". Normally the reference would be
* considered ambiguous, but if you provide a default repository it will
* be correctly resolved.
*/
public function withDefaultRepository(PhabricatorRepository $repository) {
$this->defaultRepository = $repository;
return $this;
}
public function withRepositoryIDs(array $repository_ids) {
$this->repositoryIDs = array_unique($repository_ids);
return $this;
}
public function needCommitData($need) {
$this->needCommitData = $need;
return $this;
}
public function needDrafts($need) {
$this->needDrafts = $need;
return $this;
}
public function needIdentities($need) {
$this->needIdentities = $need;
return $this;
}
public function needAuditRequests($need) {
$this->needAuditRequests = $need;
return $this;
}
public function withAuditIDs(array $ids) {
$this->auditIDs = $ids;
return $this;
}
public function withAuditorPHIDs(array $auditor_phids) {
$this->auditorPHIDs = $auditor_phids;
return $this;
}
public function withResponsiblePHIDs(array $responsible_phids) {
$this->responsiblePHIDs = $responsible_phids;
return $this;
}
public function withPackagePHIDs(array $package_phids) {
$this->packagePHIDs = $package_phids;
return $this;
}
public function withUnreachable($unreachable) {
$this->unreachable = $unreachable;
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withEpochRange($min, $max) {
$this->epochMin = $min;
$this->epochMax = $max;
return $this;
}
public function withImporting($importing) {
$this->importing = $importing;
return $this;
}
public function withAncestorsOf(array $refs) {
$this->ancestorsOf = $refs;
return $this;
}
public function getIdentifierMap() {
if ($this->identifierMap === null) {
throw new Exception(
pht(
'You must %s the query before accessing the identifier map.',
'execute()'));
}
return $this->identifierMap;
}
protected function getPrimaryTableAlias() {
return 'commit';
}
protected function willExecute() {
if ($this->identifierMap === null) {
$this->identifierMap = array();
}
}
public function newResultObject() {
return new PhabricatorRepositoryCommit();
}
protected function loadPage() {
$table = $this->newResultObject();
$conn = $table->establishConnection('r');
$subqueries = array();
if ($this->responsiblePHIDs) {
$base_authors = $this->authorPHIDs;
$base_auditors = $this->auditorPHIDs;
$responsible_phids = $this->responsiblePHIDs;
if ($base_authors) {
$all_authors = array_merge($base_authors, $responsible_phids);
} else {
$all_authors = $responsible_phids;
}
if ($base_auditors) {
$all_auditors = array_merge($base_auditors, $responsible_phids);
} else {
$all_auditors = $responsible_phids;
}
$this->authorPHIDs = $all_authors;
$this->auditorPHIDs = $base_auditors;
$subqueries[] = $this->buildStandardPageQuery(
$conn,
$table->getTableName());
$this->authorPHIDs = $base_authors;
$this->auditorPHIDs = $all_auditors;
$subqueries[] = $this->buildStandardPageQuery(
$conn,
$table->getTableName());
} else {
$subqueries[] = $this->buildStandardPageQuery(
$conn,
$table->getTableName());
}
if (count($subqueries) > 1) {
foreach ($subqueries as $key => $subquery) {
$subqueries[$key] = '('.$subquery.')';
}
$query = qsprintf(
$conn,
'%Q %Q %Q',
implode(' UNION DISTINCT ', $subqueries),
$this->buildOrderClause($conn, true),
$this->buildLimitClause($conn));
} else {
$query = head($subqueries);
}
$rows = queryfx_all($conn, '%Q', $query);
$rows = $this->didLoadRawRows($rows);
return $table->loadAllFromArray($rows);
}
protected function willFilterPage(array $commits) {
$repository_ids = mpull($commits, 'getRepositoryID', 'getRepositoryID');
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withIDs($repository_ids)
->execute();
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
$result = array();
foreach ($commits as $key => $commit) {
$repo = idx($repos, $commit->getRepositoryID());
if ($repo) {
$commit->attachRepository($repo);
} else {
$this->didRejectResult($commit);
unset($commits[$key]);
continue;
}
// Build the identifierMap
if ($this->identifiers !== null) {
$ids = $this->identifiers;
$prefixes = array(
'r'.$commit->getRepository()->getCallsign(),
'r'.$commit->getRepository()->getCallsign().':',
'R'.$commit->getRepository()->getID().':',
'', // No prefix is valid too and will only match the commitIdentifier
);
$suffix = $commit->getCommitIdentifier();
if ($commit->getRepository()->isSVN()) {
foreach ($prefixes as $prefix) {
if (isset($ids[$prefix.$suffix])) {
$result[$prefix.$suffix][] = $commit;
}
}
} else {
// This awkward construction is so we can link the commits up in O(N)
// time instead of O(N^2).
for ($ii = $min_qualified; $ii <= strlen($suffix); $ii++) {
$part = substr($suffix, 0, $ii);
foreach ($prefixes as $prefix) {
if (isset($ids[$prefix.$part])) {
$result[$prefix.$part][] = $commit;
}
}
}
}
}
}
if ($result) {
foreach ($result as $identifier => $matching_commits) {
if (count($matching_commits) == 1) {
$result[$identifier] = head($matching_commits);
} else {
// This reference is ambiguous (it matches more than one commit) so
// don't link it.
unset($result[$identifier]);
}
}
$this->identifierMap += $result;
}
return $commits;
}
protected function didFilterPage(array $commits) {
$viewer = $this->getViewer();
if ($this->mustFilterRefs) {
// If this flag is set, the query has an "Ancestors Of" constraint and
// at least one of the constraining refs had too many ancestors for us
// to apply the constraint with a big "commitIdentifier IN (%Ls)" clause.
// We're going to filter each page and hope we get a full result set
// before the query overheats.
$ancestor_list = mpull($commits, 'getCommitIdentifier');
$ancestor_list = array_values($ancestor_list);
foreach ($this->ancestorsOf as $ref) {
try {
$ancestor_list = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
DiffusionRequest::newFromDictionary(
array(
'repository' => $this->refRepository,
'user' => $viewer,
)),
'diffusion.internal.ancestors',
array(
'ref' => $ref,
'commits' => $ancestor_list,
));
} catch (ConduitClientException $ex) {
throw new PhabricatorSearchConstraintException(
$ex->getMessage());
}
if (!$ancestor_list) {
break;
}
}
$ancestor_list = array_fuse($ancestor_list);
foreach ($commits as $key => $commit) {
$identifier = $commit->getCommitIdentifier();
if (!isset($ancestor_list[$identifier])) {
$this->didRejectResult($commit);
unset($commits[$key]);
}
}
if (!$commits) {
return $commits;
}
}
if ($this->needCommitData) {
$data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
'commitID in (%Ld)',
mpull($commits, 'getID'));
$data = mpull($data, null, 'getCommitID');
foreach ($commits as $commit) {
$commit_data = idx($data, $commit->getID());
if (!$commit_data) {
$commit_data = new PhabricatorRepositoryCommitData();
}
$commit->attachCommitData($commit_data);
}
}
if ($this->needAuditRequests) {
$requests = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere(
'commitPHID IN (%Ls)',
mpull($commits, 'getPHID'));
$requests = mgroup($requests, 'getCommitPHID');
foreach ($commits as $commit) {
$audit_requests = idx($requests, $commit->getPHID(), array());
$commit->attachAudits($audit_requests);
foreach ($audit_requests as $audit_request) {
$audit_request->attachCommit($commit);
}
}
}
if ($this->needIdentities) {
$identity_phids = array_merge(
mpull($commits, 'getAuthorIdentityPHID'),
mpull($commits, 'getCommitterIdentityPHID'));
$data = id(new PhabricatorRepositoryIdentityQuery())
->withPHIDs($identity_phids)
->setViewer($this->getViewer())
->execute();
$data = mpull($data, null, 'getPHID');
foreach ($commits as $commit) {
$author_identity = idx($data, $commit->getAuthorIdentityPHID());
$committer_identity = idx($data, $commit->getCommitterIdentityPHID());
$commit->attachIdentities($author_identity, $committer_identity);
}
}
if ($this->needDrafts) {
PhabricatorDraftEngine::attachDrafts(
$viewer,
$commits);
}
return $commits;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->repositoryPHIDs !== null) {
$map_repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withPHIDs($this->repositoryPHIDs)
->execute();
if (!$map_repositories) {
throw new PhabricatorEmptyQueryException();
}
$repository_ids = mpull($map_repositories, 'getID');
if ($this->repositoryIDs !== null) {
$repository_ids = array_merge($repository_ids, $this->repositoryIDs);
}
$this->withRepositoryIDs($repository_ids);
}
if ($this->ancestorsOf !== null) {
if (count($this->repositoryIDs) !== 1) {
throw new PhabricatorSearchConstraintException(
pht(
'To search for commits which are ancestors of particular refs, '.
'you must constrain the search to exactly one repository.'));
}
$repository_id = head($this->repositoryIDs);
$history_limit = $this->getRawResultLimit() * 32;
$viewer = $this->getViewer();
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIDs(array($repository_id))
->executeOne();
if (!$repository) {
throw new PhabricatorEmptyQueryException();
}
if ($repository->isSVN()) {
throw new PhabricatorSearchConstraintException(
pht(
'Subversion does not support searching for ancestors of '.
'a particular ref. This operation is not meaningful in '.
'Subversion.'));
}
if ($repository->isHg()) {
throw new PhabricatorSearchConstraintException(
pht(
'Mercurial does not currently support searching for ancestors of '.
'a particular ref.'));
}
$can_constrain = true;
$history_identifiers = array();
foreach ($this->ancestorsOf as $key => $ref) {
try {
$raw_history = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
DiffusionRequest::newFromDictionary(
array(
'repository' => $repository,
'user' => $viewer,
)),
'diffusion.historyquery',
array(
'commit' => $ref,
'limit' => $history_limit,
));
} catch (ConduitClientException $ex) {
throw new PhabricatorSearchConstraintException(
$ex->getMessage());
}
$ref_identifiers = array();
foreach ($raw_history['pathChanges'] as $change) {
$ref_identifiers[] = $change['commitIdentifier'];
}
// If this ref had fewer total commits than the limit, we're safe to
// apply the constraint as a large `IN (...)` query for a list of
// commit identifiers. This is efficient.
if ($history_limit) {
if (count($ref_identifiers) >= $history_limit) {
$can_constrain = false;
break;
}
}
$history_identifiers += array_fuse($ref_identifiers);
}
// If all refs had a small number of ancestors, we can just put the
// constraint into the query here and we're done. Otherwise, we need
// to filter each page after it comes out of the MySQL layer.
if ($can_constrain) {
$where[] = qsprintf(
$conn,
'commit.commitIdentifier IN (%Ls)',
$history_identifiers);
} else {
$this->mustFilterRefs = true;
$this->refRepository = $repository;
}
}
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'commit.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'commit.phid IN (%Ls)',
$this->phids);
}
if ($this->repositoryIDs !== null) {
$where[] = qsprintf(
$conn,
'commit.repositoryID IN (%Ld)',
$this->repositoryIDs);
}
if ($this->authorPHIDs !== null) {
$where[] = qsprintf(
$conn,
'commit.authorPHID IN (%Ls)',
$this->authorPHIDs);
}
if ($this->epochMin !== null) {
$where[] = qsprintf(
$conn,
'commit.epoch >= %d',
$this->epochMin);
}
if ($this->epochMax !== null) {
$where[] = qsprintf(
$conn,
'commit.epoch <= %d',
$this->epochMax);
}
if ($this->importing !== null) {
if ($this->importing) {
$where[] = qsprintf(
$conn,
'(commit.importStatus & %d) != %d',
PhabricatorRepositoryCommit::IMPORTED_ALL,
PhabricatorRepositoryCommit::IMPORTED_ALL);
} else {
$where[] = qsprintf(
$conn,
'(commit.importStatus & %d) = %d',
PhabricatorRepositoryCommit::IMPORTED_ALL,
PhabricatorRepositoryCommit::IMPORTED_ALL);
}
}
if ($this->identifiers !== null) {
$min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH;
$min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH;
$refs = array();
$bare = array();
foreach ($this->identifiers as $identifier) {
$matches = null;
preg_match('/^(?:[rR]([A-Z]+:?|[0-9]+:))?(.*)$/',
$identifier, $matches);
$repo = nonempty(rtrim($matches[1], ':'), null);
$commit_identifier = nonempty($matches[2], null);
if ($repo === null) {
if ($this->defaultRepository) {
$repo = $this->defaultRepository->getPHID();
}
}
if ($repo === null) {
if (strlen($commit_identifier) < $min_unqualified) {
continue;
}
$bare[] = $commit_identifier;
} else {
$refs[] = array(
'repository' => $repo,
'identifier' => $commit_identifier,
);
}
}
$sql = array();
foreach ($bare as $identifier) {
$sql[] = qsprintf(
$conn,
'(commit.commitIdentifier LIKE %> AND '.
'LENGTH(commit.commitIdentifier) = 40)',
$identifier);
}
if ($refs) {
$repositories = ipull($refs, 'repository');
$repos = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withIdentifiers($repositories);
$repos->execute();
$repos = $repos->getIdentifierMap();
foreach ($refs as $key => $ref) {
$repo = idx($repos, $ref['repository']);
if (!$repo) {
continue;
}
if ($repo->isSVN()) {
if (!ctype_digit((string)$ref['identifier'])) {
continue;
}
$sql[] = qsprintf(
$conn,
'(commit.repositoryID = %d AND commit.commitIdentifier = %s)',
$repo->getID(),
// NOTE: Because the 'commitIdentifier' column is a string, MySQL
// ignores the index if we hand it an integer. Hand it a string.
// See T3377.
(int)$ref['identifier']);
} else {
if (strlen($ref['identifier']) < $min_qualified) {
continue;
}
$identifier = $ref['identifier'];
if (strlen($identifier) == 40) {
// MySQL seems to do slightly better with this version if the
// clause, so issue it if we have a full commit hash.
$sql[] = qsprintf(
$conn,
'(commit.repositoryID = %d
AND commit.commitIdentifier = %s)',
$repo->getID(),
$identifier);
} else {
$sql[] = qsprintf(
$conn,
'(commit.repositoryID = %d
AND commit.commitIdentifier LIKE %>)',
$repo->getID(),
$identifier);
}
}
}
}
if (!$sql) {
// If we discarded all possible identifiers (e.g., they all referenced
// bogus repositories or were all too short), make sure the query finds
// nothing.
throw new PhabricatorEmptyQueryException(
pht('No commit identifiers.'));
}
$where[] = '('.implode(' OR ', $sql).')';
}
if ($this->auditIDs !== null) {
$where[] = qsprintf(
$conn,
'auditor.id IN (%Ld)',
$this->auditIDs);
}
if ($this->auditorPHIDs !== null) {
$where[] = qsprintf(
$conn,
'auditor.auditorPHID IN (%Ls)',
$this->auditorPHIDs);
}
if ($this->statuses !== null) {
+ $statuses = array();
+ foreach ($this->statuses as $status) {
+ $object = PhabricatorAuditCommitStatusConstants::newForLegacyStatus(
+ $status);
+ $statuses[] = $object->getLegacyKey();
+ }
+
$where[] = qsprintf(
$conn,
'commit.auditStatus IN (%Ld)',
- $this->statuses);
+ $statuses);
}
if ($this->packagePHIDs !== null) {
$where[] = qsprintf(
$conn,
'package.dst IN (%Ls)',
$this->packagePHIDs);
}
if ($this->unreachable !== null) {
if ($this->unreachable) {
$where[] = qsprintf(
$conn,
'(commit.importStatus & %d) = %d',
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
} else {
$where[] = qsprintf(
$conn,
'(commit.importStatus & %d) = 0',
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
}
}
return $where;
}
protected function didFilterResults(array $filtered) {
if ($this->identifierMap) {
foreach ($this->identifierMap as $name => $commit) {
if (isset($filtered[$commit->getPHID()])) {
unset($this->identifierMap[$name]);
}
}
}
}
private function shouldJoinAuditor() {
return ($this->auditIDs || $this->auditorPHIDs);
}
private function shouldJoinOwners() {
return (bool)$this->packagePHIDs;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$join = parent::buildJoinClauseParts($conn);
$audit_request = new PhabricatorRepositoryAuditRequest();
if ($this->shouldJoinAuditor()) {
$join[] = qsprintf(
$conn,
'JOIN %T auditor ON commit.phid = auditor.commitPHID',
$audit_request->getTableName());
}
if ($this->shouldJoinOwners()) {
$join[] = qsprintf(
$conn,
'JOIN %T package ON commit.phid = package.src
AND package.type = %s',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
DiffusionCommitHasPackageEdgeType::EDGECONST);
}
return $join;
}
protected function shouldGroupQueryResultRows() {
if ($this->shouldJoinAuditor()) {
return true;
}
if ($this->shouldJoinOwners()) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
public function getQueryApplicationClass() {
return 'PhabricatorDiffusionApplication';
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'epoch' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'epoch',
'type' => 'int',
'reverse' => false,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
$commit = $this->loadCursorObject($cursor);
return array(
'id' => $commit->getID(),
'epoch' => $commit->getEpoch(),
);
}
public function getBuiltinOrders() {
$parent = parent::getBuiltinOrders();
// Rename the default ID-based orders.
$parent['importnew'] = array(
'name' => pht('Import Date (Newest First)'),
) + $parent['newest'];
$parent['importold'] = array(
'name' => pht('Import Date (Oldest First)'),
) + $parent['oldest'];
return array(
'newest' => array(
'vector' => array('epoch', 'id'),
'name' => pht('Commit Date (Newest First)'),
),
'oldest' => array(
'vector' => array('-epoch', '-id'),
'name' => pht('Commit Date (Oldest First)'),
),
) + $parent;
}
}
diff --git a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php
index 70584ae243..25984c93e1 100644
--- a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php
+++ b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php
@@ -1,186 +1,184 @@
<?php
final class DiffusionCommitRequiredActionResultBucket
extends DiffusionCommitResultBucket {
const BUCKETKEY = 'action';
private $objects;
public function getResultBucketName() {
return pht('Bucket by Required Action');
}
protected function buildResultGroups(
PhabricatorSavedQuery $query,
array $objects) {
$this->objects = $objects;
$phids = $query->getEvaluatedParameter('responsiblePHIDs');
if (!$phids) {
throw new Exception(
pht(
'You can not bucket results by required action without '.
'specifying "Responsible Users".'));
}
$phids = array_fuse($phids);
$groups = array();
$groups[] = $this->newGroup()
->setName(pht('Needs Attention'))
->setNoDataString(pht('None of your commits have active concerns.'))
->setObjects($this->filterConcernRaised($phids));
$groups[] = $this->newGroup()
->setName(pht('Needs Verification'))
->setNoDataString(pht('No commits are awaiting your verification.'))
->setObjects($this->filterNeedsVerification($phids));
$groups[] = $this->newGroup()
->setName(pht('Ready to Audit'))
->setNoDataString(pht('No commits are waiting for you to audit them.'))
->setObjects($this->filterShouldAudit($phids));
$groups[] = $this->newGroup()
->setName(pht('Waiting on Authors'))
->setNoDataString(pht('None of your audits are waiting on authors.'))
->setObjects($this->filterWaitingOnAuthors($phids));
$groups[] = $this->newGroup()
->setName(pht('Waiting on Auditors'))
->setNoDataString(pht('None of your commits are waiting on audit.'))
->setObjects($this->filterWaitingOnAuditors($phids));
// Because you can apply these buckets to queries which include revisions
// that have been closed, add an "Other" bucket if we still have stuff
// that didn't get filtered into any of the previous buckets.
if ($this->objects) {
$groups[] = $this->newGroup()
->setName(pht('Other Commits'))
->setObjects($this->objects);
}
return $groups;
}
private function filterConcernRaised(array $phids) {
$results = array();
$objects = $this->objects;
foreach ($objects as $key => $object) {
if (empty($phids[$object->getAuthorPHID()])) {
continue;
}
if (!$object->isAuditStatusConcernRaised()) {
continue;
}
$results[$key] = $object;
unset($this->objects[$key]);
}
return $results;
}
private function filterNeedsVerification(array $phids) {
$results = array();
$objects = $this->objects;
$has_concern = array(
PhabricatorAuditStatusConstants::CONCERNED,
);
$has_concern = array_fuse($has_concern);
foreach ($objects as $key => $object) {
if (isset($phids[$object->getAuthorPHID()])) {
continue;
}
if (!$object->isAuditStatusNeedsVerification()) {
continue;
}
if (!$this->hasAuditorsWithStatus($object, $phids, $has_concern)) {
continue;
}
$results[$key] = $object;
unset($this->objects[$key]);
}
return $results;
}
private function filterShouldAudit(array $phids) {
$results = array();
$objects = $this->objects;
$should_audit = array(
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
PhabricatorAuditStatusConstants::AUDIT_REQUESTED,
);
$should_audit = array_fuse($should_audit);
foreach ($objects as $key => $object) {
if (isset($phids[$object->getAuthorPHID()])) {
continue;
}
if (!$this->hasAuditorsWithStatus($object, $phids, $should_audit)) {
continue;
}
$results[$key] = $object;
unset($this->objects[$key]);
}
return $results;
}
private function filterWaitingOnAuthors(array $phids) {
$results = array();
$objects = $this->objects;
foreach ($objects as $key => $object) {
if (!$object->isAuditStatusConcernRaised()) {
continue;
}
if (isset($phids[$object->getAuthorPHID()])) {
continue;
}
$results[$key] = $object;
unset($this->objects[$key]);
}
return $results;
}
private function filterWaitingOnAuditors(array $phids) {
$results = array();
$objects = $this->objects;
- $status_waiting = array(
- PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT,
- PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION,
- PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED,
- );
- $status_waiting = array_fuse($status_waiting);
-
foreach ($objects as $key => $object) {
- if (empty($status_waiting[$object->getAuditStatus()])) {
+ $any_waiting =
+ $object->isAuditStatusNeedsAudit() ||
+ $object->isAuditStatusNeedsVerification() ||
+ $object->isAuditStatusPartiallyAudited();
+
+ if (!$any_waiting) {
continue;
}
$results[$key] = $object;
unset($this->objects[$key]);
}
return $results;
}
}
diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php
index 1caf12a82d..bdc4a2e475 100644
--- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php
@@ -1,352 +1,353 @@
<?php
final class PhabricatorOwnersDetailController
extends PhabricatorOwnersController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$package = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->needPaths(true)
->executeOne();
if (!$package) {
return new Aphront404Response();
}
$paths = $package->getPaths();
$repository_phids = array();
foreach ($paths as $path) {
$repository_phids[$path->getRepositoryPHID()] = true;
}
if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs(array_keys($repository_phids))
->execute();
$repositories = mpull($repositories, null, 'getPHID');
} else {
$repositories = array();
}
$field_list = PhabricatorCustomField::getObjectFields(
$package,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($package);
$curtain = $this->buildCurtain($package);
$details = $this->buildPackageDetailView($package, $field_list);
if ($package->isArchived()) {
$header_icon = 'fa-ban';
$header_name = pht('Archived');
$header_color = 'dark';
} else {
$header_icon = 'fa-check';
$header_name = pht('Active');
$header_color = 'bluegrey';
}
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($package->getName())
->setStatus($header_icon, $header_color, $header_name)
->setPolicyObject($package)
->setHeaderIcon('fa-gift');
$commit_views = array();
$commit_uri = id(new PhutilURI('/diffusion/commit/'))
->setQueryParams(
array(
'package' => $package->getPHID(),
));
- $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
+ $status_concern =
+ PhabricatorAuditCommitStatusConstants::MODERN_CONCERN_RAISED;
$attention_commits = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withPackagePHIDs(array($package->getPHID()))
->withStatuses(
array(
$status_concern,
))
->needCommitData(true)
->needAuditRequests(true)
->setLimit(10)
->execute();
$view = id(new PhabricatorAuditListView())
->setUser($viewer)
->setNoDataString(pht('This package has no open problem commits.'))
->setCommits($attention_commits);
$commit_views[] = array(
'view' => $view,
'header' => pht('Needs Attention'),
'icon' => 'fa-warning',
'button' => id(new PHUIButtonView())
->setTag('a')
->setHref($commit_uri->alter('status', $status_concern))
->setIcon('fa-list-ul')
->setText(pht('View All')),
);
$all_commits = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withPackagePHIDs(array($package->getPHID()))
->needCommitData(true)
->needAuditRequests(true)
->setLimit(25)
->execute();
$view = id(new PhabricatorAuditListView())
->setUser($viewer)
->setCommits($all_commits)
->setNoDataString(pht('No commits in this package.'));
$commit_views[] = array(
'view' => $view,
'header' => pht('Recent Commits'),
'icon' => 'fa-code',
'button' => id(new PHUIButtonView())
->setTag('a')
->setHref($commit_uri)
->setIcon('fa-list-ul')
->setText(pht('View All')),
);
$commit_panels = array();
foreach ($commit_views as $commit_view) {
$commit_panel = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$commit_header = id(new PHUIHeaderView())
->setHeader($commit_view['header'])
->setHeaderIcon($commit_view['icon']);
if (isset($commit_view['button'])) {
$commit_header->addActionLink($commit_view['button']);
}
$commit_panel->setHeader($commit_header);
$commit_panel->appendChild($commit_view['view']);
$commit_panels[] = $commit_panel;
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($package->getMonogram());
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$package,
new PhabricatorOwnersPackageTransactionQuery());
$timeline->setShouldTerminate(true);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(array(
$this->renderPathsTable($paths, $repositories),
$commit_panels,
$timeline,
))
->addPropertySection(pht('Details'), $details);
return $this->newPage()
->setTitle($package->getName())
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildPackageDetailView(
PhabricatorOwnersPackage $package,
PhabricatorCustomFieldList $field_list) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
$owners = $package->getOwners();
if ($owners) {
$owner_list = $viewer->renderHandleList(mpull($owners, 'getUserPHID'));
} else {
$owner_list = phutil_tag('em', array(), pht('None'));
}
$view->addProperty(pht('Owners'), $owner_list);
$dominion = $package->getDominion();
$dominion_map = PhabricatorOwnersPackage::getDominionOptionsMap();
$spec = idx($dominion_map, $dominion, array());
$name = idx($spec, 'short', $dominion);
$view->addProperty(pht('Dominion'), $name);
$auto = $package->getAutoReview();
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
$spec = idx($autoreview_map, $auto, array());
$name = idx($spec, 'name', $auto);
$view->addProperty(pht('Auto Review'), $name);
if ($package->getAuditingEnabled()) {
$auditing = pht('Enabled');
} else {
$auditing = pht('Disabled');
}
$view->addProperty(pht('Auditing'), $auditing);
$ignored = $package->getIgnoredPathAttributes();
$ignored = array_keys($ignored);
if ($ignored) {
$ignored = implode(', ', $ignored);
} else {
$ignored = phutil_tag('em', array(), pht('None'));
}
$view->addProperty(pht('Ignored Attributes'), $ignored);
$description = $package->getDescription();
if (strlen($description)) {
$description = new PHUIRemarkupView($viewer, $description);
$view->addSectionHeader(pht('Description'));
$view->addTextContent($description);
}
$field_list->appendFieldsToPropertyList(
$package,
$viewer,
$view);
return $view;
}
private function buildCurtain(PhabricatorOwnersPackage $package) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$package,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $package->getID();
$edit_uri = $this->getApplicationURI("/edit/{$id}/");
$paths_uri = $this->getApplicationURI("/paths/{$id}/");
$curtain = $this->newCurtainView($package);
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Package'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($edit_uri));
if ($package->isArchived()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Activate Package'))
->setIcon('fa-check')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($this->getApplicationURI("/archive/{$id}/")));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Archive Package'))
->setIcon('fa-ban')
->setDisabled(!$can_edit)
->setWorkflow($can_edit)
->setHref($this->getApplicationURI("/archive/{$id}/")));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Paths'))
->setIcon('fa-folder-open')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($paths_uri));
return $curtain;
}
private function renderPathsTable(array $paths, array $repositories) {
$viewer = $this->getViewer();
$rows = array();
foreach ($paths as $path) {
$repo = idx($repositories, $path->getRepositoryPHID());
if (!$repo) {
continue;
}
$href = $repo->generateURI(
array(
'branch' => $repo->getDefaultBranch(),
'path' => $path->getPathDisplay(),
'action' => 'browse',
));
$path_link = phutil_tag(
'a',
array(
'href' => (string)$href,
),
$path->getPathDisplay());
$rows[] = array(
($path->getExcluded() ? '-' : '+'),
$repo->getName(),
$path_link,
);
}
$info = null;
if (!$paths) {
$info = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setErrors(
array(
pht(
'This package does not contain any paths yet. Use '.
'"Edit Paths" to add some.'),
));
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
null,
pht('Repository'),
pht('Path'),
))
->setColumnClasses(
array(
null,
null,
'wide',
));
if ($info) {
$table->setNotice($info);
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Paths'))
->setHeaderIcon('fa-folder-open');
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
return $box;
}
}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
index f56962d9f8..291c895231 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
@@ -1,886 +1,887 @@
<?php
final class PhabricatorRepositoryCommit
extends PhabricatorRepositoryDAO
implements
PhabricatorPolicyInterface,
PhabricatorFlaggableInterface,
PhabricatorProjectInterface,
PhabricatorTokenReceiverInterface,
PhabricatorSubscribableInterface,
PhabricatorMentionableInterface,
HarbormasterBuildableInterface,
HarbormasterCircleCIBuildableInterface,
HarbormasterBuildkiteBuildableInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorFulltextInterface,
PhabricatorFerretInterface,
PhabricatorConduitResultInterface,
PhabricatorDraftInterface {
protected $repositoryID;
protected $phid;
protected $authorIdentityPHID;
protected $committerIdentityPHID;
protected $commitIdentifier;
protected $epoch;
protected $mailKey;
protected $authorPHID;
protected $auditStatus = PhabricatorAuditCommitStatusConstants::NONE;
protected $summary = '';
protected $importStatus = 0;
const IMPORTED_MESSAGE = 1;
const IMPORTED_CHANGE = 2;
const IMPORTED_OWNERS = 4;
const IMPORTED_HERALD = 8;
const IMPORTED_ALL = 15;
const IMPORTED_CLOSEABLE = 1024;
const IMPORTED_UNREACHABLE = 2048;
private $commitData = self::ATTACHABLE;
private $audits = self::ATTACHABLE;
private $repository = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $authorIdentity = self::ATTACHABLE;
private $committerIdentity = self::ATTACHABLE;
private $drafts = array();
private $auditAuthorityPHIDs = array();
public function attachRepository(PhabricatorRepository $repository) {
$this->repository = $repository;
return $this;
}
public function getRepository($assert_attached = true) {
if ($assert_attached) {
return $this->assertAttached($this->repository);
}
return $this->repository;
}
public function isPartiallyImported($mask) {
return (($mask & $this->getImportStatus()) == $mask);
}
public function isImported() {
return $this->isPartiallyImported(self::IMPORTED_ALL);
}
public function isUnreachable() {
return $this->isPartiallyImported(self::IMPORTED_UNREACHABLE);
}
public function writeImportStatusFlag($flag) {
return $this->adjustImportStatusFlag($flag, true);
}
public function clearImportStatusFlag($flag) {
return $this->adjustImportStatusFlag($flag, false);
}
private function adjustImportStatusFlag($flag, $set) {
$conn_w = $this->establishConnection('w');
$table_name = $this->getTableName();
$id = $this->getID();
if ($set) {
queryfx(
$conn_w,
'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d',
$table_name,
$flag,
$id);
$this->setImportStatus($this->getImportStatus() | $flag);
} else {
queryfx(
$conn_w,
'UPDATE %T SET importStatus = (importStatus & ~%d) WHERE id = %d',
$table_name,
$flag,
$id);
$this->setImportStatus($this->getImportStatus() & ~$flag);
}
return $this;
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_COLUMN_SCHEMA => array(
'commitIdentifier' => 'text40',
'mailKey' => 'bytes20',
'authorPHID' => 'phid?',
'authorIdentityPHID' => 'phid?',
'committerIdentityPHID' => 'phid?',
'auditStatus' => 'uint32',
'summary' => 'text255',
'importStatus' => 'uint32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'repositoryID' => array(
'columns' => array('repositoryID', 'importStatus'),
),
'authorPHID' => array(
'columns' => array('authorPHID', 'auditStatus', 'epoch'),
),
'repositoryID_2' => array(
'columns' => array('repositoryID', 'epoch'),
),
'key_commit_identity' => array(
'columns' => array('commitIdentifier', 'repositoryID'),
'unique' => true,
),
'key_epoch' => array(
'columns' => array('epoch'),
),
'key_author' => array(
'columns' => array('authorPHID', 'epoch'),
),
),
self::CONFIG_NO_MUTATE => array(
'importStatus',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorRepositoryCommitPHIDType::TYPECONST);
}
public function loadCommitData() {
if (!$this->getID()) {
return null;
}
return id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$this->getID());
}
public function attachCommitData(
PhabricatorRepositoryCommitData $data = null) {
$this->commitData = $data;
return $this;
}
public function getCommitData() {
return $this->assertAttached($this->commitData);
}
public function attachAudits(array $audits) {
assert_instances_of($audits, 'PhabricatorRepositoryAuditRequest');
$this->audits = $audits;
return $this;
}
public function getAudits() {
return $this->assertAttached($this->audits);
}
public function hasAttachedAudits() {
return ($this->audits !== self::ATTACHABLE);
}
public function attachIdentities(
PhabricatorRepositoryIdentity $author = null,
PhabricatorRepositoryIdentity $committer = null) {
$this->authorIdentity = $author;
$this->committerIdentity = $committer;
return $this;
}
public function getAuthorIdentity() {
return $this->assertAttached($this->authorIdentity);
}
public function getCommitterIdentity() {
return $this->assertAttached($this->committerIdentity);
}
public function loadAndAttachAuditAuthority(
PhabricatorUser $viewer,
$actor_phid = null) {
if ($actor_phid === null) {
$actor_phid = $viewer->getPHID();
}
// TODO: This method is a little weird and sketchy, but worlds better than
// what came before it. Eventually, this should probably live in a Query
// class.
// Figure out which requests the actor has authority over: these are user
// requests where they are the auditor, and packages and projects they are
// a member of.
if (!$actor_phid) {
$attach_key = $viewer->getCacheFragment();
$phids = array();
} else {
$attach_key = $actor_phid;
// At least currently, 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. This may change or depend on
// config.
$actor_is_author = ($actor_phid == $this->getAuthorPHID());
if ($actor_is_author) {
$phids = array($actor_phid);
} else {
$phids = array();
$phids[$actor_phid] = true;
$owned_packages = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withAuthorityPHIDs(array($actor_phid))
->execute();
foreach ($owned_packages as $package) {
$phids[$package->getPHID()] = true;
}
$projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withMemberPHIDs(array($actor_phid))
->execute();
foreach ($projects as $project) {
$phids[$project->getPHID()] = true;
}
$phids = array_keys($phids);
}
}
$this->auditAuthorityPHIDs[$attach_key] = array_fuse($phids);
return $this;
}
public function hasAuditAuthority(
PhabricatorUser $viewer,
PhabricatorRepositoryAuditRequest $audit,
$actor_phid = null) {
if ($actor_phid === null) {
$actor_phid = $viewer->getPHID();
}
if (!$actor_phid) {
$attach_key = $viewer->getCacheFragment();
} else {
$attach_key = $actor_phid;
}
$map = $this->assertAttachedKey($this->auditAuthorityPHIDs, $attach_key);
if (!$actor_phid) {
return false;
}
return isset($map[$audit->getAuditorPHID()]);
}
public function writeOwnersEdges(array $package_phids) {
$src_phid = $this->getPHID();
$edge_type = DiffusionCommitHasPackageEdgeType::EDGECONST;
$editor = new PhabricatorEdgeEditor();
$dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$src_phid,
$edge_type);
foreach ($dst_phids as $dst_phid) {
$editor->removeEdge($src_phid, $edge_type, $dst_phid);
}
foreach ($package_phids as $package_phid) {
$editor->addEdge($src_phid, $edge_type, $package_phid);
}
$editor->save();
return $this;
}
public function getAuditorPHIDsForEdit() {
$audits = $this->getAudits();
return mpull($audits, 'getAuditorPHID');
}
public function save() {
if (!$this->mailKey) {
$this->mailKey = Filesystem::readRandomCharacters(20);
}
return parent::save();
}
public function delete() {
$data = $this->loadCommitData();
$audits = id(new PhabricatorRepositoryAuditRequest())
->loadAllWhere('commitPHID = %s', $this->getPHID());
$this->openTransaction();
if ($data) {
$data->delete();
}
foreach ($audits as $audit) {
$audit->delete();
}
$result = parent::delete();
$this->saveTransaction();
return $result;
}
public function getDateCreated() {
// This is primarily to make analysis of commits with the Fact engine work.
return $this->getEpoch();
}
public function getURI() {
return '/'.$this->getMonogram();
}
/**
* Synchronize a commit's overall audit status with the individual audit
* triggers.
*/
public function updateAuditStatus(array $requests) {
assert_instances_of($requests, 'PhabricatorRepositoryAuditRequest');
$any_concern = false;
$any_accept = false;
$any_need = false;
foreach ($requests as $request) {
switch ($request->getAuditStatus()) {
case PhabricatorAuditStatusConstants::AUDIT_REQUIRED:
case PhabricatorAuditStatusConstants::AUDIT_REQUESTED:
$any_need = true;
break;
case PhabricatorAuditStatusConstants::ACCEPTED:
$any_accept = true;
break;
case PhabricatorAuditStatusConstants::CONCERNED:
$any_concern = true;
break;
}
}
- $current_status = $this->getAuditStatus();
- $status_verify = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION;
-
if ($any_concern) {
- if ($current_status == $status_verify) {
+ if ($this->isAuditStatusNeedsVerification()) {
// If the change is in "Needs Verification", we keep it there as
// long as any auditors still have concerns.
- $status = $status_verify;
+ $status = PhabricatorAuditCommitStatusConstants::NEEDS_VERIFICATION;
} else {
$status = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
}
} else if ($any_accept) {
if ($any_need) {
$status = PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED;
} else {
$status = PhabricatorAuditCommitStatusConstants::FULLY_AUDITED;
}
} else if ($any_need) {
$status = PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT;
} else {
$status = PhabricatorAuditCommitStatusConstants::NONE;
}
return $this->setAuditStatus($status);
}
public function getMonogram() {
$repository = $this->getRepository();
$callsign = $repository->getCallsign();
$identifier = $this->getCommitIdentifier();
if ($callsign !== null) {
return "r{$callsign}{$identifier}";
} else {
$id = $repository->getID();
return "R{$id}:{$identifier}";
}
}
public function getDisplayName() {
$repository = $this->getRepository();
$identifier = $this->getCommitIdentifier();
return $repository->formatCommitName($identifier);
}
/**
* Return a local display name for use in the context of the containing
* repository.
*
* In Git and Mercurial, this returns only a short hash, like "abcdef012345".
* See @{method:getDisplayName} for a short name that always includes
* repository context.
*
* @return string Short human-readable name for use inside a repository.
*/
public function getLocalName() {
$repository = $this->getRepository();
$identifier = $this->getCommitIdentifier();
return $repository->formatCommitName($identifier, $local = true);
}
/**
* Make a strong effort to find a way to render this commit's committer.
* This currently attempts to use @{PhabricatorRepositoryIdentity}, and
* falls back to examining the commit detail information. After we force
* the migration to using identities, update this method to remove the
* fallback. See T12164 for details.
*/
public function renderAnyCommitter(PhabricatorUser $viewer, $handles) {
$committer = $this->renderCommitter($viewer, $handles);
if ($committer) {
return $committer;
}
return $this->renderAuthor($viewer, $handles);
}
public function renderCommitter(PhabricatorUser $viewer, $handles) {
$committer_phid = $this->getCommitterDisplayPHID();
if ($committer_phid) {
return $handles[$committer_phid]->renderLink();
}
$data = $this->getCommitData();
$committer_name = $data->getCommitDetail('committer');
if (strlen($committer_name)) {
return DiffusionView::renderName($committer_name);
}
return null;
}
public function renderAuthor(PhabricatorUser $viewer, $handles) {
$author_phid = $this->getAuthorDisplayPHID();
if ($author_phid) {
return $handles[$author_phid]->renderLink();
}
$data = $this->getCommitData();
$author_name = $data->getAuthorName();
if (strlen($author_name)) {
return DiffusionView::renderName($author_name);
}
return null;
}
public function loadIdentities(PhabricatorUser $viewer) {
if ($this->authorIdentity !== self::ATTACHABLE) {
return $this;
}
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIDs(array($this->getID()))
->needIdentities(true)
->executeOne();
$author_identity = $commit->getAuthorIdentity();
$committer_identity = $commit->getCommitterIdentity();
return $this->attachIdentities($author_identity, $committer_identity);
}
public function hasCommitterIdentity() {
return ($this->getCommitterIdentity() !== null);
}
public function hasAuthorIdentity() {
return ($this->getAuthorIdentity() !== null);
}
public function getCommitterDisplayPHID() {
if ($this->hasCommitterIdentity()) {
return $this->getCommitterIdentity()->getIdentityDisplayPHID();
}
$data = $this->getCommitData();
return $data->getCommitDetail('committerPHID');
}
public function getAuthorDisplayPHID() {
if ($this->hasAuthorIdentity()) {
return $this->getAuthorIdentity()->getIdentityDisplayPHID();
}
$data = $this->getCommitData();
return $data->getCommitDetail('authorPHID');
}
public function getAuditStatusObject() {
$status = $this->getAuditStatus();
return PhabricatorAuditCommitStatusConstants::newForLegacyStatus($status);
}
public function isAuditStatusNoAudit() {
return $this->getAuditStatusObject()->isNoAudit();
}
+ public function isAuditStatusNeedsAudit() {
+ return $this->getAuditStatusObject()->isNeedsAudit();
+ }
+
public function isAuditStatusConcernRaised() {
return $this->getAuditStatusObject()->isConcernRaised();
}
public function isAuditStatusNeedsVerification() {
return $this->getAuditStatusObject()->isNeedsVerification();
}
public function isAuditStatusPartiallyAudited() {
return $this->getAuditStatusObject()->isPartiallyAudited();
}
public function isAuditStatusAudited() {
return $this->getAuditStatusObject()->isAudited();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getRepository()->getPolicy($capability);
case PhabricatorPolicyCapability::CAN_EDIT:
return PhabricatorPolicies::POLICY_USER;
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getRepository()->hasAutomaticCapability($capability, $viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'Commits inherit the policies of the repository they belong to.');
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return array(
$this->getAuthorPHID(),
);
}
/* -( Stuff for serialization )---------------------------------------------- */
/**
* NOTE: this is not a complete serialization; only the 'protected' fields are
* involved. This is due to ease of (ab)using the Lisk abstraction to get this
* done, as well as complexity of the other fields.
*/
public function toDictionary() {
return array(
'repositoryID' => $this->getRepositoryID(),
'phid' => $this->getPHID(),
'commitIdentifier' => $this->getCommitIdentifier(),
'epoch' => $this->getEpoch(),
'mailKey' => $this->getMailKey(),
'authorPHID' => $this->getAuthorPHID(),
'auditStatus' => $this->getAuditStatus(),
'summary' => $this->getSummary(),
'importStatus' => $this->getImportStatus(),
);
}
public static function newFromDictionary(array $dict) {
return id(new PhabricatorRepositoryCommit())
->loadFromArray($dict);
}
/* -( HarbormasterBuildableInterface )------------------------------------- */
public function getHarbormasterBuildableDisplayPHID() {
return $this->getHarbormasterBuildablePHID();
}
public function getHarbormasterBuildablePHID() {
return $this->getPHID();
}
public function getHarbormasterContainerPHID() {
return $this->getRepository()->getPHID();
}
public function getBuildVariables() {
$results = array();
$results['buildable.commit'] = $this->getCommitIdentifier();
$repo = $this->getRepository();
$results['repository.callsign'] = $repo->getCallsign();
$results['repository.phid'] = $repo->getPHID();
$results['repository.vcs'] = $repo->getVersionControlSystem();
$results['repository.uri'] = $repo->getPublicCloneURI();
return $results;
}
public function getAvailableBuildVariables() {
return array(
'buildable.commit' => pht('The commit identifier, if applicable.'),
'repository.callsign' =>
pht('The callsign of the repository in Phabricator.'),
'repository.phid' =>
pht('The PHID of the repository in Phabricator.'),
'repository.vcs' =>
pht('The version control system, either "svn", "hg" or "git".'),
'repository.uri' =>
pht('The URI to clone or checkout the repository from.'),
);
}
public function newBuildableEngine() {
return new DiffusionBuildableEngine();
}
/* -( HarbormasterCircleCIBuildableInterface )----------------------------- */
public function getCircleCIGitHubRepositoryURI() {
$repository = $this->getRepository();
$commit_phid = $this->getPHID();
$repository_phid = $repository->getPHID();
if ($repository->isHosted()) {
throw new Exception(
pht(
'This commit ("%s") is associated with a hosted repository '.
'("%s"). Repositories must be imported from GitHub to be built '.
'with CircleCI.',
$commit_phid,
$repository_phid));
}
$remote_uri = $repository->getRemoteURI();
$path = HarbormasterCircleCIBuildStepImplementation::getGitHubPath(
$remote_uri);
if (!$path) {
throw new Exception(
pht(
'This commit ("%s") is associated with a repository ("%s") that '.
'with a remote URI ("%s") that does not appear to be hosted on '.
'GitHub. Repositories must be hosted on GitHub to be built with '.
'CircleCI.',
$commit_phid,
$repository_phid,
$remote_uri));
}
return $remote_uri;
}
public function getCircleCIBuildIdentifierType() {
return 'revision';
}
public function getCircleCIBuildIdentifier() {
return $this->getCommitIdentifier();
}
/* -( HarbormasterBuildkiteBuildableInterface )---------------------------- */
public function getBuildkiteBranch() {
$viewer = PhabricatorUser::getOmnipotentUser();
$repository = $this->getRepository();
$branches = DiffusionQuery::callConduitWithDiffusionRequest(
$viewer,
DiffusionRequest::newFromDictionary(
array(
'repository' => $repository,
'user' => $viewer,
)),
'diffusion.branchquery',
array(
'contains' => $this->getCommitIdentifier(),
'repository' => $repository->getPHID(),
));
if (!$branches) {
throw new Exception(
pht(
'Commit "%s" is not an ancestor of any branch head, so it can not '.
'be built with Buildkite.',
$this->getCommitIdentifier()));
}
$branch = head($branches);
return 'refs/heads/'.$branch['shortName'];
}
public function getBuildkiteCommit() {
return $this->getCommitIdentifier();
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('diffusion.fields');
}
public function getCustomFieldBaseClass() {
return 'PhabricatorCommitCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
// TODO: This should also list auditors, but handling that is a bit messy
// right now because we are not guaranteed to have the data. (It should not
// include resigned auditors.)
return ($phid == $this->getAuthorPHID());
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorAuditEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorAuditTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
$xactions = $timeline->getTransactions();
$path_ids = array();
foreach ($xactions as $xaction) {
if ($xaction->hasComment()) {
$path_id = $xaction->getComment()->getPathID();
if ($path_id) {
$path_ids[] = $path_id;
}
}
}
$path_map = array();
if ($path_ids) {
$path_map = id(new DiffusionPathQuery())
->withPathIDs($path_ids)
->execute();
$path_map = ipull($path_map, 'path', 'id');
}
return $timeline->setPathMap($path_map);
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new DiffusionCommitFulltextEngine();
}
/* -( PhabricatorFerretInterface )----------------------------------------- */
public function newFerretEngine() {
return new DiffusionCommitFerretEngine();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('identifier')
->setType('string')
->setDescription(pht('The commit identifier.')),
);
}
public function getFieldValuesForConduit() {
// NOTE: This data should be similar to the information returned about
// commmits by "differential.diff.search" with the "commits" attachment.
return array(
'identifier' => $this->getCommitIdentifier(),
);
}
public function getConduitSearchAttachments() {
return array();
}
/* -( PhabricatorDraftInterface )------------------------------------------ */
public function newDraftEngine() {
return new DiffusionCommitDraftEngine();
}
public function getHasDraft(PhabricatorUser $viewer) {
return $this->assertAttachedKey($this->drafts, $viewer->getCacheFragment());
}
public function attachHasDraft(PhabricatorUser $viewer, $has_draft) {
$this->drafts[$viewer->getCacheFragment()] = $has_draft;
return $this;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 3, 6:52 PM (3 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166207
Default Alt Text
(76 KB)

Event Timeline