Source of file AdminFormgenerator.php

Size: 29,125 Bytes - Last Modified: 2016-05-18T03:08:27+02:00

buildproject/core/module_system/admin/AdminFormgenerator.php

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
<?php
/*"******************************************************************************************************
*   (c) 2007-2016 by Kajona, www.kajona.de                                                              *
*       Published under the GNU LGPL v2.1, see /system/licence_lgpl.txt                                 *
*-------------------------------------------------------------------------------------------------------*
*	$Id$                                   *
********************************************************************************************************/

namespace Kajona\System\Admin;

use Kajona\System\Admin\Formentries\FormentryBase;
use Kajona\System\Admin\Formentries\FormentryDivider;
use Kajona\System\Admin\Formentries\FormentryHeadline;
use Kajona\System\Admin\Formentries\FormentryHidden;
use Kajona\System\Admin\Formentries\FormentryPlaintext;
use Kajona\System\System\Carrier;
use Kajona\System\System\Exception;
use Kajona\System\System\Lang;
use Kajona\System\System\Model;
use Kajona\System\System\ModelInterface;
use Kajona\System\System\Reflection;
use Kajona\System\System\ReflectionEnum;
use Kajona\System\System\Resourceloader;
use Kajona\System\System\UserUser;
use Kajona\System\System\ValidatorInterface;
use Kajona\System\System\Validators\ObjectvalidatorBase;
use Kajona\System\System\Validators\SystemidValidator;


/**
 * The admin-form generator is used to create, validate and manage forms for the backend.
 * Those forms are created as automatically as possible, so the setup of the field-types, validators
 * and more is done by reflection and code-inspection. Therefore, especially the annotations on the extending
 * \Kajona\System\System\Model-objects are analyzed.
 *
 * There are three ways of adding entries to the current form, each representing a different level of
 * automation.
 * 1. generateFieldsFromObject(), everything is rendered automatically
 * 2. addDynamicField(), adds a single field based on its name
 * 3. addField(), pass a field to add it explicitly
 *
 * @author sidler@mulchprod.de
 * @since  4.0
 * @module module_formgenerator
 */
class AdminFormgenerator
{

    const STR_METHOD_POST = "POST";
    const STR_METHOD_GET = "GET";

    const  STR_TYPE_ANNOTATION = "@fieldType";
    const  STR_VALIDATOR_ANNOTATION = "@fieldValidator";
    const  STR_MANDATORY_ANNOTATION = "@fieldMandatory";
    const  STR_LABEL_ANNOTATION = "@fieldLabel";
    const  STR_HIDDEN_ANNOTATION = "@fieldHidden";
    const  STR_READONLY_ANNOTATION = "@fieldReadonly";
    const  STR_OBJECTVALIDATOR_ANNOTATION = "@objectValidator";

    const  BIT_BUTTON_SAVE = 2;
    const  BIT_BUTTON_CLOSE = 4;
    const  BIT_BUTTON_CANCEL = 8;
    const  BIT_BUTTON_SUBMIT = 16;
    const  BIT_BUTTON_DELETE = 32;
    const  BIT_BUTTON_RESET = 64;
    const  BIT_BUTTON_CONTINUE = 128;
    const  BIT_BUTTON_BACK = 256;

    const FORM_ENCTYPE_MULTIPART = "multipart/form-data";
    const FORM_ENCTYPE_TEXTPLAIN = "text/plain";

    /**
     * The list of form-entries
     *
     * @var FormentryBase[]|FormentryInterface[]
     */
    private $arrFields = array();

    /**
     * The internal name of the form. Used to generate the field-identifiers and more.
     *
     * @var string
     */
    private $strFormname = "";

    /**
     * The source-object to be rendered by the form
     *
     * @var Model
     */
    private $objSourceobject = null;

    private $arrValidationErrors = array();

    private $arrHiddenElements = array();
    private $strHiddenGroupTitle = "additional fields";
    private $bitHiddenElementsVisible = false;

    private $strFormEncoding = "";

    private $strOnSubmit = "";
    private $strMethod = "POST";
    private $objLang;

    /**
     * Creates a new instance of the form-generator.
     *
     * @param string $strFormname
     * @param Model $objSourceobject
     */
    public function __construct($strFormname, $objSourceobject)
    {
        $this->strFormname = $strFormname;
        $this->objSourceobject = $objSourceobject;

        $this->strOnSubmit = "$(this).on('submit', function() { return false; }); $(window).off('unload'); KAJONA.admin.forms.animateSubmit(this); return true;";
        $this->objLang = Lang::getInstance();
    }

