BuildFormBootstrap.php 25.8 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
        $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_CLASS] = "form-group clearfix";
57
        $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
79
80
81
82
    /**
     * @param string $addClass
     * @return string
     */
    public function getRowOpenTag($addClass = '') {
        $class = Support::doAttribute('class', [$this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_CLASS], $addClass]);

        return "<div $class>";

    }
83

84
85
86
87
88
89
    /**
     * @param $label
     * @param $input
     * @param $note
     */
    public function fillWrapLabelInputNote($label, $input, $note) {
90
        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = "<div class='col-md-$label qfq-label'>";
91
92
93
        $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
94
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START] = "<div class='col-md-$note qfq-note'>";
95
96
97
98
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END] = "</div>";

    }

Carsten  Rose's avatar
Carsten Rose committed
99
100
101
    /**
     * @return string
     */
102
103
104
105
    public function getProcessFilter() {
        return FORM_ELEMENTS_NATIVE_SUBRECORD;
    }

Carsten  Rose's avatar
Carsten Rose committed
106
107
108
    /**
     * @return string
     */
109
110
111
112
113
114
115
116
    public function doSubrecords() {
        return '';
    }

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

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

121
        //TODO: nicer error reporting - make test with 'unknown index' here - unset($this->formSpec['title']) - See #3424
122
123
124
        $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);
125
126


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

130
131
        $html .= $this->getFormTag();

132
133
134
135
136
        $class = ['tab-content', $this->formSpec[F_CLASS_BODY]];
        if ($pill == '') {
            $class[] = 'col-md-12';
        }
        $html .= "<div " . Support::doAttribute('class', $class) . ">";
137
138
139
140

        return $html;
    }

141
142
143
144
145
146
    /**
     * 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
147
        // EditFormElement Icons
148
149
        $js = '$(".' . CLASS_FORM_ELEMENT_EDIT . '").toggleClass("hidden")';
        $element = "<input type='checkbox' onchange='" . $js . "'>" .
150
            Support::wrapTag("<span title='Toggle: Edit form element icons' class='" . GLYPH_ICON . ' ' . GLYPH_ICON_TASKS . "'>", '');
151
        $element = Support::wrapTag('<label class="btn btn-default navbar-btn">', $element);
Carsten  Rose's avatar
Carsten Rose committed
152

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

Carsten  Rose's avatar
Carsten Rose committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    /**
     * Creates a button to open 'CopyForm' with the current form as source.
     *
     * @return string - the rendered button
     */
    private function buildButtonCopyForm() {

        // Show copy icon only on form 'form' and only if there is a form loaded (id>0)
        if ($this->formSpec[COLUMN_ID] != 1) {
            return '';
        }
        // current loaded form.
        $formId = $this->store->getVar(COLUMN_ID, STORE_RECORD . STORE_ZERO);

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

        $toolTip = "Duplicate form" . PHP_EOL . PHP_EOL . OnArray::toString($queryStringArray, ' = ', PHP_EOL, "'");
        $status = ($formId == 0) ? 'disabled' : '';

        return $this->buildButtonAnchor($url, 'form-view-' . $this->formSpec[F_ID], '', $toolTip, GLYPH_ICON_DUPLICATE, $status, 'btn btn-default navbar-btn');
    }

Carsten  Rose's avatar
Carsten Rose committed
186
187
188
189
190
191
192
    /**
     * Creates a link to open current form loaded in FormEditor
     *
     * @return string - the rendered Checkbox
     */
    private function buildViewForm() {

193
194
195
196
        $form = false;
        $url = '';
        $status = '';

Carsten  Rose's avatar
Carsten Rose committed
197
198
199
200
201
        switch ($this->formSpec[F_NAME]) {
            case 'form':
                $form = $this->store->getVar(F_NAME, STORE_RECORD);
                break;
            case 'formElement':
202
203
204
205
                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
206
207
208
209
210
211
                break;
            default:
                return '';
        }

        if ($form === false) {
212
213
214
215
216
217
218
219
220
221
222
223
224
225
            $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
226
227
        }

228
        return $this->buildButtonAnchor($url, 'form-view-' . $this->formSpec[F_ID], '', $toolTip, GLYPH_ICON_VIEW, $status, 'btn btn-default navbar-btn');
Carsten  Rose's avatar
Carsten Rose committed
229
230
231
232
    }

    //        glyphicon glyphicon-eye-open

