BuildFormBootstrap.php 22.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 1/25/16
 * Time: 10:00 PM
 */

namespace qfq;

use qfq;
12
use qfq\UserFormException;
13
14

require_once(__DIR__ . '/../qfq/Constants.php');
15
require_once(__DIR__ . '/../qfq/helper/OnArray.php');
16
require_once(__DIR__ . '/../qfq/AbstractBuildForm.php');
17
require_once(__DIR__ . '/../qfq/exceptions/UserFormException.php');
18

Carsten  Rose's avatar
Carsten Rose committed
19
20
21
22
/**
 * Class BuildFormBootstrap
 * @package qfq
 */
23
24
class BuildFormBootstrap extends AbstractBuildForm {

25
26
27
28
29
30
31
32
33
34
    private $isFirstPill;

    /**
     * @param array $formSpec
     * @param array $feSpecAction
     * @param array $feSpecNative
     */
    public function __construct(array $formSpec, array $feSpecAction, array $feSpecNative) {
        parent::__construct($formSpec, $feSpecAction, $feSpecNative);
        $this->isFirstPill = true;
35
36

        // Set some defaults
37
38
39
//        if (!isset($this->formSpec['class'])) {
//            $this->formSpec['class'] = 'container';
//        }
Carsten  Rose's avatar
Carsten Rose committed
40

41
//        $this->formSpec['class'] = 'none';
42
43
    }

Carsten  Rose's avatar
Carsten Rose committed
44
45
46
    /**
     *
     */
47
    public function fillWrap() {
48
49
50
51

//        $this->wrap[WRAP_SETUP_OUTER][WRAP_SETUP_START] = '<div class="tab-content">';
//        $this->wrap[WRAP_SETUP_OUTER][WRAP_SETUP_END] = '</div>';

52
53
        $this->wrap[WRAP_SETUP_TITLE][WRAP_SETUP_START] = "<div class='row hidden-xs'><div class='col-md-12'><h1>";
        $this->wrap[WRAP_SETUP_TITLE][WRAP_SETUP_END] = "</h1></div></div>";
54

55
        // Element: Label + Input + Note
56
57
        $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_CLASS] = "form-group clearfix";
        $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_START] = "<div class='" . $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_CLASS] . "'>";
58
        $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_END] = "</div>";
59

60
61
        $this->wrap[WRAP_SETUP_SUBRECORD][WRAP_SETUP_START] = "<div class='col-md-12'>";
        $this->wrap[WRAP_SETUP_SUBRECORD][WRAP_SETUP_END] = "</div>";
62

63
64
        $this->wrap[WRAP_SETUP_IN_FIELDSET][WRAP_SETUP_START] = "";
        $this->wrap[WRAP_SETUP_IN_FIELDSET][WRAP_SETUP_END] = "";
65

66
67
68
        $this->wrap[WRAP_SETUP_IN_TEMPLATE_GROUP][WRAP_SETUP_START] = "";
        $this->wrap[WRAP_SETUP_IN_TEMPLATE_GROUP][WRAP_SETUP_END] = "";

69
70
//        $this->feDivClass['radio'] = 'radio';
//        $this->feDivClass['checkbox'] = 'checkbox';
71
72
    }

73
74
75
76
77
78
    /**
     * @param $label
     * @param $input
     * @param $note
     */
    public function fillWrapLabelInputNote($label, $input, $note) {
79
        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = "<div class='col-md-$label qfq-label'>";
80
81
82
        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_END] = "</div>";
        $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START] = "<div class='col-md-$input'>";
        $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END] = "</div>";
Carsten  Rose's avatar
Carsten Rose committed
83
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START] = "<div class='col-md-$note qfq-note'>";
84
85
86
87
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END] = "</div>";

    }

Carsten  Rose's avatar
Carsten Rose committed
88
89
90
    /**
     * @return string
     */
91
92
93
94
    public function getProcessFilter() {
        return FORM_ELEMENTS_NATIVE_SUBRECORD;
    }

Carsten  Rose's avatar
Carsten Rose committed
95
96
97
    /**
     * @return string
     */
98
99
100
101
102
103
104
105
    public function doSubrecords() {
        return '';
    }

    /**
     * @return string
     */
    public function head() {
106
        $html = '';
107

108
        $html .= '<div ' . Support::doAttribute('class', $this->formSpec[F_CLASS], TRUE) . '>'; // main <div class=...> around everything, Whole FORM; class="container" or class="container-fluid"
109

110
        //TODO: nicer error reporting - make test with 'unknown index' here - unset($this->formSpec['title']) - See #3424
111
112
113
        $title = Support::wrapTag('<div class="hidden-xs col-sm-6 col-md-8">', Support::wrapTag('<h3>', $this->formSpec['title']));
        $button = Support::wrapTag('<div class="col-xs-12 col-sm-6 col-md-4">', $this->buildButtons());
        $html .= Support::wrapTag('<div class="row">', $title . $button);
114
115


116
117
        $pill = $this->buildPillNavigation(OnArray::filter($this->feSpecNative, 'type', 'pill'));
        $html .= Support::wrapTag('<div class="row">', $pill);
118

119
120
        $html .= $this->getFormTag();

121
122
123
124
125
        $class = ['tab-content', $this->formSpec[F_CLASS_BODY]];
        if ($pill == '') {
            $class[] = 'col-md-12';
        }
        $html .= "<div " . Support::doAttribute('class', $class) . ">";
126
127
128
129

        return $html;
    }

130
131
132
133
134
135
    /**
     * Creates a Checkbox, which toggles 'hide'/'unhide' via JS, on all elements with class= CLASS_FORM_ELEMENT_EDIT.
     *
     * @return string - the rendered Checkbox
     */
    private function buildShowEditFormElementCheckbox() {
Carsten  Rose's avatar
Carsten Rose committed
136
        // EditFormElement Icons
137
138
        $js = '$(".' . CLASS_FORM_ELEMENT_EDIT . '").toggleClass("hidden")';
        $element = "<input type='checkbox' onchange='" . $js . "'>" .
139
            Support::wrapTag("<span title='Toggle: Edit form element icons' class='" . GLYPH_ICON . ' ' . GLYPH_ICON_TASKS . "'>", '');
140
        $element = Support::wrapTag('<label class="btn btn-default navbar-btn">', $element);
Carsten  Rose's avatar
Carsten Rose committed
141

142
143
144
        return Support::wrapTag('<div class="btn-group" data-toggle="buttons">', $element);
    }

Carsten  Rose's avatar
Carsten Rose committed
145
146
147
148
149
150
151
    /**
     * Creates a link to open current form loaded in FormEditor
     *
     * @return string - the rendered Checkbox
     */
    private function buildViewForm() {

152
153
154
155
        $form = false;
        $url = '';
        $status = '';

Carsten  Rose's avatar
Carsten Rose committed
156
157
158
159
160
        switch ($this->formSpec[F_NAME]) {
            case 'form':
                $form = $this->store->getVar(F_NAME, STORE_RECORD);
                break;
            case 'formElement':
161
162
163
164
                if (false !== ($formId = $this->store->getVar(FE_FORM_ID, STORE_SIP . STORE_RECORD))) {
                    $row = $this->db->sql("SELECT f.name FROM Form AS f WHERE id=" . $formId, ROW_EXPECT_1);
                    $form = current($row);
                }
Carsten  Rose's avatar
Carsten Rose committed
165
166
167
168
169
170
                break;
            default:
                return '';
        }

        if ($form === false) {
171
172
173
174
175
176
177
178
179
180
181
182
183
184
            $toolTip = "Form not 'form' or 'formElement'";
            $status = 'disabled';
        } else {

            $queryStringArray = [
                'id' => $this->store->getVar(SYSTEM_EDIT_FORM_PAGE, STORE_SYSTEM),
                'form' => $form,
                'r' => 0
            ];
            $queryString = Support::arrayToQueryString($queryStringArray);
            $sip = $this->store->getSipInstance();
            $url = $sip->queryStringToSip($queryString);

            $toolTip = "View current form with r=0" . PHP_EOL . PHP_EOL . OnArray::toString($queryStringArray, ' = ', PHP_EOL, "'");
Carsten  Rose's avatar
Carsten Rose committed
185
        }
186
        return $this->buildButtonAnchor('form-view-' . $this->formSpec[F_ID], $url, $toolTip, GLYPH_ICON_VIEW, $status);
Carsten  Rose's avatar
Carsten Rose committed
187
188
189
190
191

    }

    //        glyphicon glyphicon-eye-open

192
    /**
193
     * Build Buttons panel on top right corner of form.
194
     * Simulate Submit Button: http://www.javascript-coder.com/javascript-form/javascript-form-submit.phtml
195
196
197
198
199
     *
     * @return string
     */
    private function buildButtons() {
        $buttonNew = '';
200
201
202
203
        $buttonDelete = '';
        $buttonClose = '';
        $buttonSave = '';
        $buttonEditForm = '';
Carsten  Rose's avatar
Carsten Rose committed
204
205
206
207
        $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);


        // Button: FormEdit
208
        if ($this->showDebugInfoFlag) {
209
            $toolTip = "Edit form" . PHP_EOL . PHP_EOL . OnArray::toString($this->store->getStore(STORE_SIP), ' = ', PHP_EOL, "'");
210
            $url = $this->createFormEditorUrl(FORM_NAME_FORM, $this->formSpec[F_ID]);
211

Carsten  Rose's avatar
Carsten Rose committed
212
213
214
            $buttonEditForm = $this->buildViewForm() .
                $this->buildShowEditFormElementCheckbox() .
                $this->buildButtonAnchor('form-edit-button', $url, $toolTip, GLYPH_ICON_TOOL);
215
216
217
        }

        // Button: Save
Carsten  Rose's avatar
Carsten Rose committed
218
        if (Support::findInSet(FORM_BUTTON_SAVE, $this->formSpec[F_SHOW_BUTTON]) && $this->formSpec[F_SUBMIT_BUTTON_TEXT] === '') {
219
220
            $toolTip = 'Save';

221
            if ($this->showDebugInfoFlag) {
222
                $toolTip .= PHP_EOL . "table = '" . $this->formSpec[F_TABLE_NAME] . "'" . PHP_EOL . "r = '" . $recordId . "'";
223
224
            }

225
            $buttonSave = $this->buildButtonCode('save-button', $toolTip, GLYPH_ICON_CHECK, '', $this->formSpec[F_BUTTON_ON_CHANGE_CLASS]);
226
227
228
        }

        // Button: Close
Carsten  Rose's avatar
Carsten Rose committed
229
        if (Support::findInSet(FORM_BUTTON_CLOSE, $this->formSpec[F_SHOW_BUTTON])) {
230
231
            $toolTip = 'Close';

232
            $buttonClose = $this->buildButtonCode('close-button', 'Close', GLYPH_ICON_CLOSE);
Carsten  Rose's avatar
Carsten Rose committed
233
        }
234

Carsten  Rose's avatar
Carsten Rose committed
235
        // Button: Delete
Carsten  Rose's avatar
Carsten Rose committed
236
        if (Support::findInSet(FORM_BUTTON_DELETE, $this->formSpec[F_SHOW_BUTTON])) {
237
238
            $toolTip = 'Delete';

239
            if ($this->showDebugInfoFlag && $recordId > 0) {
240
                $toolTip .= PHP_EOL . "form = '" . $this->formSpec[F_FINAL_DELETE_FORM] . "'" . PHP_EOL . "r = '" . $recordId . "'";
Carsten  Rose's avatar
Carsten Rose committed
241
            }
242
            $disabled = ($recordId > 0) ? '' : 'disabled';
Carsten  Rose's avatar
Carsten Rose committed
243

244
            $buttonDelete = $this->buildButtonCode('delete-button', $toolTip, GLYPH_ICON_DELETE, $disabled);
245
246
        }

Carsten  Rose's avatar
Carsten Rose committed
247
        // Button: New
Carsten  Rose's avatar
Carsten Rose committed
248
        if (Support::findInSet(FORM_BUTTON_NEW, $this->formSpec[F_SHOW_BUTTON])) {
249
250
            $toolTip = 'New';
            $url = $this->deriveNewRecordUrlFromExistingSip($toolTip);
Carsten  Rose's avatar
Carsten Rose committed
251

252
            $buttonNew = $this->buildButtonAnchor('form-new-button', $url, $toolTip, GLYPH_ICON_NEW);
253
254
        }

255
        // Arrangement: Edit Form / Save / Close / Delete / New
Carsten  Rose's avatar
Carsten Rose committed
256
        // Specified in reverse order cause 'pull-right' inverts the order. http://getbootstrap.com/css/#helper-classes-floats
257
258
259
260
261
        $html = '';
        $html .= Support::wrapTag('<div class="btn-group pull-right" role="group">', $buttonNew);
        $html .= Support::wrapTag('<div class="btn-group pull-right" role="group">', $buttonDelete);
        $html .= Support::wrapTag('<div class="btn-group pull-right" role="group">', $buttonSave . $buttonClose);
        $html .= Support::wrapTag('<div class="btn-group pull-right" role="group">', $buttonEditForm);
262
263
264

        $html = Support::wrapTag('<div class="btn-toolbar" role="toolbar">', $html);

265
266
267
        return $html;
    }

268
    /**
Carsten  Rose's avatar
Carsten Rose committed
269
270
271
272
273
     * Generic function to create a button with a given id, $url, $title, $icon (=glyph), $disabled
     * @param string $id
     * @param string $url
     * @param string $title
     * @param string $icon
274
275
276
277
     * @param string $disabled
     * @return string
     */
    private function buildButtonAnchor($id, $url, $title, $icon, $disabled = '') {
Carsten  Rose's avatar
Carsten Rose committed
278
279
280
281
282
283
284
285
        $icon = Support::wrapTag("<span " . Support::doAttribute('class', "glyphicon $icon") . ">", '');

        $attribute = Support::doAttribute('href', $url);
        $attribute .= Support::doAttribute('id', $id);
        $attribute .= Support::doAttribute('class', "btn btn-default navbar-btn $disabled");
        $attribute .= Support::doAttribute('title', $title);

        return Support::wrapTag("<a $attribute>", $icon);
286
287
288
    }

    /**
289
290
     * Creates a button with the given attributes. If there is no $icon given, render the button without glyph.
     *
291
292
293
     * @param string $id
     * @param string $title
     * @param string $icon
294
295
296
     * @param string $disabled
     * @return string
     */
297
    private function buildButtonCode($id, $title, $icon, $disabled = '', $buttonOnChangeClass = '') {
298
299
300
301
302
303
304
305
306

        $element = "<span class='glyphicon $icon'></span>";
        $classAdd = "navbar-btn";

        if ($icon === '') {
            $element = $title;
            $classAdd = '';
        }

307
        $class = Support::doAttribute('class', ["btn btn-default", $classAdd]);
308
        $dataClassOnChange = Support::doAttribute('data-class-on-change', $buttonOnChangeClass);
309
        $title = Support::doAttribute('title', $title);
310
        return "<button id='$id' type='button' $class $dataClassOnChange $title $disabled>$element</button>";
311
312
    }

Carsten  Rose's avatar
Carsten Rose committed
313
    /**
314
     * @param $pillArray
Carsten  Rose's avatar
Carsten Rose committed
315
     * @return string
316
     * @throws UserFormException
Carsten  Rose's avatar
Carsten Rose committed
317
     */
318
319
320
    private function buildPillNavigation($pillArray) {
        $pillButton = '';
        $pillDropdown = '';
321
        $htmlDropdown = '';
322
323
324
325
326
327
328
329
330
331
332
333

        if ($pillArray == null)
            return '';

        $maxVisiblePill = (isset($this->formSpec['maxVisiblePill']) && $this->formSpec['maxVisiblePill'] !== '') ? $this->formSpec['maxVisiblePill'] : 1000;

        // Iterate over all 'pill'
        $ii = 0;
        $active = 'class="active"';
        foreach ($pillArray as $formElement) {
            $ii++;

334
            if ($formElement[FE_NAME] === '' || $formElement[FE_LABEL] === '') {
335
                $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);
336
                $this->store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, 'name, label', STORE_SYSTEM);
337
                throw new UserFormException("Field 'name' and/or 'label' are empty", ERROR_NAME_LABEL_EMPTY);
338
339
            }

