QuickFormQuery.php 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * Created by PhpStorm.
 * User: ep
 * Date: 12/23/15
 * Time: 6:33 PM
 */

Carsten  Rose's avatar
Carsten Rose committed
9

10
11
namespace qfq;

Carsten  Rose's avatar
Carsten Rose committed
12
use qfq;
13
14
15
16
17
18
19
20
21

//use qfq\Report;

//use qfq\BuildFormPlain;
//use qfq\BuildFormTable;
//use qfq\BuildFormBootstrap;
//use qfq\UserException;
//use qfq\CodeException;
//use qfq\DbException;
22
//use qfq\helper;
23
//use qfq\Store;
Carsten  Rose's avatar
Carsten Rose committed
24

Carsten  Rose's avatar
Carsten Rose committed
25

26
require_once(__DIR__ . '/../qfq/store/Store.php');
Carsten  Rose's avatar
Carsten Rose committed
27
require_once(__DIR__ . '/../qfq/Constants.php');
28
require_once(__DIR__ . '/../qfq/Save.php');
Carsten  Rose's avatar
Carsten Rose committed
29
require_once(__DIR__ . '/../qfq/helper/KeyValueStringParser.php');
30
require_once(__DIR__ . '/../qfq/helper/HelperFormElement.php');
Carsten  Rose's avatar
Carsten Rose committed
31
require_once(__DIR__ . '/../qfq/exceptions/UserException.php');
32
33
require_once(__DIR__ . '/../qfq/exceptions/CodeException.php');
require_once(__DIR__ . '/../qfq/exceptions/DbException.php');
34
require_once(__DIR__ . '/../qfq/exceptions/ErrorHandler.php');
35
require_once(__DIR__ . '/../qfq/Database.php');
Carsten  Rose's avatar
Carsten Rose committed
36
require_once(__DIR__ . '/../qfq/Evaluate.php');
37
38
39
require_once(__DIR__ . '/../qfq/BuildFormPlain.php');
require_once(__DIR__ . '/../qfq/BuildFormTable.php');
require_once(__DIR__ . '/../qfq/BuildFormBootstrap.php');
40
require_once(__DIR__ . '/../qfq/report/Report.php');
41

42
43
44
45
46
47
48
49
50
51
52
53
/*
 * Form will be called
 * a) with a SIP identifier, or
 * b) without a SIP identifier (form setting has to allow this) and will create on the fly a new SIP.
 *
 * The SIP-Store stores:
 *  form=<formname>
 *  r=<record id>  (table.id for a single record form)
 *  keySemId,keySemIduser
 *  <further individual variables>
 */

Carsten  Rose's avatar
Carsten Rose committed
54
/**
55
 * Class Qfq
Carsten  Rose's avatar
Carsten Rose committed
56
57
 * @package qfq
 */
58
class QuickFormQuery {
59
    /**
60
     * @var \qfq\Store instantiated class
61
     */
Carsten  Rose's avatar
Carsten Rose committed
62
    protected $store = null;
63
64
65
    /**
     * @var Database instantiated class
     */
66
    protected $db = null;
67
68
69
70
    /**
     * @var Evaluate instantiated class
     */
    protected $eval = null;
71
72
73
74
75
76
77
    protected $formSpec = array();
    protected $feSpecAction = array();  // Form Definition: copy of the loaded form
    protected $feSpecNative = array(); // FormEelement Definition: all formElement.class='action' of the loaded form
    /**
     * @var array
     */
    private $t3data = array(); // FormEelement Definition: all formElement.class='native' of the loaded form
78

79
80
    private $phpUnit = false;

81
82
83
84
85
86
87
88
89
90
91
    /*
     * TODO:
     *  Preparation: setup logging, database access, record locking
     *  fill stores
     *  Check permission_create / permission_update
     *  Multi: iterate over all records, Single: activate record
     *      Check mode: Load | Save
     *      doActions 'Before'
     *      Do all FormElements
     *      doActions 'After'
     */
92

93
94
95
96
97
    /**
     * Construct the Form Class and Store too. This is the base initialization moment.
     *
     * As a result of instantiating of Form, the class Store will initially called the first time and therefore instantiated automatically.
     * Store might throw an exception, in case the URL-passed SIP is invalid.
98
     *
99
     * @param string $bodytext
100
     */
101
102
    public function __construct(array $t3data = array(), $phpUnit = false) {

103
104
        $this->phpUnit = $phpUnit;

105
        mb_internal_encoding("UTF-8");
106

107
        set_error_handler("\\qfq\\ErrorHandler::exception_error_handler");
108

109
110
111
112
113
114
115
        if (!isset($t3data['bodytext']))
            $t3data['bodytext'] = '';
        if (!isset($t3data['uid']))
            $t3data['uid'] = 0;

        $this->t3data = $t3data;

116
117
118
        $bodytext = $this->t3data['bodytext'];

        $this->store = Store::getInstance($bodytext, $phpUnit);
119
        $this->store->setVar(TYPO3_TT_CONTENT_UID, $t3data['uid'], STORE_TYPO3);
120
121
        $this->db = new Database();
        $this->eval = new Evaluate($this->store, $this->db);
Carsten  Rose's avatar
Carsten Rose committed
122
123
    }

124
    /**
125
126
     * Returns the defined forwardMode and set, if necessary, $forwardPage
     *
127
128
129
130
131
132
133
134
     * @param $forwardPage
     * @return mixed
     */
    public function getForwardMode(&$forwardPage) {
        $forwardPage = $this->formSpec['forwardPage'];
        return $this->formSpec['forwardMode'];
    }

135
    /**
136
137
     * Main entrypoint for display content: form or report
     *
138
     * @return string
Carsten  Rose's avatar
Carsten Rose committed
139
     */
140
    public function process() {
141

142
        $html = $this->doForm();
143
        $html .= $this->doReport();
Carsten  Rose's avatar
Carsten Rose committed
144

145
        return $html;
146
147
    }

148
149
    /**
     * Process form (load or save) if a formname is found.
150
     *
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
     * @return string
     * @throws CodeException
     * @throws UserException
     */
    private function doForm() {
        $html = '';

        // Form action: load or save?
        $mode = isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' ? FORM_SAVE : FORM_LOAD;

        $formName = $this->loadFormSpecification($mode);
        if ($formName === false)
            return '';

        $sipFound = $this->validateForm();
        if (!$sipFound) {
            $this->store->createSipAfterFormLoad($formName);
        }
        $this->store->fillStoreTableDefaultColumnType($this->formSpec['tableName']);

        switch ($mode) {
            case FORM_LOAD:
173
174
175
176
177
178
179
180
181
182
183
184
185
186
                switch ($this->formSpec['render']) {
                    case 'plain':
                        $build = new BuildFormPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative);
                        break;
                    case 'table':
                        $build = new BuildFormTable($this->formSpec, $this->feSpecAction, $this->feSpecNative);
                        break;
                    case 'bootstrap':
                        $build = new BuildFormBootstrap($this->formSpec, $this->feSpecAction, $this->feSpecNative);
                        break;
                    default:
                        throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
                }
                $html = $build->process();
187
188
189
190
191
192
193
194
195
196
197
198
                break;
            case FORM_SAVE:
                $save = new Save($this->formSpec, $this->feSpecAction, $this->feSpecNative);
                $save->process();
                break;
            default:
                throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
        }
        return $html;

    }

199
    /**
200
     * Load form. Evaluates form. Load FormElements.
201
202
203
204
205
     *
     * Loaded Form is in  $this->formSpec
     * Loaded 'action' FormElements are in $this->feSpecAction
     * Loaded 'native' FormElements are in $this->feSpecNative
     *
206
     * @param string $mode FORM_LOAD|FORM_SAVE
207
     * @return string|bool if found the formName, else 'false'.
208
     * @throws DbException
209
     * @throws UserException
210
     */
211
    private function loadFormSpecification($mode) {
Carsten  Rose's avatar
Carsten Rose committed
212

213
        // formName
214
215
216
        if (false === $formName = $this->getFormName()) {
            return false;
        }
217
        $this->store->setVar(SYSTEM_FORM, $formName, STORE_SYSTEM);
Carsten  Rose's avatar
Carsten Rose committed
218

219
220
221
        // Load form
        $form = $this->db->sql("SELECT * FROM Form AS f WHERE f.name LIKE ? AND f.deleted='no'", ROW_EXPECT_1,
            [$formName], 'Form not found or multiple forms with the same name.');
222
223

        $this->formSpec = $this->eval->parseArray($form);
224
        HelperFormElement::explodeParameter($this->formSpec);
225

Carsten  Rose's avatar
Carsten Rose committed
226
        // Clear
227
228
        $this->store->setVar(SYSTEM_FORM_ELEMENT, '', STORE_SYSTEM);

229
        // FE: Action
230
231
        $this->feSpecAction = $this->eval->parseArray($this->db->sql(SQL_FORM_ELEMENT_ALL_CONTAINER, ROW_REGULAR,
            ['no', $this->formSpec["id"], 'action']));
232
        HelperFormElement::explodeParameterInArrayElements($this->feSpecAction);
233
234

        // FE: Native & Container
235
236
237
        // "SELECT *, ? AS 'nestedInFieldSet' FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted = 'no' AND FIND_IN_SET(fe.class, ? ) AND fe.feIdContainer = ? AND fe.enabled='yes' ORDER BY fe.ord, fe.id";
        switch ($mode) {
            case FORM_LOAD:
238
239
                $this->feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_SPECIFIC_CONTAINER, ROW_REGULAR,
                    ['no', $this->formSpec["id"], 'native,container', 0]);
240
241
242
                break;

            case FORM_SAVE:
243
244
                $this->feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_ALL_CONTAINER, ROW_REGULAR,
                    ['no', $this->formSpec["id"], 'native']);