    /**
     * Stores the values saved with the params-array back to the currently associated object.
     * Afterwards, the object may be persisted.
     *
     * @return void
     */
    public function updateSourceObject()
    {
        foreach ($this->arrFields as $objOneField) {
            if ($objOneField->getObjSourceObject() != null) {
                $objOneField->setValueToObject();
            }
        }
    }

    /**
     * Returns an array of required fields.
     *
     * @return string[] where string[fielName] = type
     */
    public function getRequiredFields()
    {
        $arrReturn = array();
        foreach ($this->arrFields as $objOneField) {
            if ($objOneField->getBitMandatory()) {
                $arrReturn[$objOneField->getStrEntryName()] = get_class($objOneField->getObjValidator());
            }
        }

        return $arrReturn;
    }

    /**
     * Validates the current form.
     *
     * @throws Exception
     * @return bool
     */
    public function validateForm()
    {
        $objLang = Carrier::getInstance()->getObjLang();

        //1. Validate fields
        foreach ($this->arrFields as $objOneField) {

            $bitFieldIsEmpty
                = (!is_array($objOneField->getStrValue()) && trim($objOneField->getStrValue()) === "")
                || is_null($objOneField->getStrValue())
                || (is_array($objOneField->getStrValue()) && count($objOneField->getStrValue()) == 0); //if it is an array with no entries

            //mandatory field
            if ($objOneField->getBitMandatory()) {
                //if field is mandatory and empty -> validation error
                if ($bitFieldIsEmpty) {
                    $this->addValidationError($objOneField->getStrEntryName(), $objLang->getLang("commons_validator_field_empty", "system", array($objOneField->getStrLabel())));
                }
            }

            //if field is not empty -> validate
            if (!$bitFieldIsEmpty) {
                if (!$objOneField->validateValue()) {
                    $this->addValidationError($objOneField->getStrEntryName(), $objOneField->getStrValidationErrorMsg());
                }
            }
        }

        //2. Validate complete object
        if ($this->getObjSourceobject() != null) {
            $objReflection = new Reflection($this->getObjSourceobject());
            $arrObjectValidator = $objReflection->getAnnotationValuesFromClass(self::STR_OBJECTVALIDATOR_ANNOTATION);
            if (count($arrObjectValidator) == 1) {

                $strObjectValidator = $arrObjectValidator[0];
                if (!class_exists($strObjectValidator)) {
                    throw new Exception("object validator ".$strObjectValidator." not existing", Exception::$level_ERROR);
                }

                /** @var ObjectvalidatorBase $objValidator */
                $objValidator = new $strObjectValidator();

                //Keep the reference of the current object
                $objSourceObjectTemp = $this->getObjSourceobject();

                //Create a new instance of the source object and set it as source object in the formgenerator
                //Each existing field will also reference the new created source object
                $strClassName = get_class($this->objSourceobject);
                $this->objSourceobject = new $strClassName($this->objSourceobject->getStrSystemid());
                foreach ($this->arrFields as $objOneField) {
                    if ($objOneField->getObjSourceObject() != null) {
                        $objOneField->setObjSourceObject($this->objSourceobject);
                    }
                }

                //if we are in new-mode, we should fix the prev-id to the lateron matching one
                if (($this->getField("mode") != null && $this->getField("mode")->getStrValue() == "new") || Carrier::getInstance()->getParam("mode") == "new") {
                    $this->objSourceobject->setStrPrevId(Carrier::getInstance()->getParam("systemid"));
                }

                //Update the new source object values from the fields and validate the object
                $this->updateSourceObject();
                $objValidator->validateObject($this->getObjSourceobject());

                foreach ($objValidator->getArrValidationMessages() as $strKey => $arrMessages) {
                    if (!is_array($arrMessages)) {
                        throw new Exception("method validateObject must return an array of format array(\"<messageKey>\" => array())", Exception::$level_ERROR);
                    }

                    foreach ($arrMessages as $strMessage) {
                        $this->addValidationError($strKey, $strMessage);
                    }
                }

                //Set back kept reference to the formgenerator and all it's fields
                $this->objSourceobject = $objSourceObjectTemp;
                foreach ($this->arrFields as $objOneField) {
                    if ($objOneField->getObjSourceObject() != null) {
                        $objOneField->setObjSourceObject($objSourceObjectTemp);
                    }
                }
            }
        }

        return count($this->arrValidationErrors) == 0;
    }

