@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 463 lines 12 kB view raw
1<?php 2 3/** 4 * @extends PhabricatorCursorPagedPolicyAwareQuery<PhabricatorOwnersPackage> 5 */ 6final class PhabricatorOwnersPackageQuery 7 extends PhabricatorCursorPagedPolicyAwareQuery { 8 9 private $ids; 10 private $phids; 11 private $ownerPHIDs; 12 private $authorityPHIDs; 13 private $repositoryPHIDs; 14 private $paths; 15 private $statuses; 16 private $authorityModes; 17 18 private $controlMap = array(); 19 private $controlResults; 20 21 private $needPaths; 22 23 24 /** 25 * Query owner PHIDs exactly. This does not expand authorities, so a user 26 * PHID will not match projects the user is a member of. 27 */ 28 public function withOwnerPHIDs(array $phids) { 29 $this->ownerPHIDs = $phids; 30 return $this; 31 } 32 33 /** 34 * Query owner authority. This will expand authorities, so a user PHID will 35 * match both packages they own directly and packages owned by a project they 36 * are a member of. 37 */ 38 public function withAuthorityPHIDs(array $phids) { 39 $this->authorityPHIDs = $phids; 40 return $this; 41 } 42 43 public function withPHIDs(array $phids) { 44 $this->phids = $phids; 45 return $this; 46 } 47 48 public function withIDs(array $ids) { 49 $this->ids = $ids; 50 return $this; 51 } 52 53 public function withRepositoryPHIDs(array $phids) { 54 $this->repositoryPHIDs = $phids; 55 return $this; 56 } 57 58 public function withPaths(array $paths) { 59 $this->paths = $paths; 60 return $this; 61 } 62 63 public function withStatuses(array $statuses) { 64 $this->statuses = $statuses; 65 return $this; 66 } 67 68 public function withControl($repository_phid, array $paths) { 69 if (empty($this->controlMap[$repository_phid])) { 70 $this->controlMap[$repository_phid] = array(); 71 } 72 73 foreach ($paths as $path) { 74 $path = (string)$path; 75 $this->controlMap[$repository_phid][$path] = $path; 76 } 77 78 // We need to load paths to execute control queries. 79 $this->needPaths = true; 80 81 return $this; 82 } 83 84 public function withAuthorityModes(array $modes) { 85 $this->authorityModes = $modes; 86 return $this; 87 } 88 89 public function withNameNgrams($ngrams) { 90 return $this->withNgramsConstraint( 91 new PhabricatorOwnersPackageNameNgrams(), 92 $ngrams); 93 } 94 95 public function needPaths($need_paths) { 96 $this->needPaths = $need_paths; 97 return $this; 98 } 99 100 public function newResultObject() { 101 return new PhabricatorOwnersPackage(); 102 } 103 104 protected function willExecute() { 105 $this->controlResults = array(); 106 } 107 108 protected function willFilterPage(array $packages) { 109 $package_ids = mpull($packages, 'getID'); 110 111 $owners = id(new PhabricatorOwnersOwner())->loadAllWhere( 112 'packageID IN (%Ld)', 113 $package_ids); 114 $owners = mgroup($owners, 'getPackageID'); 115 foreach ($packages as $package) { 116 $package->attachOwners(idx($owners, $package->getID(), array())); 117 } 118 119 return $packages; 120 } 121 122 protected function didFilterPage(array $packages) { 123 $package_ids = mpull($packages, 'getID'); 124 125 if ($this->needPaths) { 126 $paths = id(new PhabricatorOwnersPath())->loadAllWhere( 127 'packageID IN (%Ld)', 128 $package_ids); 129 $paths = mgroup($paths, 'getPackageID'); 130 131 foreach ($packages as $package) { 132 $package->attachPaths(idx($paths, $package->getID(), array())); 133 } 134 } 135 136 if ($this->controlMap) { 137 foreach ($packages as $package) { 138 // If this package is archived, it's no longer a controlling package 139 // for any path. In particular, it can not force active packages with 140 // weak dominion to give up control. 141 if ($package->isArchived()) { 142 continue; 143 } 144 145 $this->controlResults[$package->getID()] = $package; 146 } 147 } 148 149 return $packages; 150 } 151 152 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 153 $joins = parent::buildJoinClauseParts($conn); 154 155 if ($this->shouldJoinOwnersTable()) { 156 $joins[] = qsprintf( 157 $conn, 158 'JOIN %T o ON o.packageID = p.id', 159 id(new PhabricatorOwnersOwner())->getTableName()); 160 } 161 162 if ($this->shouldJoinPathTable()) { 163 $joins[] = qsprintf( 164 $conn, 165 'JOIN %T rpath ON rpath.packageID = p.id', 166 id(new PhabricatorOwnersPath())->getTableName()); 167 } 168 169 return $joins; 170 } 171 172 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 173 $where = parent::buildWhereClauseParts($conn); 174 175 if ($this->phids !== null) { 176 $where[] = qsprintf( 177 $conn, 178 'p.phid IN (%Ls)', 179 $this->phids); 180 } 181 182 if ($this->ids !== null) { 183 $where[] = qsprintf( 184 $conn, 185 'p.id IN (%Ld)', 186 $this->ids); 187 } 188 189 if ($this->repositoryPHIDs !== null) { 190 $where[] = qsprintf( 191 $conn, 192 'rpath.repositoryPHID IN (%Ls)', 193 $this->repositoryPHIDs); 194 } 195 196 if ($this->authorityPHIDs !== null) { 197 $authority_phids = $this->expandAuthority($this->authorityPHIDs); 198 $where[] = qsprintf( 199 $conn, 200 'o.userPHID IN (%Ls)', 201 $authority_phids); 202 } 203 204 if ($this->ownerPHIDs !== null) { 205 $where[] = qsprintf( 206 $conn, 207 'o.userPHID IN (%Ls)', 208 $this->ownerPHIDs); 209 } 210 211 if ($this->paths !== null) { 212 $where[] = qsprintf( 213 $conn, 214 'rpath.pathIndex IN (%Ls)', 215 $this->getFragmentIndexesForPaths($this->paths)); 216 } 217 218 if ($this->statuses !== null) { 219 $where[] = qsprintf( 220 $conn, 221 'p.status IN (%Ls)', 222 $this->statuses); 223 } 224 225 if ($this->controlMap) { 226 $clauses = array(); 227 foreach ($this->controlMap as $repository_phid => $paths) { 228 $indexes = $this->getFragmentIndexesForPaths($paths); 229 230 $clauses[] = qsprintf( 231 $conn, 232 '(rpath.repositoryPHID = %s AND rpath.pathIndex IN (%Ls))', 233 $repository_phid, 234 $indexes); 235 } 236 $where[] = qsprintf($conn, '%LO', $clauses); 237 } 238 239 if ($this->authorityModes !== null) { 240 $where[] = qsprintf( 241 $conn, 242 'authorityMode IN (%Ls)', 243 $this->authorityModes); 244 } 245 246 return $where; 247 } 248 249 protected function shouldGroupQueryResultRows() { 250 if ($this->shouldJoinOwnersTable()) { 251 return true; 252 } 253 254 if ($this->shouldJoinPathTable()) { 255 return true; 256 } 257 258 return parent::shouldGroupQueryResultRows(); 259 } 260 261 public function getBuiltinOrders() { 262 return array( 263 'name' => array( 264 'vector' => array('name'), 265 'name' => pht('Name'), 266 ), 267 ) + parent::getBuiltinOrders(); 268 } 269 270 public function getOrderableColumns() { 271 return parent::getOrderableColumns() + array( 272 'name' => array( 273 'table' => $this->getPrimaryTableAlias(), 274 'column' => 'name', 275 'type' => 'string', 276 'unique' => true, 277 'reverse' => true, 278 ), 279 ); 280 } 281 282 protected function newPagingMapFromPartialObject($object) { 283 return array( 284 'id' => (int)$object->getID(), 285 'name' => $object->getName(), 286 ); 287 } 288 289 public function getQueryApplicationClass() { 290 return PhabricatorOwnersApplication::class; 291 } 292 293 protected function getPrimaryTableAlias() { 294 return 'p'; 295 } 296 297 private function shouldJoinOwnersTable() { 298 if ($this->ownerPHIDs !== null) { 299 return true; 300 } 301 302 if ($this->authorityPHIDs !== null) { 303 return true; 304 } 305 306 return false; 307 } 308 309 private function shouldJoinPathTable() { 310 if ($this->repositoryPHIDs !== null) { 311 return true; 312 } 313 314 if ($this->paths !== null) { 315 return true; 316 } 317 318 if ($this->controlMap) { 319 return true; 320 } 321 322 return false; 323 } 324 325 private function expandAuthority(array $phids) { 326 $projects = id(new PhabricatorProjectQuery()) 327 ->setViewer($this->getViewer()) 328 ->withMemberPHIDs($phids) 329 ->execute(); 330 $project_phids = mpull($projects, 'getPHID'); 331 332 return array_fuse($phids) + array_fuse($project_phids); 333 } 334 335 private function getFragmentsForPaths(array $paths) { 336 $fragments = array(); 337 338 foreach ($paths as $path) { 339 foreach (PhabricatorOwnersPackage::splitPath($path) as $fragment) { 340 $fragments[$fragment] = $fragment; 341 } 342 } 343 344 return $fragments; 345 } 346 347 private function getFragmentIndexesForPaths(array $paths) { 348 $indexes = array(); 349 350 foreach ($this->getFragmentsForPaths($paths) as $fragment) { 351 $indexes[] = PhabricatorHash::digestForIndex($fragment); 352 } 353 354 return $indexes; 355 } 356 357 358/* -( Path Control )------------------------------------------------------- */ 359 360 361 /** 362 * Get a list of all packages which control a path or its parent directories, 363 * ordered from weakest to strongest. 364 * 365 * The first package has the most specific claim on the path; the last 366 * package has the most general claim. Multiple packages may have claims of 367 * equal strength, so this ordering is primarily one of usability and 368 * convenience. 369 * 370 * @return list<PhabricatorOwnersPackage> List of controlling packages. 371 */ 372 public function getControllingPackagesForPath( 373 $repository_phid, 374 $path, 375 $ignore_dominion = false) { 376 $path = (string)$path; 377 378 if (!isset($this->controlMap[$repository_phid][$path])) { 379 throw new PhutilInvalidStateException('withControl'); 380 } 381 382 if ($this->controlResults === null) { 383 throw new PhutilInvalidStateException('execute'); 384 } 385 386 $packages = $this->controlResults; 387 $weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK; 388 389 $path_fragments = PhabricatorOwnersPackage::splitPath($path); 390 $fragment_count = count($path_fragments); 391 392 $matches = array(); 393 foreach ($packages as $package_id => $package) { 394 $best_match = null; 395 $include = false; 396 397 $repository_paths = $package->getPathsForRepository($repository_phid); 398 foreach ($repository_paths as $package_path) { 399 $strength = $package_path->getPathMatchStrength( 400 $path_fragments, 401 $fragment_count); 402 if ($strength > $best_match) { 403 $best_match = $strength; 404 $include = !$package_path->getExcluded(); 405 } 406 } 407 408 if ($best_match && $include) { 409 if ($ignore_dominion) { 410 $is_weak = false; 411 } else { 412 $is_weak = ($package->getDominion() == $weak_dominion); 413 } 414 $matches[$package_id] = array( 415 'strength' => $best_match, 416 'weak' => $is_weak, 417 'package' => $package, 418 ); 419 } 420 } 421 422 // At each strength level, drop weak packages if there are also strong 423 // packages of the same strength. 424 $strength_map = igroup($matches, 'strength'); 425 foreach ($strength_map as $strength => $package_list) { 426 $any_strong = false; 427 foreach ($package_list as $package_id => $package) { 428 if (!$package['weak']) { 429 $any_strong = true; 430 break; 431 } 432 } 433 if ($any_strong) { 434 foreach ($package_list as $package_id => $package) { 435 if ($package['weak']) { 436 unset($matches[$package_id]); 437 } 438 } 439 } 440 } 441 442 $matches = isort($matches, 'strength'); 443 $matches = array_reverse($matches); 444 445 $strongest = null; 446 foreach ($matches as $package_id => $match) { 447 if ($strongest === null) { 448 $strongest = $match['strength']; 449 } 450 451 if ($match['strength'] === $strongest) { 452 continue; 453 } 454 455 if ($match['weak']) { 456 unset($matches[$package_id]); 457 } 458 } 459 460 return array_values(ipull($matches, 'package')); 461 } 462 463}