@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 762 lines 21 kB view raw
1<?php 2 3final class PhabricatorRepositoryURI 4 extends PhabricatorRepositoryDAO 5 implements 6 PhabricatorApplicationTransactionInterface, 7 PhabricatorPolicyInterface, 8 PhabricatorExtendedPolicyInterface, 9 PhabricatorConduitResultInterface { 10 11 protected $repositoryPHID; 12 protected $uri; 13 protected $builtinProtocol; 14 protected $builtinIdentifier; 15 protected $credentialPHID; 16 protected $ioType; 17 protected $displayType; 18 protected $isDisabled; 19 20 private $repository = self::ATTACHABLE; 21 22 const BUILTIN_PROTOCOL_SSH = 'ssh'; 23 const BUILTIN_PROTOCOL_HTTP = 'http'; 24 const BUILTIN_PROTOCOL_HTTPS = 'https'; 25 26 const BUILTIN_IDENTIFIER_ID = 'id'; 27 const BUILTIN_IDENTIFIER_SHORTNAME = 'shortname'; 28 const BUILTIN_IDENTIFIER_CALLSIGN = 'callsign'; 29 30 const DISPLAY_DEFAULT = 'default'; 31 const DISPLAY_NEVER = 'never'; 32 const DISPLAY_ALWAYS = 'always'; 33 34 const IO_DEFAULT = 'default'; 35 const IO_OBSERVE = 'observe'; 36 const IO_MIRROR = 'mirror'; 37 const IO_NONE = 'none'; 38 const IO_READ = 'read'; 39 const IO_READWRITE = 'readwrite'; 40 41 protected function getConfiguration() { 42 return array( 43 self::CONFIG_AUX_PHID => true, 44 self::CONFIG_COLUMN_SCHEMA => array( 45 'uri' => 'text255', 46 'builtinProtocol' => 'text32?', 47 'builtinIdentifier' => 'text32?', 48 'credentialPHID' => 'phid?', 49 'ioType' => 'text32', 50 'displayType' => 'text32', 51 'isDisabled' => 'bool', 52 ), 53 self::CONFIG_KEY_SCHEMA => array( 54 'key_builtin' => array( 55 'columns' => array( 56 'repositoryPHID', 57 'builtinProtocol', 58 'builtinIdentifier', 59 ), 60 'unique' => true, 61 ), 62 ), 63 ) + parent::getConfiguration(); 64 } 65 66 public static function initializeNewURI() { 67 return id(new self()) 68 ->setIoType(self::IO_DEFAULT) 69 ->setDisplayType(self::DISPLAY_DEFAULT) 70 ->setIsDisabled(0); 71 } 72 73 public function generatePHID() { 74 return PhabricatorPHID::generateNewPHID( 75 PhabricatorRepositoryURIPHIDType::TYPECONST); 76 } 77 78 public function attachRepository(PhabricatorRepository $repository) { 79 $this->repository = $repository; 80 return $this; 81 } 82 83 public function getRepository() { 84 return $this->assertAttached($this->repository); 85 } 86 87 public function getRepositoryURIBuiltinKey() { 88 if (!$this->getBuiltinProtocol()) { 89 return null; 90 } 91 92 $parts = array( 93 $this->getBuiltinProtocol(), 94 $this->getBuiltinIdentifier(), 95 ); 96 97 return implode('.', $parts); 98 } 99 100 public function isBuiltin() { 101 return (bool)$this->getBuiltinProtocol(); 102 } 103 104 public function getEffectiveDisplayType() { 105 $display = $this->getDisplayType(); 106 107 if ($display != self::DISPLAY_DEFAULT) { 108 return $display; 109 } 110 111 return $this->getDefaultDisplayType(); 112 } 113 114 public function getDefaultDisplayType() { 115 switch ($this->getEffectiveIOType()) { 116 case self::IO_MIRROR: 117 case self::IO_OBSERVE: 118 case self::IO_NONE: 119 return self::DISPLAY_NEVER; 120 case self::IO_READ: 121 case self::IO_READWRITE: 122 // By default, only show the "best" version of the builtin URI, not the 123 // other redundant versions. 124 $repository = $this->getRepository(); 125 $other_uris = $repository->getURIs(); 126 127 $identifier_value = array( 128 self::BUILTIN_IDENTIFIER_SHORTNAME => 3, 129 self::BUILTIN_IDENTIFIER_CALLSIGN => 2, 130 self::BUILTIN_IDENTIFIER_ID => 1, 131 ); 132 133 $have_identifiers = array(); 134 foreach ($other_uris as $other_uri) { 135 if ($other_uri->getIsDisabled()) { 136 continue; 137 } 138 139 $identifier = $other_uri->getBuiltinIdentifier(); 140 if (!$identifier) { 141 continue; 142 } 143 144 $have_identifiers[$identifier] = $identifier_value[$identifier]; 145 } 146 147 $best_identifier = max($have_identifiers); 148 $this_identifier = $identifier_value[$this->getBuiltinIdentifier()]; 149 150 if ($this_identifier < $best_identifier) { 151 return self::DISPLAY_NEVER; 152 } 153 154 return self::DISPLAY_ALWAYS; 155 } 156 157 return self::DISPLAY_NEVER; 158 } 159 160 161 public function getEffectiveIOType() { 162 $io = $this->getIoType(); 163 164 if ($io != self::IO_DEFAULT) { 165 return $io; 166 } 167 168 return $this->getDefaultIOType(); 169 } 170 171 public function getDefaultIOType() { 172 if ($this->isBuiltin()) { 173 $repository = $this->getRepository(); 174 $other_uris = $repository->getURIs(); 175 176 $any_observe = false; 177 foreach ($other_uris as $other_uri) { 178 if ($other_uri->getIoType() == self::IO_OBSERVE) { 179 $any_observe = true; 180 break; 181 } 182 } 183 184 if ($any_observe) { 185 return self::IO_READ; 186 } else { 187 return self::IO_READWRITE; 188 } 189 } 190 191 return self::IO_NONE; 192 } 193 194 public function getNormalizedURI() { 195 $vcs = $this->getRepository()->getVersionControlSystem(); 196 197 $map = array( 198 PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 199 ArcanistRepositoryURINormalizer::TYPE_GIT, 200 PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => 201 ArcanistRepositoryURINormalizer::TYPE_SVN, 202 PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 203 ArcanistRepositoryURINormalizer::TYPE_MERCURIAL, 204 ); 205 206 $type = $map[$vcs]; 207 $display = (string)$this->getDisplayURI(); 208 209 $normalizer = new ArcanistRepositoryURINormalizer($type, $display); 210 211 $domain_map = self::getURINormalizerDomainMap(); 212 $normalizer->setDomainMap($domain_map); 213 214 return $normalizer->getNormalizedURI(); 215 } 216 217 public function getDisplayURI() { 218 return $this->getURIObject(); 219 } 220 221 public function getEffectiveURI() { 222 return $this->getURIObject(); 223 } 224 225 public function getURIEnvelope() { 226 $uri = $this->getEffectiveURI(); 227 228 $command_engine = $this->newCommandEngine(); 229 230 $is_http = $command_engine->isAnyHTTPProtocol(); 231 // For SVN, we use `--username` and `--password` flags separately in the 232 // CommandEngine, so we don't need to add any credentials here. 233 $is_svn = $this->getRepository()->isSVN(); 234 $credential_phid = $this->getCredentialPHID(); 235 236 if ($is_http && !$is_svn && $credential_phid) { 237 $key = PassphrasePasswordKey::loadFromPHID( 238 $credential_phid, 239 PhabricatorUser::getOmnipotentUser()); 240 241 $uri->setUser($key->getUsernameEnvelope()->openEnvelope()); 242 $uri->setPass($key->getPasswordEnvelope()->openEnvelope()); 243 } 244 245 return new PhutilOpaqueEnvelope((string)$uri); 246 } 247 248 private function getURIObject() { 249 // Users can provide Git/SCP-style URIs in the form "user@host:path". 250 // In the general case, these are not equivalent to any "ssh://..." form 251 // because the path is relative. 252 253 if ($this->isBuiltin()) { 254 $builtin_protocol = $this->getForcedProtocol(); 255 $builtin_domain = $this->getForcedHost(); 256 $raw_uri = "{$builtin_protocol}://{$builtin_domain}"; 257 } else { 258 $raw_uri = $this->getURI(); 259 } 260 261 $port = $this->getForcedPort(); 262 263 $default_ports = array( 264 'ssh' => 22, 265 'http' => 80, 266 'https' => 443, 267 ); 268 269 $uri = new PhutilURI($raw_uri); 270 271 // Make sure to remove any password from the URI before we do anything 272 // with it; this should always be provided by the associated credential. 273 $uri->setPass(null); 274 275 $protocol = $this->getForcedProtocol(); 276 if ($protocol) { 277 $uri->setProtocol($protocol); 278 } 279 280 if ($port) { 281 $uri->setPort($port); 282 } 283 284 // Remove any explicitly set default ports. 285 $uri_port = $uri->getPort(); 286 $uri_protocol = $uri->getProtocol(); 287 288 $uri_default = idx($default_ports, $uri_protocol); 289 if ($uri_default && ($uri_default == $uri_port)) { 290 $uri->setPort(null); 291 } 292 293 $user = $this->getForcedUser(); 294 if ($user) { 295 $uri->setUser($user); 296 } 297 298 $host = $this->getForcedHost(); 299 if ($host) { 300 $uri->setDomain($host); 301 } 302 303 $path = $this->getForcedPath(); 304 if ($path) { 305 $uri->setPath($path); 306 } 307 308 return $uri; 309 } 310 311 312 private function getForcedProtocol() { 313 $repository = $this->getRepository(); 314 315 switch ($this->getBuiltinProtocol()) { 316 case self::BUILTIN_PROTOCOL_SSH: 317 if ($repository->isSVN()) { 318 return 'svn+ssh'; 319 } else { 320 return 'ssh'; 321 } 322 case self::BUILTIN_PROTOCOL_HTTP: 323 return 'http'; 324 case self::BUILTIN_PROTOCOL_HTTPS: 325 return 'https'; 326 default: 327 return null; 328 } 329 } 330 331 private function getForcedUser() { 332 switch ($this->getBuiltinProtocol()) { 333 case self::BUILTIN_PROTOCOL_SSH: 334 return AlmanacKeys::getClusterSSHUser(); 335 default: 336 return null; 337 } 338 } 339 340 private function getForcedHost() { 341 $phabricator_uri = PhabricatorEnv::getURI('/'); 342 $phabricator_uri = new PhutilURI($phabricator_uri); 343 344 $phabricator_host = $phabricator_uri->getDomain(); 345 346 switch ($this->getBuiltinProtocol()) { 347 case self::BUILTIN_PROTOCOL_SSH: 348 $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host'); 349 if ($ssh_host !== null) { 350 return $ssh_host; 351 } 352 return $phabricator_host; 353 case self::BUILTIN_PROTOCOL_HTTP: 354 case self::BUILTIN_PROTOCOL_HTTPS: 355 return $phabricator_host; 356 default: 357 return null; 358 } 359 } 360 361 private function getForcedPort() { 362 $protocol = $this->getBuiltinProtocol(); 363 364 if ($protocol == self::BUILTIN_PROTOCOL_SSH) { 365 return PhabricatorEnv::getEnvConfig('diffusion.ssh-port'); 366 } 367 368 // If Phabricator is running on a nonstandard port, use that as the default 369 // port for URIs with the same protocol. 370 371 $is_http = ($protocol == self::BUILTIN_PROTOCOL_HTTP); 372 $is_https = ($protocol == self::BUILTIN_PROTOCOL_HTTPS); 373 374 if ($is_http || $is_https) { 375 $uri = PhabricatorEnv::getURI('/'); 376 $uri = new PhutilURI($uri); 377 378 $port = $uri->getPort(); 379 if (!$port) { 380 return null; 381 } 382 383 $uri_protocol = $uri->getProtocol(); 384 $use_port = 385 ($is_http && ($uri_protocol == 'http')) || 386 ($is_https && ($uri_protocol == 'https')); 387 388 if (!$use_port) { 389 return null; 390 } 391 392 return $port; 393 } 394 395 return null; 396 } 397 398 private function getForcedPath() { 399 if (!$this->isBuiltin()) { 400 return null; 401 } 402 403 $repository = $this->getRepository(); 404 405 $id = $repository->getID(); 406 $callsign = $repository->getCallsign(); 407 $short_name = $repository->getRepositorySlug(); 408 409 $clone_name = $repository->getCloneName(); 410 411 if ($repository->isGit()) { 412 $suffix = '.git'; 413 } else if ($repository->isHg()) { 414 $suffix = '/'; 415 } else { 416 $suffix = ''; 417 $clone_name = ''; 418 } 419 420 switch ($this->getBuiltinIdentifier()) { 421 case self::BUILTIN_IDENTIFIER_ID: 422 return "/diffusion/{$id}/{$clone_name}{$suffix}"; 423 case self::BUILTIN_IDENTIFIER_SHORTNAME: 424 return "/source/{$short_name}{$suffix}"; 425 case self::BUILTIN_IDENTIFIER_CALLSIGN: 426 return "/diffusion/{$callsign}/{$clone_name}{$suffix}"; 427 default: 428 return null; 429 } 430 } 431 432 public function getViewURI() { 433 $id = $this->getID(); 434 return $this->getRepository()->getPathURI("uri/view/{$id}/"); 435 } 436 437 public function getEditURI() { 438 $id = $this->getID(); 439 return $this->getRepository()->getPathURI("uri/edit/{$id}/"); 440 } 441 442 public function getAvailableIOTypeOptions() { 443 $options = array( 444 self::IO_DEFAULT, 445 self::IO_NONE, 446 ); 447 448 if ($this->isBuiltin()) { 449 $options[] = self::IO_READ; 450 $options[] = self::IO_READWRITE; 451 } else { 452 $options[] = self::IO_OBSERVE; 453 $options[] = self::IO_MIRROR; 454 } 455 456 $map = array(); 457 $io_map = self::getIOTypeMap(); 458 foreach ($options as $option) { 459 $spec = idx($io_map, $option, array()); 460 461 $label = idx($spec, 'label', $option); 462 $short = idx($spec, 'short'); 463 464 $name = pht('%s: %s', $label, $short); 465 $map[$option] = $name; 466 } 467 468 return $map; 469 } 470 471 public function getAvailableDisplayTypeOptions() { 472 $options = array( 473 self::DISPLAY_DEFAULT, 474 self::DISPLAY_ALWAYS, 475 self::DISPLAY_NEVER, 476 ); 477 478 $map = array(); 479 $display_map = self::getDisplayTypeMap(); 480 foreach ($options as $option) { 481 $spec = idx($display_map, $option, array()); 482 483 $label = idx($spec, 'label', $option); 484 $short = idx($spec, 'short'); 485 486 $name = pht('%s: %s', $label, $short); 487 $map[$option] = $name; 488 } 489 490 return $map; 491 } 492 493 public static function getIOTypeMap() { 494 return array( 495 self::IO_DEFAULT => array( 496 'label' => pht('Default'), 497 'short' => pht('Use default behavior.'), 498 ), 499 self::IO_OBSERVE => array( 500 'icon' => 'fa-download', 501 'color' => 'green', 502 'label' => pht('Observe'), 503 'note' => pht( 504 'Changes to this URI will be observed and pulled.'), 505 'short' => pht('Copy from a remote.'), 506 ), 507 self::IO_MIRROR => array( 508 'icon' => 'fa-upload', 509 'color' => 'green', 510 'label' => pht('Mirror'), 511 'note' => pht( 512 'A copy of any changes will be pushed to this URI.'), 513 'short' => pht('Push a copy to a remote.'), 514 ), 515 self::IO_NONE => array( 516 'icon' => 'fa-times', 517 'color' => 'grey', 518 'label' => pht('No I/O'), 519 'note' => pht( 520 'No changes will be pushed or pulled from this URI.'), 521 'short' => pht('Do not perform any I/O.'), 522 ), 523 self::IO_READ => array( 524 'icon' => 'fa-folder', 525 'color' => 'blue', 526 'label' => pht('Read Only'), 527 'note' => pht( 528 'A read-only copy of the repository will be served from this URI.'), 529 'short' => pht('Serve repository in read-only mode.'), 530 ), 531 self::IO_READWRITE => array( 532 'icon' => 'fa-folder-open', 533 'color' => 'blue', 534 'label' => pht('Read/Write'), 535 'note' => pht( 536 'A read/write copy of the repository will be served from this URI.'), 537 'short' => pht('Serve repository in read/write mode.'), 538 ), 539 ); 540 } 541 542 public static function getDisplayTypeMap() { 543 return array( 544 self::DISPLAY_DEFAULT => array( 545 'label' => pht('Default'), 546 'short' => pht('Use default behavior.'), 547 ), 548 self::DISPLAY_ALWAYS => array( 549 'icon' => 'fa-eye', 550 'color' => 'green', 551 'label' => pht('Visible'), 552 'note' => pht('This URI will be shown to users as a clone URI.'), 553 'short' => pht('Show as a clone URI.'), 554 ), 555 self::DISPLAY_NEVER => array( 556 'icon' => 'fa-eye-slash', 557 'color' => 'grey', 558 'label' => pht('Hidden'), 559 'note' => pht( 560 'This URI will be hidden from users.'), 561 'short' => pht('Do not show as a clone URI.'), 562 ), 563 ); 564 } 565 566 public function newCommandEngine() { 567 $repository = $this->getRepository(); 568 569 return DiffusionCommandEngine::newCommandEngine($repository) 570 ->setCredentialPHID($this->getCredentialPHID()) 571 ->setURI($this->getEffectiveURI()); 572 } 573 574 public function getURIScore() { 575 $score = 0; 576 577 $io_points = array( 578 self::IO_READWRITE => 200, 579 self::IO_READ => 100, 580 ); 581 $score += idx($io_points, $this->getEffectiveIOType(), 0); 582 583 $protocol_points = array( 584 self::BUILTIN_PROTOCOL_SSH => 30, 585 self::BUILTIN_PROTOCOL_HTTPS => 20, 586 self::BUILTIN_PROTOCOL_HTTP => 10, 587 ); 588 $score += idx($protocol_points, $this->getBuiltinProtocol(), 0); 589 590 $identifier_points = array( 591 self::BUILTIN_IDENTIFIER_SHORTNAME => 3, 592 self::BUILTIN_IDENTIFIER_CALLSIGN => 2, 593 self::BUILTIN_IDENTIFIER_ID => 1, 594 ); 595 $score += idx($identifier_points, $this->getBuiltinIdentifier(), 0); 596 597 return $score; 598 } 599 600 601 602/* -( PhabricatorApplicationTransactionInterface )------------------------- */ 603 604 605 public function getApplicationTransactionEditor() { 606 return new DiffusionURIEditor(); 607 } 608 609 public function getApplicationTransactionTemplate() { 610 return new PhabricatorRepositoryURITransaction(); 611 } 612 613 614/* -( PhabricatorPolicyInterface )----------------------------------------- */ 615 616 617 public function getCapabilities() { 618 return array( 619 PhabricatorPolicyCapability::CAN_VIEW, 620 PhabricatorPolicyCapability::CAN_EDIT, 621 ); 622 } 623 624 public function getPolicy($capability) { 625 switch ($capability) { 626 case PhabricatorPolicyCapability::CAN_VIEW: 627 case PhabricatorPolicyCapability::CAN_EDIT: 628 return PhabricatorPolicies::getMostOpenPolicy(); 629 } 630 } 631 632 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 633 return false; 634 } 635 636 637/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */ 638 639 640 public function getExtendedPolicy($capability, PhabricatorUser $viewer) { 641 $extended = array(); 642 643 switch ($capability) { 644 case PhabricatorPolicyCapability::CAN_EDIT: 645 // To edit a repository URI, you must be able to edit the 646 // corresponding repository. 647 $extended[] = array($this->getRepository(), $capability); 648 break; 649 } 650 651 return $extended; 652 } 653 654 655/* -( PhabricatorConduitResultInterface )---------------------------------- */ 656 657 658 public function getFieldSpecificationsForConduit() { 659 return array( 660 id(new PhabricatorConduitSearchFieldSpecification()) 661 ->setKey('repositoryPHID') 662 ->setType('phid') 663 ->setDescription(pht('The associated repository PHID.')), 664 id(new PhabricatorConduitSearchFieldSpecification()) 665 ->setKey('uri') 666 ->setType('map<string, string>') 667 ->setDescription(pht('The raw and effective URI.')), 668 id(new PhabricatorConduitSearchFieldSpecification()) 669 ->setKey('io') 670 ->setType('map<string, const>') 671 ->setDescription( 672 pht('The raw, default, and effective I/O Type settings.')), 673 id(new PhabricatorConduitSearchFieldSpecification()) 674 ->setKey('display') 675 ->setType('map<string, const>') 676 ->setDescription( 677 pht('The raw, default, and effective Display Type settings.')), 678 id(new PhabricatorConduitSearchFieldSpecification()) 679 ->setKey('credentialPHID') 680 ->setType('phid?') 681 ->setDescription( 682 pht('The associated credential PHID, if one exists.')), 683 id(new PhabricatorConduitSearchFieldSpecification()) 684 ->setKey('disabled') 685 ->setType('bool') 686 ->setDescription(pht('True if the URI is disabled.')), 687 id(new PhabricatorConduitSearchFieldSpecification()) 688 ->setKey('builtin') 689 ->setType('map<string, string>') 690 ->setDescription( 691 pht('Information about builtin URIs.')), 692 id(new PhabricatorConduitSearchFieldSpecification()) 693 ->setKey('dateCreated') 694 ->setType('int') 695 ->setDescription( 696 pht('Epoch timestamp when the object was created.')), 697 id(new PhabricatorConduitSearchFieldSpecification()) 698 ->setKey('dateModified') 699 ->setType('int') 700 ->setDescription( 701 pht('Epoch timestamp when the object was last updated.')), 702 ); 703 } 704 705 public function getFieldValuesForConduit() { 706 return array( 707 'repositoryPHID' => $this->getRepositoryPHID(), 708 'uri' => array( 709 'raw' => $this->getURI(), 710 'display' => (string)$this->getDisplayURI(), 711 'effective' => (string)$this->getEffectiveURI(), 712 'normalized' => (string)$this->getNormalizedURI(), 713 ), 714 'io' => array( 715 'raw' => $this->getIOType(), 716 'default' => $this->getDefaultIOType(), 717 'effective' => $this->getEffectiveIOType(), 718 ), 719 'display' => array( 720 'raw' => $this->getDisplayType(), 721 'default' => $this->getDefaultDisplayType(), 722 'effective' => $this->getEffectiveDisplayType(), 723 ), 724 'credentialPHID' => $this->getCredentialPHID(), 725 'disabled' => (bool)$this->getIsDisabled(), 726 'builtin' => array( 727 'protocol' => $this->getBuiltinProtocol(), 728 'identifier' => $this->getBuiltinIdentifier(), 729 ), 730 'dateCreated' => $this->getDateCreated(), 731 'dateModified' => $this->getDateModified(), 732 ); 733 } 734 735 public function getConduitSearchAttachments() { 736 return array(); 737 } 738 739 public static function getURINormalizerDomainMap() { 740 $domain_map = array(); 741 742 // See T13435. If the domain for a repository URI is same as the install 743 // base URI, store it as a "<base-uri>" token instead of the actual domain 744 // so that the index does not fall out of date if the install moves. 745 746 $base_uri = PhabricatorEnv::getURI('/'); 747 $base_uri = new PhutilURI($base_uri); 748 $base_domain = $base_uri->getDomain(); 749 $domain_map['<base-uri>'] = $base_domain; 750 751 // Likewise, store a token for the "SSH Host" domain so it can be changed 752 // without requiring an index rebuild. 753 754 $ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host'); 755 if (phutil_nonempty_string($ssh_host)) { 756 $domain_map['<ssh-host>'] = $ssh_host; 757 } 758 759 return $domain_map; 760 } 761 762}