Path: blob/master/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
12256 views
<?php12final class PhabricatorOwnersPackageQuery3extends PhabricatorCursorPagedPolicyAwareQuery {45private $ids;6private $phids;7private $ownerPHIDs;8private $authorityPHIDs;9private $repositoryPHIDs;10private $paths;11private $statuses;12private $authorityModes;1314private $controlMap = array();15private $controlResults;1617private $needPaths;181920/**21* Query owner PHIDs exactly. This does not expand authorities, so a user22* PHID will not match projects the user is a member of.23*/24public function withOwnerPHIDs(array $phids) {25$this->ownerPHIDs = $phids;26return $this;27}2829/**30* Query owner authority. This will expand authorities, so a user PHID will31* match both packages they own directly and packages owned by a project they32* are a member of.33*/34public function withAuthorityPHIDs(array $phids) {35$this->authorityPHIDs = $phids;36return $this;37}3839public function withPHIDs(array $phids) {40$this->phids = $phids;41return $this;42}4344public function withIDs(array $ids) {45$this->ids = $ids;46return $this;47}4849public function withRepositoryPHIDs(array $phids) {50$this->repositoryPHIDs = $phids;51return $this;52}5354public function withPaths(array $paths) {55$this->paths = $paths;56return $this;57}5859public function withStatuses(array $statuses) {60$this->statuses = $statuses;61return $this;62}6364public function withControl($repository_phid, array $paths) {65if (empty($this->controlMap[$repository_phid])) {66$this->controlMap[$repository_phid] = array();67}6869foreach ($paths as $path) {70$path = (string)$path;71$this->controlMap[$repository_phid][$path] = $path;72}7374// We need to load paths to execute control queries.75$this->needPaths = true;7677return $this;78}7980public function withAuthorityModes(array $modes) {81$this->authorityModes = $modes;82return $this;83}8485public function withNameNgrams($ngrams) {86return $this->withNgramsConstraint(87new PhabricatorOwnersPackageNameNgrams(),88$ngrams);89}9091public function needPaths($need_paths) {92$this->needPaths = $need_paths;93return $this;94}9596public function newResultObject() {97return new PhabricatorOwnersPackage();98}99100protected function willExecute() {101$this->controlResults = array();102}103104protected function willFilterPage(array $packages) {105$package_ids = mpull($packages, 'getID');106107$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(108'packageID IN (%Ld)',109$package_ids);110$owners = mgroup($owners, 'getPackageID');111foreach ($packages as $package) {112$package->attachOwners(idx($owners, $package->getID(), array()));113}114115return $packages;116}117118protected function didFilterPage(array $packages) {119$package_ids = mpull($packages, 'getID');120121if ($this->needPaths) {122$paths = id(new PhabricatorOwnersPath())->loadAllWhere(123'packageID IN (%Ld)',124$package_ids);125$paths = mgroup($paths, 'getPackageID');126127foreach ($packages as $package) {128$package->attachPaths(idx($paths, $package->getID(), array()));129}130}131132if ($this->controlMap) {133foreach ($packages as $package) {134// If this package is archived, it's no longer a controlling package135// for any path. In particular, it can not force active packages with136// weak dominion to give up control.137if ($package->isArchived()) {138continue;139}140141$this->controlResults[$package->getID()] = $package;142}143}144145return $packages;146}147148protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {149$joins = parent::buildJoinClauseParts($conn);150151if ($this->shouldJoinOwnersTable()) {152$joins[] = qsprintf(153$conn,154'JOIN %T o ON o.packageID = p.id',155id(new PhabricatorOwnersOwner())->getTableName());156}157158if ($this->shouldJoinPathTable()) {159$joins[] = qsprintf(160$conn,161'JOIN %T rpath ON rpath.packageID = p.id',162id(new PhabricatorOwnersPath())->getTableName());163}164165return $joins;166}167168protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {169$where = parent::buildWhereClauseParts($conn);170171if ($this->phids !== null) {172$where[] = qsprintf(173$conn,174'p.phid IN (%Ls)',175$this->phids);176}177178if ($this->ids !== null) {179$where[] = qsprintf(180$conn,181'p.id IN (%Ld)',182$this->ids);183}184185if ($this->repositoryPHIDs !== null) {186$where[] = qsprintf(187$conn,188'rpath.repositoryPHID IN (%Ls)',189$this->repositoryPHIDs);190}191192if ($this->authorityPHIDs !== null) {193$authority_phids = $this->expandAuthority($this->authorityPHIDs);194$where[] = qsprintf(195$conn,196'o.userPHID IN (%Ls)',197$authority_phids);198}199200if ($this->ownerPHIDs !== null) {201$where[] = qsprintf(202$conn,203'o.userPHID IN (%Ls)',204$this->ownerPHIDs);205}206207if ($this->paths !== null) {208$where[] = qsprintf(209$conn,210'rpath.pathIndex IN (%Ls)',211$this->getFragmentIndexesForPaths($this->paths));212}213214if ($this->statuses !== null) {215$where[] = qsprintf(216$conn,217'p.status IN (%Ls)',218$this->statuses);219}220221if ($this->controlMap) {222$clauses = array();223foreach ($this->controlMap as $repository_phid => $paths) {224$indexes = $this->getFragmentIndexesForPaths($paths);225226$clauses[] = qsprintf(227$conn,228'(rpath.repositoryPHID = %s AND rpath.pathIndex IN (%Ls))',229$repository_phid,230$indexes);231}232$where[] = qsprintf($conn, '%LO', $clauses);233}234235if ($this->authorityModes !== null) {236$where[] = qsprintf(237$conn,238'authorityMode IN (%Ls)',239$this->authorityModes);240}241242return $where;243}244245protected function shouldGroupQueryResultRows() {246if ($this->shouldJoinOwnersTable()) {247return true;248}249250if ($this->shouldJoinPathTable()) {251return true;252}253254return parent::shouldGroupQueryResultRows();255}256257public function getBuiltinOrders() {258return array(259'name' => array(260'vector' => array('name'),261'name' => pht('Name'),262),263) + parent::getBuiltinOrders();264}265266public function getOrderableColumns() {267return parent::getOrderableColumns() + array(268'name' => array(269'table' => $this->getPrimaryTableAlias(),270'column' => 'name',271'type' => 'string',272'unique' => true,273'reverse' => true,274),275);276}277278protected function newPagingMapFromPartialObject($object) {279return array(280'id' => (int)$object->getID(),281'name' => $object->getName(),282);283}284285public function getQueryApplicationClass() {286return 'PhabricatorOwnersApplication';287}288289protected function getPrimaryTableAlias() {290return 'p';291}292293private function shouldJoinOwnersTable() {294if ($this->ownerPHIDs !== null) {295return true;296}297298if ($this->authorityPHIDs !== null) {299return true;300}301302return false;303}304305private function shouldJoinPathTable() {306if ($this->repositoryPHIDs !== null) {307return true;308}309310if ($this->paths !== null) {311return true;312}313314if ($this->controlMap) {315return true;316}317318return false;319}320321private function expandAuthority(array $phids) {322$projects = id(new PhabricatorProjectQuery())323->setViewer($this->getViewer())324->withMemberPHIDs($phids)325->execute();326$project_phids = mpull($projects, 'getPHID');327328return array_fuse($phids) + array_fuse($project_phids);329}330331private function getFragmentsForPaths(array $paths) {332$fragments = array();333334foreach ($paths as $path) {335foreach (PhabricatorOwnersPackage::splitPath($path) as $fragment) {336$fragments[$fragment] = $fragment;337}338}339340return $fragments;341}342343private function getFragmentIndexesForPaths(array $paths) {344$indexes = array();345346foreach ($this->getFragmentsForPaths($paths) as $fragment) {347$indexes[] = PhabricatorHash::digestForIndex($fragment);348}349350return $indexes;351}352353354/* -( Path Control )------------------------------------------------------- */355356357/**358* Get a list of all packages which control a path or its parent directories,359* ordered from weakest to strongest.360*361* The first package has the most specific claim on the path; the last362* package has the most general claim. Multiple packages may have claims of363* equal strength, so this ordering is primarily one of usability and364* convenience.365*366* @return list<PhabricatorOwnersPackage> List of controlling packages.367*/368public function getControllingPackagesForPath(369$repository_phid,370$path,371$ignore_dominion = false) {372$path = (string)$path;373374if (!isset($this->controlMap[$repository_phid][$path])) {375throw new PhutilInvalidStateException('withControl');376}377378if ($this->controlResults === null) {379throw new PhutilInvalidStateException('execute');380}381382$packages = $this->controlResults;383$weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK;384385$path_fragments = PhabricatorOwnersPackage::splitPath($path);386$fragment_count = count($path_fragments);387388$matches = array();389foreach ($packages as $package_id => $package) {390$best_match = null;391$include = false;392393$repository_paths = $package->getPathsForRepository($repository_phid);394foreach ($repository_paths as $package_path) {395$strength = $package_path->getPathMatchStrength(396$path_fragments,397$fragment_count);398if ($strength > $best_match) {399$best_match = $strength;400$include = !$package_path->getExcluded();401}402}403404if ($best_match && $include) {405if ($ignore_dominion) {406$is_weak = false;407} else {408$is_weak = ($package->getDominion() == $weak_dominion);409}410$matches[$package_id] = array(411'strength' => $best_match,412'weak' => $is_weak,413'package' => $package,414);415}416}417418// At each strength level, drop weak packages if there are also strong419// packages of the same strength.420$strength_map = igroup($matches, 'strength');421foreach ($strength_map as $strength => $package_list) {422$any_strong = false;423foreach ($package_list as $package_id => $package) {424if (!$package['weak']) {425$any_strong = true;426break;427}428}429if ($any_strong) {430foreach ($package_list as $package_id => $package) {431if ($package['weak']) {432unset($matches[$package_id]);433}434}435}436}437438$matches = isort($matches, 'strength');439$matches = array_reverse($matches);440441$strongest = null;442foreach ($matches as $package_id => $match) {443if ($strongest === null) {444$strongest = $match['strength'];445}446447if ($match['strength'] === $strongest) {448continue;449}450451if ($match['weak']) {452unset($matches[$package_id]);453}454}455456return array_values(ipull($matches, 'package'));457}458459}460461462