From 40680e459f4452123a78a4403cb099e6ea35e002 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 23 May 2013 14:39:01 -0700 Subject: [PATCH] Add a basic multipage form Summary: Ref T2232. Very busy day on IRC so I feel like I've made 20 minutes of progress in 1-minute spurts here, but this adds the basics for a form that can have multiple pages and automatically handle pagination and reading to/from the request, objects and responses. The UIExample is reasonably instructive. Basically, you make a form, add pages to the form, and add controls to the pages. The core flow control looks like this: if ($request->isFormPost()) { $form->readFromRequest($request); // (1) if ($form->isComplete()) { // (2) $response = $form->writeToResponse($response); // (3) // Process result here. // (4) } } else { $form->readFromObject($object); // (5) } The key parts are: # This reads the form state from the request, including reading all the inactive pages. # This tests if all pages are valid and the user just clicked "Done" on the last page. # This produces a "response", which might be writing to an object (for simpler forms) or creating a transaction record (for more complex forms). # Here, we would save the object or apply the transactions. # When the user views the form for the first time, we preload all the values from some object (which might just be empty). Ultimate goal here is to fix repository creation to not be a terrible pit of awfulness. There are probably a lot of rough edges and missing features still, but this seems to not be totally crazy. I'm using two submit buttons with different names which doesn't work on IE7 or something, but we can JS our way out of that if we need to. Test Plan: Paged forward and backward through the form. Reviewers: btrahan, chad Reviewed By: btrahan CC: aran Maniphest Tasks: T2232 Differential Revision: https://secure.phabricator.com/D6003 --- src/__celerity_resource_map__.php | 94 +++---- src/__phutil_library_map__.php | 8 + .../examples/PhabricatorPagedFormExample.php | 67 +++++ src/view/form/PHUIFormPageView.php | 133 +++++++++ src/view/form/PHUIPagedFormView.php | 252 ++++++++++++++++++ src/view/form/control/AphrontFormControl.php | 51 ++++ .../control/PHUIFormMultiSubmitControl.php | 38 +++ webroot/rsrc/css/aphront/form-view.css | 6 + webroot/rsrc/css/core/buttons.css | 4 +- 9 files changed, 604 insertions(+), 49 deletions(-) create mode 100644 src/applications/uiexample/examples/PhabricatorPagedFormExample.php create mode 100644 src/view/form/PHUIFormPageView.php create mode 100644 src/view/form/PHUIPagedFormView.php create mode 100644 src/view/form/control/PHUIFormMultiSubmitControl.php diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index f25b2f6211..bd6d122ee4 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -800,7 +800,7 @@ celerity_register_resource_map(array( ), 'aphront-form-view-css' => array( - 'uri' => '/res/70404bd5/rsrc/css/aphront/form-view.css', + 'uri' => '/res/901cc9be/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( @@ -2933,7 +2933,7 @@ celerity_register_resource_map(array( ), 'phabricator-core-buttons-css' => array( - 'uri' => '/res/da2e42df/rsrc/css/core/buttons.css', + 'uri' => '/res/9250d98f/rsrc/css/core/buttons.css', 'type' => 'css', 'requires' => array( @@ -3981,7 +3981,7 @@ celerity_register_resource_map(array( ), array( 'packages' => array( - '190fa563' => + 'efb6389a' => array( 'name' => 'core.pkg.css', 'symbols' => @@ -4030,7 +4030,7 @@ celerity_register_resource_map(array( 41 => 'phabricator-property-list-view-css', 42 => 'phabricator-tag-view-css', ), - 'uri' => '/res/pkg/190fa563/core.pkg.css', + 'uri' => '/res/pkg/efb6389a/core.pkg.css', 'type' => 'css', ), '77faef00' => @@ -4224,16 +4224,16 @@ celerity_register_resource_map(array( 'reverse' => array( 'aphront-attached-file-view-css' => '6b1fccc6', - 'aphront-dialog-view-css' => '190fa563', - 'aphront-error-view-css' => '190fa563', - 'aphront-form-view-css' => '190fa563', - 'aphront-list-filter-view-css' => '190fa563', - 'aphront-pager-view-css' => '190fa563', - 'aphront-panel-view-css' => '190fa563', - 'aphront-table-view-css' => '190fa563', - 'aphront-tokenizer-control-css' => '190fa563', - 'aphront-tooltip-css' => '190fa563', - 'aphront-typeahead-control-css' => '190fa563', + 'aphront-dialog-view-css' => 'efb6389a', + 'aphront-error-view-css' => 'efb6389a', + 'aphront-form-view-css' => 'efb6389a', + 'aphront-list-filter-view-css' => 'efb6389a', + 'aphront-pager-view-css' => 'efb6389a', + 'aphront-panel-view-css' => 'efb6389a', + 'aphront-table-view-css' => 'efb6389a', + 'aphront-tokenizer-control-css' => 'efb6389a', + 'aphront-tooltip-css' => 'efb6389a', + 'aphront-typeahead-control-css' => 'efb6389a', 'differential-changeset-view-css' => 'dd27a69b', 'differential-core-view-css' => 'dd27a69b', 'differential-inline-comment-editor' => '9488bb69', @@ -4247,7 +4247,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => 'dd27a69b', 'diffusion-commit-view-css' => 'c8ce2d88', 'diffusion-icons-css' => 'c8ce2d88', - 'global-drag-and-drop-css' => '190fa563', + 'global-drag-and-drop-css' => 'efb6389a', 'inline-comment-summary-css' => 'dd27a69b', 'javelin-aphlict' => '77faef00', 'javelin-behavior' => 'c1359b5d', @@ -4321,56 +4321,56 @@ celerity_register_resource_map(array( 'javelin-util' => 'c1359b5d', 'javelin-vector' => 'c1359b5d', 'javelin-workflow' => 'c1359b5d', - 'lightbox-attachment-css' => '190fa563', + 'lightbox-attachment-css' => 'efb6389a', 'maniphest-task-summary-css' => '6b1fccc6', 'maniphest-transaction-detail-css' => '6b1fccc6', - 'phabricator-action-list-view-css' => '190fa563', - 'phabricator-application-launch-view-css' => '190fa563', + 'phabricator-action-list-view-css' => 'efb6389a', + 'phabricator-application-launch-view-css' => 'efb6389a', 'phabricator-busy' => '77faef00', 'phabricator-content-source-view-css' => 'dd27a69b', - 'phabricator-core-buttons-css' => '190fa563', - 'phabricator-core-css' => '190fa563', - 'phabricator-crumbs-view-css' => '190fa563', - 'phabricator-directory-css' => '190fa563', + 'phabricator-core-buttons-css' => 'efb6389a', + 'phabricator-core-css' => 'efb6389a', + 'phabricator-crumbs-view-css' => 'efb6389a', + 'phabricator-directory-css' => 'efb6389a', 'phabricator-drag-and-drop-file-upload' => '9488bb69', 'phabricator-dropdown-menu' => '77faef00', 'phabricator-file-upload' => '77faef00', - 'phabricator-filetree-view-css' => '190fa563', - 'phabricator-flag-css' => '190fa563', - 'phabricator-form-view-css' => '190fa563', - 'phabricator-header-view-css' => '190fa563', + 'phabricator-filetree-view-css' => 'efb6389a', + 'phabricator-flag-css' => 'efb6389a', + 'phabricator-form-view-css' => 'efb6389a', + 'phabricator-header-view-css' => 'efb6389a', 'phabricator-hovercard' => '77faef00', - 'phabricator-jump-nav' => '190fa563', + 'phabricator-jump-nav' => 'efb6389a', 'phabricator-keyboard-shortcut' => '77faef00', 'phabricator-keyboard-shortcut-manager' => '77faef00', - 'phabricator-main-menu-view' => '190fa563', + 'phabricator-main-menu-view' => 'efb6389a', 'phabricator-menu-item' => '77faef00', - 'phabricator-nav-view-css' => '190fa563', + 'phabricator-nav-view-css' => 'efb6389a', 'phabricator-notification' => '77faef00', - 'phabricator-notification-css' => '190fa563', - 'phabricator-notification-menu-css' => '190fa563', - 'phabricator-object-item-list-view-css' => '190fa563', + 'phabricator-notification-css' => 'efb6389a', + 'phabricator-notification-menu-css' => 'efb6389a', + 'phabricator-object-item-list-view-css' => 'efb6389a', 'phabricator-object-selector-css' => 'dd27a69b', 'phabricator-phtize' => '77faef00', 'phabricator-prefab' => '77faef00', 'phabricator-project-tag-css' => '6b1fccc6', - 'phabricator-property-list-view-css' => '190fa563', - 'phabricator-remarkup-css' => '190fa563', + 'phabricator-property-list-view-css' => 'efb6389a', + 'phabricator-remarkup-css' => 'efb6389a', 'phabricator-shaped-request' => '9488bb69', - 'phabricator-side-menu-view-css' => '190fa563', - 'phabricator-standard-page-view' => '190fa563', - 'phabricator-tag-view-css' => '190fa563', + 'phabricator-side-menu-view-css' => 'efb6389a', + 'phabricator-standard-page-view' => 'efb6389a', + 'phabricator-tag-view-css' => 'efb6389a', 'phabricator-textareautils' => '77faef00', 'phabricator-tooltip' => '77faef00', - 'phabricator-transaction-view-css' => '190fa563', - 'phabricator-zindex-css' => '190fa563', - 'phui-form-css' => '190fa563', - 'phui-icon-view-css' => '190fa563', - 'spacing-css' => '190fa563', - 'sprite-apps-large-css' => '190fa563', - 'sprite-gradient-css' => '190fa563', - 'sprite-icons-css' => '190fa563', - 'sprite-menu-css' => '190fa563', - 'syntax-highlighting-css' => '190fa563', + 'phabricator-transaction-view-css' => 'efb6389a', + 'phabricator-zindex-css' => 'efb6389a', + 'phui-form-css' => 'efb6389a', + 'phui-icon-view-css' => 'efb6389a', + 'spacing-css' => 'efb6389a', + 'sprite-apps-large-css' => 'efb6389a', + 'sprite-gradient-css' => 'efb6389a', + 'sprite-icons-css' => 'efb6389a', + 'sprite-menu-css' => 'efb6389a', + 'syntax-highlighting-css' => 'efb6389a', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f5df925d46..f32a1c0774 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -671,8 +671,11 @@ phutil_register_library_map(array( 'PHUIBoxView' => 'view/phui/PHUIBoxView.php', 'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php', 'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php', + 'PHUIFormMultiSubmitControl' => 'view/form/control/PHUIFormMultiSubmitControl.php', + 'PHUIFormPageView' => 'view/form/PHUIFormPageView.php', 'PHUIIconExample' => 'applications/uiexample/examples/PHUIIconExample.php', 'PHUIIconView' => 'view/phui/PHUIIconView.php', + 'PHUIPagedFormView' => 'view/form/PHUIPagedFormView.php', 'PHUITextExample' => 'applications/uiexample/examples/PHUITextExample.php', 'PHUITextView' => 'view/phui/PHUITextView.php', 'PackageCreateMail' => 'applications/owners/mail/PackageCreateMail.php', @@ -1218,6 +1221,7 @@ phutil_register_library_map(array( 'PhabricatorPHIDController' => 'applications/phid/controller/PhabricatorPHIDController.php', 'PhabricatorPHIDLookupController' => 'applications/phid/controller/PhabricatorPHIDLookupController.php', 'PhabricatorPHPMailerConfigOptions' => 'applications/config/option/PhabricatorPHPMailerConfigOptions.php', + 'PhabricatorPagedFormExample' => 'applications/uiexample/examples/PhabricatorPagedFormExample.php', 'PhabricatorPaste' => 'applications/paste/storage/PhabricatorPaste.php', 'PhabricatorPasteController' => 'applications/paste/controller/PhabricatorPasteController.php', 'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php', @@ -2442,8 +2446,11 @@ phutil_register_library_map(array( 'PHUIBoxView' => 'AphrontTagView', 'PHUIFeedStoryExample' => 'PhabricatorUIExample', 'PHUIFeedStoryView' => 'AphrontView', + 'PHUIFormMultiSubmitControl' => 'AphrontFormControl', + 'PHUIFormPageView' => 'AphrontView', 'PHUIIconExample' => 'PhabricatorUIExample', 'PHUIIconView' => 'AphrontTagView', + 'PHUIPagedFormView' => 'AphrontTagView', 'PHUITextExample' => 'PhabricatorUIExample', 'PHUITextView' => 'AphrontTagView', 'PackageCreateMail' => 'PackageMail', @@ -2973,6 +2980,7 @@ phutil_register_library_map(array( 'PhabricatorPHIDController' => 'PhabricatorController', 'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController', 'PhabricatorPHPMailerConfigOptions' => 'PhabricatorApplicationConfigOptions', + 'PhabricatorPagedFormExample' => 'PhabricatorUIExample', 'PhabricatorPaste' => array( 0 => 'PhabricatorPasteDAO', diff --git a/src/applications/uiexample/examples/PhabricatorPagedFormExample.php b/src/applications/uiexample/examples/PhabricatorPagedFormExample.php new file mode 100644 index 0000000000..e34e4cf23e --- /dev/null +++ b/src/applications/uiexample/examples/PhabricatorPagedFormExample.php @@ -0,0 +1,67 @@ +PHUIPagedFormView')); + } + + public function renderExample() { + $request = $this->getRequest(); + $user = $request->getUser(); + + + $page1 = id(new PHUIFormPageView()) + ->addControl( + id(new AphrontFormTextControl()) + ->setName('page1') + ->setLabel('Page 1')); + + $page2 = id(new PHUIFormPageView()) + ->addControl( + id(new AphrontFormTextControl()) + ->setName('page2') + ->setLabel('Page 2')); + + $page3 = id(new PHUIFormPageView()) + ->addControl( + id(new AphrontFormTextControl()) + ->setName('page3') + ->setLabel('Page 3')); + + $page4 = id(new PHUIFormPageView()) + ->addControl( + id(new AphrontFormTextControl()) + ->setName('page4') + ->setLabel('Page 4')); + + $form = new PHUIPagedFormView(); + $form->setUser($user); + + $form->addPage('page1', $page1); + $form->addPage('page2', $page2); + $form->addPage('page3', $page3); + $form->addPage('page4', $page4); + + if ($request->isFormPost()) { + $form->readFromRequest($request); + if ($form->isComplete()) { + return id(new AphrontDialogView()) + ->setUser($user) + ->setTitle(pht('Form Complete')) + ->appendChild(pht('You submitted the form. Well done!')) + ->addCancelButton($request->getRequestURI(), pht('Again!')); + } + } else { + $form->readFromObject(null); + } + + return $form; + } +} diff --git a/src/view/form/PHUIFormPageView.php b/src/view/form/PHUIFormPageView.php new file mode 100644 index 0000000000..a984ba40a7 --- /dev/null +++ b/src/view/form/PHUIFormPageView.php @@ -0,0 +1,133 @@ +getName(); + + if (!strlen($name)) { + throw new Exception("Form control has no name!"); + } + + if (isset($this->controls[$name])) { + throw new Exception( + "Form page contains duplicate control with name '{$name}'!"); + } + + $this->controls[$name] = $control; + $control->setFormPage($this); + + return $this; + } + + public function getControls() { + return $this->controls; + } + + public function getControl($name) { + if (empty($this->controls[$name])) { + throw new Exception("No page control '{$name}'!"); + } + return $this->controls[$name]; + } + + protected function canAppendChild() { + return false; + } + + public function setPagedFormView(PHUIPagedFormView $view, $key) { + if ($this->key) { + throw new Exception("This page is already part of a form!"); + } + $this->form = $view; + $this->key = $key; + return $this; + } + + public function getKey() { + return $this->key; + } + + public function render() { + return $this->getControls(); + } + + public function getForm() { + return $this->form; + } + + public function getRequestKey($key) { + return $this->getForm()->getRequestKey('p:'.$this->key.':'.$key); + } + + public function validateObjectType($object) { + return true; + } + + public function validateResponseType($response) { + return true; + } + + protected function validateControls() { + $result = true; + foreach ($this->getControls() as $name => $control) { + if (!$control->isValid()) { + $result = false; + break; + } + } + + return $result; + } + + public function isValid() { + if ($this->isValid === null) { + $this->isValid = $this->validateControls(); + } + return $this->isValid; + } + + public function readFromRequest(AphrontRequest $request) { + foreach ($this->getControls() as $name => $control) { + $control->readValueFromRequest($request); + } + + return $this; + } + + public function readFromObject($object) { + return $this; + } + + public function writeToResponse($response) { + return $this; + } + + public function readSerializedValues(AphrontRequest $request) { + foreach ($this->getControls() as $name => $control) { + $key = $this->getRequestKey($name); + $control->readSerializedValue($request->getStr($key)); + } + + return $this; + } + + public function getSerializedValues() { + $dict = array(); + foreach ($this->getControls() as $name => $control) { + $key = $this->getRequestKey($name); + $dict[$key] = $control->getSerializedValue(); + } + return $dict; + } + +} diff --git a/src/view/form/PHUIPagedFormView.php b/src/view/form/PHUIPagedFormView.php new file mode 100644 index 0000000000..16786743af --- /dev/null +++ b/src/view/form/PHUIPagedFormView.php @@ -0,0 +1,252 @@ +pages[$key])) { + throw new Exception("Duplicate page with key '{$key}'!"); + } + $this->pages[$key] = $page; + $page->setPagedFormView($this, $key); + return $this; + } + + + /** + * @task page + */ + public function getPage($key) { + if (!$this->pageExists($key)) { + throw new Exception("No page '{$key}' exists!"); + } + return $this->pages[$key]; + } + + + /** + * @task page + */ + public function pageExists($key) { + return isset($this->pages[$key]); + } + + + /** + * @task page + */ + protected function getPageIndex($key) { + $page = $this->getPage($key); + + $index = 0; + foreach ($this->pages as $target_page) { + if ($page === $target_page) { + break; + } + $index++; + } + + return $index; + } + + + /** + * @task page + */ + protected function getPageByIndex($index) { + foreach ($this->pages as $page) { + if (!$index) { + return $page; + } + $index--; + } + + throw new Exception("Requesting out-of-bounds page '{$index}'."); + } + + protected function getLastIndex() { + return count($this->pages) - 1; + } + + protected function isFirstPage(PHUIFormPageView $page) { + return ($this->getPageIndex($page->getKey()) === 0); + + } + + protected function isLastPage(PHUIFormPageView $page) { + return ($this->getPageIndex($page->getKey()) === (count($this->pages) - 1)); + } + + public function getSelectedPage() { + return $this->selectedPage; + } + + public function readFromObject($object) { + foreach ($this->pages as $page) { + $page->validateObjectType($object); + $page->readFromObject($object); + } + + return $this->processForm(); + } + + public function writeToResponse($response) { + foreach ($this->pages as $page) { + $page->validateResponseType($response); + $response = $page->writeToResponse($page); + } + + return $response; + } + + public function readFromRequest(AphrontRequest $request) { + $active_page = $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->prevPage = $request->getStr('__back__'); + + return $this->processForm(); + } + + public function setName($name) { + $this->name = $name; + return $this; + } + + + public function getValue($page, $key, $default = null) { + return $this->getPage($page)->getValue($key, $default); + } + + public function setValue($page, $key, $value) { + $this->getPage($page)->setValue($key, $value); + return $this; + } + + public function processForm() { + foreach ($this->pages as $key => $page) { + if (!$page->isValid()) { + break; + } + } + + if ($this->pageExists($this->choosePage)) { + $selected = $this->getPage($this->choosePage); + } else { + $selected = $this->getPageByIndex(0); + } + + $is_attempt_complete = false; + if ($this->prevPage) { + $prev_index = $this->getPageIndex($selected->getKey()) - 1;; + $index = max(0, $prev_index); + $selected = $this->getPageByIndex($index); + } else if ($this->nextPage) { + $next_index = $this->getPageIndex($selected->getKey()) + 1; + if ($next_index > $this->getLastIndex()) { + $is_attempt_complete = true; + } + $index = min($this->getLastIndex(), $next_index); + $selected = $this->getPageByIndex($index); + } + + $validation_error = false; + foreach ($this->pages as $key => $page) { + if (!$page->isValid()) { + $validation_error = true; + break; + } + if ($page === $selected) { + break; + } + } + + if ($is_attempt_complete && !$validation_error) { + $this->complete = true; + } else { + $this->selectedPage = $page; + } + + return $this; + } + + public function isComplete() { + return $this->complete; + } + + public function getRequestKey($key) { + return $this->name.':'.$key; + } + + public function getTagContent() { + $form = id(new AphrontFormView()) + ->setUser($this->getUser()); + + $selected_page = $this->getSelectedPage(); + if (!$selected_page) { + throw new Exception("No selected page!"); + } + + $form->addHiddenInput( + $this->getRequestKey('page'), + $selected_page->getKey()); + + foreach ($this->pages as $page) { + if ($page == $selected_page) { + continue; + } + foreach ($page->getSerializedValues() as $key => $value) { + $form->addHiddenInput($key, $value); + } + } + + $submit = id(new PHUIFormMultiSubmitControl()); + + if (!$this->isFirstPage($selected_page)) { + $submit->addBackButton(); + } + + if ($this->isLastPage($selected_page)) { + $submit->addSubmitButton(pht("Save")); + } else { + $submit->addSubmitButton(pht("Continue \xC2\xBB")); + } + + $form->appendChild($selected_page); + $form->appendChild($submit); + + return $form; + } + +} diff --git a/src/view/form/control/AphrontFormControl.php b/src/view/form/control/AphrontFormControl.php index 864aabea2b..b727b32cfe 100644 --- a/src/view/form/control/AphrontFormControl.php +++ b/src/view/form/control/AphrontFormControl.php @@ -11,6 +11,8 @@ abstract class AphrontFormControl extends AphrontView { private $id; private $controlID; private $controlStyle; + private $formPage; + private $required; public function setID($id) { $this->id = $id; @@ -84,6 +86,55 @@ abstract class AphrontFormControl extends AphrontView { return $this->value; } + public function isValid() { + if ($this->error && $this->error !== true) { + return false; + } + + if ($this->isRequired() && $this->isEmpty()) { + return false; + } + + return true; + } + + public function isRequired() { + return $this->required; + } + + public function isEmpty() { + return !strlen($this->getValue()); + } + + public function getSerializedValue() { + return $this->getValue(); + } + + public function readSerializedValue($value) { + $this->setValue($value); + return $this; + } + + public function readValueFromRequest(AphrontRequest $request) { + $this->setValue($request->getStr($this->getName())); + return $this; + } + + public function setFormPage(PHUIFormPageView $page) { + if ($this->formPage) { + throw new Exception("This control is already a member of a page!"); + } + $this->formPage = $page; + return $this; + } + + public function getFormPage() { + if ($this->formPage === null) { + throw new Exception("This control does not have a page!"); + } + return $this->formPage; + } + public function setDisabled($disabled) { $this->disabled = $disabled; return $this; diff --git a/src/view/form/control/PHUIFormMultiSubmitControl.php b/src/view/form/control/PHUIFormMultiSubmitControl.php new file mode 100644 index 0000000000..17b12376ae --- /dev/null +++ b/src/view/form/control/PHUIFormMultiSubmitControl.php @@ -0,0 +1,38 @@ +addButton('__back__', $label, 'grey'); + } + + public function addSubmitButton($label) { + return $this->addButton('__submit__', $label); + } + + public function addButton($name, $label, $class = null) { + $this->buttons[] = phutil_tag( + 'input', + array( + 'type' => 'submit', + 'name' => $name, + 'value' => $label, + 'class' => $class, + 'disabled' => $this->getDisabled() ? 'disabled' : null, + )); + } + + protected function getCustomControlClass() { + return 'phui-form-control-multi-submit'; + } + + protected function renderInput() { + return array_reverse($this->buttons); + } + +} diff --git a/webroot/rsrc/css/aphront/form-view.css b/webroot/rsrc/css/aphront/form-view.css index 96c002f0b5..467a1a6247 100644 --- a/webroot/rsrc/css/aphront/form-view.css +++ b/webroot/rsrc/css/aphront/form-view.css @@ -100,6 +100,12 @@ margin: 0.5em 0 0em 2%; } +.phui-form-control-multi-submit input { + float: right; + margin: 0.5em 0 0em 2%; + width: auto; +} + .aphront-form-control-textarea textarea.aphront-textarea-very-short { height: 44px; } diff --git a/webroot/rsrc/css/core/buttons.css b/webroot/rsrc/css/core/buttons.css index cce51d6029..d5eca01cbd 100644 --- a/webroot/rsrc/css/core/buttons.css +++ b/webroot/rsrc/css/core/buttons.css @@ -6,7 +6,7 @@ button, a.button, a.button:visited, -input.inputsubmit { +input[type="submit"] { background-color: #3477ad; color: white; text-shadow: 0 -1px rgba(0,0,0,0.75); @@ -53,7 +53,7 @@ a.green:visited { } button.grey, -input.inputaux, +input[type="submit"].grey, a.grey, a.grey:visited { background-color: #f7f7f7;