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

        $honeypot = $this->getHoneypotVars();
448
        $md5 = $this->buildInputRecordHashMd5();
449

Carsten  Rose's avatar
Carsten Rose committed
450
        return '<form ' . OnArray::toString($attribute, '=', ' ', "'") . '>' . $honeypot . $md5;
451
452
    }

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

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

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

463
464
        // Button Save at bottom of form - only if there is a button text given.
        if ($this->formSpec[F_SUBMIT_BUTTON_TEXT] !== '') {
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, $buttonText, '', '', $this->formSpec[F_BUTTON_ON_CHANGE_CLASS], 'btn btn-default');
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
        $dirtyAction = ($this->formSpec[F_DIRTY_MODE] == DIRTY_MODE_NONE) ? '' : "dirtyUrl: '$apiDir/dirty.php',";
Carsten  Rose's avatar
Carsten Rose committed
497

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

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

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

522
523
524
525
526
        return $html;
    }

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

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

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

545
546
547
        return $html;
    }

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

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

Carsten  Rose's avatar
Carsten Rose committed
569
        // Label
570
        if ($formElement[FE_BS_LABEL_COLUMNS] > 0) {
571
572
            $addClass = ($formElement[FE_MODE] == FE_MODE_REQUIRED) ? CSS_REQUIRED : '';
            $htmlLabel = $this->buildLabel($htmlFormElementName, $formElement[FE_LABEL], $addClass);
573
        }
574

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

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

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

588
        // Row
589
590
        $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] : '';
591
592

        $html = $this->customWrap($formElement, $html, FE_WRAP_ROW, -1, [$openTag, $closeTag], $formElement[FE_HTML_ID] . HTML_ID_EXTENSION_ROW);
593
594
595
596

        return $html;
    }

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

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

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

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

638
639
640
        return $wrapArray[0] . $htmlElement . $wrapArray[1];
    }

641

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

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

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

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

        $this->isFirstPill = false;

        return $html;
    }

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

        return $html;
671
672
    }

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

        return $html;
    }

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

        return $html;
    }
698
}