340
            // Anker for pill navigation
341
            $a = '<a ' . Support::doAttribute('href', '#' . $this->createAnker($formElement['id'])) . ' data-toggle="tab">' . $formElement[FE_LABEL] . '</a>';
342

343
344
345
346
347
348
349
            if ($ii <= $maxVisiblePill) {
                $pillButton .= '<li role="presentation" ' . $active . '>' . $a . '</li>';
            } else {
                $pillDropdown .= '<li>' . $a . '</li>';
            }
            $active = '';
        }
350

351
352
        // Pill Dropdown necessary?
        if ($ii > $maxVisiblePill) {
353
            $htmlDropdown = Support::wrapTag('<ul class="dropdown-menu qfq-form-pill ' . $this->formSpec[F_CLASS_PILL] . '">', $pillDropdown, true);
354
            $htmlDropdown = '<a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button">more <span class="caret"></span></a>' . $htmlDropdown;
355
            $htmlDropdown = Support::wrapTag('<li role="presentation" class="dropdown">', $htmlDropdown, false);
356
        }
357

358
        $htmlDropdown = Support::wrapTag('<ul id="' . $this->getTabId() . '" class="nav nav-pills qfq-form-pill ' . $this->formSpec[F_CLASS_PILL] . '" role="tablist">', $pillButton . $htmlDropdown);
359
        $htmlDropdown = Support::wrapTag('<div class="col-md-12">', $htmlDropdown);
360

361
        return $htmlDropdown;
362
363
364
365
366
367
368
369
370
371
372
373
    }

    /**
     * Create an identifier for the pill navigation menu
     *
     * @param $id
     * @return string
     */
    private function createAnker($id) {
        return $this->formSpec['name'] . '_' . $id;
    }

Carsten  Rose's avatar
Carsten Rose committed
374
375
376
377
378
379
380
    /**
     * @return string
     */
    private function getTabId() {
        return 'qfqTabs';
    }

381
382
383
384
385
    /**
     * Builds the complete HTML '<form ...>'-tag
     *
     * @return string
     */
386
    public function getFormTag() {
387
388
389
390

        $attribute = $this->getFormTagAtrributes();

        $attribute['class'] = 'form-horizontal';
391
        $attribute['data-toggle'] = 'validator';
392
393
394
        if (isset($this->formSpec[F_SAVE_BUTTON_ACTIVE])) {
            $attribute[DATA_ENABLE_SAVE_BUTTON] = 'true';
        }
395
396
397
398

        $honeypot = $this->getHoneypotVars();

        return '<form ' . OnArray::toString($attribute, '=', ' ', "'") . '>' . $honeypot;
399
400
    }

401
402
403
404
    /**
     * @return string
     */
    public function tail() {
405

406
        $html = '';
Carsten  Rose's avatar
Carsten Rose committed
407
        $deleteUrl = '';
408

409
410
        $formId = $this->getFormId();

411
412
        // Button Save at bottom of form - only if there is a button text given.
        if ($this->formSpec[F_SUBMIT_BUTTON_TEXT] !== '') {
413
414
415
416
417


            // Default setzen:
            $this->fillWrapLabelInputNote($this->formSpec[F_BS_LABEL_COLUMNS], $this->formSpec[F_BS_INPUT_COLUMNS], $this->formSpec[F_BS_NOTE_COLUMNS]);

418
419
            $buttonText = $this->formSpec[F_SUBMIT_BUTTON_TEXT];

420
            $htmlElement = $this->buildButtonCode('save-button', $buttonText, '', '', $this->formSpec[F_BUTTON_ON_CHANGE_CLASS]);
421
422
423
424
425
426
427
428

            $html .= $this->wrapItem(WRAP_SETUP_LABEL, '');
            $html .= $this->wrapItem(WRAP_SETUP_INPUT, $htmlElement);
            $html .= $this->wrapItem(WRAP_SETUP_NOTE, '');

            $html = $this->wrapItem(WRAP_SETUP_ELEMENT, $html);
        }

429
        $html .= '</div> <!--class="tab-content" -->';  //  <div class="tab-content">
430
//        $html .= '<input type="submit" value="Submit">';
431

Carsten  Rose's avatar
Carsten Rose committed
432
433
434
        $formId = $this->getFormId();
        $tabId = $this->getTabId();

Carsten  Rose's avatar
Carsten Rose committed
435
        if (0 < ($recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP))) {
436
            $deleteUrl = $this->createDeleteUrl($this->formSpec[F_FINAL_DELETE_FORM], '', $recordId);
Carsten  Rose's avatar
Carsten Rose committed
437
        }