    /**
     * @param string $strTargetURI If you pass null, no form-tags will be rendered.
     * @param int $intButtonConfig a list of buttons to attach to the end of the form. if you need more then the obligatory save-button,
     *                             pass them combined by a bitwise or, e.g. AdminFormgenerator::BIT_BUTTON_SAVE | AdminFormgenerator::$BIT_BUTTON_CANCEL
     *
     * @throws Exception
     * @return string
     */
    public function renderForm($strTargetURI, $intButtonConfig = 2)
    {
        $strReturn = "";

        //add a hidden systemid-field
        if ($this->objSourceobject != null && $this->objSourceobject instanceof Model) {
            $objField = new FormentryHidden($this->strFormname, "systemid");
            $objField->setStrEntryName("systemid")->setStrValue($this->objSourceobject->getSystemid())->setObjValidator(new SystemidValidator());
            $this->addField($objField);
        }

        $strGeneratedFormname = $this->strFormname;
        if($strGeneratedFormname == null) {
            $strGeneratedFormname = "form".generateSystemid();
        }
        $objToolkit = Carrier::getInstance()->getObjToolkit("admin");
        if ($strTargetURI !== null) {
            $strReturn .= $objToolkit->formHeader($strTargetURI, $strGeneratedFormname, $this->strFormEncoding, $this->strOnSubmit, $this->strMethod);
        }
        $strReturn .= $objToolkit->getValidationErrors($this);

        $strHidden = "";
        foreach ($this->arrFields as $objOneField) {
            if (in_array($objOneField->getStrEntryName(), $this->arrHiddenElements)) {
                $strHidden .= $objOneField->renderField();
            }
            else {
                $strReturn .= $objOneField->renderField();
            }
        }

        if ($strHidden != "") {
            $strReturn .= $objToolkit->formOptionalElementsWrapper($strHidden, $this->strHiddenGroupTitle, $this->bitHiddenElementsVisible);
        }

        $strButtons = "";

        if ($intButtonConfig & self::BIT_BUTTON_SUBMIT) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_submit", "system"), "submitbtn", "", "", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_SAVE) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_save", "system"), "submitbtn", "", "", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_CANCEL) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_cancel", "system"), "cancelbtn", "", "", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_CLOSE) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_close", "system"), "closebtn", "", "", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_DELETE) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_delete", "system"), "deletebtn", "", "", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_RESET) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_reset", "system"), "reset", "", "cancelbutton", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_CONTINUE) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_continue", "system"), "continuebtn", "", "", true, false);
        }

        if ($intButtonConfig & self::BIT_BUTTON_BACK) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_back", "system"), "backbtn", "", "", true, false);
        }

        $strReturn .= $objToolkit->formInputButtonWrapper($strButtons);

        if ($strTargetURI !== null) {
            $strReturn .= $objToolkit->formClose();
        }

        if (count($this->arrFields) > 0) {
            reset($this->arrFields);

            do {
                $objField = current($this->arrFields);
                if (!$objField instanceof FormentryHidden
                    && !$objField instanceof FormentryPlaintext
                    && !$objField instanceof FormentryHeadline
                    && !$objField instanceof FormentryDivider
                ) {
                    $strReturn .= $objToolkit->setBrowserFocus($objField->getStrEntryName());
                    break;
                }
            } while (next($this->arrFields) !== false);

        }

        //lock the record to avoid multiple edit-sessions - if in edit mode
        if ($this->objSourceobject != null && method_exists($this->objSourceobject, "getLockManager")) {

            $bitSkip = false;
            if ($this->getField("mode") != null && $this->getField("mode")->getStrValue() == "new") {
                $bitSkip = true;
            }

            if (!$bitSkip && !validateSystemid($this->objSourceobject->getSystemid())) {
                $bitSkip = true;
            }

            if (!$bitSkip) {
                if ($this->objSourceobject->getLockManager()->isAccessibleForCurrentUser()) {
                    $this->objSourceobject->getLockManager()->lockRecord();

                    //register a new unlock-handler
                    // KAJONA.admin.ajax.genericAjaxCall('system', 'unlockRecord', '".$this->objSourceobject->getSystemid()."');
                    $strReturn .= "<script type='text/javascript'>
                        $(window).on('unload', function() { $.ajax({url: KAJONA_WEBPATH + '/xml.php?admin=1&module=system&action=unlockRecord&systemid=".$this->objSourceobject->getSystemid()."', async:false}) ; });
//                        $('#{$strGeneratedFormname}').on('submit', function() { $(window).off('unload'); return true;});
                    </script>";
                }
                else {
                    $objUser = new UserUser($this->objSourceobject->getLockManager()->getLockId());
                    throw new Exception("Current record is already locked by user '".$objUser->getStrDisplayName()."'.\nCannot be locked for the current user", Exception::$level_ERROR);
                }
            }
        }


        return $strReturn;
    }

    /**
     * This is the most dynamically way to build a form.
     * Using this method, the current object is analyzed regarding its
     * methods and annotation. As soon as a matching property is found, the field
     * is added to the current list of form-entries.
     * Therefore the internal method addDynamicField is used.
     * In order to identify a field as relevant, the getter has to be marked with a fieldType annotation.
     *
     * @return void
     */
    public function generateFieldsFromObject()
    {

        //load all methods
        $objAnnotations = new Reflection($this->objSourceobject);

        $arrProperties = $objAnnotations->getPropertiesWithAnnotation("@fieldType");
        foreach ($arrProperties as $strPropertyName => $strDataType) {
            $this->addDynamicField($strPropertyName);
        }

        return;
    }

    /**
     * Adds a new field to the current form.
     * Therefore, the current source-object is inspected regarding the passed propertyname.
     * So it is essential to provide the matching getters and setters in order to have all
     * set up dynamically.
     *
     * @param string $strPropertyName
     *
     * @return FormentryBase|FormentryInterface
     * @throws Exception
     */
    public function addDynamicField($strPropertyName)
    {

        //try to get the matching getter
        $objReflection = new Reflection($this->objSourceobject);
        $strGetter = $objReflection->getGetter($strPropertyName);
        if ($strGetter === null) {
            throw new Exception("unable to find getter for property ".$strPropertyName."@".get_class($this->objSourceobject), Exception::$level_ERROR);
        }

        //load detailed properties

        $strType = $objReflection->getAnnotationValueForProperty($strPropertyName, self::STR_TYPE_ANNOTATION);
        $strValidator = $objReflection->getAnnotationValueForProperty($strPropertyName, self::STR_VALIDATOR_ANNOTATION);
        $strMandatory = $objReflection->getAnnotationValueForProperty($strPropertyName, self::STR_MANDATORY_ANNOTATION);
        $strLabel = $objReflection->getAnnotationValueForProperty($strPropertyName, self::STR_LABEL_ANNOTATION);
        $strHidden = $objReflection->getAnnotationValueForProperty($strPropertyName, self::STR_HIDDEN_ANNOTATION);
        $strReadonly = $objReflection->getAnnotationValueForProperty($strPropertyName, self::STR_READONLY_ANNOTATION);

        if ($strType === null) {
            $strType = "text";
        }


        $strPropertyName = Lang::getInstance()->propertyWithoutPrefix($strPropertyName);

        $objField = $this->getFormEntryInstance($strType, $strPropertyName);
        if ($strLabel !== null) {
            $objField->updateLabel($strLabel);
        }

        $bitMandatory = false;
        if ($strMandatory !== null && $strMandatory !== "false") {
            $bitMandatory = true;
        }

        $objField->setBitMandatory($bitMandatory);

        $bitReadonly = false;
        if ($strReadonly !== null && $strReadonly !== "false") {
            $bitReadonly = true;
        }

        $objField->setBitReadonly($bitReadonly);


        if ($strValidator !== null) {
            $objField->setObjValidator($this->getValidatorInstance($strValidator));
        }

        $this->addField($objField, $strPropertyName);

        if ($strHidden !== null) {
            $this->addFieldToHiddenGroup($objField);
        }

        return $objField;
    }

    /**
     * Set the position of a single field in the list of fields, so
     * the position inside the form.
     * The position is set human-readable, so the first element uses
     * the index 1.
     *
     * @param string $strField
     * @param int $intPos
     *
     * @throws Exception
     * @return void
     */
    public function setFieldToPosition($strField, $intPos)
    {

        if (!isset($this->arrFields[$strField])) {
            throw new Exception("field ".$strField." not found in list ".implode(", ", array_keys($this->arrFields)), Exception::$level_ERROR);
        }

        $objField = $this->arrFields[$strField];

        $arrNewOrder = array();

        $intI = 1;
        foreach ($this->arrFields as $strKey => $objValue) {
            //skip the same field, is inserted somewhere else
            if ($strKey == $strField) {
                continue;
            }

            if ($intI == $intPos) {
                $arrNewOrder[$strField] = $objField;
                $objField = null;
            }


            $arrNewOrder[$strKey] = $objValue;

            $intI++;
        }

        if ($objField !== null) {
            $arrNewOrder[$strField] = $objField;
        }


        $this->arrFields = $arrNewOrder;
    }


    /**
     * Loads the field-entry identified by the passed name.
     *
     * @param string $strName
     * @param string $strPropertyname
     *
     * @return FormentryBase|FormentryInterface
     * @throws Exception
     */
    private function getFormEntryInstance($strName, $strPropertyname)
    {

        //backslash given?
        //the V5 way: namespaces
        if(uniStrpos($strName, "\\") !== false) {
            $strClassname = $strName;
        }
        else {
            //backwards support for v4
            $strClassname = "class_formentry_".$strName;
            $strPath = Resourceloader::getInstance()->getPathForFile("/admin/formentries/".$strClassname.".php");

            if(!$strPath) {
                $strPath = Resourceloader::getInstance()->getPathForFile("/legacy/".$strClassname.".php");

                if($strPath == null) {
                    $strClassname = null;
                }
            }

        }

        if ($strClassname !== null) {
            return new $strClassname($this->strFormname, $strPropertyname, $this->objSourceobject);
        }
        else {
            throw new Exception("failed to load form-entry of type ".$strClassname, Exception::$level_ERROR);
        }

    }

    /**
     * Loads the validator identified by the passed name.
     *
     * @param string $strClassname
     *
     * @throws Exception
     * @return ValidatorInterface
     */
    private function getValidatorInstance($strClassname)
    {
        if (class_exists($strClassname)) {
            return new $strClassname();
        }

        if (uniStrpos($strClassname, "class_") === false) {
            $strClassname = "class_".$strClassname."_validator";
        }

        if (Resourceloader::getInstance()->getPathForFile("/system/validators/".$strClassname.".php")) {
            return new $strClassname();
        }
        else {
            throw new Exception("failed to load validator of type ".$strClassname, Exception::$level_ERROR);
        }
    }

    /**
     * Returns an language text. By default the formname is used as module name
     *
     * @param string $strText
     *
     * @param null $strModule
     *
     * @return string
     */
    protected function getLang($strText, $strModule = null, array $arrParameters = array())
    {
        return $this->objLang->getLang($strText, $strModule === null ? $this->strFormname : $strModule, $arrParameters);
    }

    /**
     * Returns an array of validation-errors
     *
     * @return array
     */
    public function getArrValidationErrors()
    {
        return $this->arrValidationErrors;
    }

    /**
     * Returns an array of validation-errors.
     * Alias for getArrValidationErrors due to backwards compatibility.
     *
     * @return array
     * @see getArrValidationErrors
     */
    public function getValidationErrors()
    {
        return $this->getArrValidationErrors();
    }

    /**
     * Adds an additional, user-specific validation-error to the current list of errors.
     *
     * @param string $strEntry
     * @param string $strMessage
     *
     * @return void
     */
    public function addValidationError($strEntry, $strMessage)
    {
        if (!array_key_exists($strEntry, $this->arrValidationErrors)) {
            $this->arrValidationErrors[$strEntry] = array();
        }
        $this->arrValidationErrors[$strEntry][] = $strMessage;
    }

    /**
     * Removes a single validation error
     *
     * @param string $strEntry
     *
     * @return void
     */
    public function removeValidationError($strEntry)
    {
        if (isset($this->arrValidationErrors[$strEntry])) {
            unset($this->arrValidationErrors[$strEntry]);
        }
    }

    /**
     * Clear all validation errors in the form
     */
    public function removeAllValidationError()
    {
        $this->arrValidationErrors = array();
    }

    /**
     * Adds a single field to the current form, the hard, manual way.
     * Use this method if you want to add custom fields to the current form.
     *
     * @param FormentryBase $objField
     * @param string $strKey
     *
     * @return FormentryBase|FormentryInterface
     */
    public function addField(FormentryBase $objField, $strKey = "")
    {
        if ($strKey == "") {
            $strKey = $objField->getStrEntryName();
        }

        $this->arrFields[$strKey] = $objField;

        return $objField;
    }

    /**
     * Returns a single entry form the fields, identified by its form-entry-name.
     *
     * @param string $strName
     *
     * @return FormentryBase|FormentryInterface
     */
    public function getField($strName)
    {
        if (isset($this->arrFields[$strName])) {
            return $this->arrFields[$strName];
        }
        else {
            return null;
        }
    }


    /**
     * Orders the fields by the given array.
     * The array must contain as values the keys of the form fields
     *
     * @param $arrFieldOrder
     * @return int
     * @throws Exception
     */
    public function orderFields($arrFieldOrder)
    {
        $intPosition = 1;

        foreach($arrFieldOrder as $strFieldName) {
            if($this->getField($strFieldName) != null) {
                $this->setFieldToPosition($strFieldName, $intPosition);
                $intPosition++;
            }
        }
        return $intPosition;
    }

    /**
     * Removes a single entry form the fields, identified by its form-entry-name.
     *
     * @param string $strName
     *
     * @return $this
     */
    public function removeField($strName)
    {
        unset($this->arrFields[$strName]);
        return $this;
    }

    /**
     * Sets the name of the group of hidden elements
     *
     * @param string $strHiddenGroupTitle
     *
     * @return $this
     */
    public function setStrHiddenGroupTitle($strHiddenGroupTitle)
    {
        $this->strHiddenGroupTitle = $strHiddenGroupTitle;
        return $this;
    }

    /**
     * Moves a single field to the list of hidden elements
     *
     * @param FormentryBase $objField
     *
     * @return FormentryBase
     */
    public function addFieldToHiddenGroup(FormentryBase $objField)
    {
        $this->arrHiddenElements[] = $objField->getStrEntryName();
        if (!isset($this->arrFields[$objField->getStrEntryName()]) && !isset($this->arrFields[$objField->getStrSourceProperty()])) {
            $this->addField($objField);
        }
        return $objField;
    }

    /**
     * Makes the group of hidden elements visible or hides the content on page-load
     *
     * @param bool $bitHiddenElementsVisible
     *
     * @return void
     */
    public function setBitHiddenElementsVisible($bitHiddenElementsVisible)
    {
        $this->bitHiddenElementsVisible = $bitHiddenElementsVisible;
    }

    /**
     * @return Model|ModelInterface
     */
    public function getObjSourceobject()
    {
        return $this->objSourceobject;
    }

    /**
     * @param Model|ModelInterface $objSource
     */
    protected function setObjSourceobject($objSource)
    {
        $this->objSourceobject = $objSource;
    }

    /**
     * @return FormentryBase[]|FormentryInterface[]
     */
    public function getArrFields()
    {
        return $this->arrFields;
    }

    /**
     * Allows to inject an onsubmit handler
     *
     * @param string $strOnSubmit
     *
     * @return void
     */
    public function setStrOnSubmit($strOnSubmit)
    {
        $this->strOnSubmit = $strOnSubmit;
    }

    /**
     * @return string
     */
    public function getStrOnSubmit()
    {
        return $this->strOnSubmit;
    }

    /**
     * @param string $strFormEncoding
     *
     * @return void
     */
    public function setStrFormEncoding($strFormEncoding)
    {
        $this->strFormEncoding = $strFormEncoding;
    }

    /**
     * @return string
     */
    public function getStrFormEncoding()
    {
        return $this->strFormEncoding;
    }

    /**
     * @return string
     */
    public function getStrFormname()
    {
        return $this->strFormname;
    }

    /**
     * @param string $strMethod
     * @throws Exception
     */
    public function setStrMethod($strMethod)
    {
        if (in_array($strMethod, array(self::STR_METHOD_GET, self::STR_METHOD_POST))) {
            $this->strMethod = $strMethod;
        } else {
            throw new Exception("Invalid form method", Exception::$level_ERROR);
        }
    }

    /**
     * @return string
     */
    public function getStrMethod()
    {
        return $this->strMethod;
    }
}