'')), array('Errors', array('separator' => '')), array('Label', array('separator' => '')), array('HtmlTag', array('tag' => 'div', 'class' => 'element')) ); /** * (non-PHPDoc) * @see \Zend_Form::construct() For the method documentation. */ public function __construct($options = null) { // Zend's plugin loader reverses the order of added prefix paths thus trying our paths first before trying // Zend paths $this->addPrefixPaths(array( array( 'prefix' => 'Icinga\\Web\\Form\\Element\\', 'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Element'), 'type' => static::ELEMENT ), array( 'prefix' => 'Icinga\\Web\\Form\\Decorator\\', 'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Decorator'), 'type' => static::DECORATOR ) )); parent::__construct($options); } /** * Set a callback that is called instead of this form's onSuccess method * * It is called using the following signature: (Form $this). * * @param callable $onSuccess Callback * * @return $this * * @throws LogicException If the callback is not callable */ public function setOnSuccess($onSuccess) { if (! is_callable($onSuccess)) { throw new LogicException('The option `onSuccess\' is not callable'); } $this->onSuccess = $onSuccess; return $this; } /** * Set the label to use for the standard submit button * * @param string $label The label to use for the submit button * * @return self */ public function setSubmitLabel($label) { $this->submitLabel = $label; return $this; } /** * Return the label being used for the standard submit button * * @return string */ public function getSubmitLabel() { return $this->submitLabel; } /** * Set the url to redirect to upon success * * @param string|Url $url The url to redirect to * * @return self */ public function setRedirectUrl($url) { $this->redirectUrl = $url; return $this; } /** * Return the url to redirect to upon success * * @return string|Url */ public function getRedirectUrl() { if ($this->redirectUrl === null) { $url = Url::fromRequest(array(), $this->getRequest()); // Be sure to remove all form dependent params because we do not want to submit it again $this->redirectUrl = $url->without(array_keys($this->getElements())); } return $this->redirectUrl; } /** * Set the view script to use when rendering this form * * @param string $viewScript The view script to use * * @return self */ public function setViewScript($viewScript) { $this->viewScript = $viewScript; return $this; } /** * Return the view script being used when rendering this form * * @return string */ public function getViewScript() { return $this->viewScript; } /** * Disable CSRF counter measure and remove its field if already added * * @param bool $disabled Set true in order to disable CSRF protection for this form, otherwise false * * @return self */ public function setTokenDisabled($disabled = true) { $this->tokenDisabled = (bool) $disabled; if ($disabled && $this->getElement($this->tokenElementName) !== null) { $this->removeElement($this->tokenElementName); } return $this; } /** * Return whether CSRF counter measures are disabled for this form * * @return bool */ public function getTokenDisabled() { return $this->tokenDisabled; } /** * Set the name to use for the CSRF element * * @param string $name The name to set * * @return self */ public function setTokenElementName($name) { $this->tokenElementName = $name; return $this; } /** * Return the name of the CSRF element * * @return string */ public function getTokenElementName() { return $this->tokenElementName; } /** * Disable form identification and remove its field if already added * * @param bool $disabled Set true in order to disable identification for this form, otherwise false * * @return self */ public function setUidDisabled($disabled = true) { $this->uidDisabled = (bool) $disabled; if ($disabled && $this->getElement($this->uidElementName) !== null) { $this->removeElement($this->uidElementName); } return $this; } /** * Return whether identification is disabled for this form * * @return bool */ public function getUidDisabled() { return $this->uidDisabled; } /** * Set the name to use for the form identification element * * @param string $name The name to set * * @return self */ public function setUidElementName($name) { $this->uidElementName = $name; return $this; } /** * Return the name of the form identification element * * @return string */ public function getUidElementName() { return $this->uidElementName; } /** * Set whether this form should validate the sent data when being automatically submitted * * @param bool $state * * @return self */ public function setValidatePartial($state) { $this->validatePartial = $state; return $this; } /** * Return whether this form should validate the sent data when being automatically submitted * * @return bool */ public function getValidatePartial() { return $this->validatePartial; } /** * Create this form * * @param array $formData The data sent by the user * * @return self */ public function create(array $formData = array()) { if (false === $this->created) { $this->createElements($formData); $this->addFormIdentification() ->addCsrfCounterMeasure() ->addSubmitButton(); if ($this->getAction() === '') { // We MUST set an action as JS gets confused otherwise, if // this form is being displayed in an additional column $this->setAction(Url::fromRequest()->without(array_keys($this->getElements()))); } $this->created = true; } return $this; } /** * Create and add elements to this form * * Intended to be implemented by concrete form classes. * * @param array $formData The data sent by the user */ public function createElements(array $formData) { } /** * Perform actions after this form was submitted using a valid request * * Intended to be implemented by concrete form classes. The base implementation returns always FALSE. * * @return null|bool Return FALSE in case no redirect should take place */ public function onSuccess() { return false; } /** * Perform actions when no form dependent data was sent * * Intended to be implemented by concrete form classes. */ public function onRequest() { } /** * Add a submit button to this form * * Uses the label previously set with Form::setSubmitLabel(). Overwrite this * method in order to add multiple submit buttons or one with a custom name. * * @return self */ public function addSubmitButton() { $submitLabel = $this->getSubmitLabel(); if ($submitLabel) { $this->addElement( 'submit', 'btn_submit', array( 'ignore' => true, 'label' => $submitLabel, 'decorators' => array( 'ViewHelper', array('HtmlTag', array('tag' => 'div')) ) ) ); } return $this; } /** * Add a subform * * @param Zend_Form $form The subform to add * @param string $name The name of the subform or null to use the name of $form * @param int $order The location where to insert the form * * @return Zend_Form */ public function addSubForm(Zend_Form $form, $name = null, $order = null) { if ($form instanceof self) { $form->removeDecorator('Form'); $form->setSubmitLabel(''); $form->setTokenDisabled(); $form->setUidDisabled(); } if ($name === null) { $name = $form->getName(); } return parent::addSubForm($form, $name, $order); } /** * Create a new element * * Icinga Web 2 loads its own default element decorators. For loading Zend's default element decorators set the * `disableLoadDefaultDecorators' option to any other value than `true'. For loading custom element decorators use * the 'decorators' option. * * @param string $type The type of the element * @param string $name The name of the element * @param mixed $options The options for the element * * @return Zend_Form_Element * * @see Form::$defaultElementDecorators For Icinga Web 2's default element decorators. */ public function createElement($type, $name, $options = null) { if ($options !== null) { if ($options instanceof Zend_Config) { $options = $options->toArray(); } if (! isset($options['decorators']) && ! array_key_exists('disabledLoadDefaultDecorators', $options) ) { $options['decorators'] = static::$defaultElementDecorators; } } else { $options = array('decorators' => static::$defaultElementDecorators); } $el = parent::createElement($type, $name, $options); if (($description = $el->getDescription()) !== null && ($label = $el->getDecorator('label')) !== false) { $label->setOptions(array( 'title' => $description, 'class' => 'has-feedback' )); } if ($el->getAttrib('autosubmit')) { $noScript = new NoScriptApply(); // Non-JS environments $decorators = $el->getDecorators(); $pos = array_search('Zend_Form_Decorator_ViewHelper', array_keys($decorators)) + 1; $el->setDecorators( array_slice($decorators, 0, $pos, true) + array(get_class($noScript) => $noScript) + array_slice($decorators, $pos, count($decorators) - $pos, true) ); $class = $el->getAttrib('class'); if (is_array($class)) { $class[] = 'autosubmit'; } elseif ($class === null) { $class = 'autosubmit'; } else { $class .= ' autosubmit'; } $el->setAttrib('class', $class); // JS environments unset($el->autosubmit); } return $el; } /** * Add a field with a unique and form specific ID * * @return self */ public function addFormIdentification() { if (false === $this->uidDisabled && $this->getElement($this->uidElementName) === null) { $this->addElement( 'hidden', $this->uidElementName, array( 'ignore' => true, 'value' => $this->getName(), 'decorators' => array('ViewHelper') ) ); } return $this; } /** * Add CSRF counter measure field to this form * * @return self */ public function addCsrfCounterMeasure() { if (false === $this->tokenDisabled && $this->getElement($this->tokenElementName) === null) { $this->addElement(new CsrfCounterMeasure($this->tokenElementName)); } return $this; } /** * Populate the elements with the given values * * @param array $defaults The values to populate the elements with */ public function populate(array $defaults) { $this->create($defaults); return parent::populate($defaults); } /** * Process the given request using this form * * Redirects to the url set with setRedirectUrl() upon success. See onSuccess() * and onRequest() wherewith you can customize the processing logic. * * @param Request $request The request to be processed * * @return Request The request supposed to be processed */ public function handleRequest(Request $request = null) { if ($request === null) { $request = $this->getRequest(); } else { $this->request = $request; } $formData = $this->getRequestData(); if ($this->getUidDisabled() || $this->wasSent($formData)) { $this->populate($formData); // Necessary to get isSubmitted() to work if (! $this->getSubmitLabel() || $this->isSubmitted()) { if ($this->isValid($formData) && (($this->onSuccess !== null && false !== call_user_func($this->onSuccess, $this)) || ($this->onSuccess === null && false !== $this->onSuccess()))) { $this->getResponse()->redirectAndExit($this->getRedirectUrl()); } } elseif ($this->getValidatePartial()) { // The form can't be processed but we may want to show validation errors though $this->isValidPartial($formData); } } else { $this->onRequest(); } return $request; } /** * Return whether the submit button of this form was pressed * * When overwriting Form::addSubmitButton() be sure to overwrite this method as well. * * @return bool True in case it was pressed, False otherwise or no submit label was set */ public function isSubmitted() { if ($this->getSubmitLabel()) { return $this->getElement('btn_submit')->isChecked(); } return false; } /** * Return whether the data sent by the user refers to this form * * Ensures that the correct form gets processed in case there are multiple forms * with equal submit button names being posted against the same route. * * @param array $formData The data sent by the user * * @return bool Whether the given data refers to this form */ public function wasSent(array $formData) { return isset($formData[$this->uidElementName]) && $formData[$this->uidElementName] === $this->getName(); } /** * Return whether the given values (possibly incomplete) are valid * * Unlike Zend_Form::isValid() this will not set NULL as value for * an element that is not present in the given data. * * @param array $formData The data to validate * * @return bool */ public function isValidPartial(array $formData) { $this->create($formData); // Ensure that disabled elements are not overwritten (http://www.zendframework.com/issues/browse/ZF-6909) foreach ($this->getElements() as $name => $element) { if ($element->getAttrib('disabled')) { $formData[$name] = $element->getValue(); } } return parent::isValidPartial($formData); } /** * Return whether the given values are valid * * @param array $formData The data to validate * * @return bool */ public function isValid($formData) { $this->create($formData); // Ensure that disabled elements are not overwritten (http://www.zendframework.com/issues/browse/ZF-6909) foreach ($this->getElements() as $name => $element) { if ($element->getAttrib('disabled')) { $formData[$name] = $element->getValue(); } } return parent::isValid($formData); } /** * Remove all elements of this form * * @return self */ public function clearElements() { $this->created = false; return parent::clearElements(); } /** * Load the default decorators * * Overwrites Zend_Form::loadDefaultDecorators to avoid having * the HtmlTag-Decorator added and to provide viewscript usage * * @return self */ public function loadDefaultDecorators() { if ($this->loadDefaultDecoratorsIsDisabled()) { return $this; } $decorators = $this->getDecorators(); if (empty($decorators)) { if ($this->viewScript) { $this->addDecorator('ViewScript', array( 'viewScript' => $this->viewScript, 'form' => $this )); } else { $this->addDecorator('FormErrors', array('onlyCustomFormErrors' => true)) ->addDecorator('FormElements') //->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')) ->addDecorator('Form'); } } return $this; } /** * Return the name of this form * * @return string */ public function getName() { $name = parent::getName(); if (! $name) { $name = get_class($this); $this->setName($name); $name = parent::getName(); } return $name; } /** * Return the request associated with this form * * Returns the global request if none has been set for this form yet. * * @return Request */ public function getRequest() { if ($this->request === null) { $this->request = Icinga::app()->getFrontController()->getRequest(); } return $this->request; } /** * Return the current Response * * @return Response */ public function getResponse() { return Icinga::app()->getFrontController()->getResponse(); } /** * Return the request data based on this form's request method * * @return array */ protected function getRequestData() { if (strtolower($this->request->getMethod()) === $this->getMethod()) { return $this->request->{'get' . ($this->request->isPost() ? 'Post' : 'Query')}(); } return array(); } /** * Get the translation domain for this form * * The returned translation domain is either determined based on this form's qualified name or it is the default * 'icinga' domain * * @return string */ protected function getTranslationDomain() { $parts = explode('\\', get_called_class()); if ($parts[1] === 'Module') { // Assume format Icinga\Module\ModuleName\Forms\... return strtolower($parts[2]); } return 'icinga'; } /** * Translate a string * * @param string $text The string to translate * @param string|null $context Optional parameter for context based translation * * @return string The translated string */ protected function translate($text, $context = null) { return Translator::translate($text, $this->getTranslationDomain(), $context); } /** * Translate a plural string * * @param string $textSingular The string in singular form to translate * @param string $textPlural The string in plural form to translate * @param integer $number The amount to determine from whether to return singular or plural * @param string|null $context Optional parameter for context based translation * * @return string The translated string */ protected function translatePlural($textSingular, $textPlural, $number, $context = null) { return Translator::translatePlural( $textSingular, $textPlural, $number, $this->getTranslationDomain(), $context ); } /** * Render this form * * @param Zend_View_Interface $view The view context to use * * @return string */ public function render(Zend_View_Interface $view = null) { $this->create(); return parent::render($view); } /** * Get the authentication manager * * @return Manager */ public function Auth() { if ($this->auth === null) { $this->auth = Manager::getInstance(); } return $this->auth; } /** * Whether the current user has the given permission * * @param string $permission Name of the permission * * @return bool */ public function hasPermission($permission) { return $this->Auth()->hasPermission($permission); } /** * Assert that the current user has the given permission * * @param string $permission Name of the permission * * @throws SecurityException If the current user lacks the given permission */ public function assertPermission($permission) { if (! $this->Auth()->hasPermission($permission)) { throw new SecurityException('No permission for %s', $permission); } } }