Lançado Adianti Framework 7.6!
Clique aqui para saber mais
menu

Adianti Solutions

API

Adianti, Framework, PHP, MVC, Active record, Front controller, IDE, RAD, Web, multiplataforma, geração de código, desenvolvimento rápido, relatórios, formulários, listagens, datagrids, gráficos, banco de dados, padrões de projeto, design patterns API do Adianti Framework.
API Docs
code
Selecione a classe

Source for file BootstrapFormBuilder.php

Documentation is available at BootstrapFormBuilder.php

  1. <?php
  2. namespace Adianti\Wrapper;
  3.  
  4. use Adianti\Core\AdiantiCoreTranslator;
  5. use Adianti\Control\TAction;
  6. use Adianti\Widget\Base\TElement;
  7. use Adianti\Widget\Base\TScript;
  8. use Adianti\Widget\Form\TField;
  9. use Adianti\Widget\Form\TForm;
  10. use Adianti\Widget\Form\TLabel;
  11. use Adianti\Widget\Form\TCheckButton;
  12. use Adianti\Widget\Form\TButton;
  13. use Adianti\Widget\Form\THidden;
  14. use Adianti\Widget\Form\TSlider;
  15. use Adianti\Widget\Form\THtmlEditor;
  16. use Adianti\Widget\Form\AdiantiFormInterface;
  17. use Adianti\Widget\Form\AdiantiWidgetInterface;
  18. use Adianti\Widget\Form\TSeekButton;
  19. use Adianti\Widget\Form\TRadioGroup;
  20. use Adianti\Widget\Form\TCheckGroup;
  21. use Adianti\Widget\Form\TMultiEntry;
  22. use Adianti\Widget\Util\TActionLink;
  23. use Adianti\Widget\Wrapper\TDBRadioGroup;
  24. use Adianti\Widget\Wrapper\TDBCheckGroup;
  25. use Adianti\Widget\Wrapper\TDBSeekButton;
  26. use Adianti\Registry\TSession;
  27. use Adianti\Widget\Form\TCheckList;
  28.  
  29. use stdClass;
  30. use Exception;
  31.  
  32. /**
  33.  * Bootstrap form builder for Adianti Framework
  34.  *
  35.  * @version    7.4
  36.  * @package    wrapper
  37.  * @author     Pablo Dall'Oglio
  38.  * @copyright  Copyright (c) 2006 Adianti Solutions Ltd. (http://www.adianti.com.br)
  39.  * @license    http://www.adianti.com.br/framework-license
  40.  */
  41. class BootstrapFormBuilder implements AdiantiFormInterface
  42. {
  43.     private $id;
  44.     private $decorated;
  45.     private $tabcontent;
  46.     private $tabcurrent;
  47.     private $current_page;
  48.     private $properties;
  49.     private $actions;
  50.     private $header_actions;
  51.     private $title;
  52.     private $column_classes;
  53.     private $header_properties;
  54.     private $padding;
  55.     private $name;
  56.     private $tabFunction;
  57.     private $tabAction;
  58.     private $field_sizes;
  59.     private $automatic_aria;
  60.     private $hidden;
  61.     private $panel;
  62.     private $client_validation;
  63.     private $csrf_validation;
  64.     
  65.     /**
  66.      * Constructor method
  67.      * @param $name form name
  68.      */
  69.     public function __construct($name 'my_form')
  70.     {
  71.         $this->decorated         new TForm($name);
  72.         $this->tabcurrent        NULL;
  73.         $this->current_page      0;
  74.         $this->header_actions    array();
  75.         $this->actions           array();
  76.         $this->padding           10;
  77.         $this->name              $name;
  78.         $this->id                'bform_' mt_rand(10000000001999999999);
  79.         $this->field_sizes       null;
  80.         $this->automatic_aria    false;
  81.         $this->client_validation false;
  82.         $this->csrf_validation   false;
  83.         $this->hidden            false;
  84.         
  85.         $this->column_classes array();
  86.         $this->column_classes[1]  ['col-sm-12'];
  87.         $this->column_classes[2]  ['col-sm-4 col-lg-2''col-sm-8 col-lg-10'];
  88.         $this->column_classes[3]  ['col-sm-4 col-lg-2''col-sm-4','col-sm-2'];
  89.         $this->column_classes[4]  ['col-sm-4 col-lg-2''col-sm-8 col-lg-4','col-sm-4 col-lg-2''col-sm-8 col-lg-4'];
  90.         $this->column_classes[5]  ['col-sm-2''col-sm-2','col-sm-2''col-sm-2''col-sm-2'];
  91.         $this->column_classes[6]  ['col-sm-2''col-sm-2','col-sm-2''col-sm-2''col-sm-2''col-sm-2'];
  92.         $this->column_classes[7]  ['col-sm-1''col-sm-1','col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1'];
  93.         $this->column_classes[8]  ['col-sm-1''col-sm-1','col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1'];
  94.         $this->column_classes[9]  ['col-sm-1''col-sm-1','col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1'];
  95.         $this->column_classes[10['col-sm-1''col-sm-1','col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1'];
  96.         $this->column_classes[11['col-sm-1''col-sm-1','col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1'];
  97.         $this->column_classes[12['col-sm-1''col-sm-1','col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1''col-sm-1'];
  98.     }
  99.     
  100.     /**
  101.      * Turn on/off client validation
  102.      */
  103.     public function setClientValidation($bool)
  104.     {
  105.         $this->client_validation $bool;
  106.     }
  107.     
  108.     /**
  109.      * Enable CSRF Protection
  110.      */
  111.     public function enableCSRFProtection()
  112.     {
  113.         $this->csrf_validation true;
  114.         
  115.         TSession::setValue('csrf_token_'.$this->name.'_before'TSession::getValue('csrf_token_'.$this->name));
  116.         TSession::setValue('csrf_token_'.$this->namebin2hex(random_bytes(32)));
  117.     }
  118.     
  119.     /**
  120.      * Add expand button
  121.      */
  122.     public function addExpandButton$label null$icon null$start_hidden true)
  123.     {
  124.         $form_name $this->getName();
  125.         
  126.         $button new TButton($form_name.'_show_hide');
  127.         $button->{'class''btn btn-info btn-sm active';
  128.         $button->setLabel($label ?? AdiantiCoreTranslator::translate('Expand'));
  129.         $button->setImage($icon ?? 'fa:search');
  130.         $button->addFunction("\$('[name={$form_name}]').slideToggle('fast'); $(this).toggleClass( 'active' )");
  131.         $this->addHeaderWidget($button);
  132.         
  133.         if ($start_hidden)
  134.         {
  135.             $this->decorated->setProperty('style', 'display:none');
  136.         }
  137.         
  138.         return $button;
  139.     }
  140.     
  141. /**    
  142.      * Hide
  143.      */
  144.     public function hide()
  145.     {
  146.         $this->hidden = true;
  147.     }
  148.     
  149. /**    
  150.      * Generate automatic aria-labels
  151.      */
  152.     public function generateAria()
  153.     {
  154.         $this->automatic_aria = true;
  155.     }
  156.     
  157. /**    
  158.      * Returns form id
  159.      */
  160.     public function getId()
  161.     {
  162.         return $this->id;
  163.     }
  164.     
  165. /**    
  166.      * Set field sizes
  167.      */
  168.     public function setFieldSizes($size)
  169.     {
  170.         $this->field_sizes = $size;
  171.     }
  172.     
  173. /**    
  174.      * Add a form title
  175.      * @param $title Form title
  176.      */
  177.     public function setFormTitle($title)
  178.     {
  179.         $this->title = $title;
  180.     }
  181.     
  182. /**    
  183.      * Set padding
  184.      * @param $padding 
  185.      */
  186.     public function setPadding($padding)
  187.     {
  188.         $this->padding = $padding;
  189.     }
  190.     
  191. /**    
  192.      * Define the current page to be shown
  193.      * @param $i An integer representing the page number (start at 0)
  194.      */
  195.     public function setCurrentPage($i)
  196.     {
  197.         $this->current_page = $i;
  198.     }
  199.     
  200. /**    
  201.      * Redirect calls to decorated object
  202.      */
  203.     public function __call($method, $parameters)
  204.     {
  205.         return call_user_func_array(array($this->decorated, $method),$parameters);
  206.     }
  207.     
  208. /**    
  209.      * Redirect assigns to decorated object
  210.      */
  211.     public function __set($property, $value)
  212.     {
  213.         return $this->decorated->$property = $value;
  214.     }
  215.     
  216. /**    
  217.      * Define a style property
  218.      * @param $name  Property Name
  219.      * @param $value Property Value
  220.      */
  221.     public function setProperty($name, $value)
  222.     {
  223.         $this->properties[$name] = $value;
  224.     }
  225.     
  226. /**    
  227.      * Define a header style property
  228.      * @param $name  Property Name
  229.      * @param $value Property Value
  230.      */
  231.     public function setHeaderProperty($name, $value)
  232.     {
  233.         $this->header_properties[$name] = $value;
  234.     }
  235.     
  236. /**    
  237.      * Set form name
  238.      * @param $name Form name
  239.      */
  240.     public function setName($name)
  241.     {
  242.         return $this->decorated->setName($name);
  243.     }
  244.     
  245. /**    
  246.      * Get form name
  247.      */
  248.     public function getName()
  249.     {
  250.         return $this->decorated->getName();
  251.     }
  252.     
  253. /**    
  254.      * Add form field
  255.      * @param $field Form field
  256.      */
  257.     public function addField(AdiantiWidgetInterface $field)
  258.     {
  259.         return $this->decorated->addField($field);
  260.     }
  261.     
  262. /**    
  263.      * Del form field
  264.      * @param $field Form field
  265.      */
  266.     public function delField(AdiantiWidgetInterface $field)
  267.     {
  268.         return $this->decorated->delField($field);
  269.     }
  270.     
  271. /**    
  272.      * Set form fields
  273.      * @param $fields Array of Form fields
  274.      */
  275.     public function setFields($fields)
  276.     {
  277.         return $this->decorated->setFields($fields);
  278.     }
  279.     
  280. /**    
  281.      * Return form field
  282.      * @param $name Field name
  283.      */
  284.     public function getField($name)
  285.     {
  286.         return $this->decorated->getField($name);
  287.     }
  288.     
  289. /**    
  290.      * Return form fields
  291.      */
  292.     public function getFields()
  293.     {
  294.         return $this->decorated->getFields();
  295.     }
  296.     
  297. /**    
  298.      * Clear form
  299.      */
  300.     public function clear( $keepDefaults = FALSE )
  301.     {
  302.         return $this->decorated->clear( $keepDefaults );
  303.     }
  304.     
  305. /**    
  306.      * Set form data
  307.      * @param $object Data object
  308.      */
  309.     public function setData($object)
  310.     {
  311.         return $this->decorated->setData($object);
  312.     }
  313.     
  314. /**    
  315.      * Get form data
  316.      * @param $class Object type of return data
  317.      */
  318.     public function getData($class = 'StdClass')
  319.     {
  320.         return $this->decorated->getData($class);
  321.     }
  322.     
  323. /**    
  324.      * Return form actions
  325.      */
  326.     public function getActions()
  327.     {
  328.         return $this->actions;
  329.     }
  330.     
  331. /**    
  332.      * Validate form data
  333.      */
  334.     public function validate()
  335.     {
  336.         if ($this->csrf_validation)
  337.         {
  338.             if (!hash_equals($_POST['csrf_token'], <a href="registry/TSession.html">TSession</a>::getValue('csrf_token_'.$this->name.'_before')))
  339.             {
  340.                 throw new Exception(<a href="core/AdiantiCoreTranslator.html">AdiantiCoreTranslator</a>::translate('CSRF Error'));
  341.             }
  342.         }
  343.         
  344.         return $this->decorated->validate();
  345.     }
  346.     
  347. /**    
  348.      * Append a notebook page
  349.      * @param $title Tab title
  350.      */
  351.     public function appendPage($title)
  352.     {
  353.         $this->tabcurrent = $title;
  354.         $this->tabcontent[$title] = array();
  355.     }
  356.     
  357. /**    
  358.      * Set tab click function
  359.      */
  360.     public function setTabFunction($function)
  361.     {
  362.         $this->tabFunction = $function;
  363.     }
  364.     
  365. /**    
  366.      * Define the action for the Notebook tab
  367.      * @param $action Action taken when the user
  368.      *  clicks over Notebook tab (A TAction object)
  369.      */
  370.     public function setTabAction(TAction $action)
  371.     {
  372.         $this->tabAction = $action;
  373.     }
  374.     
  375. /**    
  376.      * Add form fields
  377.      * @param mixed $fields,... Form fields
  378.      */
  379.     public function addFields()
  380.     {
  381.         $args = func_get_args();
  382.         
  383.         $this->validateInlineArguments($args, 'addFields');
  384.         
  385.         // object that represents a row
  386.         $row = new stdClass;
  387.         $row->{'content'} = $args;
  388.         $row->{'type'}    = 'fields';
  389.         
  390.         if ($args)
  391.         {
  392.             $this->tabcontent[$this->tabcurrent][] = $row;
  393.             
  394.             foreach ($args as $slot)
  395.             {
  396.                 foreach ($slot as $content)
  397.                 {
  398.                     if ($content instanceof <a href="widget/form/AdiantiWidgetInterface.html">AdiantiWidgetInterface</a>)
  399.                     {
  400.                         $this->decorated->addField($content);
  401.                     }
  402.                     
  403.                     if ($content instanceof <a href="wrapper/BootstrapFormBuilder.html">BootstrapFormBuilder</a>)
  404.                     {
  405.                         if ($content->getFields())
  406.                         {
  407.                             foreach ($content->getFields() as $field)
  408.                             {
  409.                                 $this->decorated->addField($field);
  410.                             }
  411.                         }
  412.                         
  413.                         $content->setTagName('div');
  414.                     }
  415.                 }
  416.             }
  417.         }
  418.         
  419.         // return, because the user may fill aditional attributes
  420.         return $row;
  421.     }
  422.     
  423. /**    
  424.      * Add a form content
  425.      * @param mixed $content,... Form content
  426.      */
  427.     public function addContent()
  428.     {
  429.         $args = func_get_args();
  430.         
  431.         $this->validateInlineArguments($args, 'addContent');
  432.         
  433.         // object that represents a row
  434.         $row = new stdClass;
  435.         $row->{'content'} = $args;
  436.         $row->{'type'}    = 'content';
  437.         
  438.         if ($args)
  439.         {
  440.             $this->tabcontent[$this->tabcurrent][] = $row;
  441.             
  442.             foreach ($args as $arg)
  443.             {
  444.                 foreach ($arg as $slot)
  445.                 {
  446.                     if (!empty($slot) && $slot instanceof <a href="wrapper/BootstrapFormBuilder.html">BootstrapFormBuilder</a>)
  447.                     {
  448.                         if ($slot->getFields())
  449.                         {
  450.                             foreach ($slot->getFields() as $field)
  451.                             {
  452.                                 $this->addField($field);
  453.                             }
  454.                         }
  455.                         $slot->setTagName('div');
  456.                     }
  457.                 }
  458.             }
  459.         }
  460.         
  461.         // return, because the user may fill aditional attributes
  462.         return $row;
  463.     }
  464.     
  465. /**    
  466.      * Validate argument type
  467.      * @param $args Array of arguments
  468.      * @param $method Generator method
  469.      */
  470.     public function validateInlineArguments($args, $method)
  471.     {
  472.         if ($args)
  473.         {
  474.             foreach ($args as $arg)
  475.             {
  476.                 if (!is_array($arg))
  477.                 {
  478.                     throw new Exception(<a href="core/AdiantiCoreTranslator.html">AdiantiCoreTranslator</a>::translate('Method ^1 must receive a parameter of type ^2', $method, 'Array'));
  479.                 }
  480.             }
  481.         }
  482.     }
  483.     
  484. /**    
  485.      * Add a form action
  486.      * @param $label Button label
  487.      * @param $action Button action
  488.      * @param $icon Button icon
  489.      */
  490.     public function addAction($label, TAction $action, $icon = 'fa:save')
  491.     {
  492.         $label_info = ($label instanceof <a href="widget/form/TLabel.html">TLabel</a>) ? $label->getValue() : $label;
  493.         $name   = 'btn_'.strtolower(str_replace(' ', '_', $label_info));
  494.         $button = new <a href="widget/form/TButton.html">TButton</a>($name);
  495.         $this->decorated->addField($button);
  496.         
  497.         // define the button action
  498.         $button->setAction($action, $label);
  499.         $button->setImage($icon);
  500.         
  501.         $this->actions[] = $button;
  502.         return $button;
  503.     }
  504.     
  505. /**    
  506.      * Add a form action link
  507.      * @param $label Button label
  508.      * @param $action Button action
  509.      * @param $icon Button icon
  510.      */
  511.     public function addActionLink($label, TAction $action, $icon = 'fa:save')
  512.     {
  513.         $label_info = ($label instanceof <a href="widget/form/TLabel.html">TLabel</a>) ? $label->getValue() : $label;
  514.         $button = new <a href="widget/util/TActionLink.html">TActionLink</a>($label_info, $action, null, null, null, $icon);
  515.         $button->{'class'} = 'btn btn-sm btn-default';
  516.         $this->actions[] = $button;
  517.         return $button;
  518.     }
  519.     
  520. /**    
  521.      * Add a form header action
  522.      * @param $label Button label
  523.      * @param $action Button action
  524.      * @param $icon Button icon
  525.      */
  526.     public function addHeaderAction($label, TAction $action, $icon = 'fa:save')
  527.     {
  528.         $label_info = ($label instanceof <a href="widget/form/TLabel.html">TLabel</a>) ? $label->getValue() : $label;
  529.         $name   = strtolower(str_replace(' ', '_', $label_info));
  530.         $button = new <a href="widget/form/TButton.html">TButton</a>($name);
  531.         $this->decorated->addField($button);
  532.         
  533.         // define the button action
  534.         $button->setAction($action, $label);
  535.         $button->setImage($icon);
  536.         
  537.         $this->header_actions[] = $button;
  538.         return $button;
  539.     }
  540.     
  541. /**    
  542.      * Add a form header widget
  543.      * @param $widget Widget
  544.      */
  545.     public function addHeaderWidget($widget)
  546.     {
  547.         $this->header_actions[] = $widget;
  548.         return $widget;
  549.     }
  550.     
  551. /**    
  552.      * Add a form footer widget
  553.      * @param $widget Widget
  554.      */
  555.     public function addFooterWidget($widget)
  556.     {
  557.         $this->actions[] = $widget;
  558.         return $widget;
  559.     }
  560.     
  561. /**    
  562.      * Add a form header action
  563.      * @param $label Button label
  564.      * @param $action Button action
  565.      * @param $icon Button icon
  566.      */
  567.     public function addHeaderActionLink($label, TAction $action, $icon = 'fa:save')
  568.     {
  569.         $label_info = ($label instanceof <a href="widget/form/TLabel.html">TLabel</a>) ? $label->getValue() : $label;
  570.         $button = new <a href="widget/util/TActionLink.html">TActionLink</a>($label_info, $action, null, null, null, $icon);
  571.         $button->{'class'} = 'btn btn-sm btn-default';
  572.         $this->header_actions[] = $button;
  573.         return $button;
  574.     }
  575.     
  576. /**    
  577.      * Add a form button
  578.      * @param $label Button label
  579.      * @param $action JS Button action
  580.      * @param $icon Button icon
  581.      */
  582.     public function addButton($label, $action, $icon = 'fa:save')
  583.     {
  584.         $label_info = ($label instanceof <a href="widget/form/TLabel.html">TLabel</a>) ? $label->getValue() : $label;
  585.         $name   = strtolower(str_replace(' ', '_', $label_info));
  586.         $button = new <a href="widget/form/TButton.html">TButton</a>($name);
  587.         if (strstr($icon, '#') !== FALSE)
  588.         {
  589.             $pieces = explode('#', $icon);
  590.             $color = $pieces[1];
  591.             $button->{'style'} = "color: #{$color}";
  592.         }
  593.         
  594.         // define the button action
  595.         $button->addFunction($action);
  596.         $button->setLabel($label);
  597.         $button->setImage($icon);
  598.         
  599.         $this->actions[] = $button;
  600.         return $button;
  601.     }
  602.     
  603.     /**
  604.      * Clear actions row
  605.      */
  606.     public function delActions()
  607.     {
  608.         if ($this->actions)
  609.         {
  610.             foreach ($this->actions as $key => $button)
  611.             {
  612.                 unset($this->actions[$key]);
  613.             }
  614.         }
  615.     }
  616.     
  617.     /**
  618.      * Return an array with action buttons
  619.      */
  620.     public function getActionButtons()
  621.     {
  622.         return $this->actions;
  623.     }
  624.     
  625.     /**
  626.      *
  627.      */
  628.     public function setColumnClasses($key, $classes)
  629.     {
  630.         $this->column_classes[$key] = $classes;
  631.     }
  632.     
  633.     /**
  634.      * Render form
  635.      */
  636.     public function render()
  637.     {
  638.         if ($this->hidden)
  639.         {
  640.             return;
  641.         }
  642.         
  643.         if ($this->csrf_validation)
  644.         {
  645.             $csrf_token = new THidden('csrf_token');
  646.             $this->addFields([$csrf_token]);
  647.             $csrf_token->setValue(<a href="registry/TSession.html">TSession</a>::getValue('csrf_token_'.$this->name));
  648.             $this->decorated->silentField('csrf_token');
  649.         }
  650.         
  651.         $this->decorated->{'class'} = 'form-horizontal';
  652.         $this->decorated->{'type'}  = 'bootstrap';
  653.         
  654.         $panel = new <a href="widget/base/TElement.html">TElement</a>('div');
  655.         $panel->{'class'}  = 'card panel';
  656.         $panel->{'style'}  = 'width: 100%';
  657.         $panel->{'widget'} = 'bootstrapformbuilder';
  658.         $panel->{'form'}   = $this->name;
  659.         $panel->{'id'}     = $this->id;
  660.         
  661.         if ($this->properties)
  662.         {
  663.             foreach ($this->properties as $property => $value)
  664.             {
  665.                 $panel->$property = $value;
  666.             }
  667.         }
  668.         
  669.         if (!empty($this->title) || count($this->header_actions) > 0)
  670.         {
  671.             $heading = new <a href="widget/base/TElement.html">TElement</a>('div');
  672.             $heading->{'class'} = 'card-header panel-heading';
  673.             $heading->add(<a href="widget/base/TElement.html">TElement</a>::tag('div', $this->title, ['class'=>'panel-title card-title']));
  674.             
  675.             if ($this->header_properties)
  676.             {
  677.                 foreach ($this->header_properties as $property => $value)
  678.                 {
  679.                     if (isset($heading->$property))
  680.                     {
  681.                         $heading->$property .= ' ' . $value;
  682.                     }
  683.                     else
  684.                     {
  685.                         $heading->$property = $value;
  686.                     }
  687.                 }
  688.             }
  689.             
  690.             if ($this->header_actions)
  691.             {
  692.                 $title_actions = new <a href="widget/base/TElement.html">TElement</a>('div');
  693.                 $title_actions->{'class'} = 'header-actions';
  694.                 $title_actions->{'style'} = 'float:right';
  695.                 $heading->add($title_actions);
  696.                 foreach ($this->header_actions as $action_button)
  697.                 {
  698.                     $title_actions->add($action_button);
  699.                 }
  700.             }
  701.             $panel->add($heading);
  702.         }
  703.         
  704.         $body = new <a href="widget/base/TElement.html">TElement</a>('div');
  705.         $body->{'class'} = 'card-body panel-body';
  706.         $body->{'style'} = 'width: 100%';
  707.         
  708.         $panel->add($this->decorated);
  709.         $this->decorated->add($body);
  710.         
  711.         if ($this->tabcurrent !== null)
  712.         {
  713.             $tabs = new <a href="widget/base/TElement.html">TElement</a>('ul');
  714.             $tabs->{'class'} = 'nav nav-tabs';
  715.             $tabs->{'role'}  = 'tablist';
  716.             
  717.             $tab_counter = 0;
  718.             foreach ($this->tabcontent as $tab => $rows)
  719.             {
  720.                 $class = ($tab_counter == $this->current_page) ? 'active' : '';
  721.                 
  722.                 $tab_li = new <a href="widget/base/TElement.html">TElement</a>('li');
  723.                 $tab_li->{'role'}  = 'presentation';
  724.                 $tab_li->{'class'} = $class . " nav-item";
  725.  
  726.                 $tab_link = new <a href="widget/base/TElement.html">TElement</a>('a');
  727.                 $tab_link->{'href'} = "#tab_{$this->id}_{$tab_counter}";
  728.                 $tab_link->{'role'} = 'tab';
  729.                 $tab_link->{'data-toggle'} = 'tab';
  730.                 $tab_link->{'aria-expanded'} = 'true';
  731.                 $tab_link->{'class'} = "nav-link " . $class;
  732.                 if ($this->tabFunction)
  733.                 {
  734.                     $tab_link->{'onclick'} = $this->tabFunction;
  735.                     $tab_link->{'data-current_page'} = $tab_counter;
  736.                 }
  737.                 
  738.                 if ($this->tabAction)
  739.                 {
  740.                     $this->tabAction->setParameter('current_page', $tab_counter);
  741.                     $string_action = $this->tabAction->serialize(FALSE);
  742.                     $tab_link->{'onclick'} = "__adianti_ajax_exec('$string_action')";
  743.                 }
  744.                 
  745.                 $tab_li->add($tab_link);
  746.                 $tab_link->add( TElement::tag('span', $tab, ['class'=>'tab-name'])); 
  747.                 
  748.                 $tabs->add($tab_li);
  749.                 $tab_counter ++;
  750.             }
  751.             
  752.             $body->add($tabs);
  753.         }
  754.         
  755.         $content = new <a href="widget/base/TElement.html">TElement</a>('div');
  756.         $content->{'class'} = 'tab-content';
  757.         $body->add($content);
  758.         
  759.         $tab_counter = 0;
  760.         foreach ($this->tabcontent as $tab => $rows)
  761.         {
  762.             $tabpanel = new <a href="widget/base/TElement.html">TElement</a>('div');
  763.             $tabpanel->{'role'}  = 'tabpanel';
  764.             $tabpanel->{'class'} = 'tab-pane ' . ( ($tab_counter == $this->current_page) ? 'active' : '' );
  765.             $tabpanel->{'style'} = 'padding:10px; margin-top: -1px;';
  766.             if ($tab)
  767.             {
  768.                 $tabpanel->{'style'} .= 'border: 1px solid #DDDDDD';
  769.             }
  770.             $tabpanel->{'id'}    = "tab_{$this->id}_{$tab_counter}";
  771.             
  772.             $content->add($tabpanel);
  773.             
  774.             if ($rows)
  775.             {
  776.                 foreach ($rows as $row)
  777.                 {
  778.                     $aria_label  = null;
  779.                     $aria_id     = null;
  780.                     
  781.                     $slots = $row->{'content'};
  782.                     $type  = $row->{'type'};
  783.                     
  784.                     $form_group = new <a href="widget/base/TElement.html">TElement</a>('div');
  785.                     $form_group->{'class'} = 'form-group tformrow row' . ' ' . ( isset($row->{'class'}) ? $row->{'class'} : '' );
  786.                     $tabpanel->add($form_group);
  787.                     $row_visual_widgets = 0;
  788.                     
  789.                     if (isset($row->{'style'}))
  790.                     {
  791.                         $form_group->{'style'} = $row->{'style'};
  792.                     }
  793.                     
  794.                     $slot_counter = count($slots);
  795.                     $row_counter  = 0;
  796.                     
  797.                     foreach ($slots as $slot)
  798.                     {
  799.                         $label_css    = ((count($slots)>1) AND (count($slot)==1) AND $slot[0] instanceof <a href="widget/form/TLabel.html">TLabel</a> AND empty($row->layout)) ? ' col-form-label control-label' : '';
  800.                         $column_class = (!empty($row->layout) ? $row->layout[$row_counter] : $this->column_classes[$slot_counter][$row_counter]);
  801.                         $slot_wrapper = new <a href="widget/base/TElement.html">TElement</a>('div');
  802.                         $slot_wrapper->{'class'} = $column_class . ' fb-field-container '.$label_css;
  803.                         $slot_wrapper->{'style'} = 'min-height:26px';
  804.                         $form_group->add($slot_wrapper);
  805.                         
  806.                         // one field per slot do not need to be wrapped
  807.                         if (count($slot)==1)
  808.                         {
  809.                             foreach ($slot as $field)
  810.                             {
  811.                                 $field_wrapper = self::wrapField($field, 'inherit', $this->field_sizes);
  812.                                 
  813.                                 $slot_wrapper->add($field_wrapper);
  814.                                 
  815.                                 if (!$field instanceof <a href="widget/form/THidden.html">THidden</a>)
  816.                                 {
  817.                                     $row_visual_widgets ++;
  818.                                 }
  819.                                 
  820.                                 if ($field instanceof <a href="widget/form/TLabel.html">TLabel</a>)
  821.                                 {
  822.                                     $aria_label = $field->getValue();
  823.                                     $aria_id    = $field->getId();
  824.                                 }
  825.                                 
  826.                                 if ($this->automatic_aria && !empty($aria_label) && !$field instanceof <a href="widget/form/TLabel.html">TLabel</a> && $field instanceof <a href="widget/form/TField.html">TField</a>)
  827.                                 {
  828.                                     $field->{'aria-label'} = $aria_label;
  829.                                     $field->{'aria-labelledby'} = $aria_id;
  830.                                 }
  831.                                 
  832.                                 if ($field instanceof <a href="widget/form/TField.html">TField</a> && $field->isRequired())
  833.                                 {
  834.                                     $field->{'aria-required'} = 'true';
  835.                                 }
  836.                             }
  837.                         }
  838.                         else // more fields must be wrapped
  839.                         {
  840.                             $field_counter = 0;
  841.                             foreach ($slot as $field)
  842.                             {
  843.                                 $field_wrapper = self::wrapField($field, 'inline-block', $this->field_sizes);
  844.                                 
  845.                                 if ( ($field_counter+1 < count($slot)) and (!$field instanceof <a href="widget/wrapper/TDBSeekButton.html">TDBSeekButton</a>) ) // padding less last element
  846.                                 {
  847.                                     $field_wrapper->{'style'} .= ';padding-right: '.$this->padding.'px;';
  848.                                 }
  849.                                 
  850.                                 $slot_wrapper->add($field_wrapper);
  851.                                 
  852.                                 if (!$field instanceof <a href="widget/form/THidden.html">THidden</a>)
  853.                                 {
  854.                                     $row_visual_widgets ++;
  855.                                 }
  856.                                 
  857.                                 if ($field instanceof <a href="widget/form/TLabel.html">TLabel</a>)
  858.                                 {
  859.                                     $aria_label = $field->getValue();
  860.                                     $aria_id    = $field->getId();
  861.                                 }
  862.                                 
  863.                                 if ($this->automatic_aria && !empty($aria_label) && !$field instanceof <a href="widget/form/TLabel.html">TLabel</a> && $field instanceof <a href="widget/form/TField.html">TField</a>)
  864.                                 {
  865.                                     $field->{'aria-label'} = $aria_label;
  866.                                     $field->{'aria-labelledby'} = $aria_id;
  867.                                 }
  868.                                 
  869.                                 if ($field instanceof <a href="widget/form/TField.html">TField</a> && $field->isRequired())
  870.                                 {
  871.                                     $field->{'aria-required'} = 'true';
  872.                                 }
  873.                                 
  874.                                 $field_counter ++;
  875.                             }
  876.                         }
  877.                         
  878.                         $row_counter ++;
  879.                     }
  880.                     
  881.                     if ($row_visual_widgets == 0)
  882.                     {
  883.                         $form_group->{'style'} = 'display:none';
  884.                     }
  885.                 }
  886.             }
  887.             $tab_counter ++;
  888.         }
  889.         
  890.         if ($this->actions)
  891.         {
  892.             $footer = new <a href="widget/base/TElement.html">TElement</a>('div');
  893.             $footer->{'class'} = 'panel-footer card-footer';
  894.             $footer->{'style'} = 'width: 100%';
  895.             $this->decorated->add($footer);
  896.             
  897.             foreach ($this->actions as $action_button)
  898.             {
  899.                 $footer->add($action_button);
  900.             }
  901.         }
  902.         
  903.         if (!$this->client_validation)
  904.         {
  905.             $this->decorated->setProperty('novalidate','');
  906.         }
  907.         
  908.         $this->panel = $panel;
  909.         return $this->panel;
  910.     }
  911.     
  912. /**    
  913.      * Show form
  914.      */
  915.     public function show()
  916.     {
  917.         if (empty($this->panel))
  918.         {
  919.             $this->render();
  920.         }
  921.         if (!$this->hidden)
  922.         {
  923.             $this->panel->show();
  924.         }
  925.     }
  926.     
  927. /**    
  928.      * Create a field wrapper
  929.      */
  930.     public static function wrapField($field, $display, $default_field_size = null)
  931.     {
  932.         $object = $field; // BC Compability
  933.         $field_size = (is_object($object) && method_exists($object, 'getSize')) ? $field->getSize() : null;
  934.         $has_underline = (!$field instanceof <a href="widget/form/TLabel.html">TLabel</a> && !$field instanceof <a href="widget/form/TRadioGroup.html">TRadioGroup</a> && !$field instanceof <a href="widget/form/TCheckGroup.html">TCheckGroup</a> && !$field instanceof <a href="widget/form/TButton.html">TButton</a> && !$field instanceof <a href="widget/form/THidden.html">THidden</a> && !$field instanceof <a href="widget/form/TSlider.html">TSlider</a> && !$field instanceof <a href="widget/form/TCheckButton.html">TCheckButton</a>);
  935.         $field_wrapper = new <a href="widget/base/TElement.html">TElement</a>('div');
  936.         $field_wrapper->{'class'} = 'fb-inline-field-container ' . ((($field instanceof <a href="widget/form/TField.html">TField</a>) and ($has_underline)) ? 'form-line' : '');
  937.         $field_wrapper->{'style'} = "display: {$display};vertical-align:top;($display=='inline-block'?'float:left':'');
  938.         
  939.         if (!empty($default_field_size))
  940.         {
  941.             if (is_array($field_size))
  942.             {
  943.                 $field_size[0] = $default_field_size;
  944.             }
  945.             else
  946.             {
  947.                 $field_size = $default_field_size;
  948.             }
  949.         }
  950.         
  951.         if ($field instanceof <a href="widget/form/TField.html">TField</a> || $field instanceof <a href="widget/form/TCheckList.html">TCheckList</a>)
  952.         {
  953.             if (is_array($field_size))
  954.             {
  955.                 $width  = $field_size[0];
  956.                 $height = $field_size[1];
  957.                 
  958.                 if ($width)
  959.                 {
  960.                     $field_wrapper->{'style'} .= ( (strpos($width,  '%') !== FALSE) ? ';width: '  . $width  : ';width: '  . $width.'px');
  961.                 }
  962.                 
  963.                 if (!$object instanceof <a href="widget/form/THtmlEditor.html">THtmlEditor</a>)
  964.                 {
  965.                     if ($height)
  966.                     {
  967.                         $field_wrapper->{'style'} .= ( (strpos($height, '%') !== FALSE) ? ';height: ' . $height : ';height: ' . $height.'px');
  968.                     }
  969.                 }
  970.             }
  971.             else if ($field_size && !$object instanceof <a href="widget/form/TRadioGroup.html">TRadioGroup</a> AND !$object instanceof <a href="widget/form/TCheckGroup.html">TCheckGroup</a>)
  972.             {
  973.                 $field_wrapper->{'style'} .= ( (strpos($field_size, '%') !== FALSE) ? ';width: '.$field_size : ';width: '.$field_size.'px');
  974.             }
  975.             
  976.             if (is_callable([$object, 'getAfterElement']) && $object->getAfterElement())
  977.             {
  978.                 $field_wrapper->{'style'} .= ';display:inline-table';
  979.             }
  980.         }
  981.         
  982.         $field_wrapper->add($field);
  983.         
  984.         if ($field instanceof <a href="widget/form/AdiantiWidgetInterface.html">AdiantiWidgetInterface</a>)
  985.         {
  986.             $input_class = 'form-control';
  987.             
  988.             if ($field instanceof <a href="widget/form/TLabel.html">TLabel</a> || $field instanceof <a href="widget/form/TCheckButton.html">TCheckButton</a>)
  989.             {
  990.                 $input_class = '';
  991.             }
  992.             else if ($field instanceof <a href="widget/form/TButton.html">TButton</a>)
  993.             {
  994.                 $input_class = empty($field->{'class'}) ? 'btn btn-default btn-sm' : '';
  995.             }
  996.             
  997.             $field_class = $input_class . ' ' . ( isset($field->{'class'}) ? $field->{'class'} : '' );
  998.             
  999.             if (trim($field_class))
  1000.             {
  1001.                 $field->{'class'} = $field_class;
  1002.             }
  1003.         }
  1004.         
  1005.         if (is_object($object) && (method_exists($object, 'setSize')))
  1006.         {
  1007.             if (in_array($object->getProperty('widget'), ['tmultisearch', 'tdbmultisearch', 'thtmleditor', 'tmultientry']))
  1008.             {
  1009.                 $object->setSize('100%', $field_size[1] - 3);
  1010.             }
  1011.             else if ( ($field_size) AND !($object instanceof <a href="widget/form/TRadioGroup.html">TRadioGroup</a> || $object instanceof <a href="widget/form/TCheckGroup.html">TCheckGroup</a>))
  1012.             {
  1013.                 $object->setSize('100%', '100%');
  1014.             }
  1015.         }
  1016.         
  1017.         return $field_wrapper;
  1018.     }
  1019.     
  1020. /**    
  1021.      *
  1022.      */
  1023.     public static function showField($form, $field, $speed = 0)
  1024.     {
  1025.         <a href="widget/base/TScript.html">TScript</a>::create("tform_show_field('{$form}', '{$field}', {$speed})");
  1026.     }
  1027.     
  1028.     /**
  1029.      *
  1030.      */
  1031.     public static function hideField($form, $field, $speed = 0)
  1032.     {
  1033.         TScript::create("tform_hide_field('{$form}', '{$field}', {$speed})");
  1034.     }
  1035.     
  1036.     /**
  1037.      * Converts the object into a string
  1038.      */
  1039.     public function __toString()
  1040.     {
  1041.         return $this->getContents();
  1042.     }
  1043.     
  1044.     /**
  1045.      * Returns the element content as a string
  1046.      */
  1047.     public function getContents()
  1048.     {
  1049.         ob_start();
  1050.         $this->show();
  1051.         $content = ob_get_contents();
  1052.         ob_end_clean();
  1053.         return $content;
  1054.     }