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; | |||||
| } | |||||
| } | } | ||||