Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2892512
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
25 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
index 6bb3b0f70c..f38ddffc26 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
@@ -1,768 +1,770 @@
<?php
final class DiffusionRepositoryCreateController
extends DiffusionRepositoryEditController {
private $edit;
private $repository;
public function willProcessRequest(array $data) {
parent::willProcessRequest($data);
$this->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_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_credential)
->setNewValue(
$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(),
'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 PassphraseCredentialControl())
->setName('credential'));
}
public function adjustAuthPage($page) {
$form = $page->getForm();
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);
$c_credential = $page->getControl('credential');
$c_credential->setDefaultUsername($remote_user);
if ($this->isSSHProtocol($proto)) {
$c_credential->setLabel(pht('SSH Key'));
$c_credential->setCredentialType(
PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE);
$provides_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE;
$page->addRemarkupInstructions(
pht(
'Choose or add the SSH credentials to use to connect to the the '.
'repository hosted at:'.
"\n\n".
" lang=text\n".
" %s",
$remote_uri),
'credential');
} else if ($this->isUsernamePasswordProtocol($proto)) {
$c_credential->setLabel(pht('Password'));
$c_credential->setAllowNull(true);
$c_credential->setCredentialType(
PassphraseCredentialTypePassword::CREDENTIAL_TYPE);
$provides_type = PassphraseCredentialTypePassword::PROVIDES_TYPE;
$page->addRemarkupInstructions(
pht(
'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 continue to the next step.",
$remote_uri),
'credential');
} else if ($proto == 'file') {
$c_credential->setHidden(true);
+ $provides_type = null;
+
$page->addRemarkupInstructions(
pht(
'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);
$c_credential = $page->getControl('credential');
$v_credential = $c_credential->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 ($this->isSSHProtocol($proto)) {
if (!$credential) {
$c_credential->setError(pht('Required'));
$page->addPageError(
pht('You must choose an SSH credential to connect over SSH.'));
}
$ssh_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE;
if ($credential->getProvidesType() !== $ssh_type) {
$c_credential->setError(pht('Invalid'));
$page->addPageError(
pht(
'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(
'You must choose a username/password credential, not some other '.
'type of credential.'));
}
}
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;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:55 (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126695
Default Alt Text
(25 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment