FillStoreForm.php 11.3 KB
Newer Older
Carsten  Rose's avatar
Carsten Rose committed
1
2
3
4
5
6
7
8
9
10
11
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 3/23/16
 * Time: 1:31 PM
 */

namespace qfq;

require_once(__DIR__ . '/Store.php');
12
require_once(__DIR__ . '/../database/Database.php');
Carsten  Rose's avatar
Carsten Rose committed
13
14
require_once(__DIR__ . '/../Constants.php');
require_once(__DIR__ . '/../helper/HelperFormElement.php');
15
require_once(__DIR__ . '/../exceptions/UserFormException.php');
Carsten  Rose's avatar
Carsten Rose committed
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

class FillStoreForm {

    /**
     * @var Store
     */
    private $store = null;

    /**
     * @var Database
     */
    private $db = null;

    /**
     * @var array
     */
    private $feSpecNative = array();

34
35
36
37
38
    /**
     * @var Evaluate
     */
    private $evaluate = null;

Carsten  Rose's avatar
Carsten Rose committed
39
40
41
42
    /**
     *
     */
    public function __construct() {
43

Carsten  Rose's avatar
Carsten Rose committed
44
45
46
        $this->store = Store::getInstance();
        $this->db = new Database();
        $this->feSpecNative = $this->loadFormElementsBasedOnSIP();
47
48
        $this->evaluate = new Evaluate($this->store, $this->db);

Carsten  Rose's avatar
Carsten Rose committed
49
50
    }

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
    /**
     * Loads a minimal definition of FormElement of the form specified in SIP.
     *
     * @return array
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     */
    private function loadFormElementsBasedOnSIP() {
        $formName = $this->store->getVar(SIP_FORM, STORE_SIP);

        // Preparation for Log, Debug
        $this->store->setVar(SYSTEM_FORM, $formName, STORE_SYSTEM);

        $feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_SIMPLE_ALL_CONTAINER, ROW_EXPECT_GE_1, [$formName],
            'Form or FormElements not found: ' . ERROR_FORM_NOT_FOUND);
67
        HelperFormElement::explodeParameterInArrayElements($feSpecNative, FE_PARAMETER);
68
69

        $feSpecTemplateGroup = $this->db->sql(SQL_FORM_ELEMENT_CONTAINER_TEMPLATE_GROUP, ROW_REGULAR, [$formName]);
70
        HelperFormElement::explodeParameterInArrayElements($feSpecTemplateGroup, FE_PARAMETER);
71
72
73
74
75
76
77

        $feSpecNative = $this->expandTemplateGroupFormElement($feSpecTemplateGroup, $feSpecNative);


        return $feSpecNative;
    }

Carsten  Rose's avatar
Carsten Rose committed
78
79
80
81
82
83
84
85
86
87
    /**
     * Checks if there are templateGroups defined. If yes, expand them. Return expanded feSpecNative array.
     *
     * @param array $feSpecTemplateGroup
     * @param array $feSpecNative
     * @return array
     */
    private function expandTemplateGroupFormElement(array $feSpecTemplateGroup, array $feSpecNative) {
        $expanded = array();

88
        if (count($feSpecTemplateGroup) == 0) {
Carsten  Rose's avatar
Carsten Rose committed
89
90
91
92
            return $feSpecNative; // No templateGroups >> nothing to do >> just return
        }

        // Iterate over all 'FormElements': part of a templateGroup?
93
94
        foreach ($feSpecNative as $fe) {
            $flagCopied = false;
Carsten  Rose's avatar
Carsten Rose committed
95

96
            if ($fe[FE_ID_CONTAINER] > 0) {
Carsten  Rose's avatar
Carsten Rose committed
97
98
                // Search for a corresponding template group.
                foreach ($feSpecTemplateGroup as $templateGroup) {
99
                    if ($fe[FE_ID_CONTAINER] == $templateGroup[FE_ID]) {
Carsten  Rose's avatar
Carsten Rose committed
100

101
                        $flagCopied = true;
Carsten  Rose's avatar
Carsten Rose committed
102
103

                        // Get max copies per template group
104
                        $maxCopies = HelperFormElement::tgGetMaxLength($templateGroup[FE_MAX_LENGTH]);
Carsten  Rose's avatar
Carsten Rose committed
105
106
107
108

                        // Copy each native FormElement
                        $template = $fe[FE_NAME];
                        for ($ii = 1; $ii <= $maxCopies; $ii++) {
109
                            $fe[FE_NAME] = str_replace(FE_TEMPLATE_GROUP_NAME_PATTERN, $ii, $template);
Carsten  Rose's avatar
Carsten Rose committed
110
111
112
113
114
115
                            $expanded[] = $fe;
                        }
                    }
                }
            }

116
            if (!$flagCopied) {
Carsten  Rose's avatar
Carsten Rose committed
117
118
119
120
121
122
123
                $expanded[] = $fe;
            }
        }

        return $expanded;
    }

Carsten  Rose's avatar
Carsten Rose committed
124
125
126
127
128
    /**
     * Copies all current form parameter from STORE_CLIENT to STORE_FORM. Checks the values against FormElement
     * definition and throws an exception if check fails. FormElements.type=hidden will be taken from STORE_SIP.
     *
     * @throws CodeException
129
     * @throws UserFormException
Carsten  Rose's avatar
Carsten Rose committed
130
131
     */
    public function process() {
132
        // The following will never be used during load (fe.type='upload').
133
        $skip = [FE_SQL_UPDATE, FE_SQL_INSERT, FE_SQL_DELETE, FE_SQL_AFTER, FE_SQL_BEFORE, FE_PARAMETER];
134

Carsten  Rose's avatar
Carsten Rose committed
135
136
137
138
139
        $html = '';
        $newValues = array();

        $clientValues = $this->store->getStore(STORE_CLIENT);

140
141
142
143
144
        // If called through 'api/...': get STORE_TYPO3 via SIP parameter.
        if (isset($clientValues[CLIENT_TYPO3VARS])) {
            $this->store->fillTypo3StoreFromSip($clientValues[CLIENT_TYPO3VARS]);
        }

Carsten  Rose's avatar
Carsten Rose committed
145
146
147
        // Retrieve SIP vars, e.g. for HIDDEN elements.
        $sipValues = $this->store->getStore(STORE_SIP);

148
        // Copy SIP Values; not necessarily defined as a FormElement.
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
        foreach ($sipValues as $key => $value) {
            switch ($key) {
                case SIP_SIP:
                case SIP_RECORD_ID:
                case SIP_FORM:
                case SIP_TABLE:
                case SIP_URLPARAM:
                case 'id':
                    break;
                default:
                    $newValues[$key] = $value;
                    break;
            }
        }

Carsten  Rose's avatar
#2067    
Carsten Rose committed
164
165
166
167
168
        // Check if there is a 'new record already saved' situation:
        // yes: the names of the input fields are submitted with '<fieldname>:0' instead of '<fieldname>:<id>'
        // no: regular situation, take real 'recordid'
        $fakeRecordId = isset($sipValues[SIP_MAKE_URLPARAM_UNIQ]) ? 0 : $sipValues[SIP_RECORD_ID];

169
        // Iterate over all FormElements. Sanatize values. Built an assoc array $newValues.
Carsten  Rose's avatar
Carsten Rose committed
170
171
172
        foreach ($this->feSpecNative AS $formElement) {

            // Never get a predefined 'id'
173
            if ($formElement[FE_NAME] === COLUMN_ID)
Carsten  Rose's avatar
Carsten Rose committed
174
175
                continue;

176
177
178
            // Preparation for Log, Debug
            $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);

179
            // Evaluate current FormElement: e.g. FE_MODE_SQL
180
            $formElement = $this->evaluate->parseArray($formElement, $skip, $debugStack);
181

182
            // Get related formElement. Construct the field name used in the form.
183
            $clientFieldName = HelperFormElement::buildFormElementName($formElement, $fakeRecordId);
Carsten  Rose's avatar
Carsten Rose committed
184

185
186
            // Some Defaults
            $formElement = Support::setFeDefaults($formElement);
Carsten  Rose's avatar
Carsten Rose committed
187

188
189
            if ($formElement[FE_TYPE] === FE_TYPE_EXTRA) {
                // Extra elements will be transferred by SIP
190
191
                if (!isset($sipValues[$formElement[FE_NAME]])) {
                    throw new CodeException("Missing the " . FE_TYPE_EXTRA . " field '" . $formElement[FE_NAME] . "' in SIP.", ERROR_MISSING_HIDDEN_FIELD_IN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
192
193
                }

194
                $newValues[$formElement[FE_NAME]] = $sipValues[$formElement[FE_NAME]];
Carsten  Rose's avatar
Carsten Rose committed
195
196
197
                continue;
            }

198
            // Checkbox Multi: collect values
199
            if ($formElement[FE_TYPE] === 'checkbox') {
200
201
202
203
                $val = $this->collectMultiValues($clientFieldName, $clientValues);
                if($val !== false) {
                    $clientValues[$clientFieldName] = $val;
                }
204
205
            }

206
            if ($formElement[FE_MODE] === FE_MODE_REQUIRED) {
207
208
209
210
211
                if (!isset($clientValues[$clientFieldName]) || ($clientValues[$clientFieldName] === '')) {
                    throw new UserFormException("Missing required value.", ERROR_REQUIRED_VALUE_EMPTY);
                }
            }

212
213
214
215
216
217
218
219
            // copy value to $newValues
            if (isset($clientValues[$clientFieldName])) {
                if ($formElement[FE_DYNAMIC_UPDATE] === 'yes' || $formElement[FE_MODE] === FE_MODE_REQUIRED || $formElement[FE_MODE] === FE_MODE_SHOW) {
                    switch ($formElement[FE_TYPE]) {
                        case 'date':
                        case 'datetime':
                        case 'time':
                            if ($clientValues[$clientFieldName] !== '') // do not check empty values
220
                                $newValues[$formElement[FE_NAME]] = $this->doDateTime($formElement, $clientValues[$clientFieldName]);
221
222
                            break;
                        default:
223
                            $val = $clientValues[$clientFieldName];
224
                            // Check only if their is something
225
                            if ($val !== '') {
226
                                $val = Sanitize::sanitize($val, $formElement[FE_CHECK_TYPE], $formElement[FE_CHECK_PATTERN], SANITIZE_EXCEPTION);
227
228
229
                                if ($formElement[FE_ENCODE] === FE_ENCODE_SPECIALCHAR) {
                                    $val = htmlspecialchars($val, ENT_QUOTES);
                                }
230
                            }
231
                            $newValues[$formElement[FE_NAME]] = $val;
232
                            break;
233
                    }
234
                }
Carsten  Rose's avatar
Carsten Rose committed
235
236
237
            }
        }

238
        $this->store->setStore($newValues, STORE_FORM, true);
Carsten  Rose's avatar
Carsten Rose committed
239
240
    }

