QuickFormQuery.php 9.89 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
81
82
83
84
85
86
87
88
89

    /*
     * 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'
     */
90

91
92
93
94
95
    /**
     * 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.
96
     *
97
     * @param string $bodytext
98
     */
99
100
    public function __construct($t3data = array(), $phpUnit = false) {
        $this->t3data = $t3data;
101

102
        set_error_handler("\\qfq\\ErrorHandler::exception_error_handler");
103

104
105
106
        $bodytext = $this->t3data['bodytext'];

        $this->store = Store::getInstance($bodytext, $phpUnit);
107
        $this->store->setVar(TYPO3_TT_CONTENT_UID, (isset($t3data['uid'])) ? $t3data['uid'] : 0, STORE_TYPO3);
108
109
        $this->db = new Database();
        $this->eval = new Evaluate($this->store, $this->db);
Carsten  Rose's avatar
Carsten Rose committed
110
111
    }

112
113
    /**
     * @return string
Carsten  Rose's avatar
Carsten Rose committed
114
     */
115
    public function process() {
116
117
        $html = '';
        $build = null;
118
        $master = null;
119

Carsten  Rose's avatar
Carsten Rose committed
120
121
        mb_internal_encoding("UTF-8");

122

123
124
        $html .= $this->doForm();
        $html .= $this->doReport();
Carsten  Rose's avatar
Carsten Rose committed
125

126
        return $html;
127
128
    }

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
    /**
     * Process form (load or save) if a formname is found.
     * @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 ($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);
        }

        switch ($mode) {
            case FORM_LOAD:
                $html .= $build->process();
                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;

    }

180
    /**
181
182
183
184
185
186
187
     * Loads specification of recent form.
     * Evaluates Form and all FormElements.
     *
     * Loaded Form is in  $this->formSpec
     * Loaded 'action' FormElements are in $this->feSpecAction
     * Loaded 'native' FormElements are in $this->feSpecNative
     *
188
     * @param string $mode FORM_LOAD|FORM_SAVE
189
     * @return string|bool if found the formName, else 'false'.
190
     * @throws DbException
191
     * @throws UserException
192
     */
193
    private function loadFormSpecification($mode) {
Carsten  Rose's avatar
Carsten Rose committed
194

195
196
197
        if (false === $formName = $this->getFormName()) {
            return false;
        }
198
        $this->store->setVar(SYSTEM_FORM, $formName, STORE_SYSTEM);
Carsten  Rose's avatar
Carsten Rose committed
199

200
        $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.');
201
202

        $this->formSpec = $this->eval->parseArray($form);
203
        HelperFormElement::explodeFieldParameter($this->formSpec);
204

Carsten  Rose's avatar
Carsten Rose committed
205
        // Clear
206
207
        $this->store->setVar(SYSTEM_FORM_ELEMENT, '', STORE_SYSTEM);

208
        // FE: Action
209
        $this->feSpecAction = $this->eval->parseArray($this->db->sql(SQL_FORM_ELEMENT_ALL_CONTAINER, ROW_REGULAR, ['no', $this->formSpec["id"], 'action']));
210
        HelperFormElement::explodeFieldParameter($this->feSpecAction);
211
212

        // FE: Native & Container
213
214
215
216
217
218
219
220
221
222
223
224
225
        // "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:
                $this->feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_SPECIFIC_CONTAINER, ROW_REGULAR, ['no', $this->formSpec["id"], 'native,container', 0]);
                break;

            case FORM_SAVE:
                $this->feSpecNative = $this->db->sql(SQL_FORM_ELEMENT_ALL_CONTAINER, ROW_REGULAR, ['no', $this->formSpec["id"], 'native']);
                break;

            default:
        }

226
        HelperFormElement::explodeFieldParameter($this->feSpecNative);
227
228

        return $formName;
Carsten  Rose's avatar
Carsten Rose committed
229
230
    }

Carsten  Rose's avatar
Carsten Rose committed
231
    /**
232
     * @return string|bool     Formname (Form.name) or FALSE if no formname found.
Carsten  Rose's avatar
Carsten Rose committed
233
     * @throws UserException
234
     * @throws CodeException
Carsten  Rose's avatar
Carsten Rose committed
235
236
     */

Carsten  Rose's avatar
Carsten Rose committed
237
238
    private function getFormName() {

239
        $formName = $this->store->getVar(SIP_FORM, STORE_TYPO3 . STORE_SIP . STORE_CLIENT);
240
        return $formName;
241
    }
Carsten  Rose's avatar
Carsten Rose committed
242

243
244
245
246
247
    /**
     * @return bool - 'true' if SIP exists, else 'false'
     * @throws CodeException
     * @throws UserException
     */
Carsten  Rose's avatar
Carsten Rose committed
248
    private function validateForm() {
249
250
251
252
253

        // 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
254
        $mode = ($r > 0) ? $this->formSpec['permitEdit'] : $this->formSpec['permitNew'];
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

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

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

        switch ($mode) {
            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:
                throw new CodeException("Unknown permission mode: '" . $mode . "'", ERROR_FORM_UNKNOWN_PERMISSION_MODE);
        }
Carsten  Rose's avatar
Carsten Rose committed
283

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

289
        return $sipFound;
290
    }
Carsten  Rose's avatar
Carsten Rose committed
291

292
293
294
    /**
     * @return string
     */
295
296
297
    private function doReport() {
        $report = new Report();

298
        $html = $report->process($this->t3data);
299
300
301
302
303

        return $html;

    }

304
}