From eab768f705520fe9df1e66015d199128fc587aa2 Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 9 Jun 2011 15:28:29 -0700 Subject: [PATCH] Allow projects to be quickly added from the Maniphest task creation interface Summary: Provide a quick workflow for adding a new project. This ended up being sort of complicated because we don't currently put forms in dialogs. I separated the actual
tag out of the display/layout of AphrontFormView to enable this (the dialog is itself a form). Limitations: if you create a new project and then remove it, it won't appear in the tokenizer until you reload the page. We need to add the ability for the datasource to drop its cache to enable this, which is super complicated. Test Plan: Used "Create new project" to add a new project when creating a task. Reviewed By: aran Reviewers: jungejason, tuomaspelkonen, aran CC: anjali, aran, epriestley Differential Revision: 422 --- src/__celerity_resource_map__.php | 140 ++++++++++-------- src/__phutil_library_map__.php | 4 + ...AphrontDefaultApplicationConfiguration.php | 1 + .../taskedit/ManiphestTaskEditController.php | 16 ++ .../controller/taskedit/__init__.php | 3 + ...habricatorProjectQuickCreateController.php | 95 ++++++++++++ .../controller/quickcreate/__init__.php | 23 +++ src/view/dialog/AphrontDialogView.php | 19 +++ src/view/form/base/AphrontFormView.php | 10 +- src/view/form/base/__init__.php | 2 + src/view/form/error/AphrontErrorView.php | 1 + .../form/layout/AphrontFormLayoutView.php | 59 ++++++++ src/view/form/layout/__init__.php | 14 ++ webroot/rsrc/css/aphront/dialog-view.css | 3 + webroot/rsrc/css/aphront/error-view.css | 4 + webroot/rsrc/css/aphront/form-view.css | 11 +- .../js/application/core/behavior-tokenizer.js | 3 + .../maniphest/behavior-project-create.js | 26 ++++ 18 files changed, 366 insertions(+), 68 deletions(-) create mode 100644 src/applications/project/controller/quickcreate/PhabricatorProjectQuickCreateController.php create mode 100644 src/applications/project/controller/quickcreate/__init__.php create mode 100644 src/view/form/layout/AphrontFormLayoutView.php create mode 100644 src/view/form/layout/__init__.php create mode 100644 webroot/rsrc/js/application/maniphest/behavior-project-create.js diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 459eccacae..c77c2c199c 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -36,7 +36,7 @@ celerity_register_resource_map(array( ), 'aphront-dialog-view-css' => array( - 'uri' => '/res/79613f9b/rsrc/css/aphront/dialog-view.css', + 'uri' => '/res/61a58113/rsrc/css/aphront/dialog-view.css', 'type' => 'css', 'requires' => array( @@ -45,7 +45,7 @@ celerity_register_resource_map(array( ), 'aphront-error-view-css' => array( - 'uri' => '/res/98c5fc69/rsrc/css/aphront/error-view.css', + 'uri' => '/res/e4c5e4ed/rsrc/css/aphront/error-view.css', 'type' => 'css', 'requires' => array( @@ -54,7 +54,7 @@ celerity_register_resource_map(array( ), 'aphront-form-view-css' => array( - 'uri' => '/res/38a347da/rsrc/css/aphront/form-view.css', + 'uri' => '/res/8ee16aba/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( @@ -161,16 +161,6 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/css/application/differential/core.css', ), - 0 => - array( - 'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js', - 'type' => 'js', - 'requires' => - array( - 0 => 'javelin-install', - ), - 'disk' => '/rsrc/js/javelin/docs/Base.js', - ), 'differential-inline-comment-editor' => array( 'uri' => '/res/5e4f0aa4/rsrc/js/application/differential/DifferentialInlineCommentEditor.js', @@ -305,7 +295,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-aphront-basic-tokenizer' => array( - 'uri' => '/res/bce3961b/rsrc/js/application/core/behavior-tokenizer.js', + 'uri' => '/res/5e183bd5/rsrc/js/application/core/behavior-tokenizer.js', 'type' => 'js', 'requires' => array( @@ -314,6 +304,7 @@ celerity_register_resource_map(array( 2 => 'javelin-tokenizer', 3 => 'javelin-typeahead-preloaded-source', 4 => 'javelin-dom', + 5 => 'javelin-stratcom', ), 'disk' => '/rsrc/js/application/core/behavior-tokenizer.js', ), @@ -344,7 +335,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-countdown-timer' => array( - 'uri' => '/res/48477cc8/rsrc/js/application/countdown/timer.js', + 'uri' => '/res/9eef8193/rsrc/js/application/countdown/timer.js', 'type' => 'js', 'requires' => array( @@ -535,6 +526,19 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/herald/herald-rule-editor.js', ), + 'javelin-behavior-maniphest-project-create' => + array( + 'uri' => '/res/85a0eaf9/rsrc/js/application/maniphest/behavior-project-create.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-dom', + 2 => 'javelin-stratcom', + 3 => 'javelin-workflow', + ), + 'disk' => '/rsrc/js/application/maniphest/behavior-project-create.js', + ), 'javelin-behavior-maniphest-transaction-controls' => array( 'uri' => '/res/94a2a395/rsrc/js/application/maniphest/behavior-transaction-controls.js', @@ -586,6 +590,16 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/owners/owners-path-editor.js', ), + 0 => + array( + 'uri' => '/res/39de799e/rsrc/js/javelin/docs/Base.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-install', + ), + 'disk' => '/rsrc/js/javelin/docs/Base.js', + ), 'javelin-behavior-phabricator-keyboard-shortcuts' => array( 'uri' => '/res/5a23bcc8/rsrc/js/application/core/behavior-keyboard-shortcuts.js', @@ -1081,6 +1095,30 @@ celerity_register_resource_map(array( ), array ( 'packages' => array ( + '01052905' => + array ( + 'name' => 'core.pkg.css', + 'symbols' => + array ( + 0 => 'phabricator-core-css', + 1 => 'phabricator-core-buttons-css', + 2 => 'phabricator-standard-page-view', + 3 => 'aphront-dialog-view-css', + 4 => 'aphront-form-view-css', + 5 => 'aphront-panel-view-css', + 6 => 'aphront-side-nav-view-css', + 7 => 'aphront-table-view-css', + 8 => 'aphront-crumbs-view-css', + 9 => 'aphront-tokenizer-control-css', + 10 => 'aphront-typeahead-control-css', + 11 => 'aphront-list-filter-view-css', + 12 => 'phabricator-directory-css', + 13 => 'phabricator-remarkup-css', + 14 => 'syntax-highlighting-css', + ), + 'uri' => '/res/pkg/01052905/core.pkg.css', + 'type' => 'css', + ), '03ef179e' => array ( 'name' => 'diffusion.pkg.css', @@ -1108,7 +1146,7 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/0e6e36aa/differential.pkg.css', 'type' => 'css', ), - '33f413ef' => + '2892314d' => array ( 'name' => 'typeahead.pkg.js', 'symbols' => @@ -1121,7 +1159,7 @@ celerity_register_resource_map(array( 5 => 'javelin-tokenizer', 6 => 'javelin-behavior-aphront-basic-tokenizer', ), - 'uri' => '/res/pkg/33f413ef/typeahead.pkg.js', + 'uri' => '/res/pkg/2892314d/typeahead.pkg.js', 'type' => 'js', ), 'c8f4dac5' => @@ -1140,30 +1178,6 @@ celerity_register_resource_map(array( 'uri' => '/res/pkg/c8f4dac5/workflow.pkg.js', 'type' => 'js', ), - 'ca51195b' => - array ( - 'name' => 'core.pkg.css', - 'symbols' => - array ( - 0 => 'phabricator-core-css', - 1 => 'phabricator-core-buttons-css', - 2 => 'phabricator-standard-page-view', - 3 => 'aphront-dialog-view-css', - 4 => 'aphront-form-view-css', - 5 => 'aphront-panel-view-css', - 6 => 'aphront-side-nav-view-css', - 7 => 'aphront-table-view-css', - 8 => 'aphront-crumbs-view-css', - 9 => 'aphront-tokenizer-control-css', - 10 => 'aphront-typeahead-control-css', - 11 => 'aphront-list-filter-view-css', - 12 => 'phabricator-directory-css', - 13 => 'phabricator-remarkup-css', - 14 => 'syntax-highlighting-css', - ), - 'uri' => '/res/pkg/ca51195b/core.pkg.css', - 'type' => 'css', - ), 'da416e1c' => array ( 'name' => 'differential.pkg.js', @@ -1200,15 +1214,15 @@ celerity_register_resource_map(array( ), 'reverse' => array ( - 'aphront-crumbs-view-css' => 'ca51195b', - 'aphront-dialog-view-css' => 'ca51195b', - 'aphront-form-view-css' => 'ca51195b', - 'aphront-list-filter-view-css' => 'ca51195b', - 'aphront-panel-view-css' => 'ca51195b', - 'aphront-side-nav-view-css' => 'ca51195b', - 'aphront-table-view-css' => 'ca51195b', - 'aphront-tokenizer-control-css' => 'ca51195b', - 'aphront-typeahead-control-css' => 'ca51195b', + 'aphront-crumbs-view-css' => '01052905', + 'aphront-dialog-view-css' => '01052905', + 'aphront-form-view-css' => '01052905', + 'aphront-list-filter-view-css' => '01052905', + 'aphront-panel-view-css' => '01052905', + 'aphront-side-nav-view-css' => '01052905', + 'aphront-table-view-css' => '01052905', + 'aphront-tokenizer-control-css' => '01052905', + 'aphront-typeahead-control-css' => '01052905', 'differential-changeset-view-css' => '0e6e36aa', 'differential-core-view-css' => '0e6e36aa', 'differential-revision-add-comment-css' => '0e6e36aa', @@ -1219,7 +1233,7 @@ celerity_register_resource_map(array( 'differential-table-of-contents-css' => '0e6e36aa', 'diffusion-commit-view-css' => '03ef179e', 'javelin-behavior' => 'db95a6d0', - 'javelin-behavior-aphront-basic-tokenizer' => '33f413ef', + 'javelin-behavior-aphront-basic-tokenizer' => '2892314d', 'javelin-behavior-aphront-form-disable-on-submit' => 'c8f4dac5', 'javelin-behavior-differential-diff-radios' => 'da416e1c', 'javelin-behavior-differential-edit-inline-comments' => 'da416e1c', @@ -1235,23 +1249,23 @@ celerity_register_resource_map(array( 'javelin-mask' => 'c8f4dac5', 'javelin-request' => 'db95a6d0', 'javelin-stratcom' => 'db95a6d0', - 'javelin-tokenizer' => '33f413ef', - 'javelin-typeahead' => '33f413ef', - 'javelin-typeahead-normalizer' => '33f413ef', - 'javelin-typeahead-ondemand-source' => '33f413ef', - 'javelin-typeahead-preloaded-source' => '33f413ef', - 'javelin-typeahead-source' => '33f413ef', + 'javelin-tokenizer' => '2892314d', + 'javelin-typeahead' => '2892314d', + 'javelin-typeahead-normalizer' => '2892314d', + 'javelin-typeahead-ondemand-source' => '2892314d', + 'javelin-typeahead-preloaded-source' => '2892314d', + 'javelin-typeahead-source' => '2892314d', 'javelin-uri' => 'db95a6d0', 'javelin-util' => 'db95a6d0', 'javelin-vector' => 'db95a6d0', 'javelin-workflow' => 'c8f4dac5', - 'phabricator-core-buttons-css' => 'ca51195b', - 'phabricator-core-css' => 'ca51195b', - 'phabricator-directory-css' => 'ca51195b', + 'phabricator-core-buttons-css' => '01052905', + 'phabricator-core-css' => '01052905', + 'phabricator-directory-css' => '01052905', 'phabricator-keyboard-shortcut' => 'c8f4dac5', 'phabricator-keyboard-shortcut-manager' => 'c8f4dac5', - 'phabricator-remarkup-css' => 'ca51195b', - 'phabricator-standard-page-view' => 'ca51195b', - 'syntax-highlighting-css' => 'ca51195b', + 'phabricator-remarkup-css' => '01052905', + 'phabricator-standard-page-view' => '01052905', + 'syntax-highlighting-css' => '01052905', ), )); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 87d3bb7295..785cf529cc 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -30,6 +30,7 @@ phutil_register_library_map(array( 'AphrontFormDividerControl' => 'view/form/control/divider', 'AphrontFormDragAndDropUploadControl' => 'view/form/control/draganddropupload', 'AphrontFormFileControl' => 'view/form/control/file', + 'AphrontFormLayoutView' => 'view/form/layout', 'AphrontFormMarkupControl' => 'view/form/control/markup', 'AphrontFormPasswordControl' => 'view/form/control/password', 'AphrontFormRecaptchaControl' => 'view/form/control/recaptcha', @@ -426,6 +427,7 @@ phutil_register_library_map(array( 'PhabricatorProjectListController' => 'applications/project/controller/list', 'PhabricatorProjectProfile' => 'applications/project/storage/profile', 'PhabricatorProjectProfileController' => 'applications/project/controller/profile', + 'PhabricatorProjectQuickCreateController' => 'applications/project/controller/quickcreate', 'PhabricatorRedirectController' => 'applications/base/controller/redirect', 'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential', 'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion', @@ -570,6 +572,7 @@ phutil_register_library_map(array( 'AphrontFormDividerControl' => 'AphrontFormControl', 'AphrontFormDragAndDropUploadControl' => 'AphrontFormControl', 'AphrontFormFileControl' => 'AphrontFormControl', + 'AphrontFormLayoutView' => 'AphrontView', 'AphrontFormMarkupControl' => 'AphrontFormControl', 'AphrontFormPasswordControl' => 'AphrontFormControl', 'AphrontFormRecaptchaControl' => 'AphrontFormControl', @@ -881,6 +884,7 @@ phutil_register_library_map(array( 'PhabricatorProjectListController' => 'PhabricatorProjectController', 'PhabricatorProjectProfile' => 'PhabricatorProjectDAO', 'PhabricatorProjectProfileController' => 'PhabricatorProjectController', + 'PhabricatorProjectQuickCreateController' => 'PhabricatorProjectController', 'PhabricatorRedirectController' => 'PhabricatorController', 'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName', 'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index 629f455f9f..314cd5cf89 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -200,6 +200,7 @@ class AphrontDefaultApplicationConfiguration 'view/(?P\d+)/$' => 'PhabricatorProjectProfileController', 'affiliation/(?P\d+)/$' => 'PhabricatorProjectAffiliationEditController', + 'quickcreate/$' => 'PhabricatorProjectQuickCreateController', ), '/r(?P[A-Z]+)(?P[a-z0-9]+)$' diff --git a/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php b/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php index 0051718021..55ff3a982d 100644 --- a/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php +++ b/src/applications/maniphest/controller/taskedit/ManiphestTaskEditController.php @@ -210,6 +210,8 @@ class ManiphestTaskEditController extends ManiphestController { $header_name = 'Create New Task'; } + $project_tokenizer_id = celerity_generate_unique_node_id(); + $form = new AphrontFormView(); $form ->setUser($user) @@ -245,8 +247,22 @@ class ManiphestTaskEditController extends ManiphestController { ->setLabel('Projects') ->setName('projects') ->setValue($projects_value) + ->setID($project_tokenizer_id) + ->setCaption( + javelin_render_tag( + 'a', + array( + 'href' => '/project/quickcreate/', + 'mustcapture' => true, + 'sigil' => 'project-create', + ), + 'Create New Project')) ->setDatasource('/typeahead/common/projects/')); + Javelin::initBehavior('maniphest-project-create', array( + 'tokenizerID' => $project_tokenizer_id, + )); + if ($files) { $file_display = array(); foreach ($files as $file) { diff --git a/src/applications/maniphest/controller/taskedit/__init__.php b/src/applications/maniphest/controller/taskedit/__init__.php index 69d1be5b3f..d524917ca9 100644 --- a/src/applications/maniphest/controller/taskedit/__init__.php +++ b/src/applications/maniphest/controller/taskedit/__init__.php @@ -18,6 +18,9 @@ phutil_require_module('phabricator', 'applications/maniphest/storage/task'); phutil_require_module('phabricator', 'applications/maniphest/storage/transaction'); phutil_require_module('phabricator', 'applications/phid/constants'); phutil_require_module('phabricator', 'applications/phid/handle/data'); +phutil_require_module('phabricator', 'infrastructure/celerity/api'); +phutil_require_module('phabricator', 'infrastructure/javelin/api'); +phutil_require_module('phabricator', 'infrastructure/javelin/markup'); phutil_require_module('phabricator', 'view/form/base'); phutil_require_module('phabricator', 'view/form/control/markup'); phutil_require_module('phabricator', 'view/form/control/select'); diff --git a/src/applications/project/controller/quickcreate/PhabricatorProjectQuickCreateController.php b/src/applications/project/controller/quickcreate/PhabricatorProjectQuickCreateController.php new file mode 100644 index 0000000000..411021ac1d --- /dev/null +++ b/src/applications/project/controller/quickcreate/PhabricatorProjectQuickCreateController.php @@ -0,0 +1,95 @@ +getRequest(); + $user = $request->getUser(); + + $project = new PhabricatorProject(); + $project->setAuthorPHID($user->getPHID()); + $profile = new PhabricatorProjectProfile(); + + $e_name = true; + $errors = array(); + + if ($request->isFormPost()) { + + $project->setName($request->getStr('name')); + $profile->setBlurb($request->getStr('blurb')); + + if (!strlen($project->getName())) { + $e_name = 'Required'; + $errors[] = 'Project name is required.'; + } else { + $e_name = null; + } + + if (!$errors) { + $project->save(); + + $profile->setProjectPHID($project->getPHID()); + $profile->save(); + + return id(new AphrontAjaxResponse()) + ->setContent(array( + 'phid' => $project->getPHID(), + 'name' => $project->getName(), + )); + } + } + + $error_view = null; + if ($errors) { + $error_view = new AphrontErrorView(); + $error_view->setTitle('Form Errors'); + $error_view->setWidth(AphrontErrorView::WIDTH_DIALOG); + $error_view->setErrors($errors); + } + + $form = id(new AphrontFormLayoutView()) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel('Name') + ->setName('name') + ->setValue($project->getName()) + ->setError($e_name)) + ->appendChild( + id(new AphrontFormTextAreaControl()) + ->setLabel('Blurb') + ->setName('blurb') + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) + ->setValue($profile->getBlurb())); + + $dialog = id(new AphrontDialogView()) + ->setUser($user) + ->setWidth(AphrontDialogView::WIDTH_FORM) + ->setTitle('Create a New Project') + ->appendChild($error_view) + ->appendChild($form) + ->addSubmitButton('Create Project') + ->addCancelButton('/project/'); + + return id(new AphrontDialogResponse())->setDialog($dialog); + } + +} diff --git a/src/applications/project/controller/quickcreate/__init__.php b/src/applications/project/controller/quickcreate/__init__.php new file mode 100644 index 0000000000..22b2b9b1b9 --- /dev/null +++ b/src/applications/project/controller/quickcreate/__init__.php @@ -0,0 +1,23 @@ +user = $user; return $this; @@ -80,6 +84,11 @@ class AphrontDialogView extends AphrontView { return $this; } + public function setWidth($width) { + $this->width = $width; + return $this; + } + final public function render() { require_celerity_resource('aphront-dialog-view-css'); @@ -114,6 +123,16 @@ class AphrontDialogView extends AphrontView { $more = $this->class; + switch ($this->width) { + case self::WIDTH_FORM: + $more .= ' aphront-dialog-view-width-'.$this->width; + break; + case self::WIDTH_DEFAULT: + break; + default: + throw new Exception("Unknown dialog width '{$this->width}'!"); + } + $attributes = array( 'class' => 'aphront-dialog-view '.$more, 'sigil' => 'jx-dialog', diff --git a/src/view/form/base/AphrontFormView.php b/src/view/form/base/AphrontFormView.php index f4b94c37a1..9adb7f4d27 100644 --- a/src/view/form/base/AphrontFormView.php +++ b/src/view/form/base/AphrontFormView.php @@ -67,18 +67,22 @@ final class AphrontFormView extends AphrontView { Javelin::initBehavior('aphront-form-disable-on-submit'); + $layout = id(new AphrontFormLayoutView()) + ->setBackgroundShading(true) + ->setPadded(true) + ->appendChild($this->renderDataInputs()) + ->appendChild($this->renderChildren()); + return javelin_render_tag( 'form', array( 'action' => $this->action, 'method' => $this->method, - 'class' => 'aphront-form-view', 'enctype' => $this->encType, 'sigil' => $this->workflow ? 'workflow' : null, 'id' => $this->id, ), - $this->renderDataInputs(). - $this->renderChildren()); + $layout->render()); } private function renderDataInputs() { diff --git a/src/view/form/base/__init__.php b/src/view/form/base/__init__.php index 4ff1b13a67..2b61fad475 100644 --- a/src/view/form/base/__init__.php +++ b/src/view/form/base/__init__.php @@ -10,8 +10,10 @@ phutil_require_module('phabricator', 'infrastructure/celerity/api'); phutil_require_module('phabricator', 'infrastructure/javelin/api'); phutil_require_module('phabricator', 'infrastructure/javelin/markup'); phutil_require_module('phabricator', 'view/base'); +phutil_require_module('phabricator', 'view/form/layout'); phutil_require_module('phutil', 'markup'); +phutil_require_module('phutil', 'utils'); phutil_require_source('AphrontFormView.php'); diff --git a/src/view/form/error/AphrontErrorView.php b/src/view/form/error/AphrontErrorView.php index e3dfafe98a..47fa413b2c 100644 --- a/src/view/form/error/AphrontErrorView.php +++ b/src/view/form/error/AphrontErrorView.php @@ -24,6 +24,7 @@ final class AphrontErrorView extends AphrontView { const WIDTH_DEFAULT = 'default'; const WIDTH_WIDE = 'wide'; + const WIDTH_DIALOG = 'dialog'; private $title; private $errors; diff --git a/src/view/form/layout/AphrontFormLayoutView.php b/src/view/form/layout/AphrontFormLayoutView.php new file mode 100644 index 0000000000..2551d6b53b --- /dev/null +++ b/src/view/form/layout/AphrontFormLayoutView.php @@ -0,0 +1,59 @@ + tag. Useful on its own for creating forms in other forms (like + * dialogs) or forms which aren't submittable. + */ +final class AphrontFormLayoutView extends AphrontView { + + private $backgroundShading; + private $padded; + + public function setBackgroundShading($shading) { + $this->backgroundShading = $shading; + return $this; + } + + public function setPadded($padded) { + $this->padded = $padded; + return $this; + } + + public function render() { + $classes = array('aphront-form-view'); + + if ($this->backgroundShading) { + $classes[] = 'aphront-form-view-shaded'; + } + + if ($this->padded) { + $classes[] = 'aphront-form-view-padded'; + } + + $classes = implode(' ', $classes); + + return phutil_render_tag( + 'div', + array( + 'class' => $classes, + ), + $this->renderChildren()); + } +} diff --git a/src/view/form/layout/__init__.php b/src/view/form/layout/__init__.php new file mode 100644 index 0000000000..38c9fa6170 --- /dev/null +++ b/src/view/form/layout/__init__.php @@ -0,0 +1,14 @@ +