233
    /**
234
     * Build Buttons panel on top right corner of form.
235
     * Simulate Submit Button: http://www.javascript-coder.com/javascript-form/javascript-form-submit.phtml
236
237
238
239
240
     *
     * @return string
     */
    private function buildButtons() {
        $buttonNew = '';
241
242
243
244
        $buttonDelete = '';
        $buttonClose = '';
        $buttonSave = '';
        $buttonEditForm = '';
Carsten  Rose's avatar
Carsten Rose committed
245
246
247
        $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);

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

Carsten  Rose's avatar
Carsten Rose committed
252
253
            $buttonEditForm = $this->buildViewForm() .
                $this->buildShowEditFormElementCheckbox() .
Carsten  Rose's avatar
Carsten Rose committed
254
                $this->buildButtonCopyForm() .
255
                $this->buildButtonAnchor($url, 'form-edit-button', '', $toolTip, GLYPH_ICON_TOOL, '', 'btn btn-default navbar-btn');
256
257
258
        }

        // Button: Save
Carsten  Rose's avatar
Carsten Rose committed
259
        if (Support::findInSet(FORM_BUTTON_SAVE, $this->formSpec[F_SHOW_BUTTON]) && $this->formSpec[F_SUBMIT_BUTTON_TEXT] === '') {
260
261
            $toolTip = $this->formSpec[F_SAVE_BUTTON_TOOLTIP];
            // In debugMode every button link should show the information behind the SIP.
262
            if ($this->showDebugInfoFlag) {
263
                $toolTip .= PHP_EOL . "table = '" . $this->formSpec[F_TABLE_NAME] . "'" . PHP_EOL . "r = '" . $recordId . "'";
264
265
            }

266
267
            $buttonSave = $this->buildButtonCode('save-button', $this->formSpec[F_SAVE_BUTTON_TEXT], $toolTip,
                $this->formSpec[F_SAVE_BUTTON_GLYPH_ICON], '', $this->formSpec[F_BUTTON_ON_CHANGE_CLASS], $this->formSpec[F_SAVE_BUTTON_CLASS]);
268
269
270
        }

        // Button: Close
Carsten  Rose's avatar
Carsten Rose committed
271
        if (Support::findInSet(FORM_BUTTON_CLOSE, $this->formSpec[F_SHOW_BUTTON])) {
272
273
274
            $buttonClose = $this->buildButtonCode('close-button', $this->formSpec[F_CLOSE_BUTTON_TEXT],
                $this->formSpec[F_CLOSE_BUTTON_TOOLTIP],
                $this->formSpec[F_CLOSE_BUTTON_GLYPH_ICON], '', '', $this->formSpec[F_CLOSE_BUTTON_CLASS]);
Carsten  Rose's avatar
Carsten Rose committed
275
        }
276

Carsten  Rose's avatar
Carsten Rose committed
277
        // Button: Delete
Carsten  Rose's avatar
Carsten Rose committed
278
        if (Support::findInSet(FORM_BUTTON_DELETE, $this->formSpec[F_SHOW_BUTTON])) {
279
            $toolTip = $this->formSpec[F_DELETE_BUTTON_TOOLTIP];
280

281
            if ($this->showDebugInfoFlag && $recordId > 0) {
282
                $toolTip .= PHP_EOL . "form = '" . $this->formSpec[F_FINAL_DELETE_FORM] . "'" . PHP_EOL . "r = '" . $recordId . "'";
Carsten  Rose's avatar
Carsten Rose committed
283
            }
284
            $disabled = ($recordId > 0) ? '' : 'disabled';
Carsten  Rose's avatar
Carsten Rose committed
285

286
287
            $buttonDelete = $this->buildButtonCode('delete-button', $this->formSpec[F_DELETE_BUTTON_TEXT], $toolTip,
                $this->formSpec[F_DELETE_BUTTON_GLYPH_ICON], $disabled, '', $this->formSpec[F_DELETE_BUTTON_CLASS]);
288
289
        }

