Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php
index c77e99d8c2..75d3e052e4 100644
--- a/src/applications/differential/query/DifferentialRevisionQuery.php
+++ b/src/applications/differential/query/DifferentialRevisionQuery.php
@@ -1,1137 +1,1091 @@
<?php
/**
* @task config Query Configuration
* @task exec Query Execution
* @task internal Internals
*/
final class DifferentialRevisionQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
- private $pathIDs = array();
-
private $authors = array();
private $draftAuthors = array();
private $ccs = array();
private $reviewers = array();
private $revIDs = array();
private $commitHashes = array();
private $phids = array();
private $responsibles = array();
private $branches = array();
private $repositoryPHIDs;
private $updatedEpochMin;
private $updatedEpochMax;
private $statuses;
private $isOpen;
private $createdEpochMin;
private $createdEpochMax;
private $noReviewers;
private $paths;
const ORDER_MODIFIED = 'order-modified';
const ORDER_CREATED = 'order-created';
private $needActiveDiffs = false;
private $needDiffIDs = false;
private $needCommitPHIDs = false;
private $needHashes = false;
private $needReviewers = false;
private $needReviewerAuthority;
private $needDrafts;
private $needFlags;
/* -( Query Configuration )------------------------------------------------ */
-
- /**
- * Filter results to revisions which affect a Diffusion path ID in a given
- * repository. You can call this multiple times to select revisions for
- * several paths.
- *
- * @param int Diffusion repository ID.
- * @param int Diffusion path ID.
- * @return this
- * @task config
- */
- public function withPath($repository_id, $path_id) {
- $this->pathIDs[] = array(
- 'repositoryID' => $repository_id,
- 'pathID' => $path_id,
- );
- return $this;
- }
-
/**
* Find revisions affecting one or more items in a list of paths.
*
* @param list<string> List of file paths.
* @return this
* @task config
*/
public function withPaths(array $paths) {
$this->paths = $paths;
return $this;
}
/**
* Filter results to revisions authored by one of the given PHIDs. Calling
* this function will clear anything set by previous calls to
* @{method:withAuthors}.
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public function withAuthors(array $author_phids) {
$this->authors = $author_phids;
return $this;
}
/**
* Filter results to revisions which CC one of the listed people. Calling this
* function will clear anything set by previous calls to @{method:withCCs}.
*
* @param array List of PHIDs of subscribers.
* @return this
* @task config
*/
public function withCCs(array $cc_phids) {
$this->ccs = $cc_phids;
return $this;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* reviewers. Calling this function will clear anything set by previous calls
* to @{method:withReviewers}.
*
* @param array List of PHIDs of reviewers
* @return this
* @task config
*/
public function withReviewers(array $reviewer_phids) {
if ($reviewer_phids === array()) {
throw new Exception(
pht(
'Empty "withReviewers()" constraint is invalid. Provide one or '.
'more values, or remove the constraint.'));
}
$with_none = false;
foreach ($reviewer_phids as $key => $phid) {
switch ($phid) {
case DifferentialNoReviewersDatasource::FUNCTION_TOKEN:
$with_none = true;
unset($reviewer_phids[$key]);
break;
default:
break;
}
}
$this->noReviewers = $with_none;
if ($reviewer_phids) {
$this->reviewers = array_values($reviewer_phids);
}
return $this;
}
/**
* Filter results to revisions that have one of the provided commit hashes.
* Calling this function will clear anything set by previous calls to
* @{method:withCommitHashes}.
*
* @param array List of pairs <Class
* ArcanistDifferentialRevisionHash::HASH_$type constant,
* hash>
* @return this
* @task config
*/
public function withCommitHashes(array $commit_hashes) {
$this->commitHashes = $commit_hashes;
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withIsOpen($is_open) {
$this->isOpen = $is_open;
return $this;
}
/**
* Filter results to revisions on given branches.
*
* @param list List of branch names.
* @return this
* @task config
*/
public function withBranches(array $branches) {
$this->branches = $branches;
return $this;
}
/**
* Filter results to only return revisions whose ids are in the given set.
*
* @param array List of revision ids
* @return this
* @task config
*/
public function withIDs(array $ids) {
$this->revIDs = $ids;
return $this;
}
/**
* Filter results to only return revisions whose PHIDs are in the given set.
*
* @param array List of revision PHIDs
* @return this
* @task config
*/
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
/**
* Given a set of users, filter results to return only revisions they are
* responsible for (i.e., they are either authors or reviewers).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public function withResponsibleUsers(array $responsible_phids) {
$this->responsibles = $responsible_phids;
return $this;
}
public function withRepositoryPHIDs(array $repository_phids) {
$this->repositoryPHIDs = $repository_phids;
return $this;
}
public function withUpdatedEpochBetween($min, $max) {
$this->updatedEpochMin = $min;
$this->updatedEpochMax = $max;
return $this;
}
public function withCreatedEpochBetween($min, $max) {
$this->createdEpochMin = $min;
$this->createdEpochMax = $max;
return $this;
}
/**
* Set whether or not the query should load the active diff for each
* revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needActiveDiffs($need_active_diffs) {
$this->needActiveDiffs = $need_active_diffs;
return $this;
}
/**
* Set whether or not the query should load the associated commit PHIDs for
* each revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public function needCommitPHIDs($need_commit_phids) {
$this->needCommitPHIDs = $need_commit_phids;
return $this;
}
/**
* Set whether or not the query should load associated diff IDs for each
* revision.
*
* @param bool True to load and attach diff IDs.
* @return this
* @task config
*/
public function needDiffIDs($need_diff_ids) {
$this->needDiffIDs = $need_diff_ids;
return $this;
}
/**
* Set whether or not the query should load associated commit hashes for each
* revision.
*
* @param bool True to load and attach commit hashes.
* @return this
* @task config
*/
public function needHashes($need_hashes) {
$this->needHashes = $need_hashes;
return $this;
}
/**
* Set whether or not the query should load associated reviewers.
*
* @param bool True to load and attach reviewers.
* @return this
* @task config
*/
public function needReviewers($need_reviewers) {
$this->needReviewers = $need_reviewers;
return $this;
}
/**
* Request information about the viewer's authority to act on behalf of each
* reviewer. In particular, they have authority to act on behalf of projects
* they are a member of.
*
* @param bool True to load and attach authority.
* @return this
* @task config
*/
public function needReviewerAuthority($need_reviewer_authority) {
$this->needReviewerAuthority = $need_reviewer_authority;
return $this;
}
public function needFlags($need_flags) {
$this->needFlags = $need_flags;
return $this;
}
public function needDrafts($need_drafts) {
$this->needDrafts = $need_drafts;
return $this;
}
/* -( Query Execution )---------------------------------------------------- */
public function newResultObject() {
return new DifferentialRevision();
}
/**
* Execute the query as configured, returning matching
* @{class:DifferentialRevision} objects.
*
* @return list List of matching DifferentialRevision objects.
* @task exec
*/
protected function loadPage() {
$data = $this->loadData();
$data = $this->didLoadRawRows($data);
$table = $this->newResultObject();
return $table->loadAllFromArray($data);
}
protected function willFilterPage(array $revisions) {
$viewer = $this->getViewer();
$repository_phids = mpull($revisions, 'getRepositoryPHID');
$repository_phids = array_filter($repository_phids);
$repositories = array();
if ($repository_phids) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($this->getViewer())
->withPHIDs($repository_phids)
->execute();
$repositories = mpull($repositories, null, 'getPHID');
}
// If a revision is associated with a repository:
//
// - the viewer must be able to see the repository; or
// - the viewer must have an automatic view capability.
//
// In the latter case, we'll load the revision but not load the repository.
$can_view = PhabricatorPolicyCapability::CAN_VIEW;
foreach ($revisions as $key => $revision) {
$repo_phid = $revision->getRepositoryPHID();
if (!$repo_phid) {
// The revision has no associated repository. Attach `null` and move on.
$revision->attachRepository(null);
continue;
}
$repository = idx($repositories, $repo_phid);
if ($repository) {
// The revision has an associated repository, and the viewer can see
// it. Attach it and move on.
$revision->attachRepository($repository);
continue;
}
if ($revision->hasAutomaticCapability($can_view, $viewer)) {
// The revision has an associated repository which the viewer can not
// see, but the viewer has an automatic capability on this revision.
// Load the revision without attaching a repository.
$revision->attachRepository(null);
continue;
}
if ($this->getViewer()->isOmnipotent()) {
// The viewer is omnipotent. Allow the revision to load even without
// a repository.
$revision->attachRepository(null);
continue;
}
// The revision has an associated repository, and the viewer can't see
// it, and the viewer has no special capabilities. Filter out this
// revision.
$this->didRejectResult($revision);
unset($revisions[$key]);
}
if (!$revisions) {
return array();
}
$table = new DifferentialRevision();
$conn_r = $table->establishConnection('r');
if ($this->needCommitPHIDs) {
$this->loadCommitPHIDs($revisions);
}
$need_active = $this->needActiveDiffs;
$need_ids = $need_active || $this->needDiffIDs;
if ($need_ids) {
$this->loadDiffIDs($conn_r, $revisions);
}
if ($need_active) {
$this->loadActiveDiffs($conn_r, $revisions);
}
if ($this->needHashes) {
$this->loadHashes($conn_r, $revisions);
}
if ($this->needReviewers || $this->needReviewerAuthority) {
$this->loadReviewers($conn_r, $revisions);
}
return $revisions;
}
protected function didFilterPage(array $revisions) {
$viewer = $this->getViewer();
if ($this->needFlags) {
$flags = id(new PhabricatorFlagQuery())
->setViewer($viewer)
->withOwnerPHIDs(array($viewer->getPHID()))
->withObjectPHIDs(mpull($revisions, 'getPHID'))
->execute();
$flags = mpull($flags, null, 'getObjectPHID');
foreach ($revisions as $revision) {
$revision->attachFlag(
$viewer,
idx($flags, $revision->getPHID()));
}
}
if ($this->needDrafts) {
PhabricatorDraftEngine::attachDrafts(
$viewer,
$revisions);
}
return $revisions;
}
private function loadData() {
$table = $this->newResultObject();
$conn = $table->establishConnection('r');
$selects = array();
// NOTE: If the query includes "responsiblePHIDs", we execute it as a
// UNION of revisions they own and revisions they're reviewing. This has
// much better performance than doing it with JOIN/WHERE.
if ($this->responsibles) {
$basic_authors = $this->authors;
$basic_reviewers = $this->reviewers;
try {
// Build the query where the responsible users are authors.
$this->authors = array_merge($basic_authors, $this->responsibles);
$this->reviewers = $basic_reviewers;
$selects[] = $this->buildSelectStatement($conn);
// Build the query where the responsible users are reviewers, or
// projects they are members of are reviewers.
$this->authors = $basic_authors;
$this->reviewers = array_merge($basic_reviewers, $this->responsibles);
$selects[] = $this->buildSelectStatement($conn);
// Put everything back like it was.
$this->authors = $basic_authors;
$this->reviewers = $basic_reviewers;
} catch (Exception $ex) {
$this->authors = $basic_authors;
$this->reviewers = $basic_reviewers;
throw $ex;
}
} else {
$selects[] = $this->buildSelectStatement($conn);
}
if (count($selects) > 1) {
$unions = null;
foreach ($selects as $select) {
if (!$unions) {
$unions = $select;
continue;
}
$unions = qsprintf(
$conn,
'%Q UNION DISTINCT %Q',
$unions,
$select);
}
$query = qsprintf(
$conn,
'%Q %Q %Q',
$unions,
$this->buildOrderClause($conn, true),
$this->buildLimitClause($conn));
} else {
$query = head($selects);
}
return queryfx_all($conn, '%Q', $query);
}
private function buildSelectStatement(AphrontDatabaseConnection $conn_r) {
$table = new DifferentialRevision();
$select = $this->buildSelectClause($conn_r);
$from = qsprintf(
$conn_r,
'FROM %T r',
$table->getTableName());
$joins = $this->buildJoinsClause($conn_r);
$where = $this->buildWhereClause($conn_r);
$group_by = $this->buildGroupClause($conn_r);
$having = $this->buildHavingClause($conn_r);
$order_by = $this->buildOrderClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
return qsprintf(
$conn_r,
'(%Q %Q %Q %Q %Q %Q %Q %Q)',
$select,
$from,
$joins,
$where,
$group_by,
$having,
$order_by,
$limit);
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
private function buildJoinsClause(AphrontDatabaseConnection $conn) {
$joins = array();
- if ($this->pathIDs) {
- $path_table = new DifferentialAffectedPath();
- $joins[] = qsprintf(
- $conn,
- 'JOIN %T p ON p.revisionID = r.id',
- $path_table->getTableName());
- }
if ($this->paths) {
$path_table = new DifferentialAffectedPath();
$joins[] = qsprintf(
$conn,
'JOIN %R paths ON paths.revisionID = r.id',
$path_table);
}
if ($this->commitHashes) {
$joins[] = qsprintf(
$conn,
'JOIN %T hash_rel ON hash_rel.revisionID = r.id',
ArcanistDifferentialRevisionHash::TABLE_NAME);
}
if ($this->ccs) {
$joins[] = qsprintf(
$conn,
'JOIN %T e_ccs ON e_ccs.src = r.phid '.
'AND e_ccs.type = %s '.
'AND e_ccs.dst in (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorObjectHasSubscriberEdgeType::EDGECONST,
$this->ccs);
}
if ($this->reviewers) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T reviewer ON reviewer.revisionPHID = r.phid
AND reviewer.reviewerStatus != %s
AND reviewer.reviewerPHID in (%Ls)',
id(new DifferentialReviewer())->getTableName(),
DifferentialReviewerStatus::STATUS_RESIGNED,
$this->reviewers);
}
if ($this->noReviewers) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T no_reviewer ON no_reviewer.revisionPHID = r.phid
AND no_reviewer.reviewerStatus != %s',
id(new DifferentialReviewer())->getTableName(),
DifferentialReviewerStatus::STATUS_RESIGNED);
}
if ($this->draftAuthors) {
$joins[] = qsprintf(
$conn,
'JOIN %T has_draft ON has_draft.srcPHID = r.phid
AND has_draft.type = %s
AND has_draft.dstPHID IN (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorObjectHasDraftEdgeType::EDGECONST,
$this->draftAuthors);
}
$joins[] = $this->buildJoinClauseParts($conn);
return $this->formatJoinClause($conn, $joins);
}
/**
* @task internal
*/
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
$viewer = $this->getViewer();
$where = array();
- if ($this->pathIDs) {
- $path_clauses = array();
- $repo_info = igroup($this->pathIDs, 'repositoryID');
- foreach ($repo_info as $repository_id => $paths) {
- $path_clauses[] = qsprintf(
- $conn,
- '(p.repositoryID = %d AND p.pathID IN (%Ld))',
- $repository_id,
- ipull($paths, 'pathID'));
- }
- $path_clauses = qsprintf($conn, '%LO', $path_clauses);
- $where[] = $path_clauses;
- }
-
if ($this->paths !== null) {
$paths = $this->paths;
$path_map = id(new DiffusionPathIDQuery($paths))
->loadPathIDs();
if (!$path_map) {
// If none of the paths have entries in the PathID table, we can not
// possibly find any revisions affecting them.
throw new PhabricatorEmptyQueryException();
}
$where[] = qsprintf(
$conn,
'paths.pathID IN (%Ld)',
array_fuse($path_map));
// If we have repository PHIDs, additionally constrain this query to
// try to help MySQL execute it efficiently.
if ($this->repositoryPHIDs !== null) {
$repositories = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->setParentQuery($this)
->withPHIDs($this->repositoryPHIDs)
->execute();
if (!$repositories) {
throw new PhabricatorEmptyQueryException();
}
$repository_ids = mpull($repositories, 'getID');
$where[] = qsprintf(
$conn,
'paths.repositoryID IN (%Ld)',
$repository_ids);
}
}
if ($this->authors) {
$where[] = qsprintf(
$conn,
'r.authorPHID IN (%Ls)',
$this->authors);
}
if ($this->revIDs) {
$where[] = qsprintf(
$conn,
'r.id IN (%Ld)',
$this->revIDs);
}
if ($this->repositoryPHIDs) {
$where[] = qsprintf(
$conn,
'r.repositoryPHID IN (%Ls)',
$this->repositoryPHIDs);
}
if ($this->commitHashes) {
$hash_clauses = array();
foreach ($this->commitHashes as $info) {
list($type, $hash) = $info;
$hash_clauses[] = qsprintf(
$conn,
'(hash_rel.type = %s AND hash_rel.hash = %s)',
$type,
$hash);
}
$hash_clauses = qsprintf($conn, '%LO', $hash_clauses);
$where[] = $hash_clauses;
}
if ($this->phids) {
$where[] = qsprintf(
$conn,
'r.phid IN (%Ls)',
$this->phids);
}
if ($this->branches) {
$where[] = qsprintf(
$conn,
'r.branchName in (%Ls)',
$this->branches);
}
if ($this->updatedEpochMin !== null) {
$where[] = qsprintf(
$conn,
'r.dateModified >= %d',
$this->updatedEpochMin);
}
if ($this->updatedEpochMax !== null) {
$where[] = qsprintf(
$conn,
'r.dateModified <= %d',
$this->updatedEpochMax);
}
if ($this->createdEpochMin !== null) {
$where[] = qsprintf(
$conn,
'r.dateCreated >= %d',
$this->createdEpochMin);
}
if ($this->createdEpochMax !== null) {
$where[] = qsprintf(
$conn,
'r.dateCreated <= %d',
$this->createdEpochMax);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'r.status in (%Ls)',
$this->statuses);
}
if ($this->isOpen !== null) {
if ($this->isOpen) {
$statuses = DifferentialLegacyQuery::getModernValues(
DifferentialLegacyQuery::STATUS_OPEN);
} else {
$statuses = DifferentialLegacyQuery::getModernValues(
DifferentialLegacyQuery::STATUS_CLOSED);
}
$where[] = qsprintf(
$conn,
'r.status in (%Ls)',
$statuses);
}
$reviewer_subclauses = array();
if ($this->noReviewers) {
$reviewer_subclauses[] = qsprintf(
$conn,
'no_reviewer.reviewerPHID IS NULL');
}
if ($this->reviewers) {
$reviewer_subclauses[] = qsprintf(
$conn,
'reviewer.reviewerPHID IS NOT NULL');
}
if ($reviewer_subclauses) {
$where[] = qsprintf($conn, '%LO', $reviewer_subclauses);
}
$where[] = $this->buildWhereClauseParts($conn);
return $this->formatWhereClause($conn, $where);
}
/**
* @task internal
*/
protected function shouldGroupQueryResultRows() {
- if (count($this->pathIDs) > 1) {
- return true;
- }
-
if ($this->paths) {
// (If we have exactly one repository and exactly one path, we don't
// technically need to group, but it's simpler to always group.)
return true;
}
if (count($this->ccs) > 1) {
return true;
}
if (count($this->reviewers) > 1) {
return true;
}
if (count($this->commitHashes) > 1) {
return true;
}
if ($this->noReviewers) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
public function getBuiltinOrders() {
$orders = parent::getBuiltinOrders() + array(
'updated' => array(
'vector' => array('updated', 'id'),
'name' => pht('Date Updated (Latest First)'),
'aliases' => array(self::ORDER_MODIFIED),
),
'outdated' => array(
'vector' => array('-updated', '-id'),
'name' => pht('Date Updated (Oldest First)'),
),
);
// Alias the "newest" builtin to the historical key for it.
$orders['newest']['aliases'][] = self::ORDER_CREATED;
return $orders;
}
protected function getDefaultOrderVector() {
return array('updated', 'id');
}
public function getOrderableColumns() {
return array(
'updated' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'dateModified',
'type' => 'int',
),
) + parent::getOrderableColumns();
}
protected function newPagingMapFromPartialObject($object) {
return array(
'id' => (int)$object->getID(),
'updated' => (int)$object->getDateModified(),
);
}
private function loadCommitPHIDs(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
if (!$revisions) {
return;
}
$revisions = mpull($revisions, null, 'getPHID');
$edge_query = id(new PhabricatorEdgeQuery())
->withSourcePHIDs(array_keys($revisions))
->withEdgeTypes(
array(
DifferentialRevisionHasCommitEdgeType::EDGECONST,
));
$edge_query->execute();
foreach ($revisions as $phid => $revision) {
$commit_phids = $edge_query->getDestinationPHIDs(array($phid));
$revision->attachCommitPHIDs($commit_phids);
}
}
private function loadDiffIDs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$diff_ids = queryfx_all(
$conn_r,
'SELECT revisionID, id FROM %T WHERE revisionID IN (%Ld)
ORDER BY id DESC',
$diff_table->getTableName(),
mpull($revisions, 'getID'));
$diff_ids = igroup($diff_ids, 'revisionID');
foreach ($revisions as $revision) {
$ids = idx($diff_ids, $revision->getID(), array());
$ids = ipull($ids, 'id');
$revision->attachDiffIDs($ids);
}
}
private function loadActiveDiffs($conn_r, array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$diff_table = new DifferentialDiff();
$load_ids = array();
foreach ($revisions as $revision) {
$diffs = $revision->getDiffIDs();
if ($diffs) {
$load_ids[] = max($diffs);
}
}
$active_diffs = array();
if ($load_ids) {
$active_diffs = $diff_table->loadAllWhere(
'id IN (%Ld)',
$load_ids);
}
$active_diffs = mpull($active_diffs, null, 'getRevisionID');
foreach ($revisions as $revision) {
$revision->attachActiveDiff(idx($active_diffs, $revision->getID()));
}
}
private function loadHashes(
AphrontDatabaseConnection $conn_r,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE revisionID IN (%Ld)',
'differential_revisionhash',
mpull($revisions, 'getID'));
$data = igroup($data, 'revisionID');
foreach ($revisions as $revision) {
$hashes = idx($data, $revision->getID(), array());
$list = array();
foreach ($hashes as $hash) {
$list[] = array($hash['type'], $hash['hash']);
}
$revision->attachHashes($list);
}
}
private function loadReviewers(
AphrontDatabaseConnection $conn,
array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$reviewer_table = new DifferentialReviewer();
$reviewer_rows = queryfx_all(
$conn,
'SELECT * FROM %T WHERE revisionPHID IN (%Ls)
ORDER BY id ASC',
$reviewer_table->getTableName(),
mpull($revisions, 'getPHID'));
$reviewer_list = $reviewer_table->loadAllFromArray($reviewer_rows);
$reviewer_map = mgroup($reviewer_list, 'getRevisionPHID');
foreach ($reviewer_map as $key => $reviewers) {
$reviewer_map[$key] = mpull($reviewers, null, 'getReviewerPHID');
}
$viewer = $this->getViewer();
$viewer_phid = $viewer->getPHID();
$allow_key = 'differential.allow-self-accept';
$allow_self = PhabricatorEnv::getEnvConfig($allow_key);
// Figure out which of these reviewers the viewer has authority to act as.
if ($this->needReviewerAuthority && $viewer_phid) {
$authority = $this->loadReviewerAuthority(
$revisions,
$reviewer_map,
$allow_self);
}
foreach ($revisions as $revision) {
$reviewers = idx($reviewer_map, $revision->getPHID(), array());
foreach ($reviewers as $reviewer_phid => $reviewer) {
if ($this->needReviewerAuthority) {
if (!$viewer_phid) {
// Logged-out users never have authority.
$has_authority = false;
} else if ((!$allow_self) &&
($revision->getAuthorPHID() == $viewer_phid)) {
// The author can never have authority unless we allow self-accept.
$has_authority = false;
} else {
// Otherwise, look up whether the viewer has authority.
$has_authority = isset($authority[$reviewer_phid]);
}
$reviewer->attachAuthority($viewer, $has_authority);
}
$reviewers[$reviewer_phid] = $reviewer;
}
$revision->attachReviewers($reviewers);
}
}
private function loadReviewerAuthority(
array $revisions,
array $reviewers,
$allow_self) {
$revision_map = mpull($revisions, null, 'getPHID');
$viewer_phid = $this->getViewer()->getPHID();
// Find all the project/package reviewers which the user may have authority
// over.
$project_phids = array();
$package_phids = array();
$project_type = PhabricatorProjectProjectPHIDType::TYPECONST;
$package_type = PhabricatorOwnersPackagePHIDType::TYPECONST;
foreach ($reviewers as $revision_phid => $reviewer_list) {
if (!$allow_self) {
if ($revision_map[$revision_phid]->getAuthorPHID() == $viewer_phid) {
// If self-review isn't permitted, the user will never have
// authority over projects on revisions they authored because you
// can't accept your own revisions, so we don't need to load any
// data about these reviewers.
continue;
}
}
foreach ($reviewer_list as $reviewer_phid => $reviewer) {
$phid_type = phid_get_type($reviewer_phid);
if ($phid_type == $project_type) {
$project_phids[] = $reviewer_phid;
}
if ($phid_type == $package_type) {
$package_phids[] = $reviewer_phid;
}
}
}
// The viewer has authority over themselves.
$user_authority = array_fuse(array($viewer_phid));
// And over any projects they are a member of.
$project_authority = array();
if ($project_phids) {
$project_authority = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPHIDs($project_phids)
->withMemberPHIDs(array($viewer_phid))
->execute();
$project_authority = mpull($project_authority, 'getPHID');
$project_authority = array_fuse($project_authority);
}
// And over any packages they own.
$package_authority = array();
if ($package_phids) {
$package_authority = id(new PhabricatorOwnersPackageQuery())
->setViewer($this->getViewer())
->withPHIDs($package_phids)
->withAuthorityPHIDs(array($viewer_phid))
->execute();
$package_authority = mpull($package_authority, 'getPHID');
$package_authority = array_fuse($package_authority);
}
return $user_authority + $project_authority + $package_authority;
}
public function getQueryApplicationClass() {
return 'PhabricatorDifferentialApplication';
}
protected function getPrimaryTableAlias() {
return 'r';
}
}
diff --git a/src/applications/diffusion/controller/DiffusionBrowseController.php b/src/applications/diffusion/controller/DiffusionBrowseController.php
index 042c21d232..549c0d649f 100644
--- a/src/applications/diffusion/controller/DiffusionBrowseController.php
+++ b/src/applications/diffusion/controller/DiffusionBrowseController.php
@@ -1,1069 +1,1064 @@
<?php
final class DiffusionBrowseController extends DiffusionController {
private $lintCommit;
private $lintMessages;
private $corpusButtons = array();
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
$drequest = $this->getDiffusionRequest();
// Figure out if we're browsing a directory, a file, or a search result
// list.
$grep = $request->getStr('grep');
if (strlen($grep)) {
return $this->browseSearch();
}
$pager = id(new PHUIPagerView())
->readFromRequest($request);
$results = DiffusionBrowseResultSet::newFromConduit(
$this->callConduitWithDiffusionRequest(
'diffusion.browsequery',
array(
'path' => $drequest->getPath(),
'commit' => $drequest->getStableCommit(),
'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1,
)));
$reason = $results->getReasonForEmptyResultSet();
$is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);
if ($is_file) {
return $this->browseFile();
}
$paths = $results->getPaths();
$paths = $pager->sliceResults($paths);
$results->setPaths($paths);
return $this->browseDirectory($results, $pager);
}
private function browseSearch() {
$drequest = $this->getDiffusionRequest();
$header = $this->buildHeaderView($drequest);
$path = nonempty(basename($drequest->getPath()), '/');
$search_results = $this->renderSearchResults();
$search_form = $this->renderSearchForm($path);
$search_form = phutil_tag(
'div',
array(
'class' => 'diffusion-mobile-search-form',
),
$search_form);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$crumbs->setBorder(true);
$tabs = $this->buildTabsView('code');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setTabs($tabs)
->setFooter(
array(
$search_form,
$search_results,
));
return $this->newPage()
->setTitle(
array(
nonempty(basename($drequest->getPath()), '/'),
$drequest->getRepository()->getDisplayName(),
))
->setCrumbs($crumbs)
->appendChild($view);
}
private function browseFile() {
$viewer = $this->getViewer();
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$before = $request->getStr('before');
if ($before) {
return $this->buildBeforeResponse($before);
}
$path = $drequest->getPath();
$params = array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
);
$view = $request->getStr('view');
$byte_limit = null;
if ($view !== 'raw') {
$byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
$time_limit = 10;
$params += array(
'timeout' => $time_limit,
'byteLimit' => $byte_limit,
);
}
$response = $this->callConduitWithDiffusionRequest(
'diffusion.filecontentquery',
$params);
$hit_byte_limit = $response['tooHuge'];
$hit_time_limit = $response['tooSlow'];
$file_phid = $response['filePHID'];
$show_editor = false;
if ($hit_byte_limit) {
$corpus = $this->buildErrorCorpus(
pht(
'This file is larger than %s byte(s), and too large to display '.
'in the web UI.',
phutil_format_bytes($byte_limit)));
} else if ($hit_time_limit) {
$corpus = $this->buildErrorCorpus(
pht(
'This file took too long to load from the repository (more than '.
'%s second(s)).',
new PhutilNumber($time_limit)));
} else {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($file_phid))
->executeOne();
if (!$file) {
throw new Exception(pht('Failed to load content file!'));
}
if ($view === 'raw') {
return $file->getRedirectResponse();
}
$data = $file->loadFileData();
$lfs_ref = $this->getGitLFSRef($repository, $data);
if ($lfs_ref) {
if ($view == 'git-lfs') {
$file = $this->loadGitLFSFile($lfs_ref);
// Rename the file locally so we generate a better vanity URI for
// it. In storage, it just has a name like "lfs-13f9a94c0923...",
// since we don't get any hints about possible human-readable names
// at upload time.
$basename = basename($drequest->getPath());
$file->makeEphemeral();
$file->setName($basename);
return $file->getRedirectResponse();
}
$corpus = $this->buildGitLFSCorpus($lfs_ref);
} else {
$show_editor = true;
$ref = id(new PhabricatorDocumentRef())
->setFile($file);
$engine = id(new DiffusionDocumentRenderingEngine())
->setRequest($request)
->setDiffusionRequest($drequest);
$corpus = $engine->newDocumentView($ref);
$this->corpusButtons[] = $this->renderFileButton();
}
}
$bar = $this->buildButtonBar($drequest, $show_editor);
$header = $this->buildHeaderView($drequest);
$header->setHeaderIcon('fa-file-code-o');
$follow = $request->getStr('follow');
$follow_notice = null;
if ($follow) {
$follow_notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle(pht('Unable to Continue'));
switch ($follow) {
case 'first':
$follow_notice->appendChild(
pht(
'Unable to continue tracing the history of this file because '.
'this commit is the first commit in the repository.'));
break;
case 'created':
$follow_notice->appendChild(
pht(
'Unable to continue tracing the history of this file because '.
'this commit created the file.'));
break;
}
}
$renamed = $request->getStr('renamed');
$renamed_notice = null;
if ($renamed) {
$renamed_notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setTitle(pht('File Renamed'))
->appendChild(
pht(
'File history passes through a rename from "%s" to "%s".',
$drequest->getPath(),
$renamed));
}
$open_revisions = $this->buildOpenRevisions();
$owners_list = $this->buildOwnersList($drequest);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$crumbs->setBorder(true);
$basename = basename($this->getDiffusionRequest()->getPath());
$tabs = $this->buildTabsView('code');
$bar->setRight($this->corpusButtons);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setTabs($tabs)
->setFooter(array(
$bar,
$follow_notice,
$renamed_notice,
$corpus,
$open_revisions,
$owners_list,
));
$title = array($basename, $repository->getDisplayName());
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
public function browseDirectory(
DiffusionBrowseResultSet $results,
PHUIPagerView $pager) {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$reason = $results->getReasonForEmptyResultSet();
$this->buildActionButtons($drequest, true);
$details = $this->buildPropertyView($drequest);
$header = $this->buildHeaderView($drequest);
$header->setHeaderIcon('fa-folder-open');
$empty_result = null;
$browse_panel = null;
if (!$results->isValidResults()) {
$empty_result = new DiffusionEmptyResultView();
$empty_result->setDiffusionRequest($drequest);
$empty_result->setDiffusionBrowseResultSet($results);
$empty_result->setView($request->getStr('view'));
} else {
$phids = array();
foreach ($results->getPaths() as $result) {
$data = $result->getLastCommitData();
if ($data) {
if ($data->getCommitDetail('authorPHID')) {
$phids[$data->getCommitDetail('authorPHID')] = true;
}
}
}
$phids = array_keys($phids);
$handles = $this->loadViewerHandles($phids);
$browse_table = id(new DiffusionBrowseTableView())
->setDiffusionRequest($drequest)
->setHandles($handles)
->setPaths($results->getPaths())
->setUser($request->getUser());
$title = nonempty(basename($drequest->getPath()), '/');
$icon = 'fa-folder-open';
$browse_header = $this->buildPanelHeaderView($title, $icon);
$browse_panel = id(new PHUIObjectBoxView())
->setHeader($browse_header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($browse_table)
->addClass('diffusion-mobile-view')
->setPager($pager);
}
$open_revisions = $this->buildOpenRevisions();
$readme = $this->renderDirectoryReadme($results);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'browse',
));
$crumbs->setBorder(true);
$tabs = $this->buildTabsView('code');
$owners_list = $this->buildOwnersList($drequest);
$bar = id(new PHUILeftRightView())
->setRight($this->corpusButtons)
->addClass('diffusion-action-bar');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setTabs($tabs)
->setFooter(
array(
$bar,
$empty_result,
$browse_panel,
$open_revisions,
$owners_list,
$readme,
));
if ($details) {
$view->addPropertySection(pht('Details'), $details);
}
return $this->newPage()
->setTitle(array(
nonempty(basename($drequest->getPath()), '/'),
$repository->getDisplayName(),
))
->setCrumbs($crumbs)
->appendChild(
array(
$view,
));
}
private function renderSearchResults() {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$results = array();
$pager = id(new PHUIPagerView())
->readFromRequest($request);
$search_mode = null;
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$results = array();
break;
default:
if (strlen($this->getRequest()->getStr('grep'))) {
$search_mode = 'grep';
$query_string = $request->getStr('grep');
$results = $this->callConduitWithDiffusionRequest(
'diffusion.searchquery',
array(
'grep' => $query_string,
'commit' => $drequest->getStableCommit(),
'path' => $drequest->getPath(),
'limit' => $pager->getPageSize() + 1,
'offset' => $pager->getOffset(),
));
}
break;
}
$results = $pager->sliceResults($results);
$table = null;
$header = null;
if ($search_mode == 'grep') {
$table = $this->renderGrepResults($results, $query_string);
$title = pht(
'File content matching "%s" under "%s"',
$query_string,
nonempty($drequest->getPath(), '/'));
$header = id(new PHUIHeaderView())
->setHeader($title)
->addClass('diffusion-search-result-header');
}
return array($header, $table, $pager);
}
private function renderGrepResults(array $results, $pattern) {
$drequest = $this->getDiffusionRequest();
require_celerity_resource('phabricator-search-results-css');
if (!$results) {
return id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
->appendChild(
pht(
'The pattern you searched for was not found in the content of any '.
'files.'));
}
$grouped = array();
foreach ($results as $file) {
list($path, $line, $string) = $file;
$grouped[$path][] = array($line, $string);
}
$view = array();
foreach ($grouped as $path => $matches) {
$view[] = id(new DiffusionPatternSearchView())
->setPath($path)
->setMatches($matches)
->setPattern($pattern)
->setDiffusionRequest($drequest)
->render();
}
return $view;
}
private function buildButtonBar(
DiffusionRequest $drequest,
$show_editor) {
$viewer = $this->getViewer();
$base_uri = $this->getRequest()->getRequestURI();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
$line = nonempty((int)$drequest->getLine(), 1);
$buttons = array();
$editor_uri = null;
$editor_template = null;
$link_engine = PhabricatorEditorURIEngine::newForViewer($viewer);
if ($link_engine) {
$link_engine->setRepository($repository);
$editor_uri = $link_engine->getURIForPath($path, $line);
$editor_template = $link_engine->getURITokensForPath($path);
}
$buttons[] =
id(new PHUIButtonView())
->setTag('a')
->setText(pht('Last Change'))
->setColor(PHUIButtonView::GREY)
->setHref(
$drequest->generateURI(
array(
'action' => 'change',
)))
->setIcon('fa-backward');
if ($editor_uri) {
$buttons[] =
id(new PHUIButtonView())
->setTag('a')
->setText(pht('Open File'))
->setHref($editor_uri)
->setIcon('fa-pencil')
->setID('editor_link')
->setMetadata(array('template' => $editor_template))
->setDisabled(!$editor_uri)
->setColor(PHUIButtonView::GREY);
}
$bar = id(new PHUILeftRightView())
->setLeft($buttons)
->addClass('diffusion-action-bar full-mobile-buttons');
return $bar;
}
private function buildOwnersList(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorOwnersApplication',
$viewer);
if (!$have_owners) {
return null;
}
$repository = $drequest->getRepository();
$package_query = id(new PhabricatorOwnersPackageQuery())
->setViewer($viewer)
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
->withControl(
$repository->getPHID(),
array(
$drequest->getPath(),
));
$package_query->execute();
$packages = $package_query->getControllingPackagesForPath(
$repository->getPHID(),
$drequest->getPath());
$ownership = id(new PHUIObjectItemListView())
->setUser($viewer)
->setNoDataString(pht('No Owners'));
if ($packages) {
foreach ($packages as $package) {
$item = id(new PHUIObjectItemView())
->setObject($package)
->setObjectName($package->getMonogram())
->setHeader($package->getName())
->setHref($package->getURI());
$owners = $package->getOwners();
if ($owners) {
$owner_list = $viewer->renderHandleList(
mpull($owners, 'getUserPHID'));
} else {
$owner_list = phutil_tag('em', array(), pht('None'));
}
$item->addAttribute(pht('Owners: %s', $owner_list));
$auto = $package->getAutoReview();
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
$spec = idx($autoreview_map, $auto, array());
$name = idx($spec, 'name', $auto);
$item->addIcon('fa-code', $name);
$rule = $package->newAuditingRule();
$item->addIcon($rule->getIconIcon(), $rule->getDisplayName());
if ($package->isArchived()) {
$item->setDisabled(true);
}
$ownership->addItem($item);
}
}
$view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Owner Packages'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view')
->setObjectList($ownership);
return $view;
}
private function renderFileButton($file_uri = null, $label = null) {
$base_uri = $this->getRequest()->getRequestURI();
if ($file_uri) {
$text = pht('Download File');
$href = $file_uri;
$icon = 'fa-download';
} else {
$text = pht('Raw File');
$href = $base_uri->alter('view', 'raw');
$icon = 'fa-file-text';
}
if ($label !== null) {
$text = $label;
}
$button = id(new PHUIButtonView())
->setTag('a')
->setText($text)
->setHref($href)
->setIcon($icon)
->setColor(PHUIButtonView::GREY);
return $button;
}
private function renderGitLFSButton() {
$viewer = $this->getViewer();
$uri = $this->getRequest()->getRequestURI();
$href = $uri->alter('view', 'git-lfs');
$text = pht('Download from Git LFS');
$icon = 'fa-download';
return id(new PHUIButtonView())
->setTag('a')
->setText($text)
->setHref($href)
->setIcon($icon)
->setColor(PHUIButtonView::GREY);
}
private function buildErrorCorpus($message) {
$text = id(new PHUIBoxView())
->addPadding(PHUI::PADDING_LARGE)
->appendChild($message);
$header = id(new PHUIHeaderView())
->setHeader(pht('Details'));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($text);
return $box;
}
private function buildBeforeResponse($before) {
$request = $this->getRequest();
$drequest = $this->getDiffusionRequest();
// NOTE: We need to get the grandparent so we can capture filename changes
// in the parent.
$parent = $this->loadParentCommitOf($before);
$old_filename = null;
$was_created = false;
if ($parent) {
$grandparent = $this->loadParentCommitOf($parent);
if ($grandparent) {
$rename_query = new DiffusionRenameHistoryQuery();
$rename_query->setRequest($drequest);
$rename_query->setOldCommit($grandparent);
$rename_query->setViewer($request->getUser());
$old_filename = $rename_query->loadOldFilename();
$was_created = $rename_query->getWasCreated();
}
}
$follow = null;
if ($was_created) {
// If the file was created in history, that means older commits won't
// have it. Since we know it existed at 'before', it must have been
// created then; jump there.
$target_commit = $before;
$follow = 'created';
} else if ($parent) {
// If we found a parent, jump to it. This is the normal case.
$target_commit = $parent;
} else {
// If there's no parent, this was probably created in the initial commit?
// And the "was_created" check will fail because we can't identify the
// grandparent. Keep the user at 'before'.
$target_commit = $before;
$follow = 'first';
}
$path = $drequest->getPath();
$renamed = null;
if ($old_filename !== null &&
$old_filename !== '/'.$path) {
$renamed = $path;
$path = $old_filename;
}
$line = null;
// If there's a follow error, drop the line so the user sees the message.
if (!$follow) {
$line = $this->getBeforeLineNumber($target_commit);
}
$before_uri = $drequest->generateURI(
array(
'action' => 'browse',
'commit' => $target_commit,
'line' => $line,
'path' => $path,
));
if ($renamed === null) {
$before_uri->removeQueryParam('renamed');
} else {
$before_uri->replaceQueryParam('renamed', $renamed);
}
if ($follow === null) {
$before_uri->removeQueryParam('follow');
} else {
$before_uri->replaceQueryParam('follow', $follow);
}
return id(new AphrontRedirectResponse())->setURI($before_uri);
}
private function getBeforeLineNumber($target_commit) {
$drequest = $this->getDiffusionRequest();
$viewer = $this->getViewer();
$line = $drequest->getLine();
if (!$line) {
return null;
}
$diff_info = $this->callConduitWithDiffusionRequest(
'diffusion.rawdiffquery',
array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
'againstCommit' => $target_commit,
));
$file_phid = $diff_info['filePHID'];
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($file_phid))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'Failed to load file ("%s") returned by "%s".',
$file_phid,
'diffusion.rawdiffquery.'));
}
$raw_diff = $file->loadFileData();
$old_line = 0;
$new_line = 0;
foreach (explode("\n", $raw_diff) as $text) {
if ($text[0] == '-' || $text[0] == ' ') {
$old_line++;
}
if ($text[0] == '+' || $text[0] == ' ') {
$new_line++;
}
if ($new_line == $line) {
return $old_line;
}
}
// We didn't find the target line.
return $line;
}
private function loadParentCommitOf($commit) {
$drequest = $this->getDiffusionRequest();
$user = $this->getRequest()->getUser();
$before_req = DiffusionRequest::newFromDictionary(
array(
'user' => $user,
'repository' => $drequest->getRepository(),
'commit' => $commit,
));
$parents = DiffusionQuery::callConduitWithDiffusionRequest(
$user,
$before_req,
'diffusion.commitparentsquery',
array(
'commit' => $commit,
));
return head($parents);
}
protected function markupText($text) {
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
$engine->setConfig('viewer', $this->getRequest()->getUser());
$text = $engine->markupText($text);
$text = phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$text);
return $text;
}
protected function buildHeaderView(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$repository = $drequest->getRepository();
$commit_tag = $this->renderCommitHashTag($drequest);
$path = nonempty($drequest->getPath(), '/');
$search = $this->renderSearchForm($path);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($this->renderPathLinks($drequest, $mode = 'browse'))
->addActionItem($search)
->addTag($commit_tag)
->addClass('diffusion-browse-header');
if (!$repository->isSVN()) {
$branch_tag = $this->renderBranchTag($drequest);
$header->addTag($branch_tag);
}
return $header;
}
protected function buildPanelHeaderView($title, $icon) {
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon($icon)
->addClass('diffusion-panel-header-view');
return $header;
}
protected function buildActionButtons(
DiffusionRequest $drequest,
$is_directory = false) {
$viewer = $this->getViewer();
$repository = $drequest->getRepository();
$history_uri = $drequest->generateURI(array('action' => 'history'));
$behind_head = $drequest->getSymbolicCommit();
$compare = null;
$head_uri = $drequest->generateURI(
array(
'commit' => '',
'action' => 'browse',
));
if ($repository->supportsBranchComparison() && $is_directory) {
$compare_uri = $drequest->generateURI(array('action' => 'compare'));
$compare = id(new PHUIButtonView())
->setText(pht('Compare'))
->setIcon('fa-code-fork')
->setWorkflow(true)
->setTag('a')
->setHref($compare_uri)
->setColor(PHUIButtonView::GREY);
$this->corpusButtons[] = $compare;
}
$head = null;
if ($behind_head) {
$head = id(new PHUIButtonView())
->setTag('a')
->setText(pht('Back to HEAD'))
->setHref($head_uri)
->setIcon('fa-home')
->setColor(PHUIButtonView::GREY);
$this->corpusButtons[] = $head;
}
$history = id(new PHUIButtonView())
->setText(pht('History'))
->setHref($history_uri)
->setTag('a')
->setIcon('fa-history')
->setColor(PHUIButtonView::GREY);
$this->corpusButtons[] = $history;
}
protected function buildPropertyView(
DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer);
if ($drequest->getSymbolicType() == 'tag') {
$symbolic = $drequest->getSymbolicCommit();
$view->addProperty(pht('Tag'), $symbolic);
$tags = $this->callConduitWithDiffusionRequest(
'diffusion.tagsquery',
array(
'names' => array($symbolic),
'needMessages' => true,
));
$tags = DiffusionRepositoryTag::newFromConduit($tags);
$tags = mpull($tags, null, 'getName');
$tag = idx($tags, $symbolic);
if ($tag && strlen($tag->getMessage())) {
$view->addSectionHeader(
pht('Tag Content'), 'fa-tag');
$view->addTextContent($this->markupText($tag->getMessage()));
}
}
if ($view->hasAnyProperties()) {
return $view;
}
return null;
}
private function buildOpenRevisions() {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$path = $drequest->getPath();
- $path_map = id(new DiffusionPathIDQuery(array($path)))->loadPathIDs();
- $path_id = idx($path_map, $path);
- if (!$path_id) {
- return null;
- }
-
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
$revisions = id(new DifferentialRevisionQuery())
->setViewer($viewer)
- ->withPath($repository->getID(), $path_id)
+ ->withPaths(array($path))
+ ->withRepositoryPHIDs(array($repository->getPHID()))
->withIsOpen(true)
->withUpdatedEpochBetween($recent, null)
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
->setLimit(10)
->needReviewers(true)
->needFlags(true)
->needDrafts(true)
->execute();
if (!$revisions) {
return null;
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Open Revisions'));
$list = id(new DifferentialRevisionListView())
->setViewer($viewer)
->setRevisions($revisions)
->setNoBox(true);
$view = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view')
->appendChild($list);
return $view;
}
private function getGitLFSRef(PhabricatorRepository $repository, $data) {
if (!$repository->canUseGitLFS()) {
return null;
}
$lfs_pattern = '(^version https://git-lfs\\.github\\.com/spec/v1[\r\n])';
if (!preg_match($lfs_pattern, $data)) {
return null;
}
$matches = null;
if (!preg_match('(^oid sha256:(.*)$)m', $data, $matches)) {
return null;
}
$hash = $matches[1];
$hash = trim($hash);
return id(new PhabricatorRepositoryGitLFSRefQuery())
->setViewer($this->getViewer())
->withRepositoryPHIDs(array($repository->getPHID()))
->withObjectHashes(array($hash))
->executeOne();
}
private function buildGitLFSCorpus(PhabricatorRepositoryGitLFSRef $ref) {
// TODO: We should probably test if we can load the file PHID here and
// show the user an error if we can't, rather than making them click
// through to hit an error.
$title = basename($this->getDiffusionRequest()->getPath());
$icon = 'fa-archive';
$drequest = $this->getDiffusionRequest();
$this->buildActionButtons($drequest);
$header = $this->buildPanelHeaderView($title, $icon);
$severity = PHUIInfoView::SEVERITY_NOTICE;
$messages = array();
$messages[] = pht(
'This %s file is stored in Git Large File Storage.',
phutil_format_bytes($ref->getByteSize()));
try {
$file = $this->loadGitLFSFile($ref);
$this->corpusButtons[] = $this->renderGitLFSButton();
} catch (Exception $ex) {
$severity = PHUIInfoView::SEVERITY_ERROR;
$messages[] = pht('The data for this file could not be loaded.');
}
$this->corpusButtons[] = $this->renderFileButton(
null, pht('View Raw LFS Pointer'));
$corpus = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view')
->setCollapsed(true);
if ($messages) {
$corpus->setInfoView(
id(new PHUIInfoView())
->setSeverity($severity)
->setErrors($messages));
}
return $corpus;
}
private function loadGitLFSFile(PhabricatorRepositoryGitLFSRef $ref) {
$viewer = $this->getViewer();
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($ref->getFilePHID()))
->executeOne();
if (!$file) {
throw new Exception(
pht(
'Failed to load file object for Git LFS ref "%s"!',
$ref->getObjectHash()));
}
return $file;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Nov 26, 7:38 PM (19 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
420149
Default Alt Text
(61 KB)

Event Timeline