BuildFormBootstrap.php 26 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

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

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

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

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

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

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

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

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

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

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

Carsten  Rose's avatar
Carsten Rose committed
502
503
504
                var qfqPage = new QfqNS.QfqPage({
                    tabsId: '$tabId',
                    formId: '$formId',
505
                    submitTo: '$apiDir/save.php',
Carsten  Rose's avatar
Carsten Rose committed
506
                    dirtyUrl: '$apiDir/dirty.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
569
            $addClass = ($formElement[FE_MODE] == FE_MODE_REQUIRED) ? CSS_REQUIRED : '';
            $htmlLabel = $this->buildLabel($htmlFormElementName, $formElement[FE_LABEL], $addClass);
570
        }
571

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

Carsten  Rose's avatar
Carsten Rose committed
575
        // Input
576
        $html .= $this->customWrap($formElement, $htmlElement, FE_WRAP_INPUT, $formElement[FE_BS_INPUT_COLUMNS],
577
578
            [$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);
579

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

585
        // Row
586
587
        $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] : '';
588
589

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

        return $html;
    }

594
    /**
595
596
597
598
599
600
     * 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.
601
602
603
604
     *
     * @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
605
     * @param int $bsColumns
606
     * @param array $wrapArray Systemwide Defaults: [ 'open wrap', 'close wrap' ]
607
608
     * @param string $htmlId
     * @param string $class
609
     * @return string Wrapped $htmlElement
610
     * @throws CodeException
611
612
     * @throws \qfq\UserFormException
     */
613
    private function customWrap(array $formElement, $htmlElement, $wrapName, $bsColumns, array $wrapArray, $htmlId = '', $class = '') {
614

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

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

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

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

638

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

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

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

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

        $this->isFirstPill = false;

        return $html;
    }

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

        return $html;
668
669
    }

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

        return $html;
    }

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

        return $html;
    }
695
}