BuildFormBootstrap.php 15.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
57
        $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_START] = "<div class='form-group'>";
        $this->wrap[WRAP_SETUP_ELEMENT][WRAP_SETUP_END] = "</div>";
58

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

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

65
66
//        $this->feDivClass['radio'] = 'radio';
//        $this->feDivClass['checkbox'] = 'checkbox';
67
68
    }

69
70
71
72
73
74
    /**
     * @param $label
     * @param $input
     * @param $note
     */
    public function fillWrapLabelInputNote($label, $input, $note) {
75
        $this->wrap[WRAP_SETUP_LABEL][WRAP_SETUP_START] = "<div class='col-md-$label qfq-label'>";
76
77
78
79
80
81
82
83
        $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>";
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_START] = "<div class='col-md-$note'>";
        $this->wrap[WRAP_SETUP_NOTE][WRAP_SETUP_END] = "</div>";

    }

Carsten  Rose's avatar
Carsten Rose committed
84
85
86
    /**
     * @return string
     */
87
88
89
90
    public function getProcessFilter() {
        return FORM_ELEMENTS_NATIVE_SUBRECORD;
    }

Carsten  Rose's avatar
Carsten Rose committed
91
92
93
    /**
     * @return string
     */
94
95
96
97
98
99
100
101
    public function doSubrecords() {
        return '';
    }

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

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

106
107
108
        $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);
109
110


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

114
115
        $html .= $this->getFormTag();

116
117
118
        //TODO: Problem fuer Forms ohne Pils
        $html .= '<div class="tab-content ' . $this->formSpec[F_CLASS_BODY] . '">';
//        $html .= '<div class="col-md-12 ' . $this->formSpec[F_CLASS_BODY] . '">';
119
120
121
122

        return $html;
    }

123
    /**
124
     * Build Buttons panel on top right corner of form.
125
     * Simulate Submit Button: http://www.javascript-coder.com/javascript-form/javascript-form-submit.phtml
126
127
128
129
130
     *
     * @return string
     */
    private function buildButtons() {
        $buttonNew = '';
131
132
133
134
        $buttonDelete = '';
        $buttonClose = '';
        $buttonSave = '';
        $buttonEditForm = '';
Carsten  Rose's avatar
Carsten Rose committed
135
136
137
138
139
        $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);


        // Button: FormEdit
        if ($this->showDebugInfo) {
140
141
142
            $toolTip = "Edit form" . PHP_EOL . PHP_EOL . OnArray::toString($this->store->getStore(STORE_SIP), ' = ', PHP_EOL, "'");
            $url = $this->createFormEditUrl();

143
            $buttonEditForm = $this->buildButtonAnchor('form-edit-button', $url, $toolTip, GLYPH_ICON_TOOL);
144
145
146
        }

        // Button: Save
147
        if (Support::findInSet(FORM_BUTTON_SAVE, $this->formSpec['showButton']) && $this->formSpec[F_SUBMIT_BUTTON_TEXT] === '') {
148
149
150
            $toolTip = 'Save';

            if ($this->showDebugInfo) {
151
                $toolTip .= PHP_EOL . "table = '" . $this->formSpec[F_TABLE_NAME] . "'" . PHP_EOL . "r = '" . $recordId . "'";
152
153
            }

154
            $buttonSave = $this->buildButtonCode('save-button', $toolTip, GLYPH_ICON_CHECK);
155
156
157
158
159
160
        }

        // Button: Close
        if (Support::findInSet(FORM_BUTTON_CLOSE, $this->formSpec['showButton'])) {
            $toolTip = 'Close';

161
            $buttonClose = $this->buildButtonCode('close-button', 'Close', GLYPH_ICON_CLOSE);
Carsten  Rose's avatar
Carsten Rose committed
162
        }
163

Carsten  Rose's avatar
Carsten Rose committed
164
        // Button: Delete
165
        if (Support::findInSet(FORM_BUTTON_DELETE, $this->formSpec['showButton'])) {
166
167
            $toolTip = 'Delete';

Carsten  Rose's avatar
Carsten Rose committed
168
            if ($this->showDebugInfo && $recordId > 0) {
169
                $toolTip .= PHP_EOL . "form = '" . $this->formSpec[F_FINAL_DELETE_FORM] . "'" . PHP_EOL . "r = '" . $recordId . "'";
Carsten  Rose's avatar
Carsten Rose committed
170
            }
171
            $disabled = ($recordId > 0) ? '' : 'disabled';
Carsten  Rose's avatar
Carsten Rose committed
172

173
            $buttonDelete = $this->buildButtonCode('delete-button', $toolTip, GLYPH_ICON_DELETE, $disabled);
174
175
        }

Carsten  Rose's avatar
Carsten Rose committed
176
        // Button: New