Carsten  Rose's avatar
Carsten Rose committed
290
        // Button: New
Carsten  Rose's avatar
Carsten Rose committed
291
        if (Support::findInSet(FORM_BUTTON_NEW, $this->formSpec[F_SHOW_BUTTON])) {
292
            $url = $this->deriveNewRecordUrlFromExistingSip($toolTip);
Carsten  Rose's avatar
Carsten Rose committed
293

294
295
            $buttonNew = $this->buildButtonAnchor($url, 'form-new-button', $this->formSpec[F_NEW_BUTTON_TEXT],
                $this->formSpec[F_NEW_BUTTON_TOOLTIP], $this->formSpec[F_NEW_BUTTON_GLYPH_ICON], '', $this->formSpec[F_NEW_BUTTON_CLASS]);
296
297
        }

298
        // Arrangement: Edit Form / Save / Close / Delete / New
Carsten  Rose's avatar
Carsten Rose committed
299
        // Specified in reverse order cause 'pull-right' inverts the order. http://getbootstrap.com/css/#helper-classes-floats
300
301
302
303
304
        $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);
305
306
307

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

308
309
310
        return $html;
    }

311
    /**
312
313
     * Generic function to create a button with a given $buttonHtmlId, $url, $title, $icon (=glyph), $disabled
     *
Carsten  Rose's avatar
Carsten Rose committed
314
     * @param string $url
315
316
317
     * @param string $buttonHtmlId
     * @param string $text
     * @param string $toolTip
Carsten  Rose's avatar
Carsten Rose committed
318
     * @param string $icon
319
     * @param string $disabled
320
     * @param string $class
321
322
     * @return string
     */
323
324
325
326
327
328
329
    private function buildButtonAnchor($url, $buttonHtmlId, $text, $toolTip, $icon, $disabled = '', $class = '') {

        if ($icon === '') {
            $element = $text;
        } else {
            $element = Support::wrapTag("<span " . Support::doAttribute('class', "glyphicon $icon") . ">", $text);
        }
Carsten  Rose's avatar
Carsten Rose committed
330
331

        $attribute = Support::doAttribute('href', $url);
332
333
334
        $attribute .= Support::doAttribute('id', $buttonHtmlId);
        $attribute .= Support::doAttribute('class', "$class $disabled");
        $attribute .= Support::doAttribute('title', $toolTip);
Carsten  Rose's avatar
Carsten Rose committed
335

336
        return Support::wrapTag("<a $attribute>", $element);
337
338
339
    }

    /**
340
341
     * Creates a button with the given attributes. If there is no $icon given, render the button without glyph.
     *
342
343
344
     * @param string $buttonHtmlId
     * @param string $text
     * @param string $tooltip
345
     * @param string $icon
346
347
348
     * @param string $disabled
     * @return string
     */
349
    private function buildButtonCode($buttonHtmlId, $text, $tooltip, $icon, $disabled = '', $buttonOnChangeClass = '', $class = '') {
350
351

        if ($icon === '') {
352
353
354
            $element = $text;
        } else {
            $element = "<span class='glyphicon $icon'>$text</span>";
355
356
        }

357
        $class = Support::doAttribute('class', $class);
358
        $dataClassOnChange = Support::doAttribute('data-class-on-change', $buttonOnChangeClass);
359
360
361
        $tooltip = Support::doAttribute('title', $tooltip);

        return "<button id='$buttonHtmlId' type='button' $class $dataClassOnChange $tooltip $disabled>$element</button>";
362
363
    }

