1106 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1106 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
namespace nur\v\bs3\fo;
 | 
						|
 | 
						|
use ArrayAccess;
 | 
						|
use Countable;
 | 
						|
use nur\A;
 | 
						|
use nur\b\coll\TArrayTools;
 | 
						|
use nur\b\coll\TBaseArray;
 | 
						|
use nur\b\coll\TGenericArray;
 | 
						|
use nur\b\params\IParametrable;
 | 
						|
use nur\b\params\Tparametrable1;
 | 
						|
use nur\data\types\IType;
 | 
						|
use nur\data\types\md_utils;
 | 
						|
use nur\data\types\Metadata;
 | 
						|
use nur\F;
 | 
						|
use nur\msg;
 | 
						|
use nur\v\al;
 | 
						|
use nur\v\base\Alerter;
 | 
						|
use nur\v\base\ComponentPrintable;
 | 
						|
use nur\v\v;
 | 
						|
use nur\v\vo;
 | 
						|
 | 
						|
class Form extends ComponentPrintable implements IParametrable, ArrayAccess, Countable {
 | 
						|
  use Tparametrable1, TBaseArray, TGenericArray, TArrayTools;
 | 
						|
 | 
						|
  /** @return bool est-ce un formulaire de type "horizontal" par opposition à "inline" */
 | 
						|
  function HORIZONTAL(): bool {
 | 
						|
    return static::HORIZONTAL;
 | 
						|
  } const HORIZONTAL = false;
 | 
						|
  /** @return ?string classe pour les boutons submit en form horizontal */
 | 
						|
  function FGS_CLASS(): ?string { return null; }
 | 
						|
  /** @return ?string classe pour les labels en form horizontal */
 | 
						|
  function FGL_CLASS(): ?string { return null; }
 | 
						|
  /** @return ?string classe pour les contrôle en form horizontal */
 | 
						|
  function FGC_CLASS(): ?string { return null; }
 | 
						|
  function SHOW_HELP(): bool {
 | 
						|
    return static::SHOW_HELP;
 | 
						|
  } const SHOW_HELP = true;
 | 
						|
 | 
						|
  /** @var ?string|?array classes spécifiques à ce formulaire */
 | 
						|
  const FORM_CLASS = null;
 | 
						|
 | 
						|
  /** @var bool faut-il tenir compte du paramètre [autohide_label]? */
 | 
						|
  const ALLOW_AUTOHIDE_LABEL = false;
 | 
						|
 | 
						|
  /** @var ?array schéma des données du formulaire */
 | 
						|
  const SCHEMA = null;
 | 
						|
 | 
						|
  /** @var array[] schéma de la définition des paramètres de formulaire */
 | 
						|
  const PARAMS_SCHEMA = [
 | 
						|
    "key" => ["string", null, "nom de la clé dans le schéma"],
 | 
						|
    "name" => ["?string", null, "nom du paramètre"],
 | 
						|
    "default" => [null, null, "valeur par défaut du paramètre"],
 | 
						|
    "value" => [null, null, "valeur du paramètre à forcer"],
 | 
						|
    "label" => [null, null, "label du contrôle de formulaire"],
 | 
						|
    "control" => ["?string", null, "classe de contrôle de formulaire à utiliser"],
 | 
						|
  ];
 | 
						|
 | 
						|
  /** @var ?array paramètres de formulaire à récupérer */
 | 
						|
  const PARAMS = null;
 | 
						|
 | 
						|
  /** @var ?array paramètres à exclure de la liste calculée automatiquement */
 | 
						|
  const PARAMS_EXCLUDES = null;
 | 
						|
 | 
						|
  /**
 | 
						|
   * @var ?array paramètres de formulaire à récupérer. cette valeur est mergée
 | 
						|
   * avec PARAMS. cela permet d'augmenter le paramétrage calculé automatiquement
 | 
						|
   */
 | 
						|
  const PARAMS_OVERRIDES = null;
 | 
						|
 | 
						|
  const INVALID_MSG = null;
 | 
						|
 | 
						|
  /** @var ?array paramètres du bouton submit automatique */
 | 
						|
  const SUBMIT = "Envoyer";
 | 
						|
 | 
						|
  const CONTROL_PREFIX = "\n";
 | 
						|
 | 
						|
  const CONTROL_SUFFIX = null;
 | 
						|
 | 
						|
  const SUBMITTED_KEY = null;
 | 
						|
 | 
						|
  const CONTROL_CLASSES = [
 | 
						|
    "hidden" => ControlHidden::class,
 | 
						|
    "fixed" => ControlFixed::class,
 | 
						|
    "text" => ControlText::class,
 | 
						|
    "number" => ControlNumber::class,
 | 
						|
    "date" => ControlDate::class,
 | 
						|
    "time" => ControlTime::class,
 | 
						|
    "password" => ControlPassword::class,
 | 
						|
    "select" => ControlSelect::class,
 | 
						|
    "checkbox" => ControlCheckbox::class,
 | 
						|
    "radiobutton" => ControlRadiobutton::class,
 | 
						|
    "textarea" => ControlTextarea::class,
 | 
						|
    "file" => ControlFile::class,
 | 
						|
    "submit" => ControlSubmit::class,
 | 
						|
    "reset" => ControlReset::class,
 | 
						|
  ];
 | 
						|
 | 
						|
  /** @var int type de contrôle: name, name+value ou name+value+checked */
 | 
						|
  const N = 1, NV = 2, NVC = 3;
 | 
						|
  const CONTROL_TYPES = [
 | 
						|
    ControlHidden::class => [self::NV, null],
 | 
						|
    ControlFixed::class => [self::NV, "label"],
 | 
						|
    ControlText::class => [self::NV, "label"],
 | 
						|
    ControlNumber::class => [self::NV, "label"],
 | 
						|
    ControlDate::class => [self::NV, "label"],
 | 
						|
    ControlTime::class => [self::NV, "label"],
 | 
						|
    ControlPassword::class => [self::NV, "label"],
 | 
						|
    ControlSelect::class => [self::NV, "label"],
 | 
						|
    ControlCheckbox::class => [self::NVC, "text"],
 | 
						|
    ControlRadiobutton::class => [self::NVC, "text"],
 | 
						|
    ControlTextarea::class => [self::NV, "label"],
 | 
						|
    ControlFile::class => [self::N, "label"],
 | 
						|
    ControlSubmit::class => [self::NV, "submit"],
 | 
						|
    ControlReset::class => [self::NV, "reset"],
 | 
						|
  ];
 | 
						|
 | 
						|
  const PARAMETRABLE_PARAMS_SCHEMA = [
 | 
						|
    "action" => ["?string", null, "url destination du formulaire"],
 | 
						|
    "method" => ["?string", "get", "méthode du formulaire (post, get)"],
 | 
						|
    "enctype" => ["?string", null, "type d'encodage du formulaire"],
 | 
						|
    "upload" => ["bool", null, "ce formulaire est-il utilisé pour uploader des fichiers?"],
 | 
						|
    "class" => ["?array", null, "classes CSS du formulaire"],
 | 
						|
    "attrs" => ["?array", null, "attributs HTML supplémentaires"],
 | 
						|
    "autohide_label" => ["bool", true, "faut-il cacher le label s'il y a le placeholder?"],
 | 
						|
    "schema" => ["?array", null, "schéma des données du formulaire"],
 | 
						|
    "params" => ["?array", null, "paramètres de formulaire à récupérer"],
 | 
						|
    "params_excludes" => ["?array", null, "paramètres à exclure de la liste calculée automatiquement"],
 | 
						|
    "params_overrides" => ["?array", null, "paramètres de formulaire à récupérer à merger avec [params]"],
 | 
						|
    "alerter" => [Alerter::class, null, "instance utilisée pour afficher un message avant le bouton submit"],
 | 
						|
    "invalid_msg" => ["?content", null, "message à afficher si le formulaire est invalide"],
 | 
						|
    "submit" => ["?array", null, "paramètres du bouton submit automatique"],
 | 
						|
    "submits" => ["?array", null, "boutons submit supplémentaires"],
 | 
						|
    "autoadd_submit" => ["bool", true, "Faut-il rajouter le bouton submit automatique?"],
 | 
						|
    "control_prefix" => [null, null, "valeur à afficher avant chaque contrôle"],
 | 
						|
    "control_suffix" => [null, null, "valeur à afficher après chaque contrôle"],
 | 
						|
    "submitted_key" => ["?string", null, "nom d'un champ du formulaire qui, s'il existe, indique que le formulaire a été soumis"],
 | 
						|
    "autoload_params" => ["bool", false, "faut-il charger les paramètres de formulaire de suite?"],
 | 
						|
    "autoprint" => ["bool", false, "faut-il afficher le formulaire de suite?"],
 | 
						|
  ];
 | 
						|
 | 
						|
  function __construct(?array $params=null) {
 | 
						|
    self::set_parametrable_params_defaults($params, [
 | 
						|
      "schema" => static::SCHEMA,
 | 
						|
      "params_exclude" => static::PARAMS_EXCLUDES,
 | 
						|
      "params_overrides" => static::PARAMS_OVERRIDES,
 | 
						|
      "params" => static::PARAMS,
 | 
						|
      "invalid_msg" => q(static::INVALID_MSG),
 | 
						|
      "submit" => A::with(static::SUBMIT),
 | 
						|
      "control_prefix" => q(static::CONTROL_PREFIX),
 | 
						|
      "control_suffix" => q(static::CONTROL_SUFFIX),
 | 
						|
      "submitted_key" => static::SUBMITTED_KEY,
 | 
						|
    ]);
 | 
						|
    [$params, $attrs] = $this->splitParametrableParams($params);
 | 
						|
    if ($attrs) A::merge($params["attrs"], $attrs);
 | 
						|
    $this->initParametrableParams($params);
 | 
						|
    if ($this->ppAutoloadParams) $this->loadParams();
 | 
						|
    if ($this->ppAutoprint) $this->print();
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var ?string */
 | 
						|
  protected $ppAction;
 | 
						|
  
 | 
						|
  /** @var ?string */
 | 
						|
  protected $ppMethod;
 | 
						|
 | 
						|
  /** @var ?string */
 | 
						|
  protected $ppEnctype;
 | 
						|
 | 
						|
  function pp_setUpload(bool $upload=true): void {
 | 
						|
    if ($upload) {
 | 
						|
      $this->ppMethod = "post";
 | 
						|
      $this->ppEnctype = "multipart/form-data";
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppClass;
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppAttrs;
 | 
						|
 | 
						|
  /** @var ?bool */
 | 
						|
  protected $ppAutohideLabel;
 | 
						|
 | 
						|
  function isAutohideLabel(): bool {
 | 
						|
    return static::ALLOW_AUTOHIDE_LABEL && $this->ppAutohideLabel;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var ?Metadata */
 | 
						|
  protected $md;
 | 
						|
 | 
						|
  function pp_setSchema(?array $schema): void {
 | 
						|
    $md = null;
 | 
						|
    if ($schema !== null) $md = new Metadata($schema);
 | 
						|
    $this->md = $md;
 | 
						|
  }
 | 
						|
 | 
						|
  private static $params_md;
 | 
						|
 | 
						|
  protected static function params_md(): Metadata {
 | 
						|
    return md_utils::ensure_md(self::$params_md, self::PARAMS_SCHEMA);
 | 
						|
  }
 | 
						|
 | 
						|
  protected static function params_ensure_schema(array &$params, ?Metadata $md=null): void {
 | 
						|
    $params_md = self::params_md();
 | 
						|
    foreach ($params as $key => &$param) {
 | 
						|
      $params_md->ensureSchema($param, $key);
 | 
						|
      A::replace_n($param, "name", $key);
 | 
						|
      if ($md !== null) {
 | 
						|
        $sfield = A::get($md->getSfields(), $key);
 | 
						|
        if ($sfield !== null) {
 | 
						|
          A::replace_n($param, "default", $sfield["default"]);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      $controlClass = $param["control"];
 | 
						|
      if ($controlClass !== null) {
 | 
						|
        $controlClass = A::get(static::CONTROL_CLASSES, $controlClass, $controlClass);
 | 
						|
        $param["control"] = $controlClass;
 | 
						|
      }
 | 
						|
    }; unset($param);
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppParamsExcludes;
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppParamsOverrides;
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppParams;
 | 
						|
 | 
						|
  /** @var bool des valeurs ont-elle été attribuées à des paramètres? */
 | 
						|
  protected $paramsValuesSet;
 | 
						|
 | 
						|
  /** @var array fusion de $ppParams, $ppParamsExcludes et $ppParamsOverrides */
 | 
						|
  protected $params;
 | 
						|
 | 
						|
  /** @var ?Alerter */
 | 
						|
  protected $ppAlerter;
 | 
						|
 | 
						|
  function al(): ?Alerter {
 | 
						|
    $al = $this->ppAlerter;
 | 
						|
    if ($al === null) $al = al::get();
 | 
						|
    return $al;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var array|string|null */
 | 
						|
  protected $ppInvalidMsg;
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppSubmit;
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $ppSubmits;
 | 
						|
 | 
						|
  /** @var bool */
 | 
						|
  protected $ppAutoaddSubmit;
 | 
						|
 | 
						|
  /** @var array contenu à afficher avant chaque élément de formulaire */
 | 
						|
  protected $ppControlPrefix;
 | 
						|
 | 
						|
  function getControlPrefix(): ?array {
 | 
						|
    return $this->ppControlPrefix;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var array contenu à afficher après chaque élément de formulaire */
 | 
						|
  protected $ppControlSuffix;
 | 
						|
 | 
						|
  function getControlSuffix(): ?array {
 | 
						|
    return $this->ppControlSuffix;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var bool */
 | 
						|
  protected $ppAutoprint;
 | 
						|
 | 
						|
  /** @var ?string */
 | 
						|
  protected $ppSubmittedKey;
 | 
						|
 | 
						|
  /** @var bool */
 | 
						|
  protected $ppAutoloadParams;
 | 
						|
 | 
						|
  protected function afterSetParametrableParams(array $modifiedKeys, ?Metadata $md=null): void {
 | 
						|
    if (self::was_parametrable_param_modified($modifiedKeys, "submits")
 | 
						|
      && !self::was_parametrable_param_modified($modifiedKeys, "autoadd_submit")
 | 
						|
    ) {
 | 
						|
      $this->ppAutoaddSubmit = false;
 | 
						|
    }
 | 
						|
    if (self::was_parametrable_param_modified($modifiedKeys, "params", "params_excludes", "params_overrides")) {
 | 
						|
      $params = [];
 | 
						|
      if ($this->ppParams !== null) {
 | 
						|
        $paramsExcludes = $this->ppParamsExcludes;
 | 
						|
        $index = 0;
 | 
						|
        foreach ($this->ppParams as $key => $param) {
 | 
						|
          if ($key === $index) {
 | 
						|
            $index++;
 | 
						|
            $key = $param;
 | 
						|
          }
 | 
						|
          if ($paramsExcludes === null || !in_array($key, $paramsExcludes)) {
 | 
						|
            $params[$key] = $param;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        self::params_ensure_schema($params, $md);
 | 
						|
      }
 | 
						|
      $paramsOverrides = $this->ppParamsOverrides;
 | 
						|
      if ($paramsOverrides !== null) {
 | 
						|
        $oparams = [];
 | 
						|
        $index = 0;
 | 
						|
        foreach ($paramsOverrides as $key => $param) {
 | 
						|
          if ($key === $index) {
 | 
						|
            $index++;
 | 
						|
            $key = $param;
 | 
						|
          }
 | 
						|
          $oparams[$key] = $param;
 | 
						|
        }
 | 
						|
        self::params_ensure_schema($oparams, $md);
 | 
						|
        foreach ($oparams as $key => $param) {
 | 
						|
          if (!A::has($params, $key)) {
 | 
						|
            $params[$key] = $param;
 | 
						|
          } else {
 | 
						|
            A::merge_nn($params[$key], $param);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      $this->paramsValuesSet = false;
 | 
						|
      $this->params = $params;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  #############################################################################
 | 
						|
  ## Params
 | 
						|
 | 
						|
  private function resolveName($key, ?string &$name, &$value=null): bool {
 | 
						|
    $params = $this->params;
 | 
						|
    if (A::has($params, $key)) {
 | 
						|
      $param = $params[$key];
 | 
						|
      $name = $param["name"];
 | 
						|
      $value = $param["default"];
 | 
						|
      return true;
 | 
						|
    } else {
 | 
						|
      $name = $key;
 | 
						|
      $value = null;
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private function resolveNameType($key, ?string $name=null): array {
 | 
						|
    if ($name === null) $this->resolveName($key, $name);
 | 
						|
    $md = $this->md;
 | 
						|
    $type = $md !== null? $md->getType($key, false): null;
 | 
						|
    return [$name, $type];
 | 
						|
  }
 | 
						|
 | 
						|
  function set($key, $value, ?string $name=null): self {
 | 
						|
    $unset = false;
 | 
						|
    /** @var IType $type */
 | 
						|
    [$name, $type] = $this->resolveNameType($key, $name);
 | 
						|
    if ($type !== null) {
 | 
						|
      if ($type->isUndef($value)) $unset = true;
 | 
						|
      else $type->verifix($value, $this->results[$name]);
 | 
						|
    }
 | 
						|
    $this->paramsValuesSet = true;
 | 
						|
    if ($unset) $this->_del($key);
 | 
						|
    else $this->_set($key, $value);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  protected function paramExists(string $name): bool {
 | 
						|
    return F::has($name);
 | 
						|
  }
 | 
						|
 | 
						|
  private function fixType(IType $type, &$value): bool {
 | 
						|
    if ($type->isTrim()) $value = trim($value);
 | 
						|
    if ($value === "") $type->verifixReplaceEmpty($value);
 | 
						|
    if ($value === false) $type->verifixReplaceFalse($value);
 | 
						|
    if ($value === null) $type->verifixReplaceNull($value);
 | 
						|
    if ($type->isUndef($value)) return false;
 | 
						|
    elseif ($value === null && !$type->isAllowNull()) return false;
 | 
						|
    elseif ($value === false && !$type->isAllowFalse()) return false;
 | 
						|
    elseif ($value === "" && !$type->isAllowEmpty()) $value = null;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  protected function paramGet($key, ?string $name, &$value): bool {
 | 
						|
    /** @var IType $type */
 | 
						|
    [$name, $type] = $this->resolveNameType($key, $name);
 | 
						|
    $value = F::get($name);
 | 
						|
    if ($value === null) return false;
 | 
						|
    if ($type !== null) {
 | 
						|
      if (is_array($value)) {
 | 
						|
        $values = [];
 | 
						|
        foreach ($value as $tmp) {
 | 
						|
          if ($this->fixType($type, $tmp)) {
 | 
						|
            $values[] = $tmp;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        $value = $values;
 | 
						|
      } else {
 | 
						|
        if (!$this->fixType($type, $value)) return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  protected $submitted;
 | 
						|
 | 
						|
  protected function checkSubmitted(): ?bool {
 | 
						|
    $submittedKey = $this->ppSubmittedKey;
 | 
						|
    if ($submittedKey !== null) {
 | 
						|
      if ($this->paramGet($submittedKey, null, $value)) {
 | 
						|
        return true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    $submitted = null;
 | 
						|
    foreach (A::keys($this->params) as $key) {
 | 
						|
      if ($this->paramGet($key, null, $value)) {
 | 
						|
        if ($submitted === null) $submitted = true;
 | 
						|
      } else {
 | 
						|
        $submitted = false;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $submitted;
 | 
						|
  }
 | 
						|
 | 
						|
  /** tester si le formulaire a été soumis */
 | 
						|
  function isSubmitted(): bool {
 | 
						|
    if ($this->submitted === null) {
 | 
						|
      $this->submitted = boolval($this->checkSubmitted());
 | 
						|
    }
 | 
						|
    return $this->submitted;
 | 
						|
  }
 | 
						|
 | 
						|
  function setSubmitted(bool $submitted=true): void {
 | 
						|
    $this->submitted = $submitted;
 | 
						|
  }
 | 
						|
 | 
						|
  function loadParams(?array $keys=null): self {
 | 
						|
    $md = $this->md;
 | 
						|
    if ($this->ppParams === null && $md !== null) {
 | 
						|
      $this->ppParams = $md->getKeys();
 | 
						|
      $this->afterSetParametrableParams(["params"], $md);
 | 
						|
    }
 | 
						|
    $params = $this->params;
 | 
						|
    if ($keys === null && $params !== null) {
 | 
						|
      $keys = array_keys($params);
 | 
						|
    }
 | 
						|
    $submitted = $this->submitted;
 | 
						|
    if ($keys !== null) {
 | 
						|
      $submitted = $this->checkSubmitted();
 | 
						|
      foreach ($keys as $key) {
 | 
						|
        $this->resolveName($key, $name, $value);
 | 
						|
        if ($this->paramExists($name)) {
 | 
						|
          if ($submitted === null) $submitted = true;
 | 
						|
          if ($this->paramGet($key, $name, $value)) {
 | 
						|
            $this->set($key, $value, $name);
 | 
						|
          } else {
 | 
						|
            #XXX?
 | 
						|
            #$this->del($key);
 | 
						|
          }
 | 
						|
        } elseif ($value !== null) {
 | 
						|
          $this->set($key, $value, $name);
 | 
						|
        } else {
 | 
						|
          #XXX
 | 
						|
          #$this->del($key);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if ($submitted === null) $submitted = false;
 | 
						|
    }
 | 
						|
    $this->paramsValuesSet = true;
 | 
						|
    $this->submitted = $submitted;
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoloadParams(): self {
 | 
						|
    if (!$this->paramsValuesSet) $this->loadParams();
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function resetParams(string ...$keys): void {
 | 
						|
    foreach ($keys as $key) {
 | 
						|
      $this->_del($key);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /** retourner $params avec les $key traduits en $name */
 | 
						|
  function bp(?array $params=null): array {
 | 
						|
    $result = [];
 | 
						|
    if ($params !== null) {
 | 
						|
      $index = 0;
 | 
						|
      foreach ($params as $key => $value) {
 | 
						|
        if ($key === $index) {
 | 
						|
          $index++;
 | 
						|
          $key = $value;
 | 
						|
          $value = $this->get($key);
 | 
						|
        }
 | 
						|
        $this->resolveName($key, $name);
 | 
						|
        $result[$name] = $value;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $result;
 | 
						|
  }
 | 
						|
 | 
						|
  #############################################################################
 | 
						|
  ## Validation
 | 
						|
 | 
						|
  /** @var array */
 | 
						|
  protected $results;
 | 
						|
 | 
						|
  function setInvalid(string $key, ?string $message=null): void {
 | 
						|
    if ($message === null) $message = "Cette valeur est invalide";
 | 
						|
    $this->resolveName($key, $name);
 | 
						|
    $value = A::get($this->data, $key);
 | 
						|
    $this->results[$name] = [
 | 
						|
      "valid" => false,
 | 
						|
      "value" => $value,
 | 
						|
      "error" => $message,
 | 
						|
      "exception" => null,
 | 
						|
      "orig" => $value,
 | 
						|
      "parsed" => null,
 | 
						|
      "remains" => null,
 | 
						|
    ];
 | 
						|
  }
 | 
						|
 | 
						|
  function _getResult(string $name): ?array {
 | 
						|
    return A::get($this->results, $name);
 | 
						|
  }
 | 
						|
 | 
						|
  function getFirstInvalid(): ?string {
 | 
						|
    if ($this->results !== null) {
 | 
						|
      foreach ($this->results as $name => $result) {
 | 
						|
        if (!$result["valid"]) return $name;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  function isValidForm(): bool {
 | 
						|
    # si le formulaire n'a pas été soumis, il ne peut pas être valide
 | 
						|
    $valid = false;
 | 
						|
    if ($this->isSubmitted() && $this->results !== null) {
 | 
						|
      foreach ($this->results as $result) {
 | 
						|
        if (!$result["valid"]) return false;
 | 
						|
        $valid = true;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $valid;
 | 
						|
  }
 | 
						|
 | 
						|
  function isValid(string $key, ?array &$result=null): bool {
 | 
						|
    $this->resolveName($key, $name);
 | 
						|
    $result = A::get($this->results, $name);
 | 
						|
    return $result === null || $result["valid"];
 | 
						|
  }
 | 
						|
 | 
						|
  #############################################################################
 | 
						|
  ## Form
 | 
						|
 | 
						|
  function printAlert(): void {
 | 
						|
    $alerter = $this->ppAlerter;
 | 
						|
    if ($alerter !== null) $alerter->print();
 | 
						|
    $invalidMsg = $this->ppInvalidMsg;
 | 
						|
    if ($invalidMsg && $this->isSubmitted() && !$this->isValidForm()) {
 | 
						|
      if ($alerter !== null) $alerter->pwarning($invalidMsg);
 | 
						|
      else msg::warning($invalidMsg);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  function printStart(): void {
 | 
						|
    vo::write($this->getStart());
 | 
						|
  }
 | 
						|
 | 
						|
  function getStart(): array {
 | 
						|
    return v::start("form", [
 | 
						|
      "action" => $this->ppAction,
 | 
						|
      "method" => $this->ppMethod,
 | 
						|
      "enctype" => $this->ppEnctype,
 | 
						|
      "class" => [static::FORM_CLASS, $this->ppClass],
 | 
						|
      $this->ppAttrs,
 | 
						|
      $this->form(),
 | 
						|
    ]);
 | 
						|
  }
 | 
						|
 | 
						|
  protected function form(): ?array {
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  function printEnd(): void {
 | 
						|
    vo::write($this->getEnd());
 | 
						|
  }
 | 
						|
 | 
						|
  function getEnd(): array {
 | 
						|
    return v::end("form");
 | 
						|
  }
 | 
						|
 | 
						|
  #############################################################################
 | 
						|
  ## Controls
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $hiddenControls;
 | 
						|
 | 
						|
  function addHiddenControl($control, ?string $name=null): self {
 | 
						|
    A::set($this->hiddenControls, $name, $control);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  /** @var ?array */
 | 
						|
  protected $controls;
 | 
						|
 | 
						|
  function addControl($control, ?string $name=null): self {
 | 
						|
    A::set($this->controls, $name, $control);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  protected function buildControl(?array $param, ?string $key=null, ?string $controlClass=null, ?array $params=null): array {
 | 
						|
    if ($param === null) $param = A::get($this->params, $key);
 | 
						|
    if ($param !== null) {
 | 
						|
      [$key, $name, $default, $value, $label, $defaultControlClass,
 | 
						|
      ] = A::getdels($param, array_keys(self::PARAMS_SCHEMA));
 | 
						|
      if ($controlClass === null && $defaultControlClass !== null) {
 | 
						|
        $controlClass = $defaultControlClass;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      $name = $key;
 | 
						|
      $default = $value = null;
 | 
						|
      $label = null;
 | 
						|
      if ($controlClass === null) $controlClass = ControlText::class;
 | 
						|
    }
 | 
						|
 | 
						|
    $md = $this->md;
 | 
						|
    $type = $md !== null? $md->getType($key, false): null;
 | 
						|
    $sfields = $md !== null? $md->getSfields(): null;
 | 
						|
    if ($controlClass === null) {
 | 
						|
      $haveItems = A::has($param, "items") || A::has($param, "items_func");
 | 
						|
      $is2States = $type !== null && $type->is2States();
 | 
						|
      if ($haveItems) $controlClass = ControlSelect::class;
 | 
						|
      elseif ($is2States) $controlClass = ControlCheckbox::class;
 | 
						|
      else $controlClass = ControlText::class;
 | 
						|
    }
 | 
						|
 | 
						|
    [$controlType, $firstKey] = A::get(static::CONTROL_TYPES, $controlClass, self::NV);
 | 
						|
 | 
						|
    if ($label === null && $md !== null) {
 | 
						|
      $label = A::_pget($sfields, [$key, "title"]);
 | 
						|
    }
 | 
						|
    if ($label === null) $label = $key;
 | 
						|
    if ($firstKey !== null) $param[$firstKey] = $label;
 | 
						|
 | 
						|
    switch ($controlType) {
 | 
						|
    case self::NVC:
 | 
						|
      $param["name"] = $name;
 | 
						|
      if ($value === null) $value = $default;
 | 
						|
      if ($value === null) $value = 1;
 | 
						|
      $currentValue = $this->get($key);
 | 
						|
      if ($type !== null) {
 | 
						|
        #XXX la nécessité de formatter la valeur dépend-elle du contrôle utilisé?!
 | 
						|
        $value = $type->format($value);
 | 
						|
        $currentValue = $type->format($currentValue);
 | 
						|
      }
 | 
						|
      $param["value"] = $value;
 | 
						|
      #XXX en attendant le formattage ci-dessus, forcer la format texte pour que
 | 
						|
      # la comparaison puisse se faire
 | 
						|
      $param["checked"] = strval($currentValue) === strval($value);
 | 
						|
      break;
 | 
						|
    case self::NV:
 | 
						|
      if ($value === null) $value = $this->get($key, $default);
 | 
						|
      if ($type !== null) $value = $type->format($value);
 | 
						|
      $param["value"] = $value;
 | 
						|
    case self::N:
 | 
						|
      $param["name"] = $name;
 | 
						|
    }
 | 
						|
 | 
						|
    if ($params === null) $params = $param;
 | 
						|
    else A::update_n($params, $param);
 | 
						|
    return [new $controlClass($this, $params), $name];
 | 
						|
  }
 | 
						|
 | 
						|
  private function _prepareControls(): ?array {
 | 
						|
    if ($this->params === null) $this->loadParams();
 | 
						|
    A::ensure_array($this->controls);
 | 
						|
    return $this->params;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddControl(string $key): self {
 | 
						|
    $params = $this->_prepareControls();
 | 
						|
    if ($key == "") {
 | 
						|
      # submit
 | 
						|
      $this->addSubmit(null, $this->ppSubmit);
 | 
						|
    } elseif (A::has($this->ppSubmits, $key)) {
 | 
						|
      $submit = $this->ppSubmits[$key];
 | 
						|
      A::replace_n($submit, "id", $key);
 | 
						|
      $this->addSubmit(null, $submit);
 | 
						|
    } else {
 | 
						|
      $param = A::get($params, $key);
 | 
						|
      if ($param !== null) {
 | 
						|
        [$control, $name] = $this->buildControl($param);
 | 
						|
        $this->addControl($control, $name);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddControls(?bool $autoaddSubmit=null): self {
 | 
						|
    $params = $this->_prepareControls();
 | 
						|
    if ($params !== null) {
 | 
						|
      foreach ($params as $param) {
 | 
						|
        [$control, $name] = $this->buildControl($param);
 | 
						|
        if (A::has($this->hiddenControls, $name)) continue;
 | 
						|
        if (A::has($this->controls, $name)) continue;
 | 
						|
        $this->addControl($control, $name);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if ($autoaddSubmit === null) $autoaddSubmit = $this->ppAutoaddSubmit;
 | 
						|
    if ($autoaddSubmit && !A::has($this->controls, "")) {
 | 
						|
      $this->addSubmit(null, $this->ppSubmit);
 | 
						|
    }
 | 
						|
    $submits = $this->ppSubmits;
 | 
						|
    if ($submits !== null) {
 | 
						|
      foreach ($submits as $key => $submit) {
 | 
						|
        if (!A::has($this->controls, $key)) {
 | 
						|
          A::replace_n($submit, "id", $key);
 | 
						|
          $this->addSubmit(null, $submit);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printControls(): void {
 | 
						|
    if ($this->controls === null) $this->autoaddControls();
 | 
						|
    vo::write($this->hiddenControls);
 | 
						|
    vo::write($this->controls);
 | 
						|
  }
 | 
						|
 | 
						|
  function printControl(string $key): void {
 | 
						|
    $params = $this->_prepareControls();
 | 
						|
    if ($key == "") {
 | 
						|
      # submit
 | 
						|
      vo::write($this->submit(null, $this->ppSubmit));
 | 
						|
    } elseif (A::has($this->ppSubmits, $key)) {
 | 
						|
      $submit = $this->ppSubmits[$key];
 | 
						|
      A::replace_n($submit, "id", $key);
 | 
						|
      vo::write($this->submit(null, $submit));
 | 
						|
    } else {
 | 
						|
      $param = A::get($params, $key);
 | 
						|
      if ($param !== null) {
 | 
						|
        [$control, $name] = $this->buildControl($param);
 | 
						|
        vo::write($control);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  protected function fixControlParams(?array &$params): void {
 | 
						|
  }
 | 
						|
  protected function fixControl(Control $control): Control {
 | 
						|
    return $control;
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # section
 | 
						|
 | 
						|
  /** @var Section */
 | 
						|
  protected $section;
 | 
						|
 | 
						|
  function startSection(?array $params=null): void {
 | 
						|
  }
 | 
						|
  function endSection(): void {
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # group
 | 
						|
 | 
						|
  /** @var Group */
 | 
						|
  protected $group;
 | 
						|
 | 
						|
  function startGroup(?array $params=null): void {
 | 
						|
  }
 | 
						|
  function endGroup(): void {
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # title
 | 
						|
 | 
						|
  function title($title, ?array $params=null): ControlTitle {
 | 
						|
    A::set_nz($params, "title", $title);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return new ControlTitle($this, $params);
 | 
						|
  }
 | 
						|
 | 
						|
  function addTitle($title, ?array $params=null): self {
 | 
						|
    $this->addControl($this->title($title, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printTitle($title, ?array $params=null): void {
 | 
						|
    vo::write($this->title($title, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # hidden
 | 
						|
 | 
						|
  function hidden(string $name, $value, ?array $params=null): ControlHidden {
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    return new ControlHidden($this, $params);
 | 
						|
  }
 | 
						|
 | 
						|
  function addHidden(string $name, $value, ?array $params=null): self {
 | 
						|
    $this->addHiddenControl($this->hidden($name, $value, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddHidden(string $key, ?array $params=null): self {
 | 
						|
    $this->addHiddenControl(...$this->buildControl(null, $key, ControlHidden::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printHidden(string $name, $value, ?array $params=null): void {
 | 
						|
    vo::write($this->hidden($name, $value, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function hiddens(?array $values, ?array $names=null): array {
 | 
						|
    if ($values === null) return [];
 | 
						|
    if ($names === null) $names = array_keys($values);
 | 
						|
    $vs = [];
 | 
						|
    foreach ($names as $name) {
 | 
						|
      $value = A::get($values, $name);
 | 
						|
      $vs[] = $this->hidden($name, $value);
 | 
						|
    }
 | 
						|
    return $vs;
 | 
						|
  }
 | 
						|
 | 
						|
  function addHiddens(?array $values, ?array $names=null): self {
 | 
						|
    if ($values !== null) {
 | 
						|
      if ($names === null) $names = array_keys($values);
 | 
						|
      foreach ($names as $name) {
 | 
						|
        $value = A::get($values, $name);
 | 
						|
        $this->addHiddenControl($this->hidden($name, $value), $name);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printHiddens(?array $values, ?array $names=null): void {
 | 
						|
    vo::write($this->hiddens($values, $names));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # fixed
 | 
						|
 | 
						|
  function fixed($label, string $name, $value, ?array $params=null): ControlFixed {
 | 
						|
    A::set_nz($params, "label", $label);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlFixed($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addFixed($label, string $name, $value, ?array $params=null): self {
 | 
						|
    $this->addControl($this->fixed($label, $name, $value, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddFixed(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlFixed::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printFixed($label, string $name, $value, ?array $params=null): void {
 | 
						|
    vo::write($this->fixed($label, $name, $value, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # input
 | 
						|
 | 
						|
  function text($label, string $name, $value, ?array $params=null): ControlText {
 | 
						|
    A::set_nz($params, "label", $label);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlText($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addText($label, string $name, $value, ?array $params=null): self {
 | 
						|
    $this->addControl($this->text($label, $name, $value, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddText(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlText::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printText($label, string $name, $value, ?array $params=null): void {
 | 
						|
    vo::write($this->text($label, $name, $value, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function password($label, string $name, $value, ?array $params=null): ControlPassword {
 | 
						|
    A::set_nz($params, "label", $label);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlPassword($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addPassword($label, string $name, $value, ?array $params=null): self {
 | 
						|
    $this->addControl($this->password($label, $name, $value, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddPassword(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlPassword::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printPassword($label, string $name, $value, ?array $params=null): void {
 | 
						|
    vo::write($this->password($label, $name, $value, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # select
 | 
						|
 | 
						|
  function select($label, string $name, $value, ?array $params=null): ControlSelect {
 | 
						|
    A::set_nz($params, "label", $label);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlSelect($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addSelect($label, string $name, $value, ?array $params=null): self {
 | 
						|
    $this->addControl($this->select($label, $name, $value, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddSelect(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlSelect::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printSelect($label, string $name, $value, ?array $params=null): void {
 | 
						|
    vo::write($this->select($label, $name, $value, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # checkbox
 | 
						|
 | 
						|
  function checkbox($text, string $name, $value, ?bool $checked=null, ?array $params=null): ControlCheckbox {
 | 
						|
    A::set_nz($params, "text", $text);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    A::set_nz($params, "checked", $checked);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlCheckbox($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addCheckbox($text, string $name, $value, ?bool $checked=null, ?array $params=null): self {
 | 
						|
    $this->addControl($this->checkbox($text, $name, $value, $checked, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddCheckbox(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlCheckbox::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printCheckbox($text, string $name, $value, ?bool $checked=null, ?array $params=null): void {
 | 
						|
    vo::write($this->checkbox($text, $name, $value, $checked, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # radiobutton
 | 
						|
 | 
						|
  function radiobutton($text, string $name, $value, ?bool $checked=null, ?array $params=null): ControlRadiobutton {
 | 
						|
    A::set_nz($params, "text", $text);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    A::set_nz($params, "checked", $checked);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlRadiobutton($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addRadiobutton($text, string $name, $value, ?bool $checked=null, ?array $params=null): self {
 | 
						|
    $this->addControl($this->radiobutton($text, $name, $value, $checked, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddRadiobutton(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlRadiobutton::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printRadiobutton($text, string $name, $value, ?bool $checked=null, ?array $params=null): void {
 | 
						|
    vo::write($this->radiobutton($text, $name, $value, $checked, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # textarea
 | 
						|
 | 
						|
  function textarea($label, string $name, $value, ?array $params=null): ControlTextarea {
 | 
						|
    A::set_nz($params, "label", $label);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    A::set_nz($params, "value", $value);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlTextarea($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addTextarea($label, string $name, $value, ?array $params=null): self {
 | 
						|
    $this->addControl($this->textarea($label, $name, $value, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddTextarea(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlTextarea::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printTextarea($label, string $name, $value, ?array $params=null): void {
 | 
						|
    vo::write($this->textarea($label, $name, $value, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # file
 | 
						|
 | 
						|
  function file($label, string $name, ?array $params=null): ControlFile {
 | 
						|
    A::set_nz($params, "label", $label);
 | 
						|
    A::set_nz($params, "name", $name);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlFile($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addFile($label, string $name, ?array $params=null): self {
 | 
						|
    $this->addControl($this->file($label, $name, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddFile(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlFile::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printFile($label, string $name, ?array $params=null): void {
 | 
						|
    vo::write($this->file($label, $name, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # submit
 | 
						|
 | 
						|
  function submit($submit, ?array $params=null): ControlSubmit {
 | 
						|
    A::set_nz($params, "submit", $submit);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlSubmit($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addSubmit($submit, ?array $params=null): self {
 | 
						|
    $name = A::get($params, "id");
 | 
						|
    if ($name === null) A::get($params, "name");
 | 
						|
    if ($name === null) $name = "";
 | 
						|
    $this->addControl($this->submit($submit, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddSubmit(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlSubmit::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printSubmit($submit, ?array $params=null): void {
 | 
						|
    vo::write($this->submit($submit, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
						|
  # reset
 | 
						|
 | 
						|
  function reset($reset, ?array $params=null): ControlReset {
 | 
						|
    A::set_nz($params, "reset", $reset);
 | 
						|
    $this->fixControlParams($params);
 | 
						|
    return $this->fixControl(new ControlReset($this, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  function addReset($reset, ?array $params=null): self {
 | 
						|
    $name = A::get($params, "name", "");
 | 
						|
    $this->addControl($this->reset($reset, $params), $name);
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function autoaddReset(string $key, ?array $params=null): self {
 | 
						|
    $this->addControl(...$this->buildControl(null, $key, ControlReset::class, $params));
 | 
						|
    return $this;
 | 
						|
  }
 | 
						|
 | 
						|
  function printReset($reset, ?array $params=null): void {
 | 
						|
    vo::write($this->reset($reset, $params));
 | 
						|
  }
 | 
						|
 | 
						|
  #############################################################################
 | 
						|
 | 
						|
  function print(): void {
 | 
						|
    $this->autoloadParams();
 | 
						|
    $this->printAlert();
 | 
						|
    $this->printStart();
 | 
						|
    $this->printControls();
 | 
						|
    $this->printEnd();
 | 
						|
  }
 | 
						|
}
 |