Form: upgrade to latest forms from Director

This commit is contained in:
Thomas Gelf 2016-12-08 10:11:46 +01:00
parent 590bdd35b1
commit 0380e46552
6 changed files with 531 additions and 194 deletions

View file

@ -0,0 +1,15 @@
<?php
// Avoid complaints about missing namespace and invalid class name
// @codingStandardsIgnoreStart
class Zend_View_Helper_FormSimpleNote extends Zend_View_Helper_FormElement
{
// @codingStandardsIgnoreEnd
public function formSimpleNote($name, $value = null)
{
$info = $this->_getInfo($name, $value);
extract($info); // name, value, attribs, options, listsep, disable
return $value;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Form\Element;
use Zend_Form_Element_Xhtml;
class FormElement extends Zend_Form_Element_Xhtml
{
}

View file

@ -0,0 +1,22 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Form\Element;
class SimpleNote extends FormElement
{
public $helper = 'formSimpleNote';
/**
* Always ignore this element
* @codingStandardsIgnoreStart
*
* @var boolean
*/
protected $_ignore = true;
// @codingStandardsIgnoreEnd
public function isValid($value, $context = null)
{
return true;
}
}

View file

@ -0,0 +1,166 @@
<?php
namespace Icinga\Module\Businessprocess\Web\Form;
use Icinga\Application\Icinga;
use Icinga\Application\Modules\Module;
use Zend_Form;
abstract class QuickBaseForm extends Zend_Form
{
/**
* The Icinga module this form belongs to. Usually only set if the
* form is initialized through the FormLoader
*
* @var Module
*/
protected $icingaModule;
protected $icingaModuleName;
private $hintCount = 0;
public function __construct($options = null)
{
$this->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('<div class="hint">' . $html . '</div>', $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);
}
}
}

View file

@ -3,18 +3,17 @@
namespace Icinga\Module\Businessprocess\Web\Form; namespace Icinga\Module\Businessprocess\Web\Form;
use Icinga\Application\Icinga; use Icinga\Application\Icinga;
use Icinga\Application\Modules\Module;
use Icinga\Exception\ProgrammingError; use Icinga\Exception\ProgrammingError;
use Icinga\Web\Notification; use Icinga\Web\Notification;
use Icinga\Web\Request; use Icinga\Web\Request;
use Icinga\Web\Response;
use Icinga\Web\Url; use Icinga\Web\Url;
use Exception; use Exception;
use Zend_Form;
/** /**
* QuickForm wants to be a base class for simple forms * 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'; const ID = '__FORM_NAME';
@ -53,28 +52,37 @@ abstract class QuickForm extends Zend_Form
protected $submitButtonName; protected $submitButtonName;
protected $deleteButtonName;
protected $fakeSubmitButtonName;
/** /**
* Whether form elements have already been created * Whether form elements have already been created
*/ */
protected $didSetup = false; protected $didSetup = false;
/** protected $isApiRequest = 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;
public function __construct($options = null) public function __construct($options = null)
{ {
parent::__construct($this->handleOptions($options)); parent::__construct($options);
$this->setMethod('post'); $this->setMethod('post');
$this->getActionFromRequest()
->createIdElement()
->regenerateCsrfToken()
->setPreferredDecorators();
}
protected function getActionFromRequest()
{
$this->setAction(Url::fromRequest()); $this->setAction(Url::fromRequest());
$this->createIdElement(); return $this;
$this->regenerateCsrfToken(); }
protected function setPreferredDecorators()
{
$this->setAttrib('class', 'autofocus');
$this->setDecorators( $this->setDecorators(
array( array(
'Description', 'Description',
@ -83,31 +91,41 @@ abstract class QuickForm extends Zend_Form
'Form' 'Form'
) )
); );
}
protected function handleOptions($options = null) return $this;
{
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;
} }
protected function addSubmitButtonIfSet() protected function addSubmitButtonIfSet()
{ {
if (false !== ($label = $this->getSubmitLabel())) { if (false === ($label = $this->getSubmitLabel())) {
$el = $this->createElement('submit', $label)->setLabel($label)->setDecorators(array('ViewHelper')); return;
$this->submitButtonName = $el->getName();
$this->addElement($el);
} }
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( $grp = array(
$this->submitButtonName, $this->submitButtonName,
$this->deleteButtonName $this->deleteButtonName
@ -115,17 +133,32 @@ abstract class QuickForm extends Zend_Form
$this->addDisplayGroup($grp, 'buttons', array( $this->addDisplayGroup($grp, 'buttons', array(
'decorators' => array( 'decorators' => array(
'FormElements', 'FormElements',
array('HtmlTag', array('tag' => 'dl')),
'DtDdWrapper', 'DtDdWrapper',
), ),
'order' => 1000, '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() protected function createIdElement()
{ {
$this->detectName(); $this->detectName();
$this->addHidden(self::ID, $this->getName()); $this->addHidden(self::ID, $this->getName());
$this->getElement(self::ID)->setIgnore(true); $this->getElement(self::ID)->setIgnore(true);
return $this;
} }
public function getSentValue($name, $default = null) public function getSentValue($name, $default = null)
@ -153,13 +186,15 @@ abstract class QuickForm extends Zend_Form
return $this; return $this;
} }
protected function loadForm($name, Module $module = null) public function setApiRequest($isApiRequest = true)
{ {
if ($module === null) { $this->isApiRequest = $isApiRequest;
$module = $this->icingaModule; return $this;
} }
return FormLoader::load($name, $module); public function isApiRequest()
{
return $this->isApiRequest;
} }
public function regenerateCsrfToken() public function regenerateCsrfToken()
@ -179,43 +214,6 @@ abstract class QuickForm extends Zend_Form
return $this; 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('<div class="hint">' . $html . '</div>', $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) public function setSuccessUrl($url, $params = null)
{ {
if (! $url instanceof Url) { if (! $url instanceof Url) {
@ -238,6 +236,10 @@ abstract class QuickForm extends Zend_Form
return $url; return $url;
} }
protected function beforeSetup()
{
}
public function setup() public function setup()
{ {
} }
@ -255,35 +257,49 @@ abstract class QuickForm extends Zend_Form
return parent::setAction($action); return parent::setAction($action);
} }
public function setIcingaModule(Module $module)
{
$this->icingaModule = $module;
return $this;
}
public function hasBeenSubmitted() public function hasBeenSubmitted()
{ {
if ($this->hasBeenSubmitted === null) { if ($this->hasBeenSubmitted === null) {
$req = $this->getRequest(); $req = $this->getRequest();
if ($req->isPost()) { if ($req->isPost()) {
$post = $req->getPost(); if (! $this->hasSubmitButton()) {
$name = $this->submitButtonName; return $this->hasBeenSubmitted = $this->hasBeenSent();
if ($name === null) {
$this->hasBeenSubmitted = $this->hasBeenSent();
} else {
$el = $this->getElement($name);
$this->hasBeenSubmitted = array_key_exists($name, $post)
&& $post[$name] === $this->getSubmitLabel();
} }
$this->hasBeenSubmitted = $this->pressedButton(
$this->fakeSubmitButtonName,
$this->getSubmitLabel()
) || $this->pressedButton(
$this->submitButtonName,
$this->getSubmitLabel()
);
} else { } else {
$this->hasBeenSubmitted === false; $this->hasBeenSubmitted = false;
} }
} }
return $this->hasBeenSubmitted; 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()) protected function beforeValidation($data = array())
{ {
} }
@ -291,6 +307,7 @@ abstract class QuickForm extends Zend_Form
public function prepareElements() public function prepareElements()
{ {
if (! $this->didSetup) { if (! $this->didSetup) {
$this->beforeSetup();
$this->setup(); $this->setup();
$this->addSubmitButtonIfSet(); $this->addSubmitButtonIfSet();
$this->onSetup(); $this->onSetup();
@ -302,19 +319,23 @@ abstract class QuickForm extends Zend_Form
public function handleRequest(Request $request = null) public function handleRequest(Request $request = null)
{ {
if ($request !== null) { if ($request === null) {
$request = $this->getRequest();
} else {
$this->setRequest($request); $this->setRequest($request);
} }
$this->prepareElements();
if ($this->hasBeenSent()) { if ($this->hasBeenSent()) {
$post = $this->getRequest()->getPost(); $post = $request->getPost();
if ($this->hasBeenSubmitted()) { if ($this->hasBeenSubmitted()) {
$this->beforeValidation($post); $this->beforeValidation($post);
if ($this->isValid($post)) { if ($this->isValid($post)) {
try { try {
$this->onSuccess(); $this->onSuccess();
} catch (Exception $e) { } catch (Exception $e) {
$this->addError($e->getMessage()); $this->addException($e);
$this->onFailure(); $this->onFailure();
} }
} else { } else {
@ -330,12 +351,21 @@ abstract class QuickForm extends Zend_Form
return $this; return $this;
} }
public function translate($string) public function addException(Exception $e, $elementName = null)
{ {
if ($this->icingaModuleName === null) { $file = preg_split('/[\/\\\]/', $e->getFile(), -1, PREG_SPLIT_NO_EMPTY);
return t($string); $file = array_pop($file);
$msg = sprintf(
'%s (%s:%d)',
$e->getMessage(),
$file,
$e->getLine()
);
if ($el = $this->getElement($elementName)) {
$el->addError($msg);
} else { } else {
return mt($this->icingaModuleName, $string); $this->addError($msg);
} }
} }
@ -363,6 +393,12 @@ abstract class QuickForm extends Zend_Form
public function redirectOnSuccess($message = null) public function redirectOnSuccess($message = null)
{ {
if ($this->isApiRequest()) {
// TODO: Set the status line message?
$this->successMessage = $this->getSuccessMessage($message);
return;
}
$url = $this->getSuccessUrl(); $url = $this->getSuccessUrl();
$this->notifySuccess($this->getSuccessMessage($message)); $this->notifySuccess($this->getSuccessMessage($message));
$this->redirectAndExit($url); $this->redirectAndExit($url);
@ -389,7 +425,15 @@ abstract class QuickForm extends Zend_Form
protected function redirectAndExit($url) 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() protected function onRequest()
@ -408,10 +452,15 @@ abstract class QuickForm extends Zend_Form
return $this; return $this;
} }
/**
* @return Request
*/
public function getRequest() public function getRequest()
{ {
if ($this->request === null) { if ($this->request === null) {
$this->setRequest(Icinga::app()->getFrontController()->getRequest()); /** @var Request $request */
$request = Icinga::app()->getFrontController()->getRequest();
$this->setRequest($request);
} }
return $this->request; return $this->request;
} }
@ -419,13 +468,20 @@ abstract class QuickForm extends Zend_Form
public function hasBeenSent() public function hasBeenSent()
{ {
if ($this->hasBeenSent === null) { 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()) { if ($req->isPost()) {
$post = $req->getPost(); $post = $req->getPost();
$this->hasBeenSent = array_key_exists(self::ID, $post) && $this->hasBeenSent = array_key_exists(self::ID, $post) &&
$post[self::ID] === $this->getName(); $post[self::ID] === $this->getName();
} else { } else {
$this->hasBeenSent === false; $this->hasBeenSent = false;
} }
} }

View file

@ -652,29 +652,69 @@ table.sourcecode {
/** Forms stolen from director **/ /** Forms stolen from director **/
.content form { .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] { form input[type=submit] {
.button(); .button();
border-width: 1px; border-width: 1px;
margin-top: 0.5em; margin-top: 0.5em;
&:disabled {
border-color: @gray-light;
background-color: @gray-light;
color: #fff;
}
} }
form input[type=submit]:first-of-type { form input[type=submit]:first-of-type {
border-width: 2px; 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 { form p.description {
display: none; padding: 1em 1em;
padding-bottom: 1em; margin: 0;
color: #888; font-style: italic;
padding-left: 32%; width: 100%;
font-style: italic; }
input, select, select option, textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
} }
form ul.form-errors { form ul.form-errors {
margin-bottom: 0.5em; margin-bottom: 0.5em;
ul.errors li { ul.errors li {
background: @color-critical; background: @color-critical;
font-weight: bold; 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 */ select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */
display: none; display: none;
} }
@ -708,24 +733,54 @@ select {
background: none; background: none;
} }
input[type=text], textarea, select { input[type=text], input[type=password], textarea, select {
max-width: 36em; max-width: 36em;
min-width: 20em; min-width: 20em;
width: 63%; width: 100%;
line-height: 2em;
height: 2.4em;
padding-left: 0.5em; padding-left: 0.5em;
border-style: solid; border-style: solid;
border-color: transparent; border-color: transparent;
border-bottom-color: @gray-light; border-bottom-color: @gray-lighter;
border-width: 1px 1px 1px 3px; border-width: 1px 1px 1px 3px;
background-color: white;
&.search { &.search {
background-color: transparent; background: transparent url("../img/icons/search.png") no-repeat scroll 0.5em center / 1em 1em;
padding-left: 2em; 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-focus-inner { border: 0; }
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}
select, input[type=text], textarea { select, input[type=text], textarea {
&:hover { &:hover {
border-style: dotted solid dotted solid; border-style: dotted solid dotted solid;
@ -742,58 +797,18 @@ select, input[type=text], textarea {
select[value=""] { select[value=""] {
color: blue; color: blue;
border: 1px solid #666; border: 1px solid #666;
background-color: white;
} }
select option { select option {
color: inherit; color: inherit;
padding-left: 0.5em; padding-left: 0.5em;
background-color: white;
} }
select option[value=""] { select option[value=""] {
color: #aaa; color: #aaa;
} background-color: white;
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';
}
}
} }
form dt label { form dt label {
@ -806,21 +821,77 @@ form dt label {
content: '*' 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 { form dd {
display: inline; padding: 0.3em 0.5em;
min-height: 2.5em;
vertical-align: top;
margin: 0; margin: 0;
} }
form dt {
padding: 0.5em 0.5em;
margin: 0;
}
form dt.active, form dd.active {
background-color: @tr-active-color;
}
form dt { form dt {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
min-width: 12em; min-width: 12em;
min-height: 2.5em; min-height: 2.5em;
width: 30%; 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 { form dd:after {
@ -835,7 +906,6 @@ form textarea {
form dd ul.errors { form dd ul.errors {
list-style-type: none; list-style-type: none;
padding-left: 0.3em; padding-left: 0.3em;
font-size: 0.857em;
li { li {
color: @colorCritical; 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 { form {
#_FAKE_SUBMIT { #_FAKE_SUBMIT {
position: absolute; 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 **/ /** End of forms **/