438

439
440
441
        $actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
        $actionDelete = FILE_ACTION . '=' . FILE_ACTION_DELETE;

442
443
444
        $apiDir = API_DIR;
        $apiDeletePhp = API_DIR . '/' . API_DELETE_PHP;

445
        $html .= '</form>';  //  <form class="form-horizontal" ...
446
447
448
449
450
451
        $html .= <<<EOF
        <script type="text/javascript">
            $(function () {
                'use strict';
                QfqNS.Log.level = 0;

Carsten  Rose's avatar
Carsten Rose committed
452
453
454
                var qfqPage = new QfqNS.QfqPage({
                    tabsId: '$tabId',
                    formId: '$formId',
455
                    submitTo: '$apiDir/save.php',
Carsten  Rose's avatar
Carsten Rose committed
456
                    deleteUrl: '$deleteUrl',
457
458
459
                    refreshUrl: '$apiDir/load.php',
                    fileUploadTo: '$apiDir/file.php?$actionUpload',
                    fileDeleteUrl: '$apiDir/file.php?$actionDelete'
Carsten  Rose's avatar
Carsten Rose committed
460
461
                });

462
                var qfqRecordList = new QfqNS.QfqRecordList('$apiDeletePhp');
Carsten  Rose's avatar
Carsten Rose committed
463
            })
464
465
         </script>
EOF;
466
        $html .= '</div>';  //  <div class="container-fluid"> === main <div class=...> around everything
467

468
469
470
471
472
        return $html;
    }

    /**
     * @param array $formElement
473
     * @param $htmlFormElementName
474
475
476
     * @param $value
     * @return mixed
     */
477
    public function buildPill(array $formElement, $htmlFormElementName, $value, array &$json) {
478
        $html = '';
479
480
481
482
        // save parent processed FE's
        $tmpStore = $this->feSpecNative;

        // child FE's
483
484
485
        $this->feSpecNative = $this->db->getNativeFormElements(SQL_FORM_ELEMENT_SPECIFIC_CONTAINER,
            ['yes', $this->formSpec["id"], 'native,container', $formElement['id']], $this->formSpec);

Carsten  Rose's avatar
Carsten Rose committed
486
        $html = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), FORM_ELEMENTS_NATIVE_SUBRECORD, 0, $json);
487
488
489
490

        // restore parent processed FE's
        $this->feSpecNative = $tmpStore;

491
492
493
        return $html;
    }

494
    /**
495
496
     * @param array $formElement Complete FormElement, especially some FE_WRAP
     * @param string $htmlElement Content to wrap.
497
     * @param $htmlFormElementName
498
     * @return string               Wrapped $htmlElement
499
     * @throws \qfq\UserFormException
500
     */
501
    public function buildRowNative(array $formElement, $htmlElement, $htmlFormElementName) {
502
        $html = '';
503
        $htmlLabel = '';
504

Carsten  Rose's avatar
Carsten Rose committed
505
        // Label
506
        if ($formElement[FE_BS_LABEL_COLUMNS] > 0) {
507
            $htmlLabel = $this->buildLabel($htmlFormElementName, $formElement[FE_LABEL]);
508
        }
509

510
        $html .= $this->customWrap($formElement, $htmlLabel, FE_WRAP_LABEL, $formElement[FE_BS_LABEL_COLUMNS],
511
            [$this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START], $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_END]], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_LABEL);
512

Carsten  Rose's avatar
Carsten Rose committed
513
        // Input
514
        $html .= $this->customWrap($formElement, $htmlElement, FE_WRAP_INPUT, $formElement[FE_BS_INPUT_COLUMNS],
515
            [$this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START], $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END]], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_INPUT);
516