177
        if (Support::findInSet(FORM_BUTTON_NEW, $this->formSpec['showButton'])) {
178
179
            $toolTip = 'New';
            $url = $this->deriveNewRecordUrlFromExistingSip($toolTip);
Carsten  Rose's avatar
Carsten Rose committed
180

181
            $buttonNew = $this->buildButtonAnchor('form-new-button', $url, $toolTip, GLYPH_ICON_NEW);
182
183
        }

184
        // Arrangement: Edit Form / Save / Close / Delete / New
Carsten  Rose's avatar
Carsten Rose committed
185
        // Specified in reverse order cause 'pull-right' inverts the order. http://getbootstrap.com/css/#helper-classes-floats
186
187
188
189
190
        $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);
191
192
193

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

194
195
196
        return $html;
    }

197
198
199
200
201
202
203
204
205
    /**
     * @param $id
     * @param $url
     * @param $title
     * @param $icon
     * @param string $disabled
     * @return string
     */
    private function buildButtonAnchor($id, $url, $title, $icon, $disabled = '') {
206
        return "<a " . Support::doAttribute('href', $url) . " id='$id' class='btn btn-default navbar-btn $disabled' " . Support::doAttribute('title', $title) . "><span class='glyphicon $icon'></span></a>";
207
208
209
    }

    /**
210
211
     * Creates a button with the given attributes. If there is no $icon given, render the button without glyph.
     *
212
213
214
215
216
217
218
     * @param $id
     * @param $title
     * @param $icon
     * @param string $disabled
     * @return string
     */
    private function buildButtonCode($id, $title, $icon, $disabled = '') {
219
220
221
222
223
224
225
226
227
228

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

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

        return "<button id='$id' type='button' class='btn btn-default $classAdd $disabled' " . Support::doAttribute('title', $title) . ">$element</button>";
229
230
    }

Carsten  Rose's avatar
Carsten Rose committed
231
    /**
232
     * @param $pillArray
Carsten  Rose's avatar
Carsten Rose committed
233
     * @return string
234
     * @throws UserFormException
Carsten  Rose's avatar
Carsten Rose committed
235
     */
236
237
238
    private function buildPillNavigation($pillArray) {
        $pillButton = '';
        $pillDropdown = '';
239
        $htmlDropdown = '';
240
241
242
243
244
245
246
247
248
249
250
251

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

252
            if ($formElement[FE_NAME] === '' || $formElement[FE_LABEL] === '') {
253
                $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);
254
                $this->store->setVar(SYSTEM_FORM_ELEMENT_COLUMN, 'name, label', STORE_SYSTEM);
255
                throw new UserFormException("Field 'name' and/or 'label' are empty", ERROR_NAME_LABEL_EMPTY);
256
257
            }

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

261
262
263
264
265
266
267
            if ($ii <= $maxVisiblePill) {
                $pillButton .= '<li role="presentation" ' . $active . '>' . $a . '</li>';
            } else {
                $pillDropdown .= '<li>' . $a . '</li>';
            }
            $active = '';
        }
268

269
270
        // Pill Dropdown necessary?
        if ($ii > $maxVisiblePill) {
271
            $htmlDropdown = Support::wrapTag('<ul class="dropdown-menu qfq-form-pill ' . $this->formSpec[F_CLASS_PILL] . '">', $pillDropdown, true);
272
            $htmlDropdown = '<a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button">more <span class="caret"></span></a>' . $htmlDropdown;
273
            $htmlDropdown = Support::wrapTag('<li role="presentation" class="dropdown">', $htmlDropdown, false);
274
        }
275

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

279
        return $htmlDropdown;
280
281
282
283
284
285
286
287
288
289
290
291
    }

    /**
     * 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
292
293
294
295
296
297
298
    /**
     * @return string
     */
    private function getTabId() {
        return 'qfqTabs';
    }

299
300
301
302
303
    /**
     * Builds the complete HTML '<form ...>'-tag
     *
     * @return string
     */
