diff --git a/resources/sql/patches/20131121.repocredentials.1.col.sql b/resources/sql/patches/20131121.repocredentials.1.col.sql new file mode 100644 index 0000000000..9471c7189b --- /dev/null +++ b/resources/sql/patches/20131121.repocredentials.1.col.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_repository.repository + ADD credentialPHID VARCHAR(64) COLLATE utf8_bin; diff --git a/resources/sql/patches/20131121.repocredentials.2.mig.php b/resources/sql/patches/20131121.repocredentials.2.mig.php new file mode 100644 index 0000000000..d9735d6b5b --- /dev/null +++ b/resources/sql/patches/20131121.repocredentials.2.mig.php @@ -0,0 +1,104 @@ +establishConnection('w'); +$viewer = PhabricatorUser::getOmnipotentUser(); + +$map = array(); +foreach (new LiskMigrationIterator($table) as $repository) { + $callsign = $repository->getCallsign(); + echo "Examining repository {$callsign}...\n"; + + if ($repository->getCredentialPHID()) { + echo "...already has a Credential.\n"; + continue; + } + + $uri = $repository->getRemoteURI(); + if (!$uri) { + echo "...no remote URI.\n"; + continue; + } + + $uri = new PhutilURI($uri); + + $proto = strtolower($uri->getProtocol()); + if ($proto == 'http' || $proto == 'https' || $proto == 'svn') { + $username = $repository->getDetail('http-login'); + $secret = $repository->getDetail('http-pass'); + $type = PassphraseCredentialTypePassword::CREDENTIAL_TYPE; + } else { + $username = $repository->getDetail('ssh-login'); + $file = $repository->getDetail('ssh-keyfile'); + if ($file) { + $secret = $file; + $type = PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE; + } else { + $secret = $repository->getDetail('ssh-key'); + $type = PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE; + } + } + + if (!$username || !$secret) { + echo "...no credentials set.\n"; + continue; + } + + $map[$type][$username][$secret][] = $repository; + echo "...will migrate.\n"; +} + +$passphrase = new PassphraseSecret(); +$passphrase->openTransaction(); +$table->openTransaction(); + +foreach ($map as $credential_type => $credential_usernames) { + $type = PassphraseCredentialType::getTypeByConstant($credential_type); + foreach ($credential_usernames as $username => $credential_secrets) { + foreach ($credential_secrets as $secret_plaintext => $repositories) { + $callsigns = mpull($repositories, 'getCallsign'); + $name = pht( + 'Migrated Repository Credential (%s)', + implode(', ', $callsigns)); + + echo "Creating: {$name}...\n"; + + $secret = id(new PassphraseSecret()) + ->setSecretData($secret_plaintext) + ->save(); + + $secret_id = $secret->getID(); + + $credential = PassphraseCredential::initializeNewCredential($viewer) + ->setCredentialType($type->getCredentialType()) + ->setProvidesType($type->getProvidesType()) + ->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN) + ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN) + ->setName($name) + ->setUsername($username) + ->setSecretID($secret_id) + ->save(); + + foreach ($repositories as $repository) { + queryfx( + $conn_w, + 'UPDATE %T SET credentialPHID = %s WHERE id = %d', + $table->getTableName(), + $credential->getPHID(), + $repository->getID()); + + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_USES_CREDENTIAL; + + id(new PhabricatorEdgeEditor()) + ->setActor($viewer) + ->addEdge($repository->getPHID(), $edge_type, $credential->getPHID()) + ->save(); + } + } + } +} + +$table->saveTransaction(); +$passphrase->saveTransaction(); + +echo "Done.\n"; diff --git a/scripts/ssh/ssh-connect.php b/scripts/ssh/ssh-connect.php index 2f28778565..03e1caa77a 100755 --- a/scripts/ssh/ssh-connect.php +++ b/scripts/ssh/ssh-connect.php @@ -1,71 +1,57 @@ #!/usr/bin/env php setViewer(PhabricatorUser::getOmnipotentUser()) + ->setViewer($viewer) ->withCallsigns(array($target_name)) ->executeOne(); if (!$repository) { throw new Exception(pht('No repository with callsign "%s"!', $target_name)); } $pattern = array(); $arguments = array(); $pattern[] = 'ssh'; $pattern[] = '-o'; $pattern[] = 'StrictHostKeyChecking=no'; -$login = $repository->getSSHLogin(); -if (strlen($login)) { - $pattern[] = '-l'; - $pattern[] = '%P'; - $arguments[] = new PhutilOpaqueEnvelope($login); -} - -$ssh_identity = null; - -$key = $repository->getDetail('ssh-key'); -$keyfile = $repository->getDetail('ssh-keyfile'); -if ($keyfile) { - $ssh_identity = $keyfile; -} else if ($key) { - $tmpfile = new TempFile('phabricator-repository-ssh-key'); - chmod($tmpfile, 0600); - Filesystem::writeFile($tmpfile, $key); - $ssh_identity = (string)$tmpfile; -} +$credential_phid = $repository->getCredentialPHID(); +if ($credential_phid) { + $key = PassphraseSSHKey::loadFromPHID($credential_phid, $viewer); -if ($ssh_identity) { - $pattern[] = '-i'; - $pattern[] = '%P'; - $arguments[] = new PhutilOpaqueEnvelope($keyfile); + $pattern[] = '-l %P'; + $arguments[] = $key->getUsernameEnvelope(); + $pattern[] = '-i %P'; + $arguments[] = $key->getKeyfileEnvelope(); } $pattern[] = '--'; $passthru_args = array_slice($argv, 1); foreach ($passthru_args as $passthru_arg) { $pattern[] = '%s'; $arguments[] = $passthru_arg; } $pattern = implode(' ', $pattern); array_unshift($arguments, $pattern); $err = newv('PhutilExecPassthru', $arguments) ->execute(); exit($err); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php index 36ad1feb83..6bb3b0f70c 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -1,818 +1,768 @@ edit = $data['edit']; } public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); // NOTE: We can end up here via either "Create Repository", or via // "Import Repository", or via "Edit Remote". In the latter case, we show // only a few of the pages. $repository = null; switch ($this->edit) { case 'remote': $repository = $this->getDiffusionRequest()->getRepository(); // Make sure we have CAN_EDIT. PhabricatorPolicyFilter::requireCapability( $viewer, $repository, PhabricatorPolicyCapability::CAN_EDIT); $this->setRepository($repository); $cancel_uri = $this->getRepositoryControllerURI($repository, 'edit/'); break; case 'import': case 'create': $this->requireApplicationCapability( DiffusionCapabilityCreateRepositories::CAPABILITY); $cancel_uri = $this->getApplicationURI('new/'); break; default: throw new Exception("Invalid edit operation!"); } $form = id(new PHUIPagedFormView()) ->setUser($viewer) ->setCancelURI($cancel_uri); switch ($this->edit) { case 'remote': $title = pht('Edit Remote'); $form ->addPage('remote-uri', $this->buildRemoteURIPage()) ->addPage('auth', $this->buildAuthPage()); break; case 'create': $title = pht('Create Repository'); $form ->addPage('vcs', $this->buildVCSPage()) ->addPage('name', $this->buildNamePage()) ->addPage('done', $this->buildDonePage()); break; case 'import': $title = pht('Import Repository'); $form ->addPage('vcs', $this->buildVCSPage()) ->addPage('name', $this->buildNamePage()) ->addPage('remote-uri', $this->buildRemoteURIPage()) ->addPage('auth', $this->buildAuthPage()) ->addPage('done', $this->buildDonePage()); break; } if ($request->isFormPost()) { $form->readFromRequest($request); if ($form->isComplete()) { $is_create = ($this->edit === 'import' || $this->edit === 'create'); $is_auth = ($this->edit == 'import' || $this->edit == 'remote'); $is_init = ($this->edit == 'create'); if ($is_create) { $repository = PhabricatorRepository::initializeNewRepository( $viewer); } $template = id(new PhabricatorRepositoryTransaction()); $type_name = PhabricatorRepositoryTransaction::TYPE_NAME; $type_vcs = PhabricatorRepositoryTransaction::TYPE_VCS; $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH; $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; - $type_ssh_login = PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN; - $type_ssh_key = PhabricatorRepositoryTransaction::TYPE_SSH_KEY; - $type_ssh_keyfile = PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE; - $type_http_login = PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN; - $type_http_pass = PhabricatorRepositoryTransaction::TYPE_HTTP_PASS; $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; + $type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; $xactions = array(); // If we're creating a new repository, set all this core stuff. if ($is_create) { $callsign = $form->getPage('name') ->getControl('callsign')->getValue(); // We must set this to a unique value to save the repository // initially, and it's immutable, so we don't bother using // transactions to apply this change. $repository->setCallsign($callsign); // Put the repository in "Importing" mode until we finish // parsing it. $repository->setDetail('importing', true); $xactions[] = id(clone $template) ->setTransactionType($type_name) ->setNewValue( $form->getPage('name')->getControl('name')->getValue()); $xactions[] = id(clone $template) ->setTransactionType($type_vcs) ->setNewValue( $form->getPage('vcs')->getControl('vcs')->getValue()); $activate = $form->getPage('done') ->getControl('activate')->getValue(); $xactions[] = id(clone $template) ->setTransactionType($type_activate) ->setNewValue( ($activate == 'start')); $default_local_path = PhabricatorEnv::getEnvConfig( 'repository.default-local-path'); $default_local_path = rtrim($default_local_path, '/'); $default_local_path = $default_local_path.'/'.$callsign.'/'; $xactions[] = id(clone $template) ->setTransactionType($type_local_path) ->setNewValue($default_local_path); } if ($is_init) { $xactions[] = id(clone $template) ->setTransactionType($type_hosting) ->setNewValue(true); } if ($is_auth) { $xactions[] = id(clone $template) ->setTransactionType($type_remote_uri) ->setNewValue( $form->getPage('remote-uri')->getControl('remoteURI') ->getValue()); $xactions[] = id(clone $template) - ->setTransactionType($type_ssh_login) + ->setTransactionType($type_credential) ->setNewValue( - $form->getPage('auth')->getControl('ssh-login')->getValue()); - - $xactions[] = id(clone $template) - ->setTransactionType($type_ssh_key) - ->setNewValue( - $form->getPage('auth')->getControl('ssh-key')->getValue()); - - $xactions[] = id(clone $template) - ->setTransactionType($type_ssh_keyfile) - ->setNewValue( - $form->getPage('auth')->getControl('ssh-keyfile')->getValue()); - - $xactions[] = id(clone $template) - ->setTransactionType($type_http_login) - ->setNewValue( - $form->getPage('auth')->getControl('http-login')->getValue()); - - $xactions[] = id(clone $template) - ->setTransactionType($type_http_pass) - ->setNewValue( - $form->getPage('auth')->getControl('http-pass')->getValue()); + $form->getPage('auth')->getControl('credential')->getValue()); } id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, $xactions); $repo_uri = $this->getRepositoryControllerURI($repository, 'edit/'); return id(new AphrontRedirectResponse())->setURI($repo_uri); } } else { $dict = array(); if ($repository) { $dict = array( 'remoteURI' => $repository->getRemoteURI(), - 'ssh-login' => $repository->getDetail('ssh-login'), - 'ssh-key' => $repository->getDetail('ssh-key'), - 'ssh-keyfile' => $repository->getDetail('ssh-keyfile'), - 'http-login' => $repository->getDetail('http-login'), - 'http-pass' => $repository->getDetail('http-pass'), + 'credential' => $repository->getCredentialPHID(), ); } $form->readFromObject($dict); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName($title)); return $this->buildApplicationPage( array( $crumbs, $form, ), array( 'title' => $title, 'device' => true, )); } /* -( Page: VCS Type )----------------------------------------------------- */ private function buildVCSPage() { $is_import = ($this->edit == 'import'); if ($is_import) { $git_str = pht( 'Import a Git repository (for example, a repository hosted '. 'on GitHub).'); $hg_str = pht( 'Import a Mercurial repository (for example, a repository '. 'hosted on Bitbucket).'); $svn_str = pht('Import a Subversion repository.'); } else { $git_str = pht('Create a new, empty Git repository.'); $hg_str = pht('Create a new, empty Mercurial repository.'); $svn_str = pht('Create a new, empty Subversion repository.'); } $control = id(new AphrontFormRadioButtonControl()) ->setName('vcs') ->setLabel(pht('Type')) ->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT, pht('Git'), $git_str) ->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL, pht('Mercurial'), $hg_str) ->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_SVN, pht('Subversion'), $svn_str); if ($is_import) { $control->addButton( PhabricatorRepositoryType::REPOSITORY_TYPE_PERFORCE, pht('Perforce'), pht( 'Perforce is not directly supported, but you can import '. 'a Perforce repository as a Git repository using %s.', phutil_tag( 'a', array( 'href' => 'http://www.perforce.com/product/components/git-fusion', 'target' => '_blank', ), pht('Perforce Git Fusion'))), 'disabled', $disabled = true); } return id(new PHUIFormPageView()) ->setPageName(pht('Repository Type')) ->setUser($this->getRequest()->getUser()) ->setValidateFormPageCallback(array($this, 'validateVCSPage')) ->addControl($control); } public function validateVCSPage(PHUIFormPageView $page) { $valid = array( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => true, PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => true, PhabricatorRepositoryType::REPOSITORY_TYPE_SVN => true, ); $c_vcs = $page->getControl('vcs'); $v_vcs = $c_vcs->getValue(); if (!$v_vcs) { $c_vcs->setError(pht('Required')); $page->addPageError( pht('You must select a version control system.')); } else if (empty($valid[$v_vcs])) { $c_vcs->setError(pht('Invalid')); $page->addPageError( pht('You must select a valid version control system.')); } return $c_vcs->isValid(); } /* -( Page: Name and Callsign )-------------------------------------------- */ private function buildNamePage() { return id(new PHUIFormPageView()) ->setUser($this->getRequest()->getUser()) ->setPageName(pht('Repository Name and Location')) ->setValidateFormPageCallback(array($this, 'validateNamePage')) ->addRemarkupInstructions( pht( '**Choose a human-readable name for this repository**, like '. '"CompanyName Mobile App" or "CompanyName Backend Server". You '. 'can change this later.')) ->addControl( id(new AphrontFormTextControl()) ->setName('name') ->setLabel(pht('Name')) ->setCaption(pht('Human-readable repository name.'))) ->addRemarkupInstructions( pht( '**Choose a "Callsign" for the repository.** This is a short, '. 'unique string which identifies commits elsewhere in Phabricator. '. 'For example, you might use `M` for your mobile app repository '. 'and `B` for your backend repository.'. "\n\n". '**Callsigns must be UPPERCASE**, and can not be edited after the '. 'repository is created. Generally, you should choose short '. 'callsigns.')) ->addControl( id(new AphrontFormTextControl()) ->setName('callsign') ->setLabel(pht('Callsign')) ->setCaption(pht('Short UPPERCASE identifier.'))); } public function validateNamePage(PHUIFormPageView $page) { $c_name = $page->getControl('name'); $v_name = $c_name->getValue(); if (!strlen($v_name)) { $c_name->setError(pht('Required')); $page->addPageError( pht('You must choose a name for this repository.')); } $c_call = $page->getControl('callsign'); $v_call = $c_call->getValue(); if (!strlen($v_call)) { $c_call->setError(pht('Required')); $page->addPageError( pht('You must choose a callsign for this repository.')); } else if (!preg_match('/^[A-Z]+$/', $v_call)) { $c_call->setError(pht('Invalid')); $page->addPageError( pht('The callsign must contain only UPPERCASE letters.')); } else { $exists = false; try { $repo = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getRequest()->getUser()) ->withCallsigns(array($v_call)) ->executeOne(); $exists = (bool)$repo; } catch (PhabricatorPolicyException $ex) { $exists = true; } if ($exists) { $c_call->setError(pht('Not Unique')); $page->addPageError( pht( 'Another repository already uses that callsign. You must choose '. 'a unique callsign.')); } } return $c_name->isValid() && $c_call->isValid(); } /* -( Page: Remote URI )--------------------------------------------------- */ private function buildRemoteURIPage() { return id(new PHUIFormPageView()) ->setUser($this->getRequest()->getUser()) ->setPageName(pht('Repository Remote URI')) ->setValidateFormPageCallback(array($this, 'validateRemoteURIPage')) ->setAdjustFormPageCallback(array($this, 'adjustRemoteURIPage')) ->addControl( id(new AphrontFormTextControl()) ->setName('remoteURI')); } public function adjustRemoteURIPage(PHUIFormPageView $page) { $form = $page->getForm(); $is_git = false; $is_svn = false; $is_mercurial = false; if ($this->getRepository()) { $vcs = $this->getRepository()->getVersionControlSystem(); } else { $vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); } switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $is_svn = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $is_mercurial = true; break; default: throw new Exception("Unsupported VCS!"); } $has_local = ($is_git || $is_mercurial); if ($is_git) { $uri_label = pht('Remote URI'); $instructions = pht( 'Enter the URI to clone this Git repository from. It should usually '. 'look like one of these examples:'. "\n\n". "| Example Git Remote URIs |\n". "| ----------------------- |\n". "| `git@github.com:example/example.git` |\n". "| `ssh://user@host.com/git/example.git` |\n". "| `file:///local/path/to/repo` |\n". "| `https://example.com/repository.git` |\n"); } else if ($is_mercurial) { $uri_label = pht('Remote URI'); $instructions = pht( 'Enter the URI to clone this Mercurial repository from. It should '. 'usually look like one of these examples:'. "\n\n". "| Example Mercurial Remote URIs |\n". "| ----------------------- |\n". "| `ssh://hg@bitbucket.org/example/repository` |\n". "| `file:///local/path/to/repo` |\n"); } else if ($is_svn) { $uri_label = pht('Repository Root'); $instructions = pht( 'Enter the **Repository Root** for this Subversion repository. '. 'You can figure this out by running `svn info` in a working copy '. 'and looking at the value in the `Repository Root` field. It '. 'should be a URI and will usually look like these:'. "\n\n". "| Example Subversion Repository Root URIs |\n". "| ------------------------------ |\n". "| `http://svn.example.org/svnroot/` |\n". "| `svn+ssh://svn.example.com/svnroot/` |\n". "| `svn://svn.example.net/svnroot/` |\n". "| `file:///local/path/to/svnroot/` |\n". "\n\n". "Make sure you specify the root of the repository, not a ". "subdirectory."); } else { throw new Exception("Unsupported VCS!"); } $page->addRemarkupInstructions($instructions, 'remoteURI'); $page->getControl('remoteURI')->setLabel($uri_label); } public function validateRemoteURIPage(PHUIFormPageView $page) { $c_remote = $page->getControl('remoteURI'); $v_remote = $c_remote->getValue(); if (!strlen($v_remote)) { $c_remote->setError(pht('Required')); $page->addPageError( pht("You must specify a URI.")); } else { $proto = $this->getRemoteURIProtocol($v_remote); if ($proto === 'file') { if (!preg_match('@^file:///@', $v_remote)) { $c_remote->setError(pht('Invalid')); $page->addPageError( pht( "URIs using the 'file://' protocol should have three slashes ". "(e.g., 'file:///absolute/path/to/file'). You only have two. ". "Add another one.")); } } // Catch confusion between Git/SCP-style URIs and normal URIs. See T3619 // for discussion. This is usually a user adding "ssh://" to an implicit // SSH Git URI. if ($proto == 'ssh') { if (preg_match('(^[^:@]+://[^/:]+:[^\d])', $v_remote)) { $c_remote->setError(pht('Invalid')); $page->addPageError( pht( "The Remote URI is not formatted correctly. Remote URIs ". "with an explicit protocol should be in the form ". "'proto://domain/path', not 'proto://domain:/path'. ". "The ':/path' syntax is only valid in SCP-style URIs.")); } } switch ($proto) { case 'ssh': case 'http': case 'https': case 'file': case 'git': case 'svn': case 'svn+ssh': break; default: $c_remote->setError(pht('Invalid')); $page->addPageError( pht( "The URI protocol is unrecognized. It should begin ". "'ssh://', 'http://', 'https://', 'file://', 'git://', ". "'svn://', 'svn+ssh://', or be in the form ". "'git@domain.com:path'.")); break; } } return $c_remote->isValid(); } /* -( Page: Authentication )----------------------------------------------- */ public function buildAuthPage() { return id(new PHUIFormPageView()) ->setPageName(pht('Authentication')) ->setUser($this->getRequest()->getUser()) ->setAdjustFormPageCallback(array($this, 'adjustAuthPage')) ->addControl( - id(new AphrontFormTextControl()) - ->setName('ssh-login') - ->setLabel('SSH User')) - ->addControl( - id(new AphrontFormTextAreaControl()) - ->setName('ssh-key') - ->setLabel('SSH Private Key') - ->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT) - ->setCaption( - hsprintf('Specify the entire private key, or...'))) - ->addControl( - id(new AphrontFormTextControl()) - ->setName('ssh-keyfile') - ->setLabel('SSH Private Key Path') - ->setCaption( - '...specify a path on disk where the daemon should '. - 'look for a private key.')) - ->addControl( - id(new AphrontFormTextControl()) - ->setName('http-login') - ->setLabel('Username')) - ->addControl( - id(new AphrontFormPasswordControl()) - ->setName('http-pass') - ->setLabel('Password')); + id(new PassphraseCredentialControl()) + ->setName('credential')); } public function adjustAuthPage($page) { $form = $page->getForm(); - $remote_uri = $form->getPage('remote-uri') - ->getControl('remoteURI') - ->getValue(); - if ($this->getRepository()) { $vcs = $this->getRepository()->getVersionControlSystem(); } else { $vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); } + $remote_uri = $form->getPage('remote-uri') + ->getControl('remoteURI') + ->getValue(); $proto = $this->getRemoteURIProtocol($remote_uri); $remote_user = $this->getRemoteURIUser($remote_uri); - $page->getControl('ssh-login')->setHidden(true); - $page->getControl('ssh-key')->setHidden(true); - $page->getControl('ssh-keyfile')->setHidden(true); - $page->getControl('http-login')->setHidden(true); - $page->getControl('http-pass')->setHidden(true); + $c_credential = $page->getControl('credential'); + $c_credential->setDefaultUsername($remote_user); if ($this->isSSHProtocol($proto)) { - $page->getControl('ssh-login')->setHidden(false); - $page->getControl('ssh-key')->setHidden(false); - $page->getControl('ssh-keyfile')->setHidden(false); - - $c_login = $page->getControl('ssh-login'); - if (!strlen($c_login->getValue())) { - $c_login->setValue($remote_user); - } + $c_credential->setLabel(pht('SSH Key')); + $c_credential->setCredentialType( + PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE); + $provides_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE; $page->addRemarkupInstructions( pht( - 'Enter the username and private key to use to connect to the '. - 'the repository hosted at:'. + 'Choose or add the SSH credentials to use to connect to the the '. + 'repository hosted at:'. "\n\n". " lang=text\n". - " %s". - "\n\n". - 'You can either copy/paste the entire private key, or put it '. - 'somewhere on disk and provide the path to it.', + " %s", $remote_uri), - 'ssh-login'); - + 'credential'); } else if ($this->isUsernamePasswordProtocol($proto)) { - $page->getControl('http-login')->setHidden(false); - $page->getControl('http-pass')->setHidden(false); + $c_credential->setLabel(pht('Password')); + $c_credential->setAllowNull(true); + $c_credential->setCredentialType( + PassphraseCredentialTypePassword::CREDENTIAL_TYPE); + $provides_type = PassphraseCredentialTypePassword::PROVIDES_TYPE; $page->addRemarkupInstructions( pht( - 'Enter the a username and pasword used to connect to the '. + 'Choose the a username and pasword used to connect to the '. 'repository hosted at:'. "\n\n". " lang=text\n". " %s". "\n\n". "If this repository does not require a username or password, ". - "you can leave these fields blank.", + "you can continue to the next step.", $remote_uri), - 'http-login'); + 'credential'); } else if ($proto == 'file') { + $c_credential->setHidden(true); $page->addRemarkupInstructions( pht( - 'You do not need to configure any authentication options for '. - 'repositories accessed over the `file://` protocol. Continue '. - 'to the next step.'), - 'ssh-login'); + 'You do not need to configure any credentials for repositories '. + 'accessed over the `file://` protocol. Continue to the next step.'), + 'credential'); } else { throw new Exception("Unknown URI protocol!"); } + + if ($provides_type) { + $viewer = $this->getRequest()->getUser(); + + $options = id(new PassphraseCredentialQuery()) + ->setViewer($viewer) + ->withIsDestroyed(false) + ->withProvidesTypes(array($provides_type)) + ->execute(); + + $c_credential->setOptions($options); + } + } public function validateAuthPage(PHUIFormPageView $page) { $form = $page->getForm(); $remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue(); $proto = $this->getRemoteURIProtocol($remote_uri); - if ($this->isSSHProtocol($proto)) { - $c_user = $page->getControl('ssh-login'); - $c_key = $page->getControl('ssh-key'); - $c_file = $page->getControl('ssh-keyfile'); + $c_credential = $page->getControl('credential'); + $v_credential = $c_credential->getValue(); - $v_user = $c_user->getValue(); - $v_key = $c_key->getValue(); - $v_file = $c_file->getValue(); + // NOTE: We're using the omnipotent user here because the viewer might be + // editing a repository they're allowed to edit which uses a credential they + // are not allowed to see. This is fine, as long as they don't change it. + $credential = id(new PassphraseCredentialQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs(array($v_credential)) + ->executeOne(); - if (!strlen($v_user)) { - $c_user->setError(pht('Required')); + if ($this->isSSHProtocol($proto)) { + if (!$credential) { + $c_credential->setError(pht('Required')); $page->addPageError( - pht('You must provide an SSH login username to connect over SSH.')); + pht('You must choose an SSH credential to connect over SSH.')); } - if (!strlen($v_key) && !strlen($v_file)) { - $c_key->setError(pht('No Key')); - $c_file->setError(pht('No Key')); + $ssh_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE; + if ($credential->getProvidesType() !== $ssh_type) { + $c_credential->setError(pht('Invalid')); $page->addPageError( pht( - 'You must provide either a private key or the path to a private '. - 'key to connect over SSH.')); - } else if (strlen($v_key) && strlen($v_file)) { - $c_key->setError(pht('Ambiguous')); - $c_file->setError(pht('Ambiguous')); - $page->addPageError( - pht( - 'Provide either a private key or the path to a private key, not '. - 'both.')); - } else if (!preg_match('/PRIVATE KEY/', $v_key)) { - $c_key->setError(pht('Invalid')); + 'You must choose an SSH credential, not some other type '. + 'of credential.')); + } + + } else if ($this->isUsernamePasswordProtocol($proto)) { + if ($credential) { + $password_type = PassphraseCredentialTypePassword::PROVIDES_TYPE; + if ($credential->getProvidesType() !== $password_type) { + $c_credential->setError(pht('Invalid')); $page->addPageError( pht( - 'The private key you provided is missing the PRIVATE KEY header. '. - 'You should include the header and footer. (Did you provide a '. - 'public key by mistake?)')); + 'You must choose a username/password credential, not some other '. + 'type of credential.')); + } } - return $c_user->isValid() && - $c_key->isValid() && - $c_file->isValid(); - } else if ($this->isUsernamePasswordProtocol($proto)) { - return true; + return $c_credential->isValid(); } else { return true; } } /* -( Page: Done )--------------------------------------------------------- */ private function buildDonePage() { $is_create = ($this->edit == 'create'); if ($is_create) { $now_label = pht('Create Repository Now'); $now_caption = pht( 'Create the repository right away. This will create the repository '. 'using default settings.'); $wait_label = pht('Configure More Options First'); $wait_caption = pht( 'Configure more options before creating the repository. '. 'This will let you fine-tune settings. You can create the repository '. 'whenever you are ready.'); } else { $now_label = pht('Start Import Now'); $now_caption = pht( 'Start importing the repository right away. This will import '. 'the entire repository using default settings.'); $wait_label = pht('Configure More Options First'); $wait_caption = pht( 'Configure more options before beginning the repository '. 'import. This will let you fine-tune settings. You can '. 'start the import whenever you are ready.'); } return id(new PHUIFormPageView()) ->setPageName(pht('Repository Ready!')) ->setValidateFormPageCallback(array($this, 'validateDonePage')) ->setUser($this->getRequest()->getUser()) ->addControl( id(new AphrontFormRadioButtonControl()) ->setName('activate') ->setLabel(pht('Start Now')) ->addButton( 'start', $now_label, $now_caption) ->addButton( 'wait', $wait_label, $wait_caption)); } public function validateDonePage(PHUIFormPageView $page) { $c_activate = $page->getControl('activate'); $v_activate = $c_activate->getValue(); if ($v_activate != 'start' && $v_activate != 'wait') { $c_activate->setError(pht('Required')); $page->addPageError( pht('Make a choice about repository activation.')); } return $c_activate->isValid(); } /* -( Internal )----------------------------------------------------------- */ private function getRemoteURIProtocol($raw_uri) { $uri = new PhutilURI($raw_uri); if ($uri->getProtocol()) { return strtolower($uri->getProtocol()); } $git_uri = new PhutilGitURI($raw_uri); if (strlen($git_uri->getDomain()) && strlen($git_uri->getPath())) { return 'ssh'; } return null; } private function getRemoteURIUser($raw_uri) { $uri = new PhutilURI($raw_uri); if ($uri->getUser()) { return $uri->getUser(); } $git_uri = new PhutilGitURI($raw_uri); if (strlen($git_uri->getDomain()) && strlen($git_uri->getPath())) { return $git_uri->getUser(); } return null; } private function isSSHProtocol($proto) { return ($proto == 'git' || $proto == 'ssh' || $proto == 'svn+ssh'); } private function isUsernamePasswordProtocol($proto) { return ($proto == 'http' || $proto == 'https' || $proto == 'svn'); } private function setRepository(PhabricatorRepository $repository) { $this->repository = $repository; return $this; } private function getRepository() { return $this->repository; } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php index 5ba4423481..999ae7845f 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -1,898 +1,906 @@ getRequest(); $viewer = $request->getUser(); $drequest = $this->diffusionRequest; $repository = $drequest->getRepository(); PhabricatorPolicyFilter::requireCapability( $viewer, $repository, PhabricatorPolicyCapability::CAN_EDIT); $is_svn = false; $is_git = false; $is_hg = false; switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $is_git = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $is_svn = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $is_hg = true; break; } $has_branches = ($is_git || $is_hg); $has_local = $repository->usesLocalWorkingCopy(); $crumbs = $this->buildApplicationCrumbs($is_main = true); $title = pht('Edit %s', $repository->getName()); $header = id(new PHUIHeaderView()) ->setHeader($title); if ($repository->isTracked()) { $header->setStatus('oh-ok', '', pht('Active')); } else { $header->setStatus('policy-noone', '', pht('Inactive')); } $basic_actions = $this->buildBasicActions($repository); $basic_properties = $this->buildBasicProperties($repository, $basic_actions); $policy_actions = $this->buildPolicyActions($repository); $policy_properties = $this->buildPolicyProperties($repository, $policy_actions); $remote_properties = null; if (!$repository->isHosted()) { $remote_properties = $this->buildRemoteProperties( $repository, $this->buildRemoteActions($repository)); } $encoding_actions = $this->buildEncodingActions($repository); $encoding_properties = $this->buildEncodingProperties($repository, $encoding_actions); $hosting_properties = $this->buildHostingProperties( $repository, $this->buildHostingActions($repository)); $branches_properties = null; if ($has_branches) { $branches_properties = $this->buildBranchesProperties( $repository, $this->buildBranchesActions($repository)); } $subversion_properties = null; if ($is_svn) { $subversion_properties = $this->buildSubversionProperties( $repository, $this->buildSubversionActions($repository)); } $local_properties = null; if ($has_local) { $local_properties = $this->buildLocalProperties( $repository, $this->buildLocalActions($repository)); } $actions_properties = $this->buildActionsProperties( $repository, $this->buildActionsActions($repository)); $xactions = id(new PhabricatorRepositoryTransactionQuery()) ->setViewer($viewer) ->withObjectPHIDs(array($repository->getPHID())) ->execute(); $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer); foreach ($xactions as $xaction) { if ($xaction->getComment()) { $engine->addObject( $xaction->getComment(), PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); } } $engine->process(); $xaction_view = id(new PhabricatorApplicationTransactionView()) ->setUser($viewer) ->setObjectPHID($repository->getPHID()) ->setTransactions($xactions) ->setMarkupEngine($engine); $obj_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($basic_properties) ->addPropertyList($policy_properties) ->addPropertyList($hosting_properties); if ($remote_properties) { $obj_box->addPropertyList($remote_properties); } if ($local_properties) { $obj_box->addPropertyList($local_properties); } $obj_box->addPropertyList($encoding_properties); if ($branches_properties) { $obj_box->addPropertyList($branches_properties); } if ($subversion_properties) { $obj_box->addPropertyList($subversion_properties); } $obj_box->addPropertyList($actions_properties); return $this->buildApplicationPage( array( $crumbs, $obj_box, $xaction_view, ), array( 'title' => $title, 'device' => true, )); } private function buildBasicActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Basic Information')) ->setHref($this->getRepositoryControllerURI($repository, 'edit/basic/')); $view->addAction($edit); $activate = id(new PhabricatorActionView()) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/activate/')) ->setWorkflow(true); if ($repository->isTracked()) { $activate ->setIcon('disable') ->setName(pht('Deactivate Repository')); } else { $activate ->setIcon('enable') ->setName(pht('Activate Repository')); } $view->addAction($activate); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Delete Repository')) ->setIcon('delete') ->setHref( $this->getRepositoryControllerURI($repository, 'edit/delete/')) ->setWorkflow(true)); return $view; } private function buildBasicProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $type = PhabricatorRepositoryType::getNameForRepositoryType( $repository->getVersionControlSystem()); $view->addProperty(pht('Type'), $type); $view->addProperty(pht('Callsign'), $repository->getCallsign()); $view->addProperty( pht('Status'), $this->buildRepositoryStatus($repository)); $description = $repository->getDetail('description'); $view->addSectionHeader(pht('Description')); if (!strlen($description)) { $description = phutil_tag('em', array(), pht('No description provided.')); } else { $description = PhabricatorMarkupEngine::renderOneObject( $repository, 'description', $viewer); } $view->addTextContent($description); return $view; } private function buildEncodingActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Text Encoding')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/encoding/')); $view->addAction($edit); return $view; } private function buildEncodingProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Text Encoding')); $encoding = $repository->getDetail('encoding'); if (!$encoding) { $encoding = phutil_tag('em', array(), pht('Use Default (UTF-8)')); } $view->addProperty(pht('Encoding'), $encoding); return $view; } private function buildPolicyActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Policies')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/policy/')); $view->addAction($edit); return $view; } private function buildPolicyProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Policies')); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $viewer, $repository); $view->addProperty( pht('Visible To'), $descriptions[PhabricatorPolicyCapability::CAN_VIEW]); $view->addProperty( pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); $pushable = $repository->isHosted() ? $descriptions[DiffusionCapabilityPush::CAPABILITY] : phutil_tag('em', array(), pht('Not a Hosted Repository')); $view->addProperty(pht('Pushable By'), $pushable); return $view; } private function buildBranchesActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Branches')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/branches/')); $view->addAction($edit); return $view; } private function buildBranchesProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Branches')); $default_branch = nonempty( $repository->getHumanReadableDetail('default-branch'), phutil_tag('em', array(), $repository->getDefaultBranch())); $view->addProperty(pht('Default Branch'), $default_branch); $track_only = nonempty( $repository->getHumanReadableDetail('branch-filter', array()), phutil_tag('em', array(), pht('Track All Branches'))); $view->addProperty(pht('Track Only'), $track_only); $autoclose_only = nonempty( $repository->getHumanReadableDetail('close-commits-filter', array()), phutil_tag('em', array(), pht('Autoclose On All Branches'))); if ($repository->getDetail('disable-autoclose')) { $autoclose_only = phutil_tag('em', array(), pht('Disabled')); } $view->addProperty(pht('Autoclose Only'), $autoclose_only); return $view; } private function buildSubversionActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Subversion Info')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/subversion/')); $view->addAction($edit); return $view; } private function buildSubversionProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Subversion')); $svn_uuid = nonempty( $repository->getUUID(), phutil_tag('em', array(), pht('Not Configured'))); $view->addProperty(pht('Subversion UUID'), $svn_uuid); $svn_subpath = nonempty( $repository->getHumanReadableDetail('svn-subpath'), phutil_tag('em', array(), pht('Import Entire Repository'))); $view->addProperty(pht('Import Only'), $svn_subpath); return $view; } private function buildActionsActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Actions')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/actions/')); $view->addAction($edit); return $view; } private function buildActionsProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Actions')); $notify = $repository->getDetail('herald-disabled') ? pht('Off') : pht('On'); $notify = phutil_tag('em', array(), $notify); $view->addProperty(pht('Publish/Notify'), $notify); $autoclose = $repository->getDetail('disable-autoclose') ? pht('Off') : pht('On'); $autoclose = phutil_tag('em', array(), $autoclose); $view->addProperty(pht('Autoclose'), $autoclose); return $view; } private function buildRemoteActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Remote')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/remote/')); $view->addAction($edit); return $view; } private function buildRemoteProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Remote')); $view->addProperty( pht('Remote URI'), $repository->getHumanReadableDetail('remote-uri')); + $credential_phid = $repository->getCredentialPHID(); + if ($credential_phid) { + $this->loadHandles(array($credential_phid)); + $view->addProperty( + pht('Credential'), + $this->getHandle($credential_phid)->renderLink()); + } + return $view; } private function buildLocalActions(PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($viewer); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Local')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/local/')); $view->addAction($edit); return $view; } private function buildLocalProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions) ->addSectionHeader(pht('Local')); $view->addProperty( pht('Local Path'), $repository->getHumanReadableDetail('local-path')); return $view; } private function buildHostingActions(PhabricatorRepository $repository) { $user = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setObjectURI($this->getRequest()->getRequestURI()) ->setUser($user); $edit = id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Hosting')) ->setHref( $this->getRepositoryControllerURI($repository, 'edit/hosting/')); $view->addAction($edit); return $view; } private function buildHostingProperties( PhabricatorRepository $repository, PhabricatorActionListView $actions) { $user = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($user) ->setActionList($actions) ->addSectionHeader(pht('Hosting')); $hosting = $repository->isHosted() ? pht('Hosted on Phabricator') : pht('Hosted Elsewhere'); $view->addProperty(pht('Hosting'), phutil_tag('em', array(), $hosting)); $view->addProperty( pht('Serve over HTTP'), phutil_tag( 'em', array(), PhabricatorRepository::getProtocolAvailabilityName( $repository->getServeOverHTTP()))); $view->addProperty( pht('Serve over SSH'), phutil_tag( 'em', array(), PhabricatorRepository::getProtocolAvailabilityName( $repository->getServeOverSSH()))); return $view; } private function buildRepositoryStatus( PhabricatorRepository $repository) { $viewer = $this->getRequest()->getUser(); $view = new PHUIStatusListView(); $messages = id(new PhabricatorRepositoryStatusMessage()) ->loadAllWhere('repositoryID = %d', $repository->getID()); $messages = mpull($messages, null, 'getStatusType'); if ($repository->isTracked()) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Repository Active'))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning') ->setTarget(pht('Repository Inactive')) ->setNote( pht('Activate this repository to begin or resume import.'))); return $view; } $binaries = array(); switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svn'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } if ($repository->isHosted()) { if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-http-backend'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) { switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binaries[] = 'git-receive-pack'; $binaries[] = 'git-upload-pack'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binaries[] = 'svnserve'; $binaries[] = 'svnadmin'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binaries[] = 'hg'; break; } } } $binaries = array_unique($binaries); foreach ($binaries as $binary) { $where = Filesystem::resolveBinary($binary); if (!$where) { $config_href = '/config/edit/environment.append-paths/'; $config_link = phutil_tag( 'a', array( 'href' => $config_href, ), 'environment.append-paths'); $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget( pht('Missing Binary %s', phutil_tag('tt', array(), $binary))) ->setNote(pht( "Unable to find this binary in the webserver's PATH. You may ". "need to configure %s.", $config_link))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget( pht('Found Binary %s', phutil_tag('tt', array(), $binary))) ->setNote(phutil_tag('tt', array(), $where))); } } $doc_href = PhabricatorEnv::getDocLink( 'article/Managing_Daemons_with_phd.html'); $daemon_instructions = pht( 'Use %s to start daemons. See %s.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag( 'a', array( 'href' => $doc_href, ), pht('Managing Daemons with phd'))); $pull_daemon = id(new PhabricatorDaemonLogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) ->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon')) ->setLimit(1) ->execute(); if ($pull_daemon) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Pull Daemon Running'))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('Pull Daemon Not Running')) ->setNote($daemon_instructions)); } $task_daemon = id(new PhabricatorDaemonLogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) ->withDaemonClasses(array('PhabricatorTaskmasterDaemon')) ->setLimit(1) ->execute(); if ($task_daemon) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Task Daemon Running'))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('Task Daemon Not Running')) ->setNote($daemon_instructions)); } if ($repository->usesLocalWorkingCopy()) { $local_parent = dirname($repository->getLocalPath()); if (Filesystem::pathExists($local_parent)) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Storage Directory OK')) ->setNote(phutil_tag('tt', array(), $local_parent))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('No Storage Directory')) ->setNote( pht( 'Storage directory %s does not exist, or is not readable by '. 'the webserver. Create this directory or make it readable.', phutil_tag('tt', array(), $local_parent)))); return $view; } $local_path = $repository->getLocalPath(); $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('Initialization Error')) ->setNote($message->getParameter('message'))); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: if (Filesystem::pathExists($local_path)) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Working Copy OK')) ->setNote(phutil_tag('tt', array(), $local_path))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('Working Copy Error')) ->setNote( pht( 'Working copy %s has been deleted, or is not '. 'readable by the webserver. Make this directory '. 'readable. If it has been deleted, the daemons should '. 'restore it automatically.', phutil_tag('tt', array(), $local_path)))); return $view; } break; case PhabricatorRepositoryStatusMessage::CODE_WORKING: $view->addItem( id(new PHUIStatusItemView()) ->setIcon('time-green') ->setTarget(pht('Initializing Working Copy')) ->setNote(pht('Daemons are initializing the working copy.'))); return $view; default: $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('Unknown Init Status')) ->setNote($message->getStatusCode())); return $view; } } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('time-orange') ->setTarget(pht('No Working Copy Yet')) ->setNote( pht('Waiting for daemons to build a working copy.'))); return $view; } } $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH); if ($message) { switch ($message->getStatusCode()) { case PhabricatorRepositoryStatusMessage::CODE_ERROR: $view->addItem( id(new PHUIStatusItemView()) ->setIcon('warning-red') ->setTarget(pht('Update Error')) ->setNote($message->getParameter('message'))); return $view; case PhabricatorRepositoryStatusMessage::CODE_OKAY: $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Updates OK')) ->setNote( pht( 'Last updated %s.', phabricator_datetime($message->getEpoch(), $viewer)))); break; } } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('time-orange') ->setTarget(pht('Waiting For Update')) ->setNote( pht('Waiting for daemons to read updates.'))); } if ($repository->isImporting()) { $progress = queryfx_all( $repository->establishConnection('r'), 'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d GROUP BY importStatus', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID()); $done = 0; $total = 0; foreach ($progress as $row) { $total += $row['N'] * 4; $status = $row['importStatus']; if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) { $done += $row['N']; } if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) { $done += $row['N']; } } if ($total) { $percentage = 100 * ($done / $total); } else { $percentage = 0; } $percentage = sprintf('%.1f%%', $percentage); $view->addItem( id(new PHUIStatusItemView()) ->setIcon('time-green') ->setTarget(pht('Importing')) ->setNote( pht('%s Complete', $percentage))); } else { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('accept-green') ->setTarget(pht('Fully Imported'))); } if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) { $view->addItem( id(new PHUIStatusItemView()) ->setIcon('up') ->setTarget(pht('Prioritized')) ->setNote(pht('This repository will be updated soon.'))); } return $view; } } diff --git a/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php b/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php index 493372262b..f2951353ed 100644 --- a/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php +++ b/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php @@ -1,46 +1,46 @@ \d+)' => 'PassphraseCredentialViewController', '/passphrase/' => array( '(?:query/(?P[^/]+)/)?' => 'PassphraseCredentialListController', 'create/' => 'PassphraseCredentialCreateController', 'edit/(?:(?P\d+)/)?' => 'PassphraseCredentialEditController', 'destroy/(?P\d+)/' => 'PassphraseCredentialDestroyController', 'reveal/(?P\d+)/' => 'PassphraseCredentialRevealController', )); } } diff --git a/src/applications/passphrase/keys/PassphraseAbstractKey.php b/src/applications/passphrase/keys/PassphraseAbstractKey.php index 76cba1b39e..cf532d3fac 100644 --- a/src/applications/passphrase/keys/PassphraseAbstractKey.php +++ b/src/applications/passphrase/keys/PassphraseAbstractKey.php @@ -1,66 +1,74 @@ credential) { throw new Exception(pht("Credential is required!")); } return $this->credential; } private function loadCredential( $phid, PhabricatorUser $viewer) { $credential = id(new PassphraseCredentialQuery()) ->setViewer($viewer) ->withPHIDs(array($phid)) ->needSecrets(true) ->executeOne(); if (!$credential) { throw new Exception(pht('Failed to load credential "%s"!', $phid)); } return $credential; } private function validateCredential( PassphraseCredential $credential, $provides_type) { - $type = $credential->getCredentialType(); - if ($type->getProvides() !== $provides_type) { + $type = $credential->getCredentialTypeImplementation(); + + if (!$type) { + throw new Exception( + pht( + 'Credential "%s" is of unknown type "%s"!', + 'K'.$credential->getID(), + $credential->getCredentialType())); + } + + if ($type->getProvidesType() !== $provides_type) { throw new Exception( pht( 'Credential "%s" must provide "%s", but provides "%s"!', 'K'.$credential->getID(), $provides_type, - $type->getProvides())); + $type->getProvidesType())); } - } protected function loadAndValidateFromPHID( $phid, PhabricatorUser $viewer, $type) { $credential = $this->loadCredential($phid, $viewer); $this->validateCredential($credential, $type); $this->credential = $credential; return $this; } public function getUsernameEnvelope() { $credential = $this->requireCredential(); return new PhutilOpaqueEnvelope($credential->getUsername()); } } diff --git a/src/applications/passphrase/storage/PassphraseCredential.php b/src/applications/passphrase/storage/PassphraseCredential.php index 880bf5f170..75c68766c0 100644 --- a/src/applications/passphrase/storage/PassphraseCredential.php +++ b/src/applications/passphrase/storage/PassphraseCredential.php @@ -1,76 +1,81 @@ setName('') ->setUsername('') ->setDescription('') ->setIsDestroyed(0) ->setViewPolicy($actor->getPHID()) ->setEditPolicy($actor->getPHID()); } public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PassphrasePHIDTypeCredential::TYPECONST); } public function attachSecret(PhutilOpaqueEnvelope $secret = null) { $this->secret = $secret; return $this; } public function getSecret() { return $this->assertAttached($this->secret); } + public function getCredentialTypeImplementation() { + $type = $this->getCredentialType(); + return PassphraseCredentialType::getTypeByConstant($type); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return false; } public function describeAutomaticCapability($capability) { return null; } } diff --git a/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php b/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php index cb6237bbd1..630e7c0d53 100644 --- a/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php +++ b/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php @@ -1,139 +1,134 @@ 'required string', 'vcs' => 'required enum', 'callsign' => 'required string', 'description' => 'optional string', 'encoding' => 'optional string', 'tracking' => 'optional bool', 'uri' => 'optional string', - 'sshUser' => 'optional string', - 'sshKey' => 'optional string', - 'sshKeyFile' => 'optional string', - 'httpUser' => 'optional string', - 'httpPassword' => 'optional string', + 'credentialPHID' => 'optional string', 'localPath' => 'optional string', 'svnSubpath' => 'optional string', 'branchFilter' => 'optional list', 'closeCommitsFilter' => 'optional list', 'pullFrequency' => 'optional int', 'defaultBranch' => 'optional string', 'heraldEnabled' => 'optional bool, default = true', 'autocloseEnabled' => 'optional bool, default = true', 'svnUUID' => 'optional string', ); } public function defineReturnType() { return 'nonempty dict'; } public function defineErrorTypes() { return array( 'ERR-PERMISSIONS' => 'You do not have the authority to call this method.', 'ERR-DUPLICATE' => 'Duplicate repository callsign.', 'ERR-BAD-CALLSIGN' => 'Callsign is required and must be ALL UPPERCASE LETTERS.', 'ERR-UNKNOWN-REPOSITORY-VCS' => 'Unknown repository VCS type.', ); } protected function execute(ConduitAPIRequest $request) { $application = id(new PhabricatorApplicationQuery()) ->setViewer($request->getUser()) ->withClasses(array('PhabricatorApplicationDiffusion')) ->executeOne(); PhabricatorPolicyFilter::requireCapability( $request->getUser(), $application, DiffusionCapabilityCreateRepositories::CAPABILITY); // TODO: This has some duplication with (and lacks some of the validation // of) the web workflow; refactor things so they can share more code as this // stabilizes. Specifically, this should move to transactions since they // work properly now. $repository = PhabricatorRepository::initializeNewRepository( $request->getUser()); $repository->setName($request->getValue('name')); $callsign = $request->getValue('callsign'); if (!preg_match('/^[A-Z]+$/', $callsign)) { throw new ConduitException('ERR-BAD-CALLSIGN'); } $repository->setCallsign($callsign); $vcs = $request->getValue('vcs'); $map = array( 'git' => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT, 'hg' => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL, 'svn' => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN, ); if (empty($map[$vcs])) { throw new ConduitException('ERR-UNKNOWN-REPOSITORY-VCS'); } $repository->setVersionControlSystem($map[$vcs]); + $repository->setCredentialPHID($request->getValue('credentialPHID')); + $details = array( 'encoding' => $request->getValue('encoding'), 'description' => $request->getValue('description'), 'tracking-enabled' => (bool)$request->getValue('tracking', true), 'remote-uri' => $request->getValue('uri'), 'local-path' => $request->getValue('localPath'), 'branch-filter' => array_fill_keys( $request->getValue('branchFilter', array()), true), 'close-commits-filter' => array_fill_keys( $request->getValue('closeCommitsFilter', array()), true), 'pull-frequency' => $request->getValue('pullFrequency'), 'default-branch' => $request->getValue('defaultBranch'), - 'ssh-login' => $request->getValue('sshUser'), - 'ssh-key' => $request->getValue('sshKey'), - 'ssh-keyfile' => $request->getValue('sshKeyFile'), 'herald-disabled' => !$request->getValue('heraldEnabled', true), 'svn-subpath' => $request->getValue('svnSubpath'), 'disable-autoclose' => !$request->getValue('autocloseEnabled', true), ); foreach ($details as $key => $value) { $repository->setDetail($key, $value); } try { $repository->save(); } catch (AphrontQueryDuplicateKeyException $ex) { throw new ConduitException('ERR-DUPLICATE'); } return $repository->toDictionary(); } } diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php index cf28febdf0..e534402391 100644 --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -1,289 +1,296 @@ getTransactionType()) { case PhabricatorRepositoryTransaction::TYPE_VCS: return $object->getVersionControlSystem(); case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: return $object->isTracked(); case PhabricatorRepositoryTransaction::TYPE_NAME: return $object->getName(); case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: return $object->getDetail('description'); case PhabricatorRepositoryTransaction::TYPE_ENCODING: return $object->getDetail('encoding'); case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: return $object->getDetail('default-branch'); case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: return array_keys($object->getDetail('branch-filter', array())); case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: return array_keys($object->getDetail('close-commits-filter', array())); case PhabricatorRepositoryTransaction::TYPE_UUID: return $object->getUUID(); case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: return $object->getDetail('svn-subpath'); case PhabricatorRepositoryTransaction::TYPE_NOTIFY: return (int)!$object->getDetail('herald-disabled'); case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: return (int)!$object->getDetail('disable-autoclose'); case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: return $object->getDetail('remote-uri'); - case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN: - return $object->getDetail('ssh-login'); - case PhabricatorRepositoryTransaction::TYPE_SSH_KEY: - return $object->getDetail('ssh-key'); - case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE: - return $object->getDetail('ssh-keyfile'); - case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN: - return $object->getDetail('http-login'); - case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS: - return $object->getDetail('http-pass'); case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: return $object->getDetail('local-path'); case PhabricatorRepositoryTransaction::TYPE_HOSTING: return $object->isHosted(); case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: return $object->getServeOverHTTP(); case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: return $object->getServeOverSSH(); case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: return $object->getPushPolicy(); + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: + return $object->getCredentialPHID(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: case PhabricatorRepositoryTransaction::TYPE_NAME: case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: case PhabricatorRepositoryTransaction::TYPE_ENCODING: case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: case PhabricatorRepositoryTransaction::TYPE_UUID: case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN: case PhabricatorRepositoryTransaction::TYPE_SSH_KEY: case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE: case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN: case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS: case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: case PhabricatorRepositoryTransaction::TYPE_VCS: case PhabricatorRepositoryTransaction::TYPE_HOSTING: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: return $xaction->getNewValue(); case PhabricatorRepositoryTransaction::TYPE_NOTIFY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: return (int)$xaction->getNewValue(); } } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorRepositoryTransaction::TYPE_VCS: $object->setVersionControlSystem($xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: $object->setDetail('tracking-enabled', $xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_NAME: $object->setName($xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: $object->setDetail('description', $xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: $object->setDetail('default-branch', $xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: $object->setDetail( 'branch-filter', array_fill_keys($xaction->getNewValue(), true)); break; case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: $object->setDetail( 'close-commits-filter', array_fill_keys($xaction->getNewValue(), true)); break; case PhabricatorRepositoryTransaction::TYPE_UUID: $object->setUUID($xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: $object->setDetail('svn-subpath', $xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_NOTIFY: $object->setDetail('herald-disabled', (int)!$xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: $object->setDetail('disable-autoclose', (int)!$xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: $object->setDetail('remote-uri', $xaction->getNewValue()); break; - case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN: - $object->setDetail('ssh-login', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_SSH_KEY: - $object->setDetail('ssh-key', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE: - $object->setDetail('ssh-keyfile', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN: - $object->setDetail('http-login', $xaction->getNewValue()); - break; - case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS: - $object->setDetail('http-pass', $xaction->getNewValue()); - break; case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: $object->setDetail('local-path', $xaction->getNewValue()); break; case PhabricatorRepositoryTransaction::TYPE_HOSTING: return $object->setHosted($xaction->getNewValue()); case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: return $object->setServeOverHTTP($xaction->getNewValue()); case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: return $object->setServeOverSSH($xaction->getNewValue()); case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: return $object->setPushPolicy($xaction->getNewValue()); + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: + return $object->setCredentialPHID($xaction->getNewValue()); case PhabricatorRepositoryTransaction::TYPE_ENCODING: // Make sure the encoding is valid by converting to UTF-8. This tests // that the user has mbstring installed, and also that they didn't type // a garbage encoding name. Note that we're converting from UTF-8 to // the target encoding, because mbstring is fine with converting from // a nonsense encoding. $encoding = $xaction->getNewValue(); if (strlen($encoding)) { try { phutil_utf8_convert('.', $encoding, 'UTF-8'); } catch (Exception $ex) { throw new PhutilProxyException( pht( "Error setting repository encoding '%s': %s'", $encoding, $ex->getMessage()), $ex); } } $object->setDetail('encoding', $encoding); break; } } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { - return; + + switch ($xaction->getTransactionType()) { + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: + // Adjust the object <-> credential edge for this repository. + + $old_phid = $xaction->getOldValue(); + $new_phid = $xaction->getNewValue(); + + $editor = id(new PhabricatorEdgeEditor()) + ->setActor($this->requireActor()); + + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_USES_CREDENTIAL; + $src_phid = $object->getPHID(); + + if ($old_phid) { + $editor->removeEdge($src_phid, $edge_type, $old_phid); + } + + if ($new_phid) { + $editor->addEdge($src_phid, $edge_type, $new_phid); + } + + $editor->save(); + break; + } + } protected function mergeTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { $type = $u->getTransactionType(); switch ($type) { } return parent::mergeTransactions($u, $v); } protected function transactionHasEffect( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $type = $xaction->getTransactionType(); switch ($type) { } return parent::transactionHasEffect($object, $xaction); } protected function requireCapabilities( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorRepositoryTransaction::TYPE_ACTIVATE: case PhabricatorRepositoryTransaction::TYPE_NAME: case PhabricatorRepositoryTransaction::TYPE_DESCRIPTION: case PhabricatorRepositoryTransaction::TYPE_ENCODING: case PhabricatorRepositoryTransaction::TYPE_DEFAULT_BRANCH: case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE_ONLY: case PhabricatorRepositoryTransaction::TYPE_UUID: case PhabricatorRepositoryTransaction::TYPE_SVN_SUBPATH: case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN: case PhabricatorRepositoryTransaction::TYPE_SSH_KEY: case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE: case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN: case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS: case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: case PhabricatorRepositoryTransaction::TYPE_VCS: case PhabricatorRepositoryTransaction::TYPE_NOTIFY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: case PhabricatorRepositoryTransaction::TYPE_HOSTING: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, PhabricatorPolicyCapability::CAN_EDIT); break; } } } diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index c77a6a62c7..af00bb58e8 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1,1012 +1,1001 @@ setViewer($actor) ->withClasses(array('PhabricatorApplicationDiffusion')) ->executeOne(); $view_policy = $app->getPolicy(DiffusionCapabilityDefaultView::CAPABILITY); $edit_policy = $app->getPolicy(DiffusionCapabilityDefaultEdit::CAPABILITY); $push_policy = $app->getPolicy(DiffusionCapabilityDefaultPush::CAPABILITY); return id(new PhabricatorRepository()) ->setViewPolicy($view_policy) ->setEditPolicy($edit_policy) ->setPushPolicy($push_policy); } public function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'details' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorRepositoryPHIDTypeRepository::TYPECONST); } public function toDictionary() { return array( 'name' => $this->getName(), 'phid' => $this->getPHID(), 'callsign' => $this->getCallsign(), 'vcs' => $this->getVersionControlSystem(), 'uri' => PhabricatorEnv::getProductionURI($this->getURI()), 'remoteURI' => (string)$this->getPublicRemoteURI(), 'tracking' => $this->getDetail('tracking-enabled'), 'description' => $this->getDetail('description'), ); } public function getDetail($key, $default = null) { return idx($this->details, $key, $default); } public function getHumanReadableDetail($key, $default = null) { $value = $this->getDetail($key, $default); switch ($key) { case 'branch-filter': case 'close-commits-filter': $value = array_keys($value); $value = implode(', ', $value); break; } return $value; } public function setDetail($key, $value) { $this->details[$key] = $value; return $this; } public function attachCommitCount($count) { $this->commitCount = $count; return $this; } public function getCommitCount() { return $this->assertAttached($this->commitCount); } public function attachMostRecentCommit( PhabricatorRepositoryCommit $commit = null) { $this->mostRecentCommit = $commit; return $this; } public function getMostRecentCommit() { return $this->assertAttached($this->mostRecentCommit); } public function getDiffusionBrowseURIForPath( PhabricatorUser $user, $path, $line = null, $branch = null) { $drequest = DiffusionRequest::newFromDictionary( array( 'user' => $user, 'repository' => $this, 'path' => $path, 'branch' => $branch, )); return $drequest->generateURI( array( 'action' => 'browse', 'line' => $line, )); } public function getLocalPath() { return $this->getDetail('local-path'); } public function getSubversionBaseURI($commit = null) { $subpath = $this->getDetail('svn-subpath'); if (!strlen($subpath)) { $subpath = null; } return $this->getSubversionPathURI($subpath, $commit); } public function getSubversionPathURI($path = null, $commit = null) { $vcs = $this->getVersionControlSystem(); if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_SVN) { throw new Exception("Not a subversion repository!"); } if ($this->isHosted()) { $uri = 'file://'.$this->getLocalPath(); } else { $uri = $this->getDetail('remote-uri'); } $uri = rtrim($uri, '/'); if (strlen($path)) { $path = rawurlencode($path); $path = str_replace('%2F', '/', $path); $uri = $uri.'/'.ltrim($path, '/'); } if ($path !== null || $commit !== null) { $uri .= '@'; } if ($commit !== null) { $uri .= $commit; } return $uri; } /* -( Remote Command Execution )------------------------------------------- */ public function execRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newRemoteCommandFuture($args)->resolve(); } public function execxRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newRemoteCommandFuture($args)->resolvex(); } public function getRemoteCommandFuture($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newRemoteCommandFuture($args); } public function passthruRemoteCommand($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newRemoteCommandPassthru($args)->execute(); } private function newRemoteCommandFuture(array $argv) { $argv = $this->formatRemoteCommand($argv); $future = newv('ExecFuture', $argv); $future->setEnv($this->getRemoteCommandEnvironment()); return $future; } private function newRemoteCommandPassthru(array $argv) { $argv = $this->formatRemoteCommand($argv); $passthru = newv('PhutilExecPassthru', $argv); $passthru->setEnv($this->getRemoteCommandEnvironment()); return $passthru; } /* -( Local Command Execution )-------------------------------------------- */ public function execLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newLocalCommandFuture($args)->resolve(); } public function execxLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newLocalCommandFuture($args)->resolvex(); } public function getLocalCommandFuture($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newLocalCommandFuture($args); } public function passthruLocalCommand($pattern /* , $arg, ... */) { $args = func_get_args(); return $this->newLocalCommandPassthru($args)->execute(); } private function newLocalCommandFuture(array $argv) { $this->assertLocalExists(); $argv = $this->formatLocalCommand($argv); $future = newv('ExecFuture', $argv); $future->setEnv($this->getLocalCommandEnvironment()); if ($this->usesLocalWorkingCopy()) { $future->setCWD($this->getLocalPath()); } return $future; } private function newLocalCommandPassthru(array $argv) { $this->assertLocalExists(); $argv = $this->formatLocalCommand($argv); $future = newv('PhutilExecPassthru', $argv); $future->setEnv($this->getLocalCommandEnvironment()); if ($this->usesLocalWorkingCopy()) { $future->setCWD($this->getLocalPath()); } return $future; } /* -( Command Infrastructure )--------------------------------------------- */ private function getSSHWrapper() { $root = dirname(phutil_get_library_root('phabricator')); return $root.'/bin/ssh-connect'; } private function getCommonCommandEnvironment() { $env = array( // NOTE: Force the language to "C", which overrides locale settings. // This makes stuff print in English instead of, e.g., French, so we can // parse the output of some commands, error messages, etc. 'LANG' => 'C', ); switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: // NOTE: See T2965. Some time after Git 1.7.5.4, Git started fataling if // it can not read $HOME. For many users, $HOME points at /root (this // seems to be a default result of Apache setup). Instead, explicitly // point $HOME at a readable, empty directory so that Git looks for the // config file it's after, fails to locate it, and moves on. This is // really silly, but seems like the least damaging approach to // mitigating the issue. $root = dirname(phutil_get_library_root('phabricator')); $env['HOME'] = $root.'/support/empty/'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: // NOTE: This overrides certain configuration, extensions, and settings // which make Mercurial commands do random unusual things. $env['HGPLAIN'] = 1; break; default: throw new Exception("Unrecognized version control system."); } return $env; } private function getLocalCommandEnvironment() { return $this->getCommonCommandEnvironment(); } private function getRemoteCommandEnvironment() { $env = $this->getCommonCommandEnvironment(); if ($this->shouldUseSSH()) { // NOTE: This is read by `bin/ssh-connect`, and tells it which credentials // to use. $env['PHABRICATOR_SSH_TARGET'] = $this->getCallsign(); switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: // Force SVN to use `bin/ssh-connect`. $env['SVN_SSH'] = $this->getSSHWrapper(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: // Force Git to use `bin/ssh-connect`. $env['GIT_SSH'] = $this->getSSHWrapper(); break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: // We force Mercurial through `bin/ssh-connect` too, but it uses a // command-line flag instead of an environmental variable. break; default: throw new Exception("Unrecognized version control system."); } } return $env; } private function formatRemoteCommand(array $args) { $pattern = $args[0]; $args = array_slice($args, 1); switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: - if ($this->shouldUseHTTP()) { - $pattern = - "svn ". - "--non-interactive ". - "--no-auth-cache ". - "--trust-server-cert ". - "--username %P ". - "--password %P ". - $pattern; - array_unshift( - $args, - new PhutilOpaqueEnvelope($this->getDetail('http-login')), - new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); - } else if ($this->shouldUseSVNProtocol()) { - $pattern = - "svn ". - "--non-interactive ". - "--no-auth-cache ". - "--username %P ". - "--password %P ". - $pattern; - array_unshift( - $args, - new PhutilOpaqueEnvelope($this->getDetail('http-login')), - new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); + if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) { + $flags = array(); + $flag_args = array(); + $flags[] = '--non-interactive'; + $flags[] = '--no-auth-cache'; + if ($this->shouldUseHTTP()) { + $flags[] = '--trust-server-cert'; + } + + $credential_phid = $this->getCredentialPHID(); + if ($credential_phid) { + $key = PassphrasePasswordKey::loadFromPHID( + $credential_phid, + PhabricatorUser::getOmnipotentUser()); + $flags[] = '--username %P'; + $flags[] = '--password %P'; + $flag_args[] = $key->getUsernameEnvelope(); + $flag_args[] = $key->getPasswordEnvelope(); + } + + $flags = implode(' ', $flags); + $pattern = "svn {$flags} {$pattern}"; + $args = array_mergev(array($flag_args, $args)); } else { $pattern = "svn --non-interactive {$pattern}"; } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $pattern = "git {$pattern}"; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: if ($this->shouldUseSSH()) { $pattern = "hg --config ui.ssh=%s {$pattern}"; array_unshift( $args, $this->getSSHWrapper()); } else { $pattern = "hg {$pattern}"; } break; default: throw new Exception("Unrecognized version control system."); } array_unshift($args, $pattern); return $args; } private function formatLocalCommand(array $args) { $pattern = $args[0]; $args = array_slice($args, 1); switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $pattern = "svn --non-interactive {$pattern}"; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $pattern = "git {$pattern}"; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $pattern = "hg {$pattern}"; break; default: throw new Exception("Unrecognized version control system."); } array_unshift($args, $pattern); return $args; } public function getSSHLogin() { return $this->getDetail('ssh-login'); } public function getURI() { return '/diffusion/'.$this->getCallsign().'/'; } public function isTracked() { return $this->getDetail('tracking-enabled', false); } public function getDefaultBranch() { $default = $this->getDetail('default-branch'); if (strlen($default)) { return $default; } $default_branches = array( PhabricatorRepositoryType::REPOSITORY_TYPE_GIT => 'master', PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL => 'default', ); return idx($default_branches, $this->getVersionControlSystem()); } public function getDefaultArcanistBranch() { return coalesce($this->getDefaultBranch(), 'svn'); } private function isBranchInFilter($branch, $filter_key) { $vcs = $this->getVersionControlSystem(); $is_git = ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); $use_filter = ($is_git); if ($use_filter) { $filter = $this->getDetail($filter_key, array()); if ($filter && empty($filter[$branch])) { return false; } } // By default, all branches pass. return true; } public function shouldTrackBranch($branch) { return $this->isBranchInFilter($branch, 'branch-filter'); } public function shouldAutocloseBranch($branch) { if ($this->isImporting()) { return false; } if ($this->getDetail('disable-autoclose', false)) { return false; } return $this->isBranchInFilter($branch, 'close-commits-filter'); } public function shouldAutocloseCommit( PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data) { if ($this->getDetail('disable-autoclose', false)) { return false; } switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: return true; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: return true; default: throw new Exception("Unrecognized version control system."); } $branches = $data->getCommitDetail('seenOnBranches', array()); foreach ($branches as $branch) { if ($this->shouldAutocloseBranch($branch)) { return true; } } return false; } public function formatCommitName($commit_identifier) { $vcs = $this->getVersionControlSystem(); $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; $type_hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL; $is_git = ($vcs == $type_git); $is_hg = ($vcs == $type_hg); if ($is_git || $is_hg) { $short_identifier = substr($commit_identifier, 0, 12); } else { $short_identifier = $commit_identifier; } return 'r'.$this->getCallsign().$short_identifier; } public function isImporting() { return (bool)$this->getDetail('importing', false); } /* -( Repository URI Management )------------------------------------------ */ /** * Get the remote URI for this repository. * * @return string * @task uri */ public function getRemoteURI() { return (string)$this->getRemoteURIObject(); } /** * Get the remote URI for this repository, without authentication information. * * @return string Repository URI. * @task uri */ public function getPublicRemoteURI() { $uri = $this->getRemoteURIObject(); // Make sure we don't leak anything if this repo is using HTTP Basic Auth // with the credentials in the URI or something zany like that. // If repository is not accessed over SSH we remove both username and // password. if (!$this->shouldUseSSH()) { $uri->setUser(null); $uri->setPass(null); } return (string)$uri; } /** * Get the protocol for the repository's remote. * * @return string Protocol, like "ssh" or "git". * @task uri */ public function getRemoteProtocol() { $uri = $this->getRemoteURIObject(); if ($uri instanceof PhutilGitURI) { return 'ssh'; } else { return $uri->getProtocol(); } } /** * Get a parsed object representation of the repository's remote URI. This * may be a normal URI (returned as a @{class@libphutil:PhutilURI}) or a git * URI (returned as a @{class@libphutil:PhutilGitURI}). * * @return wild A @{class@libphutil:PhutilURI} or * @{class@libphutil:PhutilGitURI}. * @task uri */ public function getRemoteURIObject() { $raw_uri = $this->getDetail('remote-uri'); if (!$raw_uri) { return new PhutilURI(''); } if (!strncmp($raw_uri, '/', 1)) { return new PhutilURI('file://'.$raw_uri); } $uri = new PhutilURI($raw_uri); if ($uri->getProtocol()) { if ($this->isSSHProtocol($uri->getProtocol())) { if ($this->getSSHLogin()) { $uri->setUser($this->getSSHLogin()); } } return $uri; } $uri = new PhutilGitURI($raw_uri); if ($uri->getDomain()) { if ($this->getSSHLogin()) { $uri->setUser($this->getSSHLogin()); } return $uri; } throw new Exception("Remote URI '{$raw_uri}' could not be parsed!"); } /** * Determine if we should connect to the remote using SSH flags and * credentials. * * @return bool True to use the SSH protocol. * @task uri */ private function shouldUseSSH() { if ($this->isHosted()) { return false; } $protocol = $this->getRemoteProtocol(); if ($this->isSSHProtocol($protocol)) { $key = $this->getDetail('ssh-key'); $keyfile = $this->getDetail('ssh-keyfile'); if ($key || $keyfile) { return true; } } return false; } /** * Determine if we should connect to the remote using HTTP flags and * credentials. * * @return bool True to use the HTTP protocol. * @task uri */ private function shouldUseHTTP() { if ($this->isHosted()) { return false; } $protocol = $this->getRemoteProtocol(); - if ($protocol == 'http' || $protocol == 'https') { - return (bool)$this->getDetail('http-login'); - } else { - return false; - } + return ($protocol == 'http' || $protocol == 'https'); } /** * Determine if we should connect to the remote using SVN flags and * credentials. * * @return bool True to use the SVN protocol. * @task uri */ private function shouldUseSVNProtocol() { if ($this->isHosted()) { return false; } $protocol = $this->getRemoteProtocol(); - if ($protocol == 'svn') { - return (bool)$this->getDetail('http-login'); - } else { - return false; - } + return ($protocol == 'svn'); } /** * Determine if a protocol is SSH or SSH-like. * * @param string A protocol string, like "http" or "ssh". * @return bool True if the protocol is SSH-like. * @task uri */ private function isSSHProtocol($protocol) { return ($protocol == 'ssh' || $protocol == 'svn+ssh'); } public function delete() { $this->openTransaction(); $paths = id(new PhabricatorOwnersPath()) ->loadAllWhere('repositoryPHID = %s', $this->getPHID()); foreach ($paths as $path) { $path->delete(); } $projects = id(new PhabricatorRepositoryArcanistProject()) ->loadAllWhere('repositoryID = %d', $this->getID()); foreach ($projects as $project) { // note each project deletes its PhabricatorRepositorySymbols $project->delete(); } $commits = id(new PhabricatorRepositoryCommit()) ->loadAllWhere('repositoryID = %d', $this->getID()); foreach ($commits as $commit) { // note PhabricatorRepositoryAuditRequests and // PhabricatorRepositoryCommitData are deleted here too. $commit->delete(); } $conn_w = $this->establishConnection('w'); queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d', self::TABLE_FILESYSTEM, $this->getID()); queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d', self::TABLE_PATHCHANGE, $this->getID()); queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d', self::TABLE_SUMMARY, $this->getID()); $result = parent::delete(); $this->saveTransaction(); return $result; } public function isGit() { $vcs = $this->getVersionControlSystem(); return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_GIT); } public function isSVN() { $vcs = $this->getVersionControlSystem(); return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); } public function isHg() { $vcs = $this->getVersionControlSystem(); return ($vcs == PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL); } public function isHosted() { return (bool)$this->getDetail('hosting-enabled', false); } public function setHosted($enabled) { return $this->setDetail('hosting-enabled', $enabled); } public function getServeOverHTTP() { if ($this->isSVN()) { return self::SERVE_OFF; } $serve = $this->getDetail('serve-over-http', self::SERVE_OFF); return $this->normalizeServeConfigSetting($serve); } public function setServeOverHTTP($mode) { return $this->setDetail('serve-over-http', $mode); } public function getServeOverSSH() { $serve = $this->getDetail('serve-over-ssh', self::SERVE_OFF); return $this->normalizeServeConfigSetting($serve); } public function setServeOverSSH($mode) { return $this->setDetail('serve-over-ssh', $mode); } public static function getProtocolAvailabilityName($constant) { switch ($constant) { case self::SERVE_OFF: return pht('Off'); case self::SERVE_READONLY: return pht('Read Only'); case self::SERVE_READWRITE: return pht('Read/Write'); default: return pht('Unknown'); } } private function normalizeServeConfigSetting($value) { switch ($value) { case self::SERVE_OFF: case self::SERVE_READONLY: return $value; case self::SERVE_READWRITE: if ($this->isHosted()) { return self::SERVE_READWRITE; } else { return self::SERVE_READONLY; } default: return self::SERVE_OFF; } } /** * Raise more useful errors when there are basic filesystem problems. */ private function assertLocalExists() { if (!$this->usesLocalWorkingCopy()) { return; } $local = $this->getLocalPath(); Filesystem::assertExists($local); Filesystem::assertIsDirectory($local); Filesystem::assertReadable($local); } /** * Determine if the working copy is bare or not. In Git, this corresponds * to `--bare`. In Mercurial, `--noupdate`. */ public function isWorkingCopyBare() { switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: return false; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $local = $this->getLocalPath(); if (Filesystem::pathExists($local.'/.git')) { return false; } else { return true; } } } public function usesLocalWorkingCopy() { switch ($this->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: return $this->isHosted(); case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: return true; } } public function canDestroyWorkingCopy() { if ($this->isHosted()) { // Never destroy hosted working copies. return false; } $default_path = PhabricatorEnv::getEnvConfig( 'repository.default-local-path'); return Filesystem::isDescendant($this->getLocalPath(), $default_path); } public function writeStatusMessage( $status_type, $status_code, array $parameters = array()) { $table = new PhabricatorRepositoryStatusMessage(); $conn_w = $table->establishConnection('w'); $table_name = $table->getTableName(); if ($status_code === null) { queryfx( $conn_w, 'DELETE FROM %T WHERE repositoryID = %d AND statusType = %s', $table_name, $this->getID(), $status_type); } else { queryfx( $conn_w, 'INSERT INTO %T (repositoryID, statusType, statusCode, parameters, epoch) VALUES (%d, %s, %s, %s, %d) ON DUPLICATE KEY UPDATE statusCode = VALUES(statusCode), parameters = VALUES(parameters), epoch = VALUES(epoch)', $table_name, $this->getID(), $status_type, $status_code, json_encode($parameters), time()); } return $this; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, DiffusionCapabilityPush::CAPABILITY, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); case DiffusionCapabilityPush::CAPABILITY: return $this->getPushPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { return false; } public function describeAutomaticCapability($capability) { return null; } /* -( PhabricatorMarkupInterface )----------------------------------------- */ public function getMarkupFieldKey($field) { $hash = PhabricatorHash::digestForIndex($this->getMarkupText($field)); return "repo:{$hash}"; } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newMarkupEngine(array()); } public function getMarkupText($field) { return $this->getDetail('description'); } public function didMarkupText( $field, $output, PhutilMarkupEngine $engine) { require_celerity_resource('phabricator-remarkup-css'); return phutil_tag( 'div', array( 'class' => 'phabricator-remarkup', ), $output); } public function shouldUseMarkupCache($field) { return true; } } diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php index e40c139cf4..84c74276ab 100644 --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -1,364 +1,367 @@ getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_PUSH_POLICY: $phids[] = $old; $phids[] = $new; break; } return $phids; } public function shouldHide() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_REMOTE_URI: case self::TYPE_SSH_LOGIN: case self::TYPE_SSH_KEY: case self::TYPE_SSH_KEYFILE: case self::TYPE_HTTP_LOGIN: case self::TYPE_HTTP_PASS: // Hide null vs empty string changes. return (!strlen($old) && !strlen($new)); case self::TYPE_LOCAL_PATH: case self::TYPE_NAME: // Hide these on create, they aren't interesting and we have an // explicit "create" transaction. if (!strlen($old)) { return true; } break; } return parent::shouldHide(); } public function getIcon() { switch ($this->getTransactionType()) { case self::TYPE_VCS: return 'create'; } return parent::getIcon(); } public function getColor() { switch ($this->getTransactionType()) { case self::TYPE_VCS: return 'green'; } return parent::getIcon(); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_VCS: return pht( '%s created this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_ACTIVATE: if ($new) { return pht( '%s activated this repository.', $this->renderHandleLink($author_phid)); } else { return pht( '%s deactivated this repository.', $this->renderHandleLink($author_phid)); } case self::TYPE_NAME: return pht( '%s renamed this repository from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); case self::TYPE_DESCRIPTION: return pht( '%s updated the description of this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_ENCODING: if (strlen($old) && !strlen($new)) { return pht( '%s removed the "%s" encoding configured for this repository.', $this->renderHandleLink($author_phid), $old); } else if (strlen($new) && !strlen($old)) { return pht( '%s set the encoding for this repository to "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s changed the repository encoding from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } case self::TYPE_DEFAULT_BRANCH: if (!strlen($new)) { return pht( '%s removed "%s" as the default branch.', $this->renderHandleLink($author_phid), $old); } else if (!strlen($old)) { return pht( '%s set the default branch to "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s changed the default branch from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } break; case self::TYPE_TRACK_ONLY: if (!$new) { return pht( '%s set this repository to track all branches.', $this->renderHandleLink($author_phid)); } else if (!$old) { return pht( '%s set this repository to track branches: %s.', $this->renderHandleLink($author_phid), implode(', ', $new)); } else { return pht( '%s changed track branches from "%s" to "%s".', $this->renderHandleLink($author_phid), implode(', ', $old), implode(', ', $new)); } break; case self::TYPE_AUTOCLOSE_ONLY: if (!$new) { return pht( '%s set this repository to autoclose on all branches.', $this->renderHandleLink($author_phid)); } else if (!$old) { return pht( '%s set this repository to autoclose on branches: %s.', $this->renderHandleLink($author_phid), implode(', ', $new)); } else { return pht( '%s changed autoclose branches from "%s" to "%s".', $this->renderHandleLink($author_phid), implode(', ', $old), implode(', ', $new)); } break; case self::TYPE_UUID: if (!strlen($new)) { return pht( '%s removed "%s" as the repository UUID.', $this->renderHandleLink($author_phid), $old); } else if (!strlen($old)) { return pht( '%s set the repository UUID to "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s changed the repository UUID from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } break; case self::TYPE_SVN_SUBPATH: if (!strlen($new)) { return pht( '%s removed "%s" as the Import Only path.', $this->renderHandleLink($author_phid), $old); } else if (!strlen($old)) { return pht( '%s set the repository to import only "%s".', $this->renderHandleLink($author_phid), $new); } else { return pht( '%s changed the import path from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } break; case self::TYPE_NOTIFY: if ($new) { return pht( '%s enabled notifications and publishing for this repository.', $this->renderHandleLink($author_phid)); } else { return pht( '%s disabled notifications and publishing for this repository.', $this->renderHandleLink($author_phid)); } break; case self::TYPE_AUTOCLOSE: if ($new) { return pht( '%s enabled autoclose for this repository.', $this->renderHandleLink($author_phid)); } else { return pht( '%s disabled autoclose for this repository.', $this->renderHandleLink($author_phid)); } break; case self::TYPE_REMOTE_URI: if (!strlen($old)) { return pht( '%s set the remote URI for this repository to "%s".', $this->renderHandleLink($author_phid), $new); } else if (!strlen($new)) { return pht( '%s removed the remote URI for this repository.', $this->renderHandleLink($author_phid)); } else { return pht( '%s changed the remote URI for this repository from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } break; case self::TYPE_SSH_LOGIN: return pht( '%s updated the SSH login for this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_SSH_KEY: return pht( '%s updated the SSH key for this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_SSH_KEYFILE: return pht( '%s updated the SSH keyfile for this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_HTTP_LOGIN: return pht( '%s updated the HTTP login for this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_HTTP_PASS: return pht( '%s updated the HTTP password for this repository.', $this->renderHandleLink($author_phid)); case self::TYPE_LOCAL_PATH: return pht( '%s changed the local path from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); case self::TYPE_HOSTING: if ($new) { return pht( '%s changed this repository to be hosted on Phabricator.', $this->renderHandleLink($author_phid)); } else { return pht( '%s changed this repository to track a remote elsewhere.', $this->renderHandleLink($author_phid)); } case self::TYPE_PROTOCOL_HTTP: return pht( '%s changed the availability of this repository over HTTP from '. '"%s" to "%s".', $this->renderHandleLink($author_phid), PhabricatorRepository::getProtocolAvailabilityName($old), PhabricatorRepository::getProtocolAvailabilityName($new)); case self::TYPE_PROTOCOL_SSH: return pht( '%s changed the availability of this repository over SSH from '. '"%s" to "%s".', $this->renderHandleLink($author_phid), PhabricatorRepository::getProtocolAvailabilityName($old), PhabricatorRepository::getProtocolAvailabilityName($new)); case self::TYPE_PUSH_POLICY: return pht( '%s changed the push policy of this repository from "%s" to "%s".', $this->renderHandleLink($author_phid), $this->renderPolicyName($old), $this->renderPolicyName($new)); } return parent::getTitle(); } public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_DESCRIPTION: return true; } return parent::hasChangeDetails(); } public function renderChangeDetails(PhabricatorUser $viewer) { $old = $this->getOldValue(); $new = $this->getNewValue(); $view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) ->setUser($viewer) ->setOldText($old) ->setNewText($new); return $view->render(); } } diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 253633fddc..95ffc58695 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1,1781 +1,1789 @@ array( 'type' => 'db', 'name' => 'audit', 'after' => array( /* First Patch */ ), ), 'db.calendar' => array( 'type' => 'db', 'name' => 'calendar', ), 'db.chatlog' => array( 'type' => 'db', 'name' => 'chatlog', ), 'db.conduit' => array( 'type' => 'db', 'name' => 'conduit', ), 'db.countdown' => array( 'type' => 'db', 'name' => 'countdown', ), 'db.daemon' => array( 'type' => 'db', 'name' => 'daemon', ), 'db.differential' => array( 'type' => 'db', 'name' => 'differential', ), 'db.draft' => array( 'type' => 'db', 'name' => 'draft', ), 'db.drydock' => array( 'type' => 'db', 'name' => 'drydock', ), 'db.feed' => array( 'type' => 'db', 'name' => 'feed', ), 'db.file' => array( 'type' => 'db', 'name' => 'file', ), 'db.flag' => array( 'type' => 'db', 'name' => 'flag', ), 'db.harbormaster' => array( 'type' => 'db', 'name' => 'harbormaster', ), 'db.herald' => array( 'type' => 'db', 'name' => 'herald', ), 'db.maniphest' => array( 'type' => 'db', 'name' => 'maniphest', ), 'db.meta_data' => array( 'type' => 'db', 'name' => 'meta_data', ), 'db.metamta' => array( 'type' => 'db', 'name' => 'metamta', ), 'db.oauth_server' => array( 'type' => 'db', 'name' => 'oauth_server', ), 'db.owners' => array( 'type' => 'db', 'name' => 'owners', ), 'db.pastebin' => array( 'type' => 'db', 'name' => 'pastebin', ), 'db.phame' => array( 'type' => 'db', 'name' => 'phame', ), 'db.phriction' => array( 'type' => 'db', 'name' => 'phriction', ), 'db.project' => array( 'type' => 'db', 'name' => 'project', ), 'db.repository' => array( 'type' => 'db', 'name' => 'repository', ), 'db.search' => array( 'type' => 'db', 'name' => 'search', ), 'db.slowvote' => array( 'type' => 'db', 'name' => 'slowvote', ), 'db.timeline' => array( 'type' => 'db', 'name' => 'timeline', 'dead' => true, ), 'db.user' => array( 'type' => 'db', 'name' => 'user', ), 'db.worker' => array( 'type' => 'db', 'name' => 'worker', ), 'db.xhpastview' => array( 'type' => 'db', 'name' => 'xhpastview', ), 'db.cache' => array( 'type' => 'db', 'name' => 'cache', ), 'db.fact' => array( 'type' => 'db', 'name' => 'fact', ), 'db.ponder' => array( 'type' => 'db', 'name' => 'ponder', ), 'db.xhprof' => array( 'type' => 'db', 'name' => 'xhprof', ), 'db.pholio' => array( 'type' => 'db', 'name' => 'pholio', ), 'db.conpherence' => array( 'type' => 'db', 'name' => 'conpherence', ), 'db.config' => array( 'type' => 'db', 'name' => 'config', ), 'db.token' => array( 'type' => 'db', 'name' => 'token', ), 'db.releeph' => array( 'type' => 'db', 'name' => 'releeph', ), 'db.phlux' => array( 'type' => 'db', 'name' => 'phlux', ), 'db.phortune' => array( 'type' => 'db', 'name' => 'phortune', ), 'db.phrequent' => array( 'type' => 'db', 'name' => 'phrequent', ), 'db.diviner' => array( 'type' => 'db', 'name' => 'diviner', ), 'db.auth' => array( 'type' => 'db', 'name' => 'auth', ), 'db.doorkeeper' => array( 'type' => 'db', 'name' => 'doorkeeper', ), 'db.legalpad' => array( 'type' => 'db', 'name' => 'legalpad', ), 'db.policy' => array( 'type' => 'db', 'name' => 'policy', ), 'db.nuance' => array( 'type' => 'db', 'name' => 'nuance', ), 'db.passphrase' => array( 'type' => 'db', 'name' => 'passphrase', ), '0000.legacy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('0000.legacy.sql'), 'legacy' => 0, ), '000.project.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('000.project.sql'), 'legacy' => 0, ), '001.maniphest_projects.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('001.maniphest_projects.sql'), 'legacy' => 1, ), '002.oauth.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('002.oauth.sql'), 'legacy' => 2, ), '003.more_oauth.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('003.more_oauth.sql'), 'legacy' => 3, ), '004.daemonrepos.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('004.daemonrepos.sql'), 'legacy' => 4, ), '005.workers.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('005.workers.sql'), 'legacy' => 5, ), '006.repository.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('006.repository.sql'), 'legacy' => 6, ), '007.daemonlog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('007.daemonlog.sql'), 'legacy' => 7, ), '008.repoopt.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('008.repoopt.sql'), 'legacy' => 8, ), '009.repo_summary.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('009.repo_summary.sql'), 'legacy' => 9, ), '010.herald.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('010.herald.sql'), 'legacy' => 10, ), '011.badcommit.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('011.badcommit.sql'), 'legacy' => 11, ), '012.dropphidtype.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('012.dropphidtype.sql'), 'legacy' => 12, ), '013.commitdetail.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('013.commitdetail.sql'), 'legacy' => 13, ), '014.shortcuts.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('014.shortcuts.sql'), 'legacy' => 14, ), '015.preferences.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('015.preferences.sql'), 'legacy' => 15, ), '016.userrealnameindex.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('016.userrealnameindex.sql'), 'legacy' => 16, ), '017.sessionkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('017.sessionkeys.sql'), 'legacy' => 17, ), '018.owners.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('018.owners.sql'), 'legacy' => 18, ), '019.arcprojects.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('019.arcprojects.sql'), 'legacy' => 19, ), '020.pathcapital.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('020.pathcapital.sql'), 'legacy' => 20, ), '021.xhpastview.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('021.xhpastview.sql'), 'legacy' => 21, ), '022.differentialcommit.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('022.differentialcommit.sql'), 'legacy' => 22, ), '023.dxkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('023.dxkeys.sql'), 'legacy' => 23, ), '024.mlistkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('024.mlistkeys.sql'), 'legacy' => 24, ), '025.commentopt.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('025.commentopt.sql'), 'legacy' => 25, ), '026.diffpropkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('026.diffpropkey.sql'), 'legacy' => 26, ), '027.metamtakeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('027.metamtakeys.sql'), 'legacy' => 27, ), '028.systemagent.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('028.systemagent.sql'), 'legacy' => 28, ), '029.cursors.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('029.cursors.sql'), 'legacy' => 29, ), '030.imagemacro.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('030.imagemacro.sql'), 'legacy' => 30, ), '031.workerrace.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('031.workerrace.sql'), 'legacy' => 31, ), '032.viewtime.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('032.viewtime.sql'), 'legacy' => 32, ), '033.privtest.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('033.privtest.sql'), 'legacy' => 33, ), '034.savedheader.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('034.savedheader.sql'), 'legacy' => 34, ), '035.proxyimage.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('035.proxyimage.sql'), 'legacy' => 35, ), '036.mailkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('036.mailkey.sql'), 'legacy' => 36, ), '037.setuptest.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('037.setuptest.sql'), 'legacy' => 37, ), '038.admin.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('038.admin.sql'), 'legacy' => 38, ), '039.userlog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('039.userlog.sql'), 'legacy' => 39, ), '040.transform.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('040.transform.sql'), 'legacy' => 40, ), '041.heraldrepetition.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('041.heraldrepetition.sql'), 'legacy' => 41, ), '042.commentmetadata.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('042.commentmetadata.sql'), 'legacy' => 42, ), '043.pastebin.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('043.pastebin.sql'), 'legacy' => 43, ), '044.countdown.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('044.countdown.sql'), 'legacy' => 44, ), '045.timezone.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('045.timezone.sql'), 'legacy' => 45, ), '046.conduittoken.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('046.conduittoken.sql'), 'legacy' => 46, ), '047.projectstatus.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('047.projectstatus.sql'), 'legacy' => 47, ), '048.relationshipkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('048.relationshipkeys.sql'), 'legacy' => 48, ), '049.projectowner.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('049.projectowner.sql'), 'legacy' => 49, ), '050.taskdenormal.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('050.taskdenormal.sql'), 'legacy' => 50, ), '051.projectfilter.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('051.projectfilter.sql'), 'legacy' => 51, ), '052.pastelanguage.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('052.pastelanguage.sql'), 'legacy' => 52, ), '053.feed.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('053.feed.sql'), 'legacy' => 53, ), '054.subscribers.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('054.subscribers.sql'), 'legacy' => 54, ), '055.add_author_to_files.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('055.add_author_to_files.sql'), 'legacy' => 55, ), '056.slowvote.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('056.slowvote.sql'), 'legacy' => 56, ), '057.parsecache.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('057.parsecache.sql'), 'legacy' => 57, ), '058.missingkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('058.missingkeys.sql'), 'legacy' => 58, ), '059.engines.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('059.engines.php'), 'legacy' => 59, ), '060.phriction.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('060.phriction.sql'), 'legacy' => 60, ), '061.phrictioncontent.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('061.phrictioncontent.sql'), 'legacy' => 61, ), '062.phrictionmenu.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('062.phrictionmenu.sql'), 'legacy' => 62, ), '063.pasteforks.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('063.pasteforks.sql'), 'legacy' => 63, ), '064.subprojects.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('064.subprojects.sql'), 'legacy' => 64, ), '065.sshkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('065.sshkeys.sql'), 'legacy' => 65, ), '066.phrictioncontent.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('066.phrictioncontent.sql'), 'legacy' => 66, ), '067.preferences.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('067.preferences.sql'), 'legacy' => 67, ), '068.maniphestauxiliarystorage.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('068.maniphestauxiliarystorage.sql'), 'legacy' => 68, ), '069.heraldxscript.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('069.heraldxscript.sql'), 'legacy' => 69, ), '070.differentialaux.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('070.differentialaux.sql'), 'legacy' => 70, ), '071.contentsource.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('071.contentsource.sql'), 'legacy' => 71, ), '072.blamerevert.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('072.blamerevert.sql'), 'legacy' => 72, ), '073.reposymbols.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('073.reposymbols.sql'), 'legacy' => 73, ), '074.affectedpath.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('074.affectedpath.sql'), 'legacy' => 74, ), '075.revisionhash.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('075.revisionhash.sql'), 'legacy' => 75, ), '076.indexedlanguages.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('076.indexedlanguages.sql'), 'legacy' => 76, ), '077.originalemail.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('077.originalemail.sql'), 'legacy' => 77, ), '078.nametoken.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('078.nametoken.sql'), 'legacy' => 78, ), '079.nametokenindex.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('079.nametokenindex.php'), 'legacy' => 79, ), '080.filekeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('080.filekeys.sql'), 'legacy' => 80, ), '081.filekeys.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('081.filekeys.php'), 'legacy' => 81, ), '082.xactionkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('082.xactionkey.sql'), 'legacy' => 82, ), '083.dxviewtime.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('083.dxviewtime.sql'), 'legacy' => 83, ), '084.pasteauthorkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('084.pasteauthorkey.sql'), 'legacy' => 84, ), '085.packagecommitrelationship.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('085.packagecommitrelationship.sql'), 'legacy' => 85, ), '086.formeraffil.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('086.formeraffil.sql'), 'legacy' => 86, ), '087.phrictiondelete.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('087.phrictiondelete.sql'), 'legacy' => 87, ), '088.audit.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('088.audit.sql'), 'legacy' => 88, ), '089.projectwiki.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('089.projectwiki.sql'), 'legacy' => 89, ), '090.forceuniqueprojectnames.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('090.forceuniqueprojectnames.php'), 'legacy' => 90, ), '091.uniqueslugkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('091.uniqueslugkey.sql'), 'legacy' => 91, ), '092.dropgithubnotification.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('092.dropgithubnotification.sql'), 'legacy' => 92, ), '093.gitremotes.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('093.gitremotes.php'), 'legacy' => 93, ), '094.phrictioncolumn.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('094.phrictioncolumn.sql'), 'legacy' => 94, ), '095.directory.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('095.directory.sql'), 'legacy' => 95, ), '096.filename.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('096.filename.sql'), 'legacy' => 96, ), '097.heraldruletypes.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('097.heraldruletypes.sql'), 'legacy' => 97, ), '098.heraldruletypemigration.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('098.heraldruletypemigration.php'), 'legacy' => 98, ), '099.drydock.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('099.drydock.sql'), 'legacy' => 99, ), '100.projectxaction.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('100.projectxaction.sql'), 'legacy' => 100, ), '101.heraldruleapplied.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('101.heraldruleapplied.sql'), 'legacy' => 101, ), '102.heraldcleanup.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('102.heraldcleanup.php'), 'legacy' => 102, ), '103.heraldedithistory.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('103.heraldedithistory.sql'), 'legacy' => 103, ), '104.searchkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('104.searchkey.sql'), 'legacy' => 104, ), '105.mimetype.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('105.mimetype.sql'), 'legacy' => 105, ), '106.chatlog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('106.chatlog.sql'), 'legacy' => 106, ), '107.oauthserver.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('107.oauthserver.sql'), 'legacy' => 107, ), '108.oauthscope.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('108.oauthscope.sql'), 'legacy' => 108, ), '109.oauthclientphidkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('109.oauthclientphidkey.sql'), 'legacy' => 109, ), '110.commitaudit.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('110.commitaudit.sql'), 'legacy' => 110, ), '111.commitauditmigration.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('111.commitauditmigration.php'), 'legacy' => 111, ), '112.oauthaccesscoderedirecturi.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('112.oauthaccesscoderedirecturi.sql'), 'legacy' => 112, ), '113.lastreviewer.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('113.lastreviewer.sql'), 'legacy' => 113, ), '114.auditrequest.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('114.auditrequest.sql'), 'legacy' => 114, ), '115.prepareutf8.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('115.prepareutf8.sql'), 'legacy' => 115, ), '116.utf8-backup-first-expect-wait.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('116.utf8-backup-first-expect-wait.sql'), 'legacy' => 116, ), '117.repositorydescription.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('117.repositorydescription.php'), 'legacy' => 117, ), '118.auditinline.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('118.auditinline.sql'), 'legacy' => 118, ), '119.filehash.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('119.filehash.sql'), 'legacy' => 119, ), '120.noop.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('120.noop.sql'), 'legacy' => 120, ), '121.drydocklog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('121.drydocklog.sql'), 'legacy' => 121, ), '122.flag.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('122.flag.sql'), 'legacy' => 122, ), '123.heraldrulelog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('123.heraldrulelog.sql'), 'legacy' => 123, ), '124.subpriority.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('124.subpriority.sql'), 'legacy' => 124, ), '125.ipv6.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('125.ipv6.sql'), 'legacy' => 125, ), '126.edges.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('126.edges.sql'), 'legacy' => 126, ), '127.userkeybody.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('127.userkeybody.sql'), 'legacy' => 127, ), '128.phabricatorcom.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('128.phabricatorcom.sql'), 'legacy' => 128, ), '129.savedquery.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('129.savedquery.sql'), 'legacy' => 129, ), '130.denormalrevisionquery.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('130.denormalrevisionquery.sql'), 'legacy' => 130, ), '131.migraterevisionquery.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('131.migraterevisionquery.php'), 'legacy' => 131, ), '132.phame.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('132.phame.sql'), 'legacy' => 132, ), '133.imagemacro.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('133.imagemacro.sql'), 'legacy' => 133, ), '134.emptysearch.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('134.emptysearch.sql'), 'legacy' => 134, ), '135.datecommitted.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('135.datecommitted.sql'), 'legacy' => 135, ), '136.sex.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('136.sex.sql'), 'legacy' => 136, ), '137.auditmetadata.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('137.auditmetadata.sql'), 'legacy' => 137, ), '138.notification.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('138.notification.sql'), ), 'holidays.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('holidays.sql'), ), 'userstatus.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('userstatus.sql'), ), 'emailtable.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('emailtable.sql'), ), 'emailtableport.sql' => array( 'type' => 'php', 'name' => $this->getPatchPath('emailtableport.php'), ), 'emailtableremove.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('emailtableremove.sql'), ), 'phiddrop.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('phiddrop.sql'), ), 'testdatabase.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('testdatabase.sql'), ), 'ldapinfo.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('ldapinfo.sql'), ), 'threadtopic.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('threadtopic.sql'), ), 'usertranslation.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('usertranslation.sql'), ), 'differentialbookmarks.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('differentialbookmarks.sql'), ), 'harbormasterobject.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('harbormasterobject.sql'), ), 'markupcache.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('markupcache.sql'), ), 'maniphestxcache.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('maniphestxcache.sql'), ), 'migrate-maniphest-dependencies.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('migrate-maniphest-dependencies.php'), ), 'migrate-differential-dependencies.php' => array( 'type' => 'php', 'name' => $this->getPatchPath( 'migrate-differential-dependencies.php'), ), 'phameblog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('phameblog.sql'), ), 'migrate-maniphest-revisions.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('migrate-maniphest-revisions.php'), ), 'daemonstatus.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('daemonstatus.sql'), ), 'symbolcontexts.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('symbolcontexts.sql'), ), 'migrate-project-edges.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('migrate-project-edges.php'), ), 'fact-raw.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('fact-raw.sql'), ), 'ponder.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('ponder.sql') ), 'policy-project.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('policy-project.sql'), ), 'daemonstatuskey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('daemonstatuskey.sql'), ), 'edgetype.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('edgetype.sql'), ), 'ponder-comments.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('ponder-comments.sql'), ), 'pastepolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('pastepolicy.sql'), ), 'xhprof.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('xhprof.sql'), ), 'draft-metadata.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('draft-metadata.sql'), ), 'phamedomain.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('phamedomain.sql'), ), 'ponder-mailkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('ponder-mailkey.sql'), ), 'ponder-mailkey-populate.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('ponder-mailkey-populate.php'), ), 'phamepolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('phamepolicy.sql'), ), 'phameoneblog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('phameoneblog.sql'), ), 'statustxt.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('statustxt.sql'), ), 'daemontaskarchive.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('daemontaskarchive.sql'), ), 'drydocktaskid.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('drydocktaskid.sql'), ), 'drydockresoucetype.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('drydockresourcetype.sql'), ), 'liskcounters.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('liskcounters.sql'), ), 'liskcounters.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('liskcounters.php'), ), 'dropfileproxyimage.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('dropfileproxyimage.sql'), ), 'repository-lint.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('repository-lint.sql'), ), 'liskcounters-task.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('liskcounters-task.sql'), ), 'pholio.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('pholio.sql'), ), 'owners-exclude.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('owners-exclude.sql'), ), '20121209.pholioxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20121209.pholioxactions.sql'), ), '20121209.xmacroadd.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20121209.xmacroadd.sql'), ), '20121209.xmacromigrate.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20121209.xmacromigrate.php'), ), '20121209.xmacromigratekey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20121209.xmacromigratekey.sql'), ), '20121220.generalcache.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20121220.generalcache.sql'), ), '20121226.config.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20121226.config.sql'), ), '20130101.confxaction.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130101.confxaction.sql'), ), '20130102.metamtareceivedmailmessageidhash.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130102.metamtareceivedmailmessageidhash.sql'), ), '20130103.filemetadata.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130103.filemetadata.sql'), ), '20130111.conpherence.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130111.conpherence.sql'), ), '20130127.altheraldtranscript.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130127.altheraldtranscript.sql'), ), '20130201.revisionunsubscribed.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130201.revisionunsubscribed.php'), ), '20130201.revisionunsubscribed.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130201.revisionunsubscribed.sql'), ), '20130131.conpherencepics.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130131.conpherencepics.sql'), ), '20130214.chatlogchannel.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130214.chatlogchannel.sql'), ), '20130214.chatlogchannelid.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130214.chatlogchannelid.sql'), ), '20130214.token.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130214.token.sql'), ), '20130215.phabricatorfileaddttl.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130215.phabricatorfileaddttl.sql'), ), '20130217.cachettl.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130217.cachettl.sql'), ), '20130218.updatechannelid.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130218.updatechannelid.php'), ), '20130218.longdaemon.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130218.longdaemon.sql'), ), '20130219.commitsummary.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130219.commitsummary.sql'), ), '20130219.commitsummarymig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130219.commitsummarymig.php'), ), '20130222.dropchannel.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130222.dropchannel.sql'), ), '20130226.commitkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130226.commitkey.sql'), ), '20131302.maniphestvalue.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131302.maniphestvalue.sql'), ), '20130304.lintauthor.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130304.lintauthor.sql'), ), 'releeph.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('releeph.sql'), ), '20130319.phabricatorfileexplicitupload.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath( '20130319.phabricatorfileexplicitupload.sql') ), '20130319.conpherence.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130319.conpherence.sql'), ), '20130320.phlux.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130320.phlux.sql'), ), '20130317.phrictionedge.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130317.phrictionedge.sql'), ), '20130321.token.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130321.token.sql'), ), '20130310.xactionmeta.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130310.xactionmeta.sql'), ), '20130322.phortune.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130322.phortune.sql'), ), '20130323.phortunepayment.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130323.phortunepayment.sql'), ), '20130324.phortuneproduct.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130324.phortuneproduct.sql'), ), '20130330.phrequent.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130330.phrequent.sql'), ), '20130403.conpherencecache.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130403.conpherencecache.sql'), ), '20130403.conpherencecachemig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130403.conpherencecachemig.php'), ), '20130409.commitdrev.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130409.commitdrev.php'), ), '20130417.externalaccount.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130417.externalaccount.sql'), ), '20130423.updateexternalaccount.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130423.updateexternalaccount.sql'), ), '20130423.phortunepaymentrevised.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130423.phortunepaymentrevised.sql'), ), '20130423.conpherenceindices.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130423.conpherenceindices.sql'), ), '20130426.search_savedquery.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130426.search_savedquery.sql'), ), '20130502.countdownrevamp1.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130502.countdownrevamp1.sql'), ), '20130502.countdownrevamp2.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130502.countdownrevamp2.php'), ), '20130502.countdownrevamp3.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130502.countdownrevamp3.sql'), ), '20130507.releephrqsimplifycols.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130507.releephrqsimplifycols.sql'), ), '20130507.releephrqmailkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130507.releephrqmailkey.sql'), ), '20130507.releephrqmailkeypop.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130507.releephrqmailkeypop.php'), ), '20130508.search_namedquery.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130508.search_namedquery.sql'), ), '20130508.releephtransactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130508.releephtransactions.sql'), ), '20130508.releephtransactionsmig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130508.releephtransactionsmig.php'), ), '20130513.receviedmailstatus.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130513.receviedmailstatus.sql'), ), '20130519.diviner.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130519.diviner.sql'), ), '20130521.dropconphimages.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130521.dropconphimages.sql'), ), '20130523.maniphest_owners.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130523.maniphest_owners.sql'), ), '20130524.repoxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130524.repoxactions.sql'), ), '20130529.macroauthor.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130529.macroauthor.sql'), ), '20130529.macroauthormig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130529.macroauthormig.php'), ), '20130530.sessionhash.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130530.sessionhash.php'), ), '20130530.macrodatekey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130530.macrodatekey.sql'), ), '20130530.pastekeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130530.pastekeys.sql'), ), '20130531.filekeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130531.filekeys.sql'), ), '20130602.morediviner.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130602.morediviner.sql'), ), '20130602.namedqueries.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130602.namedqueries.sql'), ), '20130606.userxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130606.userxactions.sql'), ), '20130607.xaccount.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130607.xaccount.sql'), ), '20130611.migrateoauth.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130611.migrateoauth.php'), ), '20130611.nukeldap.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130611.nukeldap.php'), ), '20130613.authdb.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130613.authdb.sql'), ), '20130619.authconf.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130619.authconf.php'), ), '20130620.diffxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130620.diffxactions.sql'), ), '20130621.diffcommentphid.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130621.diffcommentphid.sql'), ), '20130621.diffcommentphidmig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130621.diffcommentphidmig.php'), ), '20130621.diffcommentunphid.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130621.diffcommentunphid.sql'), ), '20130622.doorkeeper.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130622.doorkeeper.sql'), ), '20130628.legalpadv0.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130628.legalpadv0.sql'), ), '20130701.conduitlog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130701.conduitlog.sql'), ), 'legalpad-mailkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('legalpad-mailkey.sql'), ), 'legalpad-mailkey-populate.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('legalpad-mailkey-populate.php'), ), '20130703.legalpaddocdenorm.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130703.legalpaddocdenorm.sql'), ), '20130703.legalpaddocdenorm.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130703.legalpaddocdenorm.php'), ), '20130709.legalpadsignature.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130709.legalpadsignature.sql'), ), '20130709.droptimeline.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130709.droptimeline.sql'), ), '20130711.trimrealnames.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130711.trimrealnames.php'), ), '20130714.votexactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130714.votexactions.sql'), ), '20130715.votecomments.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130715.votecomments.php'), ), '20130715.voteedges.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130715.voteedges.sql'), ), '20130711.pholioimageobsolete.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130711.pholioimageobsolete.sql'), ), '20130711.pholioimageobsolete.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130711.pholioimageobsolete.php'), ), '20130711.pholioimageobsolete2.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130711.pholioimageobsolete2.sql'), ), '20130716.archivememberlessprojects.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130716.archivememberlessprojects.php'), ), '20130722.pholioreplace.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130722.pholioreplace.sql'), ), '20130723.taskstarttime.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130723.taskstarttime.sql'), ), '20130727.ponderquestionstatus.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130727.ponderquestionstatus.sql'), ), '20130726.ponderxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130726.ponderxactions.sql'), ), '20130728.ponderunique.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130728.ponderunique.php'), ), '20130728.ponderuniquekey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130728.ponderuniquekey.sql'), ), '20130728.ponderxcomment.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130728.ponderxcomment.php'), ), '20130801.pastexactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130801.pastexactions.sql'), ), '20130801.pastexactions.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130801.pastexactions.php'), ), '20130805.pastemailkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130805.pastemailkey.sql'), ), '20130805.pasteedges.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130805.pasteedges.sql'), ), '20130805.pastemailkeypop.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130805.pastemailkeypop.php'), ), '20130802.heraldphid.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130802.heraldphid.sql'), ), '20130802.heraldphids.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130802.heraldphids.php'), ), '20130802.heraldphidukey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130802.heraldphidukey.sql'), ), '20130802.heraldxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130802.heraldxactions.sql'), ), '20130731.releephrepoid.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130731.releephrepoid.sql'), ), '20130731.releephproject.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130731.releephproject.sql'), ), '20130731.releephcutpointidentifier.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130731.releephcutpointidentifier.sql'), ), '20130814.usercustom.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130814.usercustom.sql'), ), '20130820.releephxactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130820.releephxactions.sql'), ), '20130826.divinernode.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130826.divinernode.sql'), ), '20130820.filexactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130820.filexactions.sql'), ), '20130820.filemailkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130820.filemailkey.sql'), ), '20130820.file-mailkey-populate.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130820.file-mailkey-populate.php'), ), '20130912.maniphest.1.touch.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130912.maniphest.1.touch.sql'), ), '20130912.maniphest.2.created.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130912.maniphest.2.created.sql'), ), '20130912.maniphest.3.nameindex.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130912.maniphest.3.nameindex.sql'), ), '20130912.maniphest.4.fillindex.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130912.maniphest.4.fillindex.php'), ), '20130913.maniphest.1.migratesearch.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130913.maniphest.1.migratesearch.php'), ), '20130914.usercustom.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130914.usercustom.sql'), ), '20130915.maniphestcustom.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130915.maniphestcustom.sql'), ), '20130915.maniphestmigrate.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130915.maniphestmigrate.php'), ), '20130919.mfieldconf.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130919.mfieldconf.php'), ), '20130920.repokeyspolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130920.repokeyspolicy.sql'), ), '20130921.mtransactions.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130921.mtransactions.sql'), ), '20130921.xmigratemaniphest.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130921.xmigratemaniphest.php'), ), '20130923.mrename.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130923.mrename.sql'), ), '20130924.mdraftkey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130924.mdraftkey.sql'), ), '20130925.mpolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130925.mpolicy.sql'), ), '20130925.xpolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130925.xpolicy.sql'), ), '20130926.dcustom.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130926.dcustom.sql'), ), '20130926.dinkeys.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130926.dinkeys.sql'), ), '20130927.audiomacro.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130927.audiomacro.sql'), ), '20130929.filepolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130929.filepolicy.sql'), ), '20131004.dxedgekey.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131004.dxedgekey.sql'), ), '20131004.dxreviewers.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20131004.dxreviewers.php'), ), '20131006.hdisable.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131006.hdisable.sql'), ), '20131010.pstorage.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131010.pstorage.sql'), ), '20131015.cpolicy.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131015.cpolicy.sql'), ), '20130915.maniphestqdrop.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20130915.maniphestqdrop.sql'), ), '20130926.dinline.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20130926.dinline.php'), ), '20131020.pcustom.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131020.pcustom.sql'), ), '20131020.col1.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131020.col1.sql'), ), '20131020.pxaction.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131020.pxaction.sql'), ), '20131020.pxactionmig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20131020.pxactionmig.php'), ), '20131020.harbormaster.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131020.harbormaster.sql'), ), '20131025.repopush.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131025.repopush.sql'), ), '20131026.commitstatus.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131026.commitstatus.sql'), ), '20131030.repostatusmessage.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131030.repostatusmessage.sql'), ), '20131031.vcspassword.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131031.vcspassword.sql'), ), '20131105.buildstep.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131105.buildstep.sql'), ), '20131106.diffphid.1.col.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131106.diffphid.1.col.sql'), ), '20131106.diffphid.2.mig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20131106.diffphid.2.mig.php'), ), '20131106.diffphid.3.key.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131106.diffphid.3.key.sql'), ), '20131106.nuance-v0.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131106.nuance-v0.sql'), ), '20131107.buildlog.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131107.buildlog.sql'), ), '20131112.userverified.1.col.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131112.userverified.1.col.sql'), ), '20131112.userverified.2.mig.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20131112.userverified.2.mig.php'), ), '20131118.ownerorder.php' => array( 'type' => 'php', 'name' => $this->getPatchPath('20131118.ownerorder.php'), ), '20131119.passphrase.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131119.passphrase.sql'), ), '20131120.nuancesourcetype.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131120.nuancesourcetype.sql'), ), '20131121.passphraseedge.sql' => array( 'type' => 'sql', 'name' => $this->getPatchPath('20131121.passphraseedge.sql'), ), + '20131121.repocredentials.1.col.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20131121.repocredentials.1.col.sql'), + ), + '20131121.repocredentials.2.mig.php' => array( + 'type' => 'php', + 'name' => $this->getPatchPath('20131121.repocredentials.2.mig.php'), + ), ); } }