[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 final class PhabricatorOwnersPackage extends PhabricatorOwnersDAO 4 implements PhabricatorPolicyInterface { 5 6 protected $name; 7 protected $originalName; 8 protected $auditingEnabled; 9 protected $description; 10 protected $primaryOwnerPHID; 11 12 private $unsavedOwners; 13 private $unsavedPaths; 14 private $actorPHID; 15 private $oldPrimaryOwnerPHID; 16 private $oldAuditingEnabled; 17 18 public function getCapabilities() { 19 return array( 20 PhabricatorPolicyCapability::CAN_VIEW, 21 ); 22 } 23 24 public function getPolicy($capability) { 25 return PhabricatorPolicies::POLICY_USER; 26 } 27 28 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 29 return false; 30 } 31 32 public function describeAutomaticCapability($capability) { 33 return null; 34 } 35 36 public function getConfiguration() { 37 return array( 38 // This information is better available from the history table. 39 self::CONFIG_TIMESTAMPS => false, 40 self::CONFIG_AUX_PHID => true, 41 self::CONFIG_COLUMN_SCHEMA => array( 42 'name' => 'text128', 43 'originalName' => 'text255', 44 'description' => 'text', 45 'primaryOwnerPHID' => 'phid?', 46 'auditingEnabled' => 'bool', 47 ), 48 self::CONFIG_KEY_SCHEMA => array( 49 'key_phid' => null, 50 'phid' => array( 51 'columns' => array('phid'), 52 'unique' => true, 53 ), 54 'name' => array( 55 'columns' => array('name'), 56 'unique' => true, 57 ), 58 ), 59 ) + parent::getConfiguration(); 60 } 61 62 public function generatePHID() { 63 return PhabricatorPHID::generateNewPHID('OPKG'); 64 } 65 66 public function attachUnsavedOwners(array $owners) { 67 $this->unsavedOwners = $owners; 68 return $this; 69 } 70 71 public function attachUnsavedPaths(array $paths) { 72 $this->unsavedPaths = $paths; 73 return $this; 74 } 75 76 public function attachActorPHID($actor_phid) { 77 $this->actorPHID = $actor_phid; 78 return $this; 79 } 80 81 public function getActorPHID() { 82 return $this->actorPHID; 83 } 84 85 public function attachOldPrimaryOwnerPHID($old_primary) { 86 $this->oldPrimaryOwnerPHID = $old_primary; 87 return $this; 88 } 89 90 public function getOldPrimaryOwnerPHID() { 91 return $this->oldPrimaryOwnerPHID; 92 } 93 94 public function attachOldAuditingEnabled($auditing_enabled) { 95 $this->oldAuditingEnabled = $auditing_enabled; 96 return $this; 97 } 98 99 public function getOldAuditingEnabled() { 100 return $this->oldAuditingEnabled; 101 } 102 103 public function setName($name) { 104 $this->name = $name; 105 if (!$this->getID()) { 106 $this->originalName = $name; 107 } 108 return $this; 109 } 110 111 public function loadOwners() { 112 if (!$this->getID()) { 113 return array(); 114 } 115 return id(new PhabricatorOwnersOwner())->loadAllWhere( 116 'packageID = %d', 117 $this->getID()); 118 } 119 120 public function loadPaths() { 121 if (!$this->getID()) { 122 return array(); 123 } 124 return id(new PhabricatorOwnersPath())->loadAllWhere( 125 'packageID = %d', 126 $this->getID()); 127 } 128 129 public static function loadAffectedPackages( 130 PhabricatorRepository $repository, 131 array $paths) { 132 133 if (!$paths) { 134 return array(); 135 } 136 137 return self::loadPackagesForPaths($repository, $paths); 138 } 139 140 public static function loadOwningPackages($repository, $path) { 141 if (empty($path)) { 142 return array(); 143 } 144 145 return self::loadPackagesForPaths($repository, array($path), 1); 146 } 147 148 private static function loadPackagesForPaths( 149 PhabricatorRepository $repository, 150 array $paths, 151 $limit = 0) { 152 153 $fragments = array(); 154 foreach ($paths as $path) { 155 foreach (self::splitPath($path) as $fragment) { 156 $fragments[$fragment][$path] = true; 157 } 158 } 159 160 $package = new PhabricatorOwnersPackage(); 161 $path = new PhabricatorOwnersPath(); 162 $conn = $package->establishConnection('r'); 163 164 $repository_clause = qsprintf( 165 $conn, 166 'AND p.repositoryPHID = %s', 167 $repository->getPHID()); 168 169 // NOTE: The list of $paths may be very large if we're coming from 170 // the OwnersWorker and processing, e.g., an SVN commit which created a new 171 // branch. Break it apart so that it will fit within 'max_allowed_packet', 172 // and then merge results in PHP. 173 174 $rows = array(); 175 foreach (array_chunk(array_keys($fragments), 128) as $chunk) { 176 $rows[] = queryfx_all( 177 $conn, 178 'SELECT pkg.id, p.excluded, p.path 179 FROM %T pkg JOIN %T p ON p.packageID = pkg.id 180 WHERE p.path IN (%Ls) %Q', 181 $package->getTableName(), 182 $path->getTableName(), 183 $chunk, 184 $repository_clause); 185 } 186 $rows = array_mergev($rows); 187 188 $ids = self::findLongestPathsPerPackage($rows, $fragments); 189 190 if (!$ids) { 191 return array(); 192 } 193 194 arsort($ids); 195 if ($limit) { 196 $ids = array_slice($ids, 0, $limit, $preserve_keys = true); 197 } 198 $ids = array_keys($ids); 199 200 $packages = $package->loadAllWhere('id in (%Ld)', $ids); 201 $packages = array_select_keys($packages, $ids); 202 203 return $packages; 204 } 205 206 public static function loadPackagesForRepository($repository) { 207 $package = new PhabricatorOwnersPackage(); 208 $ids = ipull( 209 queryfx_all( 210 $package->establishConnection('r'), 211 'SELECT DISTINCT packageID FROM %T WHERE repositoryPHID = %s', 212 id(new PhabricatorOwnersPath())->getTableName(), 213 $repository->getPHID()), 214 'packageID'); 215 216 return $package->loadAllWhere('id in (%Ld)', $ids); 217 } 218 219 public static function findLongestPathsPerPackage(array $rows, array $paths) { 220 $ids = array(); 221 222 foreach (igroup($rows, 'id') as $id => $package_paths) { 223 $relevant_paths = array_select_keys( 224 $paths, 225 ipull($package_paths, 'path')); 226 227 // For every package, remove all excluded paths. 228 $remove = array(); 229 foreach ($package_paths as $package_path) { 230 if ($package_path['excluded']) { 231 $remove += idx($relevant_paths, $package_path['path'], array()); 232 unset($relevant_paths[$package_path['path']]); 233 } 234 } 235 236 if ($remove) { 237 foreach ($relevant_paths as $fragment => $fragment_paths) { 238 $relevant_paths[$fragment] = array_diff_key($fragment_paths, $remove); 239 } 240 } 241 242 $relevant_paths = array_filter($relevant_paths); 243 if ($relevant_paths) { 244 $ids[$id] = max(array_map('strlen', array_keys($relevant_paths))); 245 } 246 } 247 248 return $ids; 249 } 250 251 private function getActor() { 252 // TODO: This should be cleaner, but we'd likely need to move the whole 253 // thing to an Editor (T603). 254 return PhabricatorUser::getOmnipotentUser(); 255 } 256 257 public function save() { 258 259 if ($this->getID()) { 260 $is_new = false; 261 } else { 262 $is_new = true; 263 } 264 265 $this->openTransaction(); 266 267 $ret = parent::save(); 268 269 $add_owners = array(); 270 $remove_owners = array(); 271 $all_owners = array(); 272 if ($this->unsavedOwners) { 273 $new_owners = array_fill_keys($this->unsavedOwners, true); 274 $cur_owners = array(); 275 foreach ($this->loadOwners() as $owner) { 276 if (empty($new_owners[$owner->getUserPHID()])) { 277 $remove_owners[$owner->getUserPHID()] = true; 278 $owner->delete(); 279 continue; 280 } 281 $cur_owners[$owner->getUserPHID()] = true; 282 } 283 284 $add_owners = array_diff_key($new_owners, $cur_owners); 285 $all_owners = array_merge( 286 array($this->getPrimaryOwnerPHID() => true), 287 $new_owners, 288 $remove_owners); 289 foreach ($add_owners as $phid => $ignored) { 290 $owner = new PhabricatorOwnersOwner(); 291 $owner->setPackageID($this->getID()); 292 $owner->setUserPHID($phid); 293 $owner->save(); 294 } 295 unset($this->unsavedOwners); 296 } 297 298 $add_paths = array(); 299 $remove_paths = array(); 300 $touched_repos = array(); 301 if ($this->unsavedPaths) { 302 $new_paths = igroup($this->unsavedPaths, 'repositoryPHID', 'path'); 303 $cur_paths = $this->loadPaths(); 304 foreach ($cur_paths as $key => $path) { 305 $repository_phid = $path->getRepositoryPHID(); 306 $new_path = head(idx( 307 idx($new_paths, $repository_phid, array()), 308 $path->getPath(), 309 array())); 310 $excluded = $path->getExcluded(); 311 if (!$new_path || idx($new_path, 'excluded') != $excluded) { 312 $touched_repos[$repository_phid] = true; 313 $remove_paths[$repository_phid][$path->getPath()] = $excluded; 314 $path->delete(); 315 unset($cur_paths[$key]); 316 } 317 } 318 319 $cur_paths = mgroup($cur_paths, 'getRepositoryPHID', 'getPath'); 320 foreach ($new_paths as $repository_phid => $paths) { 321 // TODO: (T603) Thread policy stuff in here. 322 323 // get repository object for path validation 324 $repository = id(new PhabricatorRepository())->loadOneWhere( 325 'phid = %s', 326 $repository_phid); 327 if (!$repository) { 328 continue; 329 } 330 foreach ($paths as $path => $dicts) { 331 $path = ltrim($path, '/'); 332 // build query to validate path 333 $drequest = DiffusionRequest::newFromDictionary( 334 array( 335 'user' => $this->getActor(), 336 'repository' => $repository, 337 'path' => $path, 338 )); 339 $results = DiffusionBrowseResultSet::newFromConduit( 340 DiffusionQuery::callConduitWithDiffusionRequest( 341 $this->getActor(), 342 $drequest, 343 'diffusion.browsequery', 344 array( 345 'commit' => $drequest->getCommit(), 346 'path' => $path, 347 'needValidityOnly' => true, 348 ))); 349 $valid = $results->isValidResults(); 350 $is_directory = true; 351 if (!$valid) { 352 switch ($results->getReasonForEmptyResultSet()) { 353 case DiffusionBrowseResultSet::REASON_IS_FILE: 354 $valid = true; 355 $is_directory = false; 356 break; 357 case DiffusionBrowseResultSet::REASON_IS_EMPTY: 358 $valid = true; 359 break; 360 } 361 } 362 if ($is_directory && substr($path, -1) != '/') { 363 $path .= '/'; 364 } 365 if (substr($path, 0, 1) != '/') { 366 $path = '/'.$path; 367 } 368 if (empty($cur_paths[$repository_phid][$path]) && $valid) { 369 $touched_repos[$repository_phid] = true; 370 $excluded = idx(reset($dicts), 'excluded', 0); 371 $add_paths[$repository_phid][$path] = $excluded; 372 $obj = new PhabricatorOwnersPath(); 373 $obj->setPackageID($this->getID()); 374 $obj->setRepositoryPHID($repository_phid); 375 $obj->setPath($path); 376 $obj->setExcluded($excluded); 377 $obj->save(); 378 } 379 } 380 } 381 unset($this->unsavedPaths); 382 } 383 384 $this->saveTransaction(); 385 386 if ($is_new) { 387 $mail = new PackageCreateMail($this); 388 } else { 389 $mail = new PackageModifyMail( 390 $this, 391 array_keys($add_owners), 392 array_keys($remove_owners), 393 array_keys($all_owners), 394 array_keys($touched_repos), 395 $add_paths, 396 $remove_paths); 397 } 398 $mail->setActor($this->getActor()); 399 $mail->send(); 400 401 return $ret; 402 } 403 404 public function delete() { 405 $mails = id(new PackageDeleteMail($this)) 406 ->setActor($this->getActor()) 407 ->prepareMails(); 408 409 $this->openTransaction(); 410 foreach ($this->loadOwners() as $owner) { 411 $owner->delete(); 412 } 413 foreach ($this->loadPaths() as $path) { 414 $path->delete(); 415 } 416 417 $ret = parent::delete(); 418 419 $this->saveTransaction(); 420 421 foreach ($mails as $mail) { 422 $mail->saveAndSend(); 423 } 424 425 return $ret; 426 } 427 428 private static function splitPath($path) { 429 $result = array('/'); 430 $trailing_slash = preg_match('@/$@', $path) ? '/' : ''; 431 $path = trim($path, '/'); 432 $parts = explode('/', $path); 433 while (count($parts)) { 434 $result[] = '/'.implode('/', $parts).$trailing_slash; 435 $trailing_slash = '/'; 436 array_pop($parts); 437 } 438 return $result; 439 } 440 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |