Changeset View
Changeset View
Standalone View
Standalone View
src/applications/project/query/PhabricatorProjectQuery.php
Show First 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | protected function getPagingValueMap($cursor, array $keys) { | ||||
); | ); | ||||
} | } | ||||
protected function loadPage() { | protected function loadPage() { | ||||
return $this->loadStandardPage($this->newResultObject()); | return $this->loadStandardPage($this->newResultObject()); | ||||
} | } | ||||
protected function willFilterPage(array $projects) { | protected function willFilterPage(array $projects) { | ||||
$project_phids = array(); | |||||
$ancestor_paths = array(); | |||||
foreach ($projects as $project) { | |||||
$project_phids[] = $project->getPHID(); | |||||
foreach ($project->getAncestorProjectPaths() as $path) { | |||||
$ancestor_paths[$path] = $path; | |||||
} | |||||
} | |||||
if ($ancestor_paths) { | |||||
$ancestors = id(new PhabricatorProject())->loadAllWhere( | |||||
'projectPath IN (%Ls)', | |||||
$ancestor_paths); | |||||
} else { | |||||
$ancestors = array(); | |||||
} | |||||
$projects = $this->linkProjectGraph($projects, $ancestors); | |||||
$viewer_phid = $this->getViewer()->getPHID(); | $viewer_phid = $this->getViewer()->getPHID(); | ||||
$project_phids = mpull($projects, 'getPHID'); | $project_phids = mpull($projects, 'getPHID'); | ||||
$member_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; | $member_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; | ||||
$watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; | $watcher_type = PhabricatorObjectHasWatcherEdgeType::EDGECONST; | ||||
$types = array(); | $types = array(); | ||||
$types[] = $member_type; | $types[] = $member_type; | ||||
if ($this->needWatchers) { | if ($this->needWatchers) { | ||||
$types[] = $watcher_type; | $types[] = $watcher_type; | ||||
} | } | ||||
$edge_query = id(new PhabricatorEdgeQuery()) | $edge_query = id(new PhabricatorEdgeQuery()) | ||||
->withSourcePHIDs($project_phids) | ->withSourcePHIDs($project_phids) | ||||
->withEdgeTypes($types); | ->withEdgeTypes($types); | ||||
// If we only need to know if the viewer is a member, we can restrict | // If we only need to know if the viewer is a member, we can restrict | ||||
// the query to just their PHID. | // the query to just their PHID. | ||||
if (!$this->needMembers && !$this->needWatchers) { | if (!$this->needMembers && !$this->needWatchers) { | ||||
$edge_query->withDestinationPHIDs(array($viewer_phid)); | $edge_query->withDestinationPHIDs(array($viewer_phid)); | ||||
} | } | ||||
$edge_query->execute(); | $edge_query->execute(); | ||||
$membership_projects = array(); | |||||
foreach ($projects as $project) { | foreach ($projects as $project) { | ||||
$project_phid = $project->getPHID(); | $project_phid = $project->getPHID(); | ||||
$member_phids = $edge_query->getDestinationPHIDs( | $member_phids = $edge_query->getDestinationPHIDs( | ||||
array($project_phid), | array($project_phid), | ||||
array($member_type)); | array($member_type)); | ||||
$project->setIsUserMember( | if (in_array($viewer_phid, $member_phids)) { | ||||
$viewer_phid, | $membership_projects[$project_phid] = $project; | ||||
in_array($viewer_phid, $member_phids)); | } | ||||
if ($this->needMembers) { | if ($this->needMembers) { | ||||
$project->attachMemberPHIDs($member_phids); | $project->attachMemberPHIDs($member_phids); | ||||
} | } | ||||
if ($this->needWatchers) { | if ($this->needWatchers) { | ||||
$watcher_phids = $edge_query->getDestinationPHIDs( | $watcher_phids = $edge_query->getDestinationPHIDs( | ||||
array($project_phid), | array($project_phid), | ||||
array($watcher_type)); | array($watcher_type)); | ||||
$project->attachWatcherPHIDs($watcher_phids); | $project->attachWatcherPHIDs($watcher_phids); | ||||
$project->setIsUserWatcher( | $project->setIsUserWatcher( | ||||
$viewer_phid, | $viewer_phid, | ||||
in_array($viewer_phid, $watcher_phids)); | in_array($viewer_phid, $watcher_phids)); | ||||
} | } | ||||
} | } | ||||
$all_graph = $this->getAllReachableAncestors($projects); | |||||
$member_graph = $this->getAllReachableAncestors($membership_projects); | |||||
foreach ($all_graph as $phid => $project) { | |||||
$is_member = isset($member_graph[$phid]); | |||||
$project->setIsUserMember($viewer_phid, $is_member); | |||||
} | |||||
return $projects; | return $projects; | ||||
} | } | ||||
protected function didFilterPage(array $projects) { | protected function didFilterPage(array $projects) { | ||||
if ($this->needImages) { | if ($this->needImages) { | ||||
$default = null; | $default = null; | ||||
$file_phids = mpull($projects, 'getProfileImagePHID'); | $file_phids = mpull($projects, 'getProfileImagePHID'); | ||||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | final class PhabricatorProjectQuery | ||||
public function getQueryApplicationClass() { | public function getQueryApplicationClass() { | ||||
return 'PhabricatorProjectApplication'; | return 'PhabricatorProjectApplication'; | ||||
} | } | ||||
protected function getPrimaryTableAlias() { | protected function getPrimaryTableAlias() { | ||||
return 'p'; | return 'p'; | ||||
} | } | ||||
private function linkProjectGraph(array $projects, array $ancestors) { | |||||
$ancestor_map = mpull($ancestors, null, 'getPHID'); | |||||
$projects_map = mpull($projects, null, 'getPHID'); | |||||
$all_map = $projects_map + $ancestor_map; | |||||
$done = array(); | |||||
foreach ($projects as $key => $project) { | |||||
$seen = array($project->getPHID() => true); | |||||
if (!$this->linkProject($project, $all_map, $done, $seen)) { | |||||
$this->didRejectResult($project); | |||||
unset($projects[$key]); | |||||
continue; | |||||
} | |||||
foreach ($project->getAncestorProjects() as $ancestor) { | |||||
$seen[$ancestor->getPHID()] = true; | |||||
} | |||||
} | |||||
return $projects; | |||||
} | |||||
private function linkProject($project, array $all, array $done, array $seen) { | |||||
$parent_phid = $project->getParentProjectPHID(); | |||||
// This project has no parent, so just attach `null` and return. | |||||
if (!$parent_phid) { | |||||
$project->attachParentProject(null); | |||||
return true; | |||||
} | |||||
// This project has a parent, but it failed to load. | |||||
if (empty($all[$parent_phid])) { | |||||
return false; | |||||
} | |||||
// Test for graph cycles. If we encounter one, we're going to hide the | |||||
// entire cycle since we can't meaningfully resolve it. | |||||
if (isset($seen[$parent_phid])) { | |||||
return false; | |||||
} | |||||
$seen[$parent_phid] = true; | |||||
$parent = $all[$parent_phid]; | |||||
$project->attachParentProject($parent); | |||||
if (!empty($done[$parent_phid])) { | |||||
return true; | |||||
} | |||||
return $this->linkProject($parent, $all, $done, $seen); | |||||
} | |||||
private function getAllReachableAncestors(array $projects) { | |||||
$ancestors = array(); | |||||
$seen = mpull($projects, null, 'getPHID'); | |||||
$stack = $projects; | |||||
while ($stack) { | |||||
$project = array_pop($stack); | |||||
$phid = $project->getPHID(); | |||||
$ancestors[$phid] = $project; | |||||
$parent_phid = $project->getParentProjectPHID(); | |||||
if (!$parent_phid) { | |||||
continue; | |||||
} | |||||
if (isset($seen[$parent_phid])) { | |||||
continue; | |||||
} | |||||
$seen[$parent_phid] = true; | |||||
$stack[] = $project->getParentProject(); | |||||
} | |||||
return $ancestors; | |||||
} | |||||
} | } |