<commit_before><?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\Resourceloader;
use Kajona\System\System\StringUtil;
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 <EMAIL>prod.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  BIT_BUTTON_SAVENEXT = 512;

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

    const STR_FORM_ON_SAVE_RELOAD_PARAM = "onsavereloadaction";

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


    /**
     * After save action is being called, this URL will used for the reload URL
     *
     * @var null
     */
    private $strOnSaveRedirectUrl = null;//

    /**
     * A list of buttons to attach to the end of the form.
     * pass them single or combined by a bitwise OR, e.g. AdminFormgenerator::BIT_BUTTON_SAVE | AdminFormgenerator::$BIT_BUTTON_CANCEL
     *
     * @var null
     */
    private $intButtonConfig = null;

    /**
     * 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'); require('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();
            }
        }
    }

    /**
     * Updates the internal value of each field. This can be used in case the form comes from a
     * cache and the request parameters have changed during the request
     */
    public final function readValues()
    {
        foreach ($this->arrFields as $objOneField) {
            $objOneField->readValue();
        }
    }

    /**
     * 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 = $objOneField->isFieldEmpty();

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

            //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 = "";
        $objToolkit = Carrier::getInstance()->getObjToolkit("admin");

        /*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);
        }

        /*add reload URL param*/
        if ($this->strOnSaveRedirectUrl != "") {
            $objField = new FormentryHidden($this->strFormname, self::STR_FORM_ON_SAVE_RELOAD_PARAM);
            $objField->setStrEntryName(self::STR_FORM_ON_SAVE_RELOAD_PARAM)->setStrValue($this->strOnSaveRedirectUrl);
            $this->addField($objField);
        }

        // we add a info field if the user can not access the record
        if ($this->shouldAcquireLock() && !$this->objSourceobject->getLockManager()->isAccessibleForCurrentUser()) {
            $objUser = new UserUser($this->objSourceobject->getLockManager()->getLockId());
            $strMessage = Lang::getInstance()->getLang("generic_record_locked", "system", array($objUser->getStrDisplayName()));

            // add info box field
            $objField = new FormentryPlaintext($this->strFormname);
            $objField->setStrValue($objToolkit->warningBox($strMessage, "alert-info"));
            $this->addField($objField, "lock_info");
            $this->setFieldToPosition("lock_info", 1);

            // set all fields to readonly
            $arrFields = $this->getArrFields();
            foreach ($arrFields as $objField) {
                $objField->setBitReadonly(true);
            }

            // we overwrite the button config to 0 so that no button is displayed
            $this->intButtonConfig = 0;
        }

        /*generate form name*/
        $strGeneratedFormname = $this->strFormname;
        if ($strGeneratedFormname == null) {
            $strGeneratedFormname = "form" . generateSystemid();
        }

        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);
        }

        /*Render form buttons*/
        $strButtons = "";

        //Check if class property is set
        if($this->intButtonConfig !== null) {
            $intButtonConfig = $this->intButtonConfig;
        }

        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);
        }

        if ($intButtonConfig & self::BIT_BUTTON_SAVENEXT) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_savenext", "system"), "savenextbtn", "", "", 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);<commit_msg>UPDATE: module_system | AdminFormgenerator > reorder continue button<commit_after><?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\Resourceloader;
use Kajona\System\System\StringUtil;
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 <EMAIL>
 * @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  BIT_BUTTON_SAVENEXT = 512;

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

    const STR_FORM_ON_SAVE_RELOAD_PARAM = "onsavereloadaction";

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


    /**
     * After save action is being called, this URL will used for the reload URL
     *
     * @var null
     */
    private $strOnSaveRedirectUrl = null;//

    /**
     * A list of buttons to attach to the end of the form.
     * pass them single or combined by a bitwise OR, e.g. AdminFormgenerator::BIT_BUTTON_SAVE | AdminFormgenerator::$BIT_BUTTON_CANCEL
     *
     * @var null
     */
    private $intButtonConfig = null;

    /**
     * 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'); require('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();
            }
        }
    }

    /**
     * Updates the internal value of each field. This can be used in case the form comes from a
     * cache and the request parameters have changed during the request
     */
    public final function readValues()
    {
        foreach ($this->arrFields as $objOneField) {
            $objOneField->readValue();
        }
    }

    /**
     * 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 = $objOneField->isFieldEmpty();

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

            //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 = "";
        $objToolkit = Carrier::getInstance()->getObjToolkit("admin");

        /*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);
        }

        /*add reload URL param*/
        if ($this->strOnSaveRedirectUrl != "") {
            $objField = new FormentryHidden($this->strFormname, self::STR_FORM_ON_SAVE_RELOAD_PARAM);
            $objField->setStrEntryName(self::STR_FORM_ON_SAVE_RELOAD_PARAM)->setStrValue($this->strOnSaveRedirectUrl);
            $this->addField($objField);
        }

        // we add a info field if the user can not access the record
        if ($this->shouldAcquireLock() && !$this->objSourceobject->getLockManager()->isAccessibleForCurrentUser()) {
            $objUser = new UserUser($this->objSourceobject->getLockManager()->getLockId());
            $strMessage = Lang::getInstance()->getLang("generic_record_locked", "system", array($objUser->getStrDisplayName()));

            // add info box field
            $objField = new FormentryPlaintext($this->strFormname);
            $objField->setStrValue($objToolkit->warningBox($strMessage, "alert-info"));
            $this->addField($objField, "lock_info");
            $this->setFieldToPosition("lock_info", 1);

            // set all fields to readonly
            $arrFields = $this->getArrFields();
            foreach ($arrFields as $objField) {
                $objField->setBitReadonly(true);
            }

            // we overwrite the button config to 0 so that no button is displayed
            $this->intButtonConfig = 0;
        }

        /*generate form name*/
        $strGeneratedFormname = $this->strFormname;
        if ($strGeneratedFormname == null) {
            $strGeneratedFormname = "form" . generateSystemid();
        }

        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);
        }

        /*Render form buttons*/
        $strButtons = "";

        //Check if class property is set
        if($this->intButtonConfig !== null) {
            $intButtonConfig = $this->intButtonConfig;
        }

        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_BACK) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_back", "system"), "backbtn", "", "", 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_SAVENEXT) {
            $strButtons .= $objToolkit->formInputSubmit(Lang::getInstance()->getLang("commons_savenext", "system"), "savenextbtn", "", "", 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);
{'fraction_non_alphanumeric': 0.1391461214672279, 'fraction_numerical': 0.0025856885147324113, 'mean_word_length': 2.425084955205437, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 278, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3213917', 'n_tokens_mistral': 9063, 'n_tokens_neox': 8782, 'n_words': 1990}