1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Build an early multi-step repository create form

Summary: Ref T2231. Ref T2232. This form doesn't do anything yet and there are no link sto it, but it lets you enter all the data to create a repository in a relatively simple, straightforward way.

Test Plan:
{F49740}
{F49741}
{F49742}
{F49743}

Reviewers: chad, btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2231, T2232

Differential Revision: https://secure.phabricator.com/D6430
This commit is contained in:
epriestley 2013-07-14 07:37:17 -07:00
parent b92acf0e5a
commit a0084bbb0d
7 changed files with 698 additions and 34 deletions

View file

@ -490,6 +490,7 @@ phutil_register_library_map(array(
'DiffusionRemarkupRule' => 'applications/diffusion/remarkup/DiffusionRemarkupRule.php', 'DiffusionRemarkupRule' => 'applications/diffusion/remarkup/DiffusionRemarkupRule.php',
'DiffusionRenameHistoryQuery' => 'applications/diffusion/query/DiffusionRenameHistoryQuery.php', 'DiffusionRenameHistoryQuery' => 'applications/diffusion/query/DiffusionRenameHistoryQuery.php',
'DiffusionRepositoryController' => 'applications/diffusion/controller/DiffusionRepositoryController.php', 'DiffusionRepositoryController' => 'applications/diffusion/controller/DiffusionRepositoryController.php',
'DiffusionRepositoryCreateController' => 'applications/diffusion/controller/DiffusionRepositoryCreateController.php',
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php', 'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php', 'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php', 'DiffusionRepositoryEditEncodingController' => 'applications/diffusion/controller/DiffusionRepositoryEditEncodingController.php',
@ -2423,6 +2424,7 @@ phutil_register_library_map(array(
'DiffusionRawDiffQuery' => 'DiffusionQuery', 'DiffusionRawDiffQuery' => 'DiffusionQuery',
'DiffusionRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'DiffusionRemarkupRule' => 'PhabricatorRemarkupRuleObject',
'DiffusionRepositoryController' => 'DiffusionController', 'DiffusionRepositoryController' => 'DiffusionController',
'DiffusionRepositoryCreateController' => 'DiffusionController',
'DiffusionRepositoryEditBasicController' => 'DiffusionController', 'DiffusionRepositoryEditBasicController' => 'DiffusionController',
'DiffusionRepositoryEditController' => 'DiffusionController', 'DiffusionRepositoryEditController' => 'DiffusionController',
'DiffusionRepositoryEditEncodingController' => 'DiffusionController', 'DiffusionRepositoryEditEncodingController' => 'DiffusionController',

View file

@ -42,6 +42,7 @@ final class PhabricatorApplicationDiffusion extends PhabricatorApplication {
=> 'DiffusionCommitController', => 'DiffusionCommitController',
'/diffusion/' => array( '/diffusion/' => array(
'' => 'DiffusionHomeController', '' => 'DiffusionHomeController',
'create/' => 'DiffusionRepositoryCreateController',
'(?P<callsign>[A-Z]+)/' => array( '(?P<callsign>[A-Z]+)/' => array(
'' => 'DiffusionRepositoryController', '' => 'DiffusionRepositoryController',

View file

@ -0,0 +1,545 @@
<?php
final class DiffusionRepositoryCreateController extends DiffusionController {
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$form = id(new PHUIPagedFormView())
->setUser($viewer)
->addPage('vcs', $this->buildVCSPage())
->addPage('name', $this->buildNamePage())
->addPage('auth', $this->buildAuthPage())
->addPage('done', $this->buildDonePage());
if ($request->isFormPost()) {
$form->readFromRequest($request);
if ($form->isComplete()) {
// TODO: This exception is heartwarming but should probably take more
// substantive actions.
throw new Exception("GOOD JOB AT FORM");
}
} else {
$form->readFromObject(null);
}
$title = pht('Import Repository');
$crumbs = $this->buildCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($title));
return $this->buildApplicationPage(
array(
$crumbs,
$form,
),
array(
'title' => $title,
'device' => true,
'dust' => true,
));
}
/* -( Page: VCS Type )----------------------------------------------------- */
private function buildVCSPage() {
return id(new PHUIFormPageView())
->setPageName(pht('Repository Type'))
->setUser($this->getRequest()->getUser())
->setValidateFormPageCallback(array($this, 'validateVCSPage'))
->addControl(
id(new AphrontFormRadioButtonControl())
->setName('vcs')
->setLabel(pht('Type'))
->addButton(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
pht('Git'),
pht(
'Import a Git repository (for example, a repository hosted '.
'on GitHub).'))
->addButton(
PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL,
pht('Mercurial'),
pht(
'Import a Mercurial repository (for example, a repository '.
'hosted on Bitbucket).'))
->addButton(
PhabricatorRepositoryType::REPOSITORY_TYPE_SVN,
pht('Subversion'),
pht('Import a Subversion repository.'))
->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));
}
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'))
->setAdjustFormPageCallback(array($this, 'adjustNamePage'))
->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.')))
->addControl(
id(new AphrontFormTextControl())
->setName('remoteURI'));
}
public function adjustNamePage(PHUIFormPageView $page) {
$form = $page->getForm();
$is_git = false;
$is_svn = false;
$is_mercurial = false;
switch ($form->getPage('vcs')->getControl('vcs')->getValue()) {
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 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.'));
}
}
$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."));
}
}
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_name->isValid() &&
$c_call->isValid() &&
$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, <em>or</em>...')))
->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'));
}
public function adjustAuthPage($page) {
$form = $page->getForm();
$remote_uri = $form->getPage('name')->getControl('remoteURI')->getValue();
$vcs = $form->getPage('vcs')->getControl('vcs')->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);
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);
}
$page->addRemarkupInstructions(
pht(
'Enter the username and private key 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.',
$remote_uri),
'ssh-login');
} else if ($this->isUsernamePasswordProtocol($proto)) {
$page->getControl('http-login')->setHidden(false);
$page->getControl('http-pass')->setHidden(false);
$page->addRemarkupInstructions(
pht(
'Enter 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.",
$remote_uri),
'http-login');
} else if ($proto == 'file') {
$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');
} else {
throw new Exception("Unknown URI protocol!");
}
}
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');
$v_user = $c_user->getValue();
$v_key = $c_key->getValue();
$v_file = $c_file->getValue();
if (!strlen($v_user)) {
$c_user->setError(pht('Required'));
$page->addPageError(
pht('You must provide an SSH login username to connect over SSH.'));
}
if (!strlen($v_key) && !strlen($v_file)) {
$c_key->setError(pht('No Key'));
$c_file->setError(pht('No Key'));
$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'));
$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?)'));
}
return $c_user->isValid() &&
$c_key->isValid() &&
$c_file->isValid();
} else if ($this->isUsernamePasswordProtocol($proto)) {
return true;
} else {
return true;
}
}
/* -( Page: Done )--------------------------------------------------------- */
private function buildDonePage() {
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',
pht('Start Import Now'),
pht(
'Start importing the repository right away. This will import '.
'the entire repository using default settings.'))
->addButton(
'wait',
pht('Configure More Options First'),
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.')));
}
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');
}
}

