Passed
Push — main ( 1f9a40...3e1577 )
by Stefan
01:51
created

FormGenerator   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 337
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 35
eloc 101
dl 0
loc 337
rs 9.6
c 1
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getConfig() 0 3 1
A getStdImage() 0 20 2
A setConfig() 0 3 1
A setFormWidth() 0 3 1
A __construct() 0 14 2
A setAction() 0 3 1
A setImagePath() 0 3 1
A getData() 0 3 1
A setTarget() 0 3 1
A getGlobalFlags() 0 3 1
A setReadOnly() 0 7 2
A getImagePath() 0 7 2
A setData() 0 3 1
A addConfigForJS() 0 3 1
A getForm() 0 29 6
A setDialog() 0 3 1
A addElement() 0 5 2
A setDebugMode() 0 6 1
A adjustColHeight() 0 5 1
A isDialog() 0 3 1
A getScript() 0 10 1
A getDebugMode() 0 3 1
A getStyle() 0 10 3
1
<?php
2
declare(strict_types=1);
3
4
namespace SKien\Formgenerator;
5
6
use SKien\Config\ConfigInterface;
7
use SKien\Config\NullConfig;
8
9
/**
10
 * Container for all form elements.
11
 *
12
 * #### History
13
 * - *2020-05-12*   initial version
14
 * - *2021-01-07*   PHP 7.4
15
 *
16
 * @package Formgenerator
17
 * @version 1.1.0
18
 * @author Stefanius <[email protected]>
19
 * @copyright MIT License - see the LICENSE file for details
20
 */
