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 **/