diff --git a/application/views/helpers/FormSimpleNote.php b/application/views/helpers/FormSimpleNote.php new file mode 100644 index 0000000..d8315f4 --- /dev/null +++ b/application/views/helpers/FormSimpleNote.php @@ -0,0 +1,15 @@ +_getInfo($name, $value); + extract($info); // name, value, attribs, options, listsep, disable + return $value; + } +} diff --git a/library/Businessprocess/Web/Form/Element/FormElement.php b/library/Businessprocess/Web/Form/Element/FormElement.php new file mode 100644 index 0000000..7647a5e --- /dev/null +++ b/library/Businessprocess/Web/Form/Element/FormElement.php @@ -0,0 +1,9 @@ +callZfConstructor($this->handleOptions($options)) + ->initializePrefixPaths(); + } + + protected function callZfConstructor($options = null) + { + parent::__construct($options); + return $this; + } + + protected function initializePrefixPaths() + { + $this->addPrefixPathsForBusinessprocess(); + if ($this->icingaModule && $this->icingaModuleName !== 'businessprocess') { + $this->addPrefixPathsForModule($this->icingaModule); + } + } + + protected function addPrefixPathsForBusinessprocess() + { + $module = Icinga::app() + ->getModuleManager() + ->loadModule('businessprocess') + ->getModule('businessprocess'); + + $this->addPrefixPathsForModule($module); + } + + public function addPrefixPathsForModule(Module $module) + { + $basedir = sprintf( + '%s/%s/Web/Form', + $module->getLibDir(), + ucfirst($module->getName()) + ); + + $this->addPrefixPaths(array( + array( + 'prefix' => __NAMESPACE__ . '\\Element\\', + 'path' => $basedir . '/Element', + 'type' => static::ELEMENT + ) + )); + + return $this; + } + + public function addHidden($name, $value = null) + { + $this->addElement('hidden', $name); + $el = $this->getElement($name); + $el->setDecorators(array('ViewHelper')); + if ($value !== null) { + $this->setDefault($name, $value); + $el->setValue($value); + } + + return $this; + } + + // TODO: Should be an element + public function addHtmlHint($html, $options = array()) + { + return $this->addHtml('
' . $html . '
', $options); + } + + public function addHtml($html, $options = array()) + { + if (array_key_exists('name', $options)) { + $name = $options['name']; + unset($options['name']); + } else { + $name = '_HINT' . ++$this->hintCount; + } + + $this->addElement('simpleNote', $name, $options); + $this->getElement($name) + ->setValue($html) + ->setIgnore(true) + ->setDecorators(array('ViewHelper')); + + return $this; + } + + public function optionalEnum($enum, $nullLabel = null) + { + if ($nullLabel === null) { + $nullLabel = $this->translate('- please choose -'); + } + + return array(null => $nullLabel) + $enum; + } + + protected function handleOptions($options = null) + { + if ($options === null) { + return $options; + } + + if (array_key_exists('icingaModule', $options)) { + /** @var Module icingaModule */ + $this->icingaModule = $options['icingaModule']; + $this->icingaModuleName = $this->icingaModule->getName(); + unset($options['icingaModule']); + } + + return $options; + } + + public function setIcingaModule(Module $module) + { + $this->icingaModule = $module; + return $this; + } + + protected function loadForm($name, Module $module = null) + { + if ($module === null) { + $module = $this->icingaModule; + } + + return FormLoader::load($name, $module); + } + + protected function valueIsEmpty($value) + { + if (is_array($value)) { + return empty($value); + } + + return strlen($value) === 0; + } + + public function translate($string) + { + if ($this->icingaModuleName === null) { + return t($string); + } else { + return mt($this->icingaModuleName, $string); + } + } +} diff --git a/library/Businessprocess/Web/Form/QuickForm.php b/library/Businessprocess/Web/Form/QuickForm.php index 861a4db..c657745 100644 --- a/library/Businessprocess/Web/Form/QuickForm.php +++ b/library/Businessprocess/Web/Form/QuickForm.php @@ -3,18 +3,17 @@ namespace Icinga\Module\Businessprocess\Web\Form; use Icinga\Application\Icinga; -use Icinga\Application\Modules\Module; use Icinga\Exception\ProgrammingError; use Icinga\Web\Notification; use Icinga\Web\Request; +use Icinga\Web\Response; use Icinga\Web\Url; use Exception; -use Zend_Form; /** * QuickForm wants to be a base class for simple forms */ -abstract class QuickForm extends Zend_Form +abstract class QuickForm extends QuickBaseForm { const ID = '__FORM_NAME'; @@ -53,28 +52,37 @@ abstract class QuickForm extends Zend_Form protected $submitButtonName; + protected $deleteButtonName; + + protected $fakeSubmitButtonName; + /** * Whether form elements have already been created */ protected $didSetup = false; - /** - * The Icinga module this form belongs to. Usually only set if the - * form is initialized through the FormLoader - */ - protected $icingaModule; - - protected $icingaModuleName; - - protected $hintCount = 0; + protected $isApiRequest = false; public function __construct($options = null) { - parent::__construct($this->handleOptions($options)); + parent::__construct($options); + $this->setMethod('post'); + $this->getActionFromRequest() + ->createIdElement() + ->regenerateCsrfToken() + ->setPreferredDecorators(); + } + + protected function getActionFromRequest() + { $this->setAction(Url::fromRequest()); - $this->createIdElement(); - $this->regenerateCsrfToken(); + return $this; + } + + protected function setPreferredDecorators() + { + $this->setAttrib('class', 'autofocus'); $this->setDecorators( array( 'Description', @@ -83,31 +91,41 @@ abstract class QuickForm extends Zend_Form 'Form' ) ); - } - protected function handleOptions($options = null) - { - if ($options === null) { - return $options; - } - - if (array_key_exists('icingaModule', $options)) { - $this->icingaModule = $options['icingaModule']; - $this->icingaModuleName = $this->icingaModule->getName(); - unset($options['icingaModule']); - } - - return $options; + return $this; } protected function addSubmitButtonIfSet() { - if (false !== ($label = $this->getSubmitLabel())) { - $el = $this->createElement('submit', $label)->setLabel($label)->setDecorators(array('ViewHelper')); - $this->submitButtonName = $el->getName(); - $this->addElement($el); + if (false === ($label = $this->getSubmitLabel())) { + return; } + if ($this->submitButtonName && $el = $this->getElement($this->submitButtonName)) { + return; + } + + $el = $this->createElement('submit', $label) + ->setLabel($label) + ->setDecorators(array('ViewHelper')); + $this->submitButtonName = $el->getName(); + $this->addElement($el); + + $fakeEl = $this->createElement('submit', '_FAKE_SUBMIT') + ->setLabel($label) + ->setDecorators(array('ViewHelper')); + $this->fakeSubmitButtonName = $fakeEl->getName(); + $this->addElement($fakeEl); + + $this->addDisplayGroup( + array($this->fakeSubmitButtonName), + 'fake_button', + array( + 'decorators' => array('FormElements'), + 'order' => 1, + ) + ); + $grp = array( $this->submitButtonName, $this->deleteButtonName @@ -115,17 +133,32 @@ abstract class QuickForm extends Zend_Form $this->addDisplayGroup($grp, 'buttons', array( 'decorators' => array( 'FormElements', + array('HtmlTag', array('tag' => 'dl')), 'DtDdWrapper', ), 'order' => 1000, )); } + protected function addSimpleDisplayGroup($elements, $name, $options) + { + if (! array_key_exists('decorators', $options)) { + $options['decorators'] = array( + 'FormElements', + array('HtmlTag', array('tag' => 'dl')), + 'Fieldset', + ); + } + return $this->addDisplayGroup($elements, $name, $options); + + } + protected function createIdElement() { $this->detectName(); $this->addHidden(self::ID, $this->getName()); $this->getElement(self::ID)->setIgnore(true); + return $this; } public function getSentValue($name, $default = null) @@ -153,13 +186,15 @@ abstract class QuickForm extends Zend_Form return $this; } - protected function loadForm($name, Module $module = null) + public function setApiRequest($isApiRequest = true) { - if ($module === null) { - $module = $this->icingaModule; - } + $this->isApiRequest = $isApiRequest; + return $this; + } - return FormLoader::load($name, $module); + public function isApiRequest() + { + return $this->isApiRequest; } public function regenerateCsrfToken() @@ -179,43 +214,6 @@ abstract class QuickForm extends Zend_Form return $this; } - public function addHidden($name, $value = null) - { - $this->addElement('hidden', $name); - $el = $this->getElement($name); - $el->setDecorators(array('ViewHelper')); - if ($value !== null) { - $this->setDefault($name, $value); - $el->setValue($value); - } - - return $this; - } - - public function addHtmlHint($html, $options = array()) - { - return $this->addHtml('
' . $html . '
', $options); - } - - public function addHtml($html, $options = array()) - { - $name = '_HINT' . ++$this->hintCount; - $this->addElement('note', $name, $options); - $this->getElement($name) - ->setValue($html) - ->setIgnore(true) - ->removeDecorator('Label'); - - return $this; - } - - public function optionalEnum($enum) - { - return array( - null => $this->translate('- please choose -') - ) + $enum; - } - public function setSuccessUrl($url, $params = null) { if (! $url instanceof Url) { @@ -238,6 +236,10 @@ abstract class QuickForm extends Zend_Form return $url; } + protected function beforeSetup() + { + } + public function setup() { } @@ -255,35 +257,49 @@ abstract class QuickForm extends Zend_Form return parent::setAction($action); } - public function setIcingaModule(Module $module) - { - $this->icingaModule = $module; - return $this; - } - public function hasBeenSubmitted() { if ($this->hasBeenSubmitted === null) { $req = $this->getRequest(); if ($req->isPost()) { - $post = $req->getPost(); - $name = $this->submitButtonName; - - if ($name === null) { - $this->hasBeenSubmitted = $this->hasBeenSent(); - } else { - $el = $this->getElement($name); - $this->hasBeenSubmitted = array_key_exists($name, $post) - && $post[$name] === $this->getSubmitLabel(); + if (! $this->hasSubmitButton()) { + return $this->hasBeenSubmitted = $this->hasBeenSent(); } + + $this->hasBeenSubmitted = $this->pressedButton( + $this->fakeSubmitButtonName, + $this->getSubmitLabel() + ) || $this->pressedButton( + $this->submitButtonName, + $this->getSubmitLabel() + ); } else { - $this->hasBeenSubmitted === false; + $this->hasBeenSubmitted = false; } } return $this->hasBeenSubmitted; } + protected function hasSubmitButton() + { + return $this->submitButtonName !== null; + } + + protected function pressedButton($name, $label) + { + $req = $this->getRequest(); + if (! $req->isPost()) { + return false; + } + + $req = $this->getRequest(); + $post = $req->getPost(); + + return array_key_exists($name, $post) + && $post[$name] === $label; + } + protected function beforeValidation($data = array()) { } @@ -291,6 +307,7 @@ abstract class QuickForm extends Zend_Form public function prepareElements() { if (! $this->didSetup) { + $this->beforeSetup(); $this->setup(); $this->addSubmitButtonIfSet(); $this->onSetup(); @@ -302,19 +319,23 @@ abstract class QuickForm extends Zend_Form public function handleRequest(Request $request = null) { - if ($request !== null) { + if ($request === null) { + $request = $this->getRequest(); + } else { $this->setRequest($request); } + $this->prepareElements(); + if ($this->hasBeenSent()) { - $post = $this->getRequest()->getPost(); + $post = $request->getPost(); if ($this->hasBeenSubmitted()) { $this->beforeValidation($post); if ($this->isValid($post)) { try { $this->onSuccess(); } catch (Exception $e) { - $this->addError($e->getMessage()); + $this->addException($e); $this->onFailure(); } } else { @@ -330,12 +351,21 @@ abstract class QuickForm extends Zend_Form return $this; } - public function translate($string) + public function addException(Exception $e, $elementName = null) { - if ($this->icingaModuleName === null) { - return t($string); + $file = preg_split('/[\/\\\]/', $e->getFile(), -1, PREG_SPLIT_NO_EMPTY); + $file = array_pop($file); + $msg = sprintf( + '%s (%s:%d)', + $e->getMessage(), + $file, + $e->getLine() + ); + + if ($el = $this->getElement($elementName)) { + $el->addError($msg); } else { - return mt($this->icingaModuleName, $string); + $this->addError($msg); } } @@ -363,6 +393,12 @@ abstract class QuickForm extends Zend_Form public function redirectOnSuccess($message = null) { + if ($this->isApiRequest()) { + // TODO: Set the status line message? + $this->successMessage = $this->getSuccessMessage($message); + return; + } + $url = $this->getSuccessUrl(); $this->notifySuccess($this->getSuccessMessage($message)); $this->redirectAndExit($url); @@ -389,7 +425,15 @@ abstract class QuickForm extends Zend_Form protected function redirectAndExit($url) { - Icinga::app()->getFrontController()->getResponse()->redirectAndExit($url); + /** @var Response $response */ + $response = Icinga::app()->getFrontController()->getResponse(); + $response->redirectAndExit($url); + } + + protected function setHttpResponseCode($code) + { + Icinga::app()->getFrontController()->getResponse()->setHttpResponseCode($code); + return $this; } protected function onRequest() @@ -408,10 +452,15 @@ abstract class QuickForm extends Zend_Form return $this; } + /** + * @return Request + */ public function getRequest() { if ($this->request === null) { - $this->setRequest(Icinga::app()->getFrontController()->getRequest()); + /** @var Request $request */ + $request = Icinga::app()->getFrontController()->getRequest(); + $this->setRequest($request); } return $this->request; } @@ -419,13 +468,20 @@ abstract class QuickForm extends Zend_Form public function hasBeenSent() { if ($this->hasBeenSent === null) { - $req = $this->getRequest(); + + /** @var Request $req */ + if ($this->request === null) { + $req = Icinga::app()->getFrontController()->getRequest(); + } else { + $req = $this->request; + } + if ($req->isPost()) { $post = $req->getPost(); $this->hasBeenSent = array_key_exists(self::ID, $post) && $post[self::ID] === $this->getName(); } else { - $this->hasBeenSent === false; + $this->hasBeenSent = false; } } diff --git a/public/css/module.less b/public/css/module.less index ce1b038..df742d3 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -652,29 +652,69 @@ table.sourcecode { /** Forms stolen from director **/ .content form { - margin-bottom: 2em; + margin-bottom: 2em; } +.content form.inline { + margin: 0; +} + +.invisible { + position: absolute; + left: -100%; +} + +form input[type=file] { + background-color: white; + padding-right: 1em; +} + + form input[type=submit] { - .button(); - border-width: 1px; - margin-top: 0.5em; + .button(); + border-width: 1px; + margin-top: 0.5em; + + &:disabled { + border-color: @gray-light; + background-color: @gray-light; + color: #fff; + } } form input[type=submit]:first-of-type { border-width: 2px; } +form input[type=submit].link-button { + color: @icinga-blue; + background: none; + border: none; + padding: 0; + + text-align: left; + + &:hover { + text-decoration: underline; + } +} + form p.description { - display: none; - padding-bottom: 1em; - color: #888; - padding-left: 32%; - font-style: italic; + padding: 1em 1em; + margin: 0; + font-style: italic; + width: 100%; +} + +input, select, select option, textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } form ul.form-errors { margin-bottom: 0.5em; + ul.errors li { background: @color-critical; font-weight: bold; @@ -683,21 +723,6 @@ form ul.form-errors { } } -fieldset { - margin: 0; - padding: 0; - border: none; - - legend { - margin: 0 0 0.5em 0; - font-size: 1.2em; - color: @icinga-blue; - border-bottom: 1px solid @gray-light; - display: block; - width: 100%; - } -} - select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */ display: none; } @@ -708,24 +733,54 @@ select { background: none; } -input[type=text], textarea, select { +input[type=text], input[type=password], textarea, select { max-width: 36em; min-width: 20em; - width: 63%; + width: 100%; + line-height: 2em; + height: 2.4em; padding-left: 0.5em; border-style: solid; border-color: transparent; - border-bottom-color: @gray-light; + border-bottom-color: @gray-lighter; border-width: 1px 1px 1px 3px; + background-color: white; &.search { - background-color: transparent; + background: transparent url("../img/icons/search.png") no-repeat scroll 0.5em center / 1em 1em; padding-left: 2em; } } +select[multiple] { + height: auto; +} + +select option { + height: 2em; + padding-top: 0.3em; +} + +select[multiple=multiple] { + height: auto; +} + +label { + line-height: 2em; +} + +form dl { + margin: 0; + padding: 0; +} + select::-moz-focus-inner { border: 0; } +select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #000; +} + select, input[type=text], textarea { &:hover { border-style: dotted solid dotted solid; @@ -742,58 +797,18 @@ select, input[type=text], textarea { select[value=""] { color: blue; border: 1px solid #666; + background-color: white; } select option { color: inherit; padding-left: 0.5em; + background-color: white; } select option[value=""] { color: #aaa; -} - - -fieldset { - legend { - padding-left: 1em; - cursor: pointer; - user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - - &:hover { - border-style: dotted; - } - - &::before { - // icon: down-dir - font-family: 'ifont'; - content: '\e81d'; - margin-left: -1em; - padding-top: 0.25em; - float: left; - color: inherit; - } - } - - &.collapsed { - - legend { - margin: 0; - } - - dd, dt { - display: none; - } - - legend::before { - // icon: right-dir - content: '\e820'; - } - - } + background-color: white; } form dt label { @@ -806,21 +821,77 @@ form dt label { content: '*' } } + + &:hover { + text-decoration: underline; + cursor: pointer; + } +} + +form fieldset { + min-width: 36em; +} + +form dd input.related-action[type='submit'] { + display: none; +} + +form dd.active li.active input.related-action[type='submit'] { + display: inline-block; +} + +form dd.active { + p.description { + color: inherit; + font-style: normal; + } } form dd { - display: inline; - min-height: 2.5em; - vertical-align: top; + padding: 0.3em 0.5em; margin: 0; } +form dt { + padding: 0.5em 0.5em; + margin: 0; +} + +form dt.active, form dd.active { + background-color: @tr-active-color; +} + form dt { display: inline-block; vertical-align: top; min-width: 12em; min-height: 2.5em; width: 30%; + &.errors label { + color: @color-critical; + } +} + +form .errors label { + color: @color-critical; +} + +form dd { + display: inline-block; + width: 63%; + min-height: 2.5em; + vertical-align: top; + margin: 0; + &.errors { + input[type=text], select { + border-color: @color-critical; + } + } + + &.full-width { + padding: 0.5em; + width: 100%; + } } form dd:after { @@ -835,7 +906,6 @@ form textarea { form dd ul.errors { list-style-type: none; padding-left: 0.3em; - font-size: 0.857em; li { color: @colorCritical; @@ -843,6 +913,22 @@ form dd ul.errors { } } +form div.hint { + padding: 1em; + background-color: @tr-hover-color; + margin: 1em 0; + max-width: 65em; + font-size: 1em; + + pre { + font-style: normal; + background-color: white; + margin: 0; + padding: 1em; + } +} + + form { #_FAKE_SUBMIT { position: absolute; @@ -850,21 +936,4 @@ form { } } -form div.hint { - padding: 1em; - background-color: #f2f4fd; - border: 1px solid lightgrey; - margin: 1em 0; - - pre { - font-style: normal; - background-color: white; - font-size: 1.25em; - margin: 0; - padding: 1em; - } -} - - - /** End of forms **/