@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.

Provide a more modern way to load packages owning a set of files

Summary:
Ref T8320. Ref T8004. This just tries to generally modernize

It also replaces the nonfunctional "Find Owners" link with a new property that just shows owning packages.

Test Plan:
- Created and edited packages.

{F720478}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T8004, T8320

Differential Revision: https://secure.phabricator.com/D13911

+242 -29
+42 -20
src/applications/diffusion/controller/DiffusionBrowseController.php
··· 111 111 ->setIcon('fa-home') 112 112 ->setDisabled(!$behind_head)); 113 113 114 - // TODO: Ideally, this should live in Owners and be event-triggered, but 115 - // there's no reasonable object for it to react to right now. 116 - 117 - $owners = 'PhabricatorOwnersApplication'; 118 - if (PhabricatorApplication::isClassInstalled($owners)) { 119 - $owners_uri = id(new PhutilURI('/owners/view/search/')) 120 - ->setQueryParams( 121 - array( 122 - 'repository' => $drequest->getCallsign(), 123 - 'path' => '/'.$drequest->getPath(), 124 - )); 125 - 126 - $view->addAction( 127 - id(new PhabricatorActionView()) 128 - ->setName(pht('Find Owners')) 129 - ->setHref((string)$owners_uri) 130 - ->setIcon('fa-users')); 131 - } 132 - 133 114 return $view; 134 115 } 135 116 ··· 137 118 DiffusionRequest $drequest, 138 119 PhabricatorActionListView $actions) { 139 120 140 - $viewer = $this->getRequest()->getUser(); 121 + $viewer = $this->getViewer(); 141 122 142 123 $view = id(new PHUIPropertyListView()) 143 124 ->setUser($viewer) ··· 178 159 $view->addSectionHeader(pht('Tag Content')); 179 160 $view->addTextContent($this->markupText($tag->getMessage())); 180 161 } 162 + } 163 + 164 + $repository = $drequest->getRepository(); 165 + 166 + $owners = 'PhabricatorOwnersApplication'; 167 + if (PhabricatorApplication::isClassInstalled($owners)) { 168 + $package_query = id(new PhabricatorOwnersPackageQuery()) 169 + ->setViewer($viewer) 170 + ->withControl( 171 + $repository->getPHID(), 172 + array( 173 + $drequest->getPath(), 174 + )); 175 + 176 + $package_query->execute(); 177 + 178 + $packages = $package_query->getControllingPackagesForPath( 179 + $repository->getPHID(), 180 + $drequest->getPath()); 181 + 182 + if ($packages) { 183 + $ownership = id(new PHUIStatusListView()) 184 + ->setUser($viewer); 185 + 186 + 187 + 188 + foreach ($packages as $package) { 189 + $icon = 'fa-list-alt'; 190 + $color = 'grey'; 191 + 192 + $item = id(new PHUIStatusItemView()) 193 + ->setIcon($icon, $color) 194 + ->setTarget($viewer->renderHandle($package->getPHID())); 195 + 196 + $ownership->addItem($item); 197 + } 198 + } else { 199 + $ownership = phutil_tag('em', array(), pht('None')); 200 + } 201 + 202 + $view->addProperty(pht('Packages'), $ownership); 181 203 } 182 204 183 205 return $view;
+2 -1
src/applications/owners/controller/PhabricatorOwnersDetailController.php
··· 13 13 $package = id(new PhabricatorOwnersPackageQuery()) 14 14 ->setViewer($viewer) 15 15 ->withIDs(array($request->getURIData('id'))) 16 + ->needPaths(true) 16 17 ->executeOne(); 17 18 if (!$package) { 18 19 return new Aphront404Response(); 19 20 } 20 21 21 - $paths = $package->loadPaths(); 22 + $paths = $package->getPaths(); 22 23 23 24 $repository_phids = array(); 24 25 foreach ($paths as $path) {
+2 -1
src/applications/owners/controller/PhabricatorOwnersPathsController.php
··· 15 15 // TODO: Support this capability. 16 16 // PhabricatorPolicyCapability::CAN_EDIT, 17 17 )) 18 + ->needPaths(true) 18 19 ->executeOne(); 19 20 if (!$package) { 20 21 return new Aphront404Response(); ··· 66 67 return id(new AphrontRedirectResponse()) 67 68 ->setURI('/owners/package/'.$package->getID().'/'); 68 69 } else { 69 - $paths = $package->loadPaths(); 70 + $paths = $package->getPaths(); 70 71 $path_refs = mpull($paths, 'getRef'); 71 72 } 72 73
+2 -4
src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php
··· 43 43 case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: 44 44 return $object->getDescription(); 45 45 case PhabricatorOwnersPackageTransaction::TYPE_PATHS: 46 - // TODO: needPaths() this on the query 47 - $paths = $object->loadPaths(); 46 + $paths = $object->getPaths(); 48 47 return mpull($paths, 'getRef'); 49 48 } 50 49 } ··· 152 151 $old = $xaction->getOldValue(); 153 152 $new = $xaction->getNewValue(); 154 153 155 - // TODO: needPaths this 156 - $paths = $object->loadPaths(); 154 + $paths = $object->getPaths(); 157 155 158 156 $diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new); 159 157 list($rem, $add) = $diffs;
+149 -2
src/applications/owners/query/PhabricatorOwnersPackageQuery.php
··· 8 8 private $ownerPHIDs; 9 9 private $repositoryPHIDs; 10 10 private $namePrefix; 11 + private $needPaths; 12 + 13 + private $controlMap = array(); 14 + private $controlResults; 11 15 12 16 /** 13 17 * Owners are direct owners, and members of owning projects. ··· 29 33 30 34 public function withRepositoryPHIDs(array $phids) { 31 35 $this->repositoryPHIDs = $phids; 36 + return $this; 37 + } 38 + 39 + public function withControl($repository_phid, array $paths) { 40 + if (empty($this->controlMap[$repository_phid])) { 41 + $this->controlMap[$repository_phid] = array(); 42 + } 43 + 44 + foreach ($paths as $path) { 45 + $this->controlMap[$repository_phid][$path] = $path; 46 + } 47 + 48 + // We need to load paths to execute control queries. 49 + $this->needPaths = true; 50 + 32 51 return $this; 33 52 } 34 53 ··· 37 56 return $this; 38 57 } 39 58 59 + public function needPaths($need_paths) { 60 + $this->needPaths = $need_paths; 61 + return $this; 62 + } 63 + 40 64 public function newResultObject() { 41 65 return new PhabricatorOwnersPackage(); 42 66 } 43 67 68 + protected function willExecute() { 69 + $this->controlResults = array(); 70 + } 71 + 44 72 protected function loadPage() { 45 73 return $this->loadStandardPage(new PhabricatorOwnersPackage()); 46 74 } 47 75 76 + protected function didFilterPage(array $packages) { 77 + if ($this->needPaths) { 78 + $package_ids = mpull($packages, 'getID'); 79 + 80 + $paths = id(new PhabricatorOwnersPath())->loadAllWhere( 81 + 'packageID IN (%Ld)', 82 + $package_ids); 83 + $paths = mgroup($paths, 'getPackageID'); 84 + 85 + foreach ($packages as $package) { 86 + $package->attachPaths(idx($paths, $package->getID(), array())); 87 + } 88 + } 89 + 90 + if ($this->controlMap) { 91 + $this->controlResults += mpull($packages, null, 'getID'); 92 + } 93 + 94 + return $packages; 95 + } 96 + 48 97 protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 49 98 $joins = parent::buildJoinClauseParts($conn); 50 99 ··· 55 104 id(new PhabricatorOwnersOwner())->getTableName()); 56 105 } 57 106 58 - if ($this->repositoryPHIDs !== null) { 107 + if ($this->shouldJoinOwnersPathTable()) { 59 108 $joins[] = qsprintf( 60 109 $conn, 61 110 'JOIN %T rpath ON rpath.packageID = p.id', ··· 115 164 phutil_utf8_strtolower($this->namePrefix)); 116 165 } 117 166 167 + if ($this->controlMap) { 168 + $clauses = array(); 169 + foreach ($this->controlMap as $repository_phid => $paths) { 170 + $fragments = array(); 171 + foreach ($paths as $path) { 172 + foreach (PhabricatorOwnersPackage::splitPath($path) as $fragment) { 173 + $fragments[$fragment] = $fragment; 174 + } 175 + } 176 + 177 + $clauses[] = qsprintf( 178 + $conn, 179 + '(rpath.repositoryPHID = %s AND rpath.path IN (%Ls))', 180 + $repository_phid, 181 + $fragments); 182 + } 183 + $where[] = implode(' OR ', $clauses); 184 + } 185 + 118 186 return $where; 119 187 } 120 188 121 189 protected function shouldGroupQueryResultRows() { 122 - if ($this->repositoryPHIDs) { 190 + if ($this->shouldJoinOwnersPathTable()) { 123 191 return true; 124 192 } 125 193 ··· 165 233 166 234 protected function getPrimaryTableAlias() { 167 235 return 'p'; 236 + } 237 + 238 + private function shouldJoinOwnersPathTable() { 239 + if ($this->repositoryPHIDs !== null) { 240 + return true; 241 + } 242 + 243 + if ($this->controlMap) { 244 + return true; 245 + } 246 + 247 + return false; 248 + } 249 + 250 + 251 + /* -( Path Control )------------------------------------------------------- */ 252 + 253 + 254 + /** 255 + * Get the package which controls a path, if one exists. 256 + * 257 + * @return PhabricatorOwnersPackage|null Package, if one exists. 258 + */ 259 + public function getControllingPackageForPath($repository_phid, $path) { 260 + $packages = $this->getControllingPackagesForPath($repository_phid, $path); 261 + 262 + if (!$packages) { 263 + return null; 264 + } 265 + 266 + return head($packages); 267 + } 268 + 269 + 270 + /** 271 + * Get a list of all packages which control a path or its parent directories, 272 + * ordered from weakest to strongest. 273 + * 274 + * The first package has the most specific claim on the path; the last 275 + * package has the most general claim. 276 + * 277 + * @return list<PhabricatorOwnersPackage> List of controlling packages. 278 + */ 279 + public function getControllingPackagesForPath($repository_phid, $path) { 280 + if (!isset($this->controlMap[$repository_phid][$path])) { 281 + throw new PhutilInvalidStateException('withControl'); 282 + } 283 + 284 + if ($this->controlResults === null) { 285 + throw new PhutilInvalidStateException('execute'); 286 + } 287 + 288 + $packages = $this->controlResults; 289 + 290 + $matches = array(); 291 + foreach ($packages as $package_id => $package) { 292 + $best_match = null; 293 + $include = false; 294 + 295 + foreach ($package->getPaths() as $package_path) { 296 + $strength = $package_path->getPathMatchStrength($path); 297 + if ($strength > $best_match) { 298 + $best_match = $strength; 299 + $include = !$package_path->getExcluded(); 300 + } 301 + } 302 + 303 + if ($best_match && $include) { 304 + $matches[$package_id] = array( 305 + 'strength' => $best_match, 306 + 'package' => $package, 307 + ); 308 + } 309 + } 310 + 311 + $matches = isort($matches, 'strength'); 312 + $matches = array_reverse($matches); 313 + 314 + return array_values(ipull($matches, 'package')); 168 315 } 169 316 170 317 }
+13 -1
src/applications/owners/storage/PhabricatorOwnersPackage.php
··· 13 13 protected $primaryOwnerPHID; 14 14 protected $mailKey; 15 15 16 + private $paths = self::ATTACHABLE; 17 + 16 18 public static function initializeNewPackage(PhabricatorUser $actor) { 17 19 return id(new PhabricatorOwnersPackage()) 18 20 ->setAuditingEnabled(0) ··· 225 227 return $ids; 226 228 } 227 229 228 - private static function splitPath($path) { 230 + public static function splitPath($path) { 229 231 $result = array('/'); 230 232 $trailing_slash = preg_match('@/$@', $path) ? '/' : ''; 231 233 $path = trim($path, '/'); ··· 236 238 array_pop($parts); 237 239 } 238 240 return $result; 241 + } 242 + 243 + public function attachPaths(array $paths) { 244 + assert_instances_of($paths, 'PhabricatorOwnersPath'); 245 + $this->paths = $paths; 246 + return $this; 247 + } 248 + 249 + public function getPaths() { 250 + return $this->assertAttached($this->paths); 239 251 } 240 252 241 253
+32
src/applications/owners/storage/PhabricatorOwnersPath.php
··· 70 70 return isset($set[$ref['repositoryPHID']][$ref['path']][$ref['excluded']]); 71 71 } 72 72 73 + /** 74 + * Get the number of directory matches between this path specification and 75 + * some real path. 76 + */ 77 + public function getPathMatchStrength($path) { 78 + $this_path = $this->getPath(); 79 + 80 + if ($this_path === '/') { 81 + // The root path "/" just matches everything with strength 1. 82 + return 1; 83 + } 84 + 85 + $self_fragments = PhabricatorOwnersPackage::splitPath($this_path); 86 + $path_fragments = PhabricatorOwnersPackage::splitPath($path); 87 + 88 + $self_count = count($self_fragments); 89 + $path_count = count($path_fragments); 90 + if ($self_count > $path_count) { 91 + // If this path is longer (and therefor more specific) than the target 92 + // path, we don't match it at all. 93 + return 0; 94 + } 95 + 96 + for ($ii = 0; $ii < $self_count; $ii++) { 97 + if ($self_fragments[$ii] != $path_fragments[$ii]) { 98 + return 0; 99 + } 100 + } 101 + 102 + return $self_count; 103 + } 104 + 73 105 }