Carsten  Rose's avatar
Carsten Rose committed
364
    /**
365
     * @param $pillArray
Carsten  Rose's avatar
Carsten Rose committed
366
     * @return string
367
     * @throws UserFormException
Carsten  Rose's avatar
Carsten Rose committed
368
     */
369
370
371
    private function buildPillNavigation($pillArray) {
        $pillButton = '';
        $pillDropdown = '';
372
        $htmlDropdown = '';
373
374
375
376
377
378
379
380
381
382
383
384

        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++;

385
            if ($formElement[FE_NAME] === '' || $formElement[FE_LABEL] === '') {
386
                $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);
387
                $this->store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, 'name, label', STORE_SYSTEM);
388
                throw new UserFormException("Field 'name' and/or 'label' are empty", ERROR_NAME_LABEL_EMPTY);
389
390
            }

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

394
395
396
397
398
399
400
            if ($ii <= $maxVisiblePill) {
                $pillButton .= '<li role="presentation" ' . $active . '>' . $a . '</li>';
            } else {
                $pillDropdown .= '<li>' . $a . '</li>';
            }
            $active = '';
        }
401

402
403
        // Pill Dropdown necessary?
        if ($ii > $maxVisiblePill) {
404
            $htmlDropdown = Support::wrapTag('<ul class="dropdown-menu qfq-form-pill ' . $this->formSpec[F_CLASS_PILL] . '">', $pillDropdown, true);
405
            $htmlDropdown = '<a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button">more <span class="caret"></span></a>' . $htmlDropdown;
406
            $htmlDropdown = Support::wrapTag('<li role="presentation" class="dropdown">', $htmlDropdown, false);
407
        }
408

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

412
        return $htmlDropdown;
413
414
415
416
417
418
419
420
421
422
423
424
    }

    /**
     * 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
425
426
427
428
429
430
431
    /**
     * @return string
     */
    private function getTabId() {
        return 'qfqTabs';
    }

432
433
434
435
436
    /**
     * Builds the complete HTML '<form ...>'-tag
     *
     * @return string
     */