304
    public function getFormTag() {
305
306
307
308

        $attribute = $this->getFormTagAtrributes();

        $attribute['class'] = 'form-horizontal';
309
        $attribute['data-toggle'] = 'validator';
310
311
312
313

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

314
315
316
317
318
    /**
     * @return string
     */
    public function tail() {
        $html = '';
319
//        $html .= $this->buildNewSip();
Carsten  Rose's avatar
Carsten Rose committed
320
        $deleteUrl = '';
321

322
323
        $formId = $this->getFormId();

324
325
326
327
328
329
330
331
332
333
334
335
336
        // Button Save at bottom of form - only if there is a button text given.
        if ($this->formSpec[F_SUBMIT_BUTTON_TEXT] !== '') {
            $buttonText = $this->formSpec[F_SUBMIT_BUTTON_TEXT];

            $htmlElement = $this->buildButtonCode('save-button', $buttonText, '');

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

337
        $html .= '</div> <!--class="tab-content" -->';  //  <div class="tab-content">
338
//        $html .= '<input type="submit" value="Submit">';
339

Carsten  Rose's avatar
Carsten Rose committed
340
341
342
        $formId = $this->getFormId();
        $tabId = $this->getTabId();

Carsten  Rose's avatar
Carsten Rose committed
343
        if (0 < ($recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP))) {
344
            $deleteUrl = $this->createDeleteUrl($this->formSpec[F_FINAL_DELETE_FORM], '', $recordId);
Carsten  Rose's avatar
Carsten Rose committed
345
        }
346

347
348
349
        $actionUpload = FILE_ACTION . '=' . FILE_ACTION_UPLOAD;
        $actionDelete = FILE_ACTION . '=' . FILE_ACTION_DELETE;

350
351
352
        $apiDir = API_DIR;
        $apiDeletePhp = API_DIR . '/' . API_DELETE_PHP;

353
        $html .= '</form>';  //  <form class="form-horizontal" ...
354
355
356
357
358
359
        $html .= <<<EOF
        <script type="text/javascript">
            $(function () {
                'use strict';
                QfqNS.Log.level = 0;

Carsten  Rose's avatar
Carsten Rose committed
360
361
362
                var qfqPage = new QfqNS.QfqPage({
                    tabsId: '$tabId',
                    formId: '$formId',
363
                    submitTo: '$apiDir/save.php',
Carsten  Rose's avatar
Carsten Rose committed
364
                    deleteUrl: '$deleteUrl',
365
366
367
                    refreshUrl: '$apiDir/load.php',
                    fileUploadTo: '$apiDir/file.php?$actionUpload',
                    fileDeleteUrl: '$apiDir/file.php?$actionDelete'
Carsten  Rose's avatar
Carsten Rose committed
368
369
                });

370
                var qfqRecordList = new QfqNS.QfqRecordList('$apiDeletePhp');
Carsten  Rose's avatar
Carsten Rose committed
371
            })
372
373
         </script>
EOF;
374
        $html .= '</div>';  //  <div class="container-fluid"> === main <div class=...> around everything
375

376
377
378
379
380
381
382
383
384
        return $html;
    }

    /**
     * @param array $formElement
     * @param $htmlFormElementId
     * @param $value
     * @return mixed
     */
385
    public function buildPill(array $formElement, $htmlFormElementId, $value, array &$json) {
386
        $html = '';
387
388
389
390
        // save parent processed FE's
        $tmpStore = $this->feSpecNative;

        // child FE's
391
392
393
        $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
394
        $html = $this->elements($this->store->getVar(SIP_RECORD_ID, STORE_SIP), FORM_ELEMENTS_NATIVE_SUBRECORD, 0, $json);
395
396
397
398

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

399
400
401
        return $html;
    }

402
    /**
403
404
     * @param array $formElement
     * @param string $htmlElement
405
406
     * @return string
     */
407
    public function buildRowNative(array $formElement, $htmlElement, $htmlFormElementId) {
408
409
        $html = '';

410
        $htmlLabel = $this->buildLabel($htmlFormElementId, $formElement[FE_LABEL]);
411
412
413

        $html .= $this->wrapItem(WRAP_SETUP_LABEL, $htmlLabel);
        $html .= $this->wrapItem(WRAP_SETUP_INPUT, $htmlElement);
414
        $html .= $this->wrapItem(WRAP_SETUP_NOTE, $formElement[FE_NOTE], true);
415
416
417
418
419
420

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

        return $html;
    }

421
422
423
424
425
    /**
     * @param $formElement
     * @param $elementHtml
     * @return string
     */
426
    public function buildRowPill(array $formElement, $elementHtml) {
427
428
        $html = '';

429
        $html .= Support::wrapTag('<div class="col-md-12 qfq-form-body ' . $this->formSpec[F_CLASS_BODY] . '">', $elementHtml);
430
431
432

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

433
        $html = Support::wrapTag('<div role="tabpanel" class="tab-pane' . $active . '" id="' . $this->createAnker($formElement['id']) . '">', $html);
434
435
436
437
438
439

        $this->isFirstPill = false;

        return $html;
    }

440
    /**
441
442
     * Builds a fieldset
     *
443
444
445
     * @param $formElement
     * @param $elementHtml
     */
446
    public function buildRowFieldset(array $formElement, $elementHtml) {
447
448
449
        $html = $elementHtml;

        return $html;
450
451
    }

452
453
454
455
456
    /**
     * @param $formElement
     * @param $elementHtml
     * @return string
     */
457
    public function buildRowSubrecord(array $formElement, $elementHtml) {
458
        $html = '';
459
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement[FE_LABEL]));
460
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $elementHtml));
461
        $html .= $this->wrapItem(WRAP_SETUP_ELEMENT, $this->wrapItem(WRAP_SETUP_SUBRECORD, $formElement[FE_NOTE]));
462
463
464

        return $html;
    }
465
}