View file

@ -5,6 +5,7 @@ final class PhabricatorRepositoryType {
const REPOSITORY_TYPE_GIT = 'git'; const REPOSITORY_TYPE_GIT = 'git';
const REPOSITORY_TYPE_SVN = 'svn'; const REPOSITORY_TYPE_SVN = 'svn';
const REPOSITORY_TYPE_MERCURIAL = 'hg'; const REPOSITORY_TYPE_MERCURIAL = 'hg';
const REPOSITORY_TYPE_PERFORCE = 'p4';
public static function getAllRepositoryTypes() { public static function getAllRepositoryTypes() {
static $map = array( static $map = array(

View file

@ -8,8 +8,78 @@ class PHUIFormPageView extends AphrontView {
private $key; private $key;
private $form; private $form;
private $controls = array(); private $controls = array();
private $content = array();
private $values = array(); private $values = array();
private $isValid; private $isValid;
private $validateFormPageCallback;
private $adjustFormPageCallback;
private $pageErrors;
private $pageName;
public function setPageName($page_name) {
$this->pageName = $page_name;
return $this;
}
public function getPageName() {
return $this->pageName;
}
public function addPageError($page_error) {
$this->pageErrors[] = $page_error;
return $this;
}
public function getPageErrors() {
return $this->pageErrors;
}
public function setAdjustFormPageCallback($adjust_form_page_callback) {
$this->adjustFormPageCallback = $adjust_form_page_callback;
return $this;
}
public function setValidateFormPageCallback($validate_form_page_callback) {
$this->validateFormPageCallback = $validate_form_page_callback;
return $this;
}
public function addInstructions($text, $before = null) {
$tag = phutil_tag(
'div',
array(
'class' => 'aphront-form-instructions',
),
$text);
$append = true;
if ($before !== null) {
for ($ii = 0; $ii < count($this->content); $ii++) {
if ($this->content[$ii] instanceof AphrontFormControl) {
if ($this->content[$ii]->getName() == $before) {
array_splice($this->content, $ii, 0, array($tag));
$append = false;
break;
}
}
}
}
if ($append) {
$this->content[] = $tag;
}
return $this;
}
public function addRemarkupInstructions($remarkup, $before = null) {
return $this->addInstructions(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($remarkup),
'default',
$this->getUser()), $before);
}
public function addControl(AphrontFormControl $control) { public function addControl(AphrontFormControl $control) {
$name = $control->getName(); $name = $control->getName();
@ -24,6 +94,7 @@ class PHUIFormPageView extends AphrontView {
} }
$this->controls[$name] = $control; $this->controls[$name] = $control;
$this->content[] = $control;
$control->setFormPage($this); $control->setFormPage($this);
return $this; return $this;
@ -53,12 +124,26 @@ class PHUIFormPageView extends AphrontView {
return $this; return $this;
} }
public function adjustFormPage() {
if ($this->adjustFormPageCallback) {
call_user_func($this->adjustFormPageCallback, $this);
}
return $this;
}
protected function validateFormPage() {
if ($this->validateFormPageCallback) {
return call_user_func($this->validateFormPageCallback, $this);
}
return true;
}
public function getKey() { public function getKey() {
return $this->key; return $this->key;
} }
public function render() { public function render() {
return $this->getControls(); return $this->content;
} }
public function getForm() { public function getForm() {
@ -91,7 +176,7 @@ class PHUIFormPageView extends AphrontView {
public function isValid() { public function isValid() {
if ($this->isValid === null) { if ($this->isValid === null) {
$this->isValid = $this->validateControls(); $this->isValid = $this->validateControls() && $this->validateFormPage();
} }
return $this->isValid; return $this->isValid;
} }

View file

@ -31,6 +31,10 @@ final class PHUIPagedFormView extends AphrontTagView {
} }
$this->pages[$key] = $page; $this->pages[$key] = $page;
$page->setPagedFormView($this, $key); $page->setPagedFormView($this, $key);
$this->selectedPage = null;
$this->complete = null;
return $this; return $this;
} }
@ -104,12 +108,7 @@ final class PHUIPagedFormView extends AphrontTagView {
} }
public function readFromObject($object) { public function readFromObject($object) {
foreach ($this->pages as $page) { return $this->processForm($is_request = false, $object);
$page->validateObjectType($object);
$page->readFromObject($object);
}
return $this->processForm();
} }
public function writeToResponse($response) { public function writeToResponse($response) {
@ -122,21 +121,11 @@ final class PHUIPagedFormView extends AphrontTagView {
} }
public function readFromRequest(AphrontRequest $request) { public function readFromRequest(AphrontRequest $request) {
$active_page = $request->getStr($this->getRequestKey('page')); $this->choosePage = $request->getStr($this->getRequestKey('page'));
foreach ($this->pages as $key => $page) {
if ($key == $active_page) {
$page->readFromRequest($request);
} else {
$page->readSerializedValues($request);
}
}
$this->choosePage = $active_page;
$this->nextPage = $request->getStr('__submit__'); $this->nextPage = $request->getStr('__submit__');
$this->prevPage = $request->getStr('__back__'); $this->prevPage = $request->getStr('__back__');
return $this->processForm(); return $this->processForm($is_request = true, $request);
} }
public function setName($name) { public function setName($name) {
@ -154,13 +143,7 @@ final class PHUIPagedFormView extends AphrontTagView {
return $this; return $this;
} }
public function processForm() { private function processForm($is_request, $source) {
foreach ($this->pages as $key => $page) {
if (!$page->isValid()) {
break;
}
}
if ($this->pageExists($this->choosePage)) { if ($this->pageExists($this->choosePage)) {
$selected = $this->getPage($this->choosePage); $selected = $this->getPage($this->choosePage);
} else { } else {
@ -182,20 +165,39 @@ final class PHUIPagedFormView extends AphrontTagView {
} }
$validation_error = false; $validation_error = false;
$found_selected = false;
foreach ($this->pages as $key => $page) { foreach ($this->pages as $key => $page) {
if (!$page->isValid()) { if ($is_request) {
$validation_error = true; if ($key === $this->choosePage) {
break; $page->readFromRequest($source);
} else {
$page->readSerializedValues($source);
}
} else {
$page->readFromObject($source);
} }
if (!$found_selected) {
$page->adjustFormPage();
}
if ($page === $selected) { if ($page === $selected) {
break; $found_selected = true;
}
if (!$found_selected || $is_attempt_complete) {
if (!$page->isValid()) {
$selected = $page;
$validation_error = true;
break;
}
} }
} }
if ($is_attempt_complete && !$validation_error) { if ($is_attempt_complete && !$validation_error) {
$this->complete = true; $this->complete = true;
} else { } else {
$this->selectedPage = $page; $this->selectedPage = $selected;
} }
return $this; return $this;
@ -222,8 +224,11 @@ final class PHUIPagedFormView extends AphrontTagView {
$this->getRequestKey('page'), $this->getRequestKey('page'),
$selected_page->getKey()); $selected_page->getKey());
$errors = array();
foreach ($this->pages as $page) { foreach ($this->pages as $page) {
if ($page == $selected_page) { if ($page == $selected_page) {
$errors = $page->getPageErrors();
continue; continue;
} }
foreach ($page->getSerializedValues() as $key => $value) { foreach ($page->getSerializedValues() as $key => $value) {
@ -246,7 +251,21 @@ final class PHUIPagedFormView extends AphrontTagView {
$form->appendChild($selected_page); $form->appendChild($selected_page);
$form->appendChild($submit); $form->appendChild($submit);
return $form; if ($errors) {
$errors = id(new AphrontErrorView())->setErrors($errors);
}
$header = null;
if ($selected_page->getPageName()) {
$header = id(new PhabricatorHeaderView())
->setHeader($selected_page->getPageName());
}
return array(
$header,
$errors,
$form,
);
} }
} }

View file

@ -13,6 +13,12 @@ abstract class AphrontFormControl extends AphrontView {
private $controlStyle; private $controlStyle;
private $formPage; private $formPage;
private $required; private $required;
private $hidden;
public function setHidden($hidden) {
$this->hidden = $hidden;
return $this;
}
public function setID($id) { public function setID($id) {
$this->id = $id; $this->id = $id;
@ -203,12 +209,17 @@ abstract class AphrontFormControl extends AphrontView {
$classes[] = 'aphront-form-control'; $classes[] = 'aphront-form-control';
$classes[] = $custom_class; $classes[] = $custom_class;
$style = $this->controlStyle;
if ($this->hidden) {
$style = 'display: none; '.$style;
}
return phutil_tag( return phutil_tag(
'div', 'div',
array( array(
'class' => implode(' ', $classes), 'class' => implode(' ', $classes),
'id' => $this->controlID, 'id' => $this->controlID,
'style' => $this->controlStyle, 'style' => $style,
), ),
array( array(
$label, $label,