437
    public function getFormTag() {
438
439
440
441

        $attribute = $this->getFormTagAtrributes();

        $attribute['class'] = 'form-horizontal';
442
        $attribute['data-toggle'] = 'validator';
443
444
445
        if (isset($this->formSpec[F_SAVE_BUTTON_ACTIVE])) {
            $attribute[DATA_ENABLE_SAVE_BUTTON] = 'true';
        }
446
447
448
449

        $honeypot = $this->getHoneypotVars();

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

452
453
454
455
    /**
     * @return string
     */
    public function tail() {
456

457
        $html = '';
Carsten  Rose's avatar
Carsten Rose committed
458
        $deleteUrl = '';
459

460
461
        $formId = $this->getFormId();

462
463
        // Button Save at bottom of form - only if there is a button text given.
        if ($this->formSpec[F_SUBMIT_BUTTON_TEXT] !== '') {
464
465
466
467
468


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

469
470
            $buttonText = $this->formSpec[F_SUBMIT_BUTTON_TEXT];

471
            $htmlElement = $this->buildButtonCode('save-button', '', $buttonText, '', '', $this->formSpec[F_BUTTON_ON_CHANGE_CLASS]);
472
473
474
475
476
477
478
479

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

480
        $html .= '</div> <!--class="tab-content" -->';  //  <div class="tab-content">
481
//        $html .= '<input type="submit" value="Submit">';
482

Carsten  Rose's avatar
Carsten Rose committed
483
484
485
        $formId = $this->getFormId();
        $tabId = $this->getTabId();

Carsten  Rose's avatar
Carsten Rose committed
486
        if (0 < ($recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP))) {
487
            $deleteUrl = $this->createDeleteUrl($this->formSpec[F_FINAL_DELETE_FORM], '', $recordId);
Carsten  Rose's avatar
Carsten Rose committed
488
        }
489

490
491
492
        $actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
        $actionDelete = FILE_ACTION . '=' . FILE_ACTION_DELETE;

493
494
495
        $apiDir = API_DIR;
        $apiDeletePhp = API_DIR . '/' . API_DELETE_PHP;

496
        $html .= '</form>';  //  <form class="form-horizontal" ...
497
498
499
500
501
502
        $html .= <<<EOF
        <script type="text/javascript">
            $(function () {
                'use strict';
                QfqNS.Log.level = 0;

Carsten  Rose's avatar
Carsten Rose committed
503
504
505
                var qfqPage = new QfqNS.QfqPage({
                    tabsId: '$tabId',
                    formId: '$formId',
506
                    submitTo: '$apiDir/save.php',
Carsten  Rose's avatar
Carsten Rose committed
507
                    deleteUrl: '$deleteUrl',
508
509
510
                    refreshUrl: '$apiDir/load.php',
                    fileUploadTo: '$apiDir/file.php?$actionUpload',
                    fileDeleteUrl: '$apiDir/file.php?$actionDelete'
Carsten  Rose's avatar
Carsten Rose committed
511
512
                });

513
                var qfqRecordList = new QfqNS.QfqRecordList('$apiDeletePhp');
Carsten  Rose's avatar
Carsten Rose committed
514
            })
515
516
         </script>
EOF;
517
        $html .= '</div>';  //  <div class="container-fluid"> === main <div class=...> around everything
518

519
520
521
522
523
        return $html;
    }

    /**
     * @param array $formElement
524
     * @param $htmlFormElementName
525
526
527
     * @param $value
     * @return mixed
     */
528
    public function buildPill(array $formElement, $htmlFormElementName, $value, array &$json) {
529
        $html = '';
530
531
532
533
        // save parent processed FE's
        $tmpStore = $this->feSpecNative;

        // child FE's
534
535
536
        $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
537
        $html = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), FORM_ELEMENTS_NATIVE_SUBRECORD, 0, $json);
538
539
540
541

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

542
543
544
        return $html;
    }

545
    /**
546
547
     * @param array $formElement Complete FormElement, especially some FE_WRAP
     * @param string $htmlElement Content to wrap.
548
     * @param $htmlFormElementName
549
     * @return string               Wrapped $htmlElement
550
     * @throws \qfq\UserFormException
551
     */
552
    public function buildRowNative(array $formElement, $htmlElement, $htmlFormElementName) {
553
        $html = '';
554
        $htmlLabel = '';
555
556
        $classHideRow = '';
        $classHideElement = '';
557

558
        if ($formElement[FE_MODE] == FE_MODE_HIDDEN) {
559
            if ($formElement[FE_FLAG_ROW_OPEN_TAG] && $formElement[FE_FLAG_ROW_CLOSE_TAG]) {
560
561
562
563
564
565
                $classHideRow = 'hidden';
            } else {
                $classHideElement = 'hidden';
            }
        }

Carsten  Rose's avatar
Carsten Rose committed
566
        // Label
567
        if ($formElement[FE_BS_LABEL_COLUMNS] > 0) {
568
            $htmlLabel = $this->buildLabel($htmlFormElementName, $formElement[FE_LABEL]);
569
        }
570

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

Carsten  Rose's avatar
Carsten Rose committed
574
        // Input
575
        $html .= $this->customWrap($formElement, $htmlElement, FE_WRAP_INPUT, $formElement[FE_BS_INPUT_COLUMNS],
576
577
            [$this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_START], $this->wrap[WRAP_SETUP_INPUT][WRAP_SETUP_END]],
            $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_INPUT, $classHideElement);
578

Carsten  Rose's avatar
Carsten Rose committed
579
        // Note
Carsten  Rose's avatar
Carsten Rose committed
580
        $note = $formElement[FE_NOTE];