245
246
247
248
249
                break;

            default:
        }

250
        HelperFormElement::explodeParameterInArrayElements($this->feSpecNative);
251
252

        return $formName;
Carsten  Rose's avatar
Carsten Rose committed
253
254
    }

Carsten  Rose's avatar
Carsten Rose committed
255
    /**
256
257
     * Get the formName from STORE_TYPO3 (bodytext), STORE_SIP or by STORE_CLIENT (URL).
     *
258
     * @return string|bool     Formname (Form.name) or FALSE if no formname found.
Carsten  Rose's avatar
Carsten Rose committed
259
     * @throws UserException
260
     * @throws CodeException
Carsten  Rose's avatar
Carsten Rose committed
261
262
     */

Carsten  Rose's avatar
Carsten Rose committed
263
264
    private function getFormName() {

265
        $formName = $this->store->getVar(SIP_FORM, STORE_TYPO3 . STORE_SIP . STORE_CLIENT);
266
        return $formName;
267
    }
Carsten  Rose's avatar
Carsten Rose committed
268

269
    /**
270
271
     * Check if loading of the given form is permit. If not, throw an exception.
     *
272
273
274
275
     * @return bool - 'true' if SIP exists, else 'false'
     * @throws CodeException
     * @throws UserException
     */
Carsten  Rose's avatar
Carsten Rose committed
276
    private function validateForm() {
277
278
279
280
281

        // Retrieve record_id either from SIP (prefered) or via URL
        $r = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_CLIENT);

        // If there is a record_id>0: EDIT else NEW
282
        $permitMode = ($r > 0) ? $this->formSpec['permitEdit'] : $this->formSpec['permitNew'];
283
284
285
286
287

        $feUserLoggedIn = isset($GLOBALS["TSFE"]->fe_user->user["uid"]) && $GLOBALS["TSFE"]->fe_user->user["uid"] > 0;

        $sipFound = $this->store->getVar(SIP_SIP, STORE_SIP) !== false;

288
        switch ($permitMode) {
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
            case  FORM_PERMISSION_SIP:
                if (!$sipFound) {
                    throw new UserException("SIP Parameter needed for this form.", ERROR_SIP_NEEDED_FOR_THIS_FORM);
                }
                break;
            case  FORM_PERMISSION_LOGGED_IN:
                if (!$feUserLoggedIn) {
                    throw new UserException("User not logged in.", ERROR_USER_NOT_LOGGED_IN);
                }
                break;
            case FORM_PERMISSION_LOGGED_OUT:
                if ($feUserLoggedIn) {
                    throw new UserException("User logged in.", ERROR_USER_LOGGED_IN);
                }
                break;
            case FORM_PERMISSION_ALWAYS:
                break;
            case FORM_PERMISSION_NEVER:
                throw new UserException("Loading form forbidden.", ERROR_FORM_FORBIDDEN);
            default:
309
                throw new CodeException("Unknown permission mode: '" . $permitMode . "'", ERROR_FORM_UNKNOWN_PERMISSION_MODE);
310
        }
Carsten  Rose's avatar
Carsten Rose committed
311

Carsten  Rose's avatar
Carsten Rose committed
312
        // Form Definition valid?
313
        if ($this->formSpec['multiMode'] !== 'none' && $this->formSpec['multiSql'] === '') {
Carsten  Rose's avatar
Carsten Rose committed
314
315
316
            throw new UserException("MultiMode selected, but MultiSQL missing", ERROR_MULTI_SQL_MISSING);
        }

317
        return $sipFound;
318
    }
Carsten  Rose's avatar
Carsten Rose committed
319

320
    /**
321
322
     * Process the SQL Queries from bodytext. Return the output.
     *
323
324
     * @return string
     */
325
    private function doReport() {
326
        $report = new Report($this->t3data, $this->store->getVar(SYSTEM_SESSIONNAME, STORE_SYSTEM), $this->phpUnit);
327

328
        $html = $report->process();
329
330
331
332
333

        return $html;

    }

334
    /**
335
336
     * Save the current form.
     *
337
338
339
340
341
342
343
344
345
     * @return string
     */
    public function saveForm() {

        $html = $this->doForm();

        return $html;
    }

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
    /**
     * Delete a record (tablename and recordid are given) or process a 'delete form'
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserException
     */
    public function delete() {

        #TODO: implement 'delete form'

        // simple delete: table and recordId are given
        $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
        $table = $this->store->getVar(SIP_TABLE, STORE_SIP);

        if( $recordId === false || $recordId<1 || $table === false || $table === '') {
            throw new UserException("Invalid or missing parameter: recordId=$recordId, table=$table", ERROR_INVALID_OR_MISSING_PARAMETER);
        }

        $this->db->sql("DELETE FROM $table WHERE id = ? LIMIT 1", ROW_REGULAR, [ $recordId ]);
    }

368
}