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