21
class FormGenerator extends FormCollection
22
{
23
    /** @var int last tab position in the form   */
24
    protected int $iLastTabindex = 0;
25
    /** @var bool set the whole form to readonly  */
26
    protected bool $bReadOnly = false;
27
    /** @var bool set debug mode to output more information  */
28
    protected bool $bDebugMode = false;
29
    /** @var bool set dialog mode (form runs in dynamic created iframe)  */
30
    protected bool $bIsDialog = false;
31
    /** @var int explicit width of the form in pixel  */
32
    protected int $iWidth = -1;
33
    /** @var FormFlags global flags for all form elements  */
34
    protected FormFlags $oGlobalFlags;
35
    /** @var string JS function for onsubmit  */
36
    protected string $strOnSubmit = '';
37
    /** @var string JS function for oncancel  */
38
    protected string $strOnCancel = '';
39
    /** @var array config values to pass as object to JS */
40
    protected array $aConfigForJS = [];
41
    /** @var array elements that creates dynamic CSS styles */
42
    protected array $aStyleElements = [];
43
    /** @var string path to the images  */
44
    protected string $strImgPath = '';
45
    /** @var string URL params for the form action  */
46
    protected string $strAction;
47
    /** @var string form target  */
48
    protected string $strFormTarget = '';
49
    /** @var FormDataInterface data provider     */
50
    protected FormDataInterface $oData;
51
    /** @var ConfigInterface configuration     */
52
    protected ConfigInterface $oConfig;
53
54
    /**
55
     * Create a FormGenerator.
56
     * Set some values to default.
57
     */
58
    public function __construct(?FormDataInterface $oData, string $strID = 'FormGenerator') 
59
    {
60
        $this->oFG = $this;
61
        $this->oData = $oData ?? new NullFormData();
62
        $this->oConfig = new NullConfig();
63
        $this->oGlobalFlags = new FormFlags();
64
        $this->strID = $strID;
65
        $this->strOnSubmit = "validateForm();";
66
        $this->strOnCancel = "javascript:history.back();";
67
        $this->strAction = $_SERVER['PHP_SELF'];
68
        if (isset($_SERVER['QUERY_STRING'])) {
69
            $this->strAction .= '?' . $_SERVER['QUERY_STRING'];
70
        }
71
        $this->bDebugMode = (error_reporting() & (E_USER_ERROR | E_USER_WARNING)) != 0; 
72
    }
73
    
74
    /**
75
     * @return \SKien\Formgenerator\FormDataInterface
76
     */
77
    public function getData() : FormDataInterface
78
    {
79
        return $this->oData;
80
    }
81
    
82
    /**
83
     * @return \SKien\Config\ConfigInterface
84
     */
85
    public function getConfig() : ConfigInterface
86
    {
87
        return $this->oConfig;
88
    }
89
    
90
    /**
91
     * @param \SKien\Formgenerator\FormDataInterface $oData
92
     */
93
    public function setData(FormDataInterface $oData) : void
94
    {
95
        $this->oData = $oData;
96
    }
97
    
98
    /**
99
     * @param \SKien\Config\ConfigInterface $oConfig
100
     */
101
    public function setConfig(ConfigInterface $oConfig) : void
102
    {
103
        $this->oConfig = $oConfig;
104
    }
105
    
106
    /**
107
     * Set the action executed when submit the form.
108
     * If not changed here, <i><b>$_SERVER['QUERY_STRING']</b></i> is used.
109
     * @param string $strAction (default = '')
110
     */
111
    public function setAction(string $strAction) : void
112
    {
113
        $this->strAction = $strAction;
114
    }
115
116
    /**
117
     * Set the path to images.
118
     * @param string $strPath
119
     */
120
    public function setImagePath(string $strPath) : void 
121
    {
122
        $this->strImgPath = $strPath;
123
    }
124
125
    /**
126
     * Get the path to the StdImages  (<b>WITHOUT trailing DIRECTORY_SEPARATOR!</b>).
127
     * If no directory set, the subdirectory 'StdImages' of this sourcefile is used. 
128
     * @return string current image path
129
     */
130
    public function getImagePath() : string
131
    {
132
        if ($this->strImgPath == '') {
133
            $this->strImgPath = str_replace(rtrim($_SERVER['DOCUMENT_ROOT'], DIRECTORY_SEPARATOR), '', __DIR__);
134
            $this->strImgPath .= DIRECTORY_SEPARATOR . 'StdImages';
135
        }
136
        return rtrim($this->strImgPath, DIRECTORY_SEPARATOR);
137
    }
138
    
139
    /**
140
     * Get filename for predifined standard images
141
     * @param int $iImage
142
     * @return array
143
     */
144
    public function getStdImage(int $iImage) : array
145
    {
146
        $aImageMap = [
147
            FormImage::IMG_DELETE      => ['Delete', 'delete.png', 'Delete content'],
148
            FormImage::IMG_SEARCH      => ['Search', 'search.png', 'Select'],
149
            FormImage::IMG_BROWSE      => ['Browse', 'browse.png', 'Browse on server'],
150
            FormImage::IMG_DATE_PICKER => ['DatePicker', 'datepicker.png', 'Select date'],
151
            FormImage::IMG_TIME_PICKER => ['TimePicker', 'timepicker.png', 'Select time'],
152
            FormImage::IMG_DTU         => ['InsertDTU', 'insert_dtu.png', 'insert current date, time and user'],
153
        ];
154
        
155
        if (!isset($aImageMap[$iImage])) {
156
            return ['', ''];
157
        }
158
        $aImage = $aImageMap[$iImage];
159
        $strImage = $this->getImagePath() . DIRECTORY_SEPARATOR;
160
        $strImage .= $this->getConfig()->getString('Images.StdImages.' . $aImage[0] . '.Image', $aImage[1]);
161
        $strTitle = $this->getConfig()->getString('Images.StdImages.' . $aImage[0] . '.Text', $aImage[2]);
162
        
163
        return [$strImage, $strTitle];
164
    }
165
    
166
    /**
167
     * Get the global flags.
168
     * @return int
169
     */
170
    public function getGlobalFlags() : int
171
    {
172
        return $this->oGlobalFlags->getFlags();
173
    }
174
    
175
    /**
176
     * Set the target
177
     * @param string $strFormTarget
178
     */
179
    public function setTarget(string $strFormTarget) : void 
180
    {
181
        $this->strFormTarget = $strFormTarget;
182
    }
183
    
184
    /**
185
     * Set explicit width of the form.
186
     * @param int $iWidth    width (-1 for default)
187
     */
188
    public function setFormWidth(int $iWidth) : void 
189
    {
190
        $this->iWidth = $iWidth;
191
    }
192
    
193
    /**
194
     * Sets the whole form to readonly. 
195
     * All elements of the form set to disabled, Save/Cancel-Buttons are replaced 
196
     * with Close-Button
197
     * @param bool $bReadOnly
198
     */
199
    public function setReadOnly(bool $bReadOnly) : void 
200
    {
201
        $this->bReadOnly = $bReadOnly;
202
        if ($bReadOnly) {
203
            $this->oGlobalFlags->add(FormFlags::READ_ONLY);
204
        } else {
205
            $this->oGlobalFlags->remove(FormFlags::READ_ONLY);
206
        }
207
    }
208
    
209
    /**
210
     * Adjust the height of the two specified columns.
211
     * If we have two columns containing fieldset, the height pf both (... and the border) 
212
     * usually differs dependent on the contents. To prettify the output, the height of the 
213
     * smaller columns is set to the height of the bigger one.
214
     * @param string $strCol1
215
     * @param string $strCol2
216
     */
217
    public function adjustColHeight(string $strCol1, string $strCol2) : void
218
    {
219
        $strScript = "window.addEventListener('load', function () {";  
220
        $strScript .= "adjustColumnHeight('" . $strCol1 . "', '" . $strCol2 . "');});";
221
        $this->add(new FormScript($strScript));
222
    }
223
    
224
    /**
225
     * Sets the debug mode to output more information.
226
     * Especialy for some elements, that need additional JS it can be helpfull to
227
     * turn on the debug mode. 
228
     * @param bool $bDebugMode
229
     */
230
    public function setDebugMode(bool $bDebugMode) : void
231
    {
232
        // Even if we plan to integrate a logger, we keep the debug mode alive because the 
233
        // logger do not cover the JS side (missing scripts, ...) and developers often 
234
        // forget to take a look at the Browser console.
235
        $this->bDebugMode = $bDebugMode;
236
    }
237
    
238
    /**
239
     * Get state of the debug mode.
240
     * @return bool
241
     */
242
    public function getDebugMode() : bool
243
    {
244
        return $this->bDebugMode;
245
    }
246
    
247
    /**
248
     * Specify the form as an 'Dialog'.
249
     * Dialog means, the form runs inside of an dynamic iframe created in the
250
     * dialog - DIV of the parent. <br/>
251
     * The cancel can easy be done by clear the content of that dialog-DIV.
252
     */
253
    public function setDialog(bool $bDialog) : void
254
    {
255
        $this->bIsDialog = $bDialog;
256
    }
257
    
258
    /**
259
     * Check, if form runs in dialog (dynamic created iframe).
260
     * @return bool
261
     */
262
    public function isDialog() : bool
263
    {
264
        return $this->bIsDialog;
265
    }
266
    
267
    /**
268
     * Add any element to the form.
269
     * @param FormElement $oElement
270
     */
271
    public function addElement(FormElement $oElement) : void 
272
    {
273
        $this->iLastTabindex += $oElement->setTabindex($this->iLastTabindex + 1);
274
        if ($oElement->bCreateStyle) {
275
            $this->aStyleElements[] = $oElement;
276
        }
277
    }
278
    
279
    /**
280
     * Add a key to the config passed to JS.
281
     * @param string $strKey
282
     * @param mixed $value
283
     */
284
    public function addConfigForJS(string $strKey, $value) : void
285
    {
286
        $this->aConfigForJS[$strKey] = $value;
287
    }
288
    
289
    /**
290
     * Create HTML for defined form.
291
     * @return string
292
     */
293
    public function getForm() : string
294
    {
295
        $strHTML = PHP_EOL;
296
        
297
        $strHTML .= '<form';
298
        if (!empty($this->strAction)) {
299
            $strHTML .= ' action="';
300
            $strHTML .= $this->strAction . '"';
301
        }
302
        if (!empty($this->strFormTarget)) {
303
            $strHTML .= ' target="' . $this->strFormTarget . '"';
304
        }
305
        if ($this->aAttrib != null) {
306
            foreach ($this->aAttrib as $strName => $strValue) {
307
                $strHTML .= ' ' . $strName . '="' . $strValue . '"';
308
            }
309
        }
310
        $strHTML .= $this->buildStyle();
311
        $strHTML .= ' method="post" id=' . $this->strID . ' onsubmit="return ' . $this->strOnSubmit . '">' . PHP_EOL;
312
        $strHTML .= '<div id=errorPopup onclick="this.style.display = \'none\';"></div>' . PHP_EOL;
313
        $iCnt = count($this->aChild);
314
        for ($i = 0; $i < $iCnt; $i++) {
315
            $strHTML .= $this->aChild[$i]->GetHTML();
316
        }
317
        
318
        $strHTML .= '</form>' . PHP_EOL;
319
        $strHTML .= PHP_EOL;
320
        
321
        return $strHTML;
322
    }
323
    
324
    /**
325
     * Build needed configuration for JS scripts.
326
     * - for validation
327
     * - scripts of all childs that have own script
328
     * @return string
329
     */
330
    public function getScript() : string
331
    {
332
        // set some general configurations
333
        $this->aConfigForJS['DebugMode'] = $this->getDebugMode();
334
        $this->aConfigForJS['FormDataValidation'] = $this->oConfig->getArray('FormDataValidation');
335
        $this->aConfigForJS['FormDataValidation']['formID'] = $this->strID;
336
        $this->aConfigForJS['JavaScript'] = $this->oConfig->getArray('JavaScript');
337
        
338
        $strScript = 'var g_oConfigFromPHP = ' . json_encode($this->aConfigForJS) . ';';
339
        return $strScript;
340
    }
341
    
342
    /**
343
     * Build needed CSS styles.
344
     * - body, if form width defined (TODO: better set width of form-element??)
345
     * - styles of all childs that have own styles
346
     * @return string
347
     */
348
    public function getStyle() : string 
349
    {
350
        $strStyle = '';
351
        if ($this->iWidth > 0) {
352
            $strStyle  = "body { width: " . $this->iWidth . "px;}" . PHP_EOL;
353
        }
354
        foreach ($this->aStyleElements as $oElement) {
355
            $strStyle .= $oElement->getStyle();
356
        }
357
        return $strStyle;   
358
    }
359
}
360