581
        $html .= $this->customWrap($formElement, $note, FE_WRAP_NOTE, $formElement[FE_BS_NOTE_COLUMNS],
582
            [$this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START], $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END]], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_NOTE);
583

584
        // Row
585
586
        $openTag = $formElement[FE_FLAG_ROW_OPEN_TAG] ? $this->getRowOpenTag($classHideRow) : '';
        $closeTag = $formElement[FE_FLAG_ROW_CLOSE_TAG] ? $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_END] : '';
587
588

        $html = $this->customWrap($formElement, $html, FE_WRAP_ROW, -1, [$openTag, $closeTag], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_ROW);
589
590
591
592

        return $html;
    }

593
    /**
594
595
596
597
598
599
     * Wrap content with $wrapArray or, if specified use $formElement[$wrapName]. Inject $htmlId in wrap.
     *
     * Result:
     * - if $bsColumns==0 and empty $formElement[$wrapName]: no wrap
     * - if $formElement[$wrapName] is given: wrap with that one. Else: wrap with $wrapArray
     * - if $htmlId is give, inject it in $wrap.
600
601
602
603
     *
     * @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
604
     * @param int $bsColumns
605
     * @param array $wrapArray Systemwide Defaults: [ 'open wrap', 'close wrap' ]
606
607
     * @param string $htmlId
     * @param string $class
608
     * @return string Wrapped $htmlElement
609
     * @throws CodeException
610
611
     * @throws \qfq\UserFormException
     */
612
    private function customWrap(array $formElement, $htmlElement, $wrapName, $bsColumns, array $wrapArray, $htmlId = '', $class = '') {
613

614
        // If $bsColumns==0: do not wrap with default.
615
616
617
618
        if ($bsColumns == 0) {
            $wrapArray[0] = '';
            $wrapArray[1] = '';
        }
619

620
        // If there is a 'per FormElement'-wrap, take it.
621
622
623
624
625
626
627
628
        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);
        }

629
630
        if ($wrapArray[0] != '') {
            $wrapArray[0] = Support::insertAttribute($wrapArray[0], 'id', $htmlId);
631
            $wrapArray[0] = Support::insertAttribute($wrapArray[0], 'class', $class); // might be problematic, if there is already a 'class' defined.
632
633
        }

634
635
636
        return $wrapArray[0] . $htmlElement . $wrapArray[1];
    }

637

638
639
640
641
642
    /**
     * @param $formElement
     * @param $elementHtml
     * @return string
     */
643
    public function buildRowPill(array $formElement, $elementHtml) {
644
645
        $html = '';

646
        $html .= Support::wrapTag('<div class="col-md-12 qfq-form-body ' . $this->formSpec[F_CLASS_BODY] . '">', $elementHtml);
647
648
649

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

650
        $html = Support::wrapTag('<div role="tabpanel" class="tab-pane' . $active . '" id="' . $this->createAnker($formElement['id']) . '">', $html);
651
652
653
654
655
656

        $this->isFirstPill = false;

        return $html;
    }

657
    /**
658
659
     * Builds a fieldset
     *
660
661
662
     * @param $formElement
     * @param $elementHtml
     */
663
    public function buildRowFieldset(array $formElement, $elementHtml) {
664
665
666
        $html = $elementHtml;

        return $html;
667
668
    }

669
670
671
672
673
674
675
676
677
678
679
680
    /**
     * Builds a templateGroup
     *
     * @param $formElement
     * @param $elementHtml
     */
    public function buildRowTemplateGroup(array $formElement, $elementHtml) {
        $html = $elementHtml;

        return $html;
    }

681
682
683
684
685
    /**
     * @param $formElement
     * @param $elementHtml
     * @return string
     */
686
    public function buildRowSubrecord(array $formElement, $elementHtml) {
687
        $html = '';
688
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement[FE_LABEL]));
689
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $elementHtml));
690
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement[FE_NOTE]));
691
692
693

        return $html;
    }
694
}