Carsten  Rose's avatar
Carsten Rose committed
517
        // Note
Carsten  Rose's avatar
Carsten Rose committed
518
        $note = $formElement[FE_NOTE];
519
        $html .= $this->customWrap($formElement, $note, FE_WRAP_NOTE, $formElement[FE_BS_NOTE_COLUMNS],
520
            [$this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START], $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END]], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_NOTE);
521
522
523
524

        // ROW
        $openTag = (Support::findInSet('row', $formElement[FE_WRAP_ROW_LABEL_INPUT_NOW])) ? $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_START] : '';
        $closeTag = (Support::findInSet('/row', $formElement[FE_WRAP_ROW_LABEL_INPUT_NOW])) ? $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_END] : '';
525
        $html = $this->customWrap($formElement, $html, FE_WRAP_ROW, 99, [$openTag, $closeTag], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_ROW);
526
527
528
529

        return $html;
    }

530
    /**
531
532
533
534
     *
     * @param array $formElement Complete FormElement, especially some FE_WRAP
     * @param string $htmlElement Content to wrap.
     * @param string $wrapName FE_WRAP_ROW, FE_WRAP_LABEL, FE_WRAP_INPUT, FE_WRAP_NOTE
535
     * @param int $bsColumns
536
     * @param array $wrapArray Systemwide Defaults: [ 'open wrap', 'close wrap' ]
537
     * @return string Wrapped $htmlElement
538
539
     * @throws \qfq\UserFormException
     */
540
    private function customWrap(array $formElement, $htmlElement, $wrapName, $bsColumns, array $wrapArray, $htmlId = '') {
541

542
        // If $bsColumns==0: do not wrap with default.
543
544
545
546
        if ($bsColumns == 0) {
            $wrapArray[0] = '';
            $wrapArray[1] = '';
        }
547

548
        // If there is a 'per FormElement'-wrap, take it.
549
550
551
552
553
554
555
556
        if (isset($formElement[$wrapName])) {
            $wrapArray = explode('|', $formElement[$wrapName], 2);
        }

        if (count($wrapArray) != 2) {
            throw new UserFormException("Need open & close wrap token for FormElement.parameter" . $wrapName . " - E.g.: <div ...>|</div>", ERROR_MISSING_VALUE);
        }

557
558
559
560
        if ($wrapArray[0] != '') {
            $wrapArray[0] = Support::insertAttribute($wrapArray[0], 'id', $htmlId);
        }

561
562
563
        return $wrapArray[0] . $htmlElement . $wrapArray[1];
    }

564
565
566
567
568
    /**
     * @param $formElement
     * @param $elementHtml
     * @return string
     */
569
    public function buildRowPill(array $formElement, $elementHtml) {
570
571
        $html = '';

572
        $html .= Support::wrapTag('<div class="col-md-12 qfq-form-body ' . $this->formSpec[F_CLASS_BODY] . '">', $elementHtml);
573
574
575

        $active = $this->isFirstPill ? ' active' : '';

576
        $html = Support::wrapTag('<div role="tabpanel" class="tab-pane' . $active . '" id="' . $this->createAnker($formElement['id']) . '">', $html);
577
578
579
580
581
582

        $this->isFirstPill = false;

        return $html;
    }

583
    /**
584
585
     * Builds a fieldset
     *
586
587
588
     * @param $formElement
     * @param $elementHtml
     */
589
    public function buildRowFieldset(array $formElement, $elementHtml) {
590
591
592
        $html = $elementHtml;

        return $html;
593
594
    }

595
596
597
598
599
600
601
602
603
604
605
606
    /**
     * Builds a templateGroup
     *
     * @param $formElement
     * @param $elementHtml
     */
    public function buildRowTemplateGroup(array $formElement, $elementHtml) {
        $html = $elementHtml;

        return $html;
    }

607
608
609
610
611
    /**
     * @param $formElement
     * @param $elementHtml
     * @return string
     */
612
    public function buildRowSubrecord(array $formElement, $elementHtml) {
613
        $html = '';
614
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement[FE_LABEL]));
615
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $elementHtml));
616
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement[FE_NOTE]));
617
618
619

        return $html;
    }
620
}