241
    /**
242
     * Steps through all $clientValues (POST vars) and collect all with the name _?_${clientFieldName} in a comma seperated string (MYSQL ENUM type).
243
     * If there is no element '_h_${clientFieldName}', than there are no multi values - return the already given `$clientValues[$clientFieldName]`.
244
     *
245
246
     * @param $clientFieldName
     * @param array $clientValues
247
     * @return string
248
     */
249
    private function collectMultiValues($clientFieldName, array $clientValues) {
250

251
        $checkboxKey = HelperFormElement::prependFormElementNameCheckBoxMulti($clientFieldName, 'h');
252

253
254
255
256
257
258
259
        // For templateGroups: all expanded FormElements will be tried to collect - this fails for not submitted fields.
        // Therefore skip not existing clientvalues.
        if(!isset($clientValues[$checkboxKey])) {
            return false;
        }

        // Check if there is a hidden value with naming in checkbox multi syntax
260
        if (isset($clientValues[$checkboxKey])) {
261
            $checkboxValue = $clientValues[$checkboxKey];
262

263
            $pattern = '/' . HelperFormElement::prependFormElementNameCheckBoxMulti($clientFieldName, '\d+') . '/';
264
265
            foreach ($clientValues as $key => $value) {
                if (1 === preg_match($pattern, $key)) {
266
                    $checkboxValue .= ',' . $value;
267
268
269
                }
            }

270
271
272
273
            if (isset($checkboxValue[0]) && $checkboxValue[0] === ',') {
                $checkboxValue = substr($checkboxValue, 1);
            }

274
275
276
277
278
279
            $clientValues[$clientFieldName] = $checkboxValue;
        }

        return $clientValues[$clientFieldName];
    }

280
281
282
    /**
     * Check  $value as date/datime/time value and convert it to FORMAT_DATE_INTERNATIONAL.
     *
283
     * @param array $formElement - if not set, set $formElement[FE_DATE_FORMAT]
284
285
286
287
288
289
     * @param string $value - date/datetime/time value in format FORMAT_DATE_INTERNATIONAL or FORMAT_DATE_GERMAN
     * @return string - checked datetime string
     * @throws UserFormException
     */
    private function doDateTime(array &$formElement, $value) {

290
        $regexp = Support::dateTimeRegexp($formElement[FE_TYPE], $formElement[FE_DATE_FORMAT]);
291
292
293
294
295
296

        if (1 !== preg_match('/' . $regexp . '/', $value, $matches)) {
            $placeholder = Support::getDateTimePlaceholder($formElement);
            throw new UserFormException("DateTime format not recognized: $placeholder / $value ", ERROR_DATE_TIME_FORMAT_NOT_RECOGNISED);
        }

297
        $showTime = $formElement[FE_TYPE] == 'date' ? '0' : '1';
298
299
300
301
        $value = Support::convertDateTime($value, FORMAT_DATE_INTERNATIONAL, '1', $showTime, $formElement[FE_SHOW_SECONDS]);

        return $value;
    }
Carsten  Rose's avatar
Carsten Rose committed
302
}