Form.php 7.44 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;
Carsten  Rose's avatar
Carsten Rose committed
13
use qfq\exceptions\UserException;
14
15
use qfq\exceptions\CodeException;
use qfq\exceptions\DbException;
16
use qfq\helper;
17
use qfq\store;
Carsten  Rose's avatar
Carsten Rose committed
18

19
require_once(__DIR__ . '/../qfq/store/Store.php');
Carsten  Rose's avatar
Carsten Rose committed
20
require_once(__DIR__ . '/../qfq/Constants.php');
Carsten  Rose's avatar
Carsten Rose committed
21
require_once(__DIR__ . '/../qfq/helper/KeyValueStringParser.php');
Carsten  Rose's avatar
Carsten Rose committed
22
require_once(__DIR__ . '/../qfq/exceptions/UserException.php');
23
24
require_once(__DIR__ . '/../qfq/exceptions/CodeException.php');
require_once(__DIR__ . '/../qfq/exceptions/DbException.php');
25
require_once(__DIR__ . '/../qfq/Database.php');
Carsten  Rose's avatar
Carsten Rose committed
26
require_once(__DIR__ . '/../qfq/Evaluate.php');
27
require_once(__DIR__ . '/../qfq/FormBuildPlain.php');
28

29
30
31
32
33
34
35
36
37
38
39
40
/*
 * 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>
 */

41
class Form {
Carsten  Rose's avatar
Carsten Rose committed
42

Carsten  Rose's avatar
Carsten Rose committed
43
    protected $store = null;
44
    protected $db = null;
45
46
47
    protected $formSpec = array();  // Form Definition: copy of the loaded form
    protected $feSpecAction = array(); // FormEelement Definition: all formElement.class='action' of the loaded form
    protected $feSpecNative = array(); // FormEelement Definition: all formElement.class='native' of the loaded form
48
49
50
51
52
53
54
55
56
57
58
59

    /*
     * 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'
     */
60
61


62
63
64
65
66
67
//    protected $formElements = null;
//    protected $userLog = null;

    /*
     *
     */
68
    public function __construct($bodytext = '') {
69

70
        $this->store = \qfq\store\Store::getInstance($bodytext);
71
        $this->db = new Database();
Carsten  Rose's avatar
Carsten Rose committed
72
73
    }

74
75
    /**
     * @return string
Carsten  Rose's avatar
Carsten Rose committed
76
     */
77
    public function process() {
78
79
        $html = '';
        $build = null;
80
        $master = null;
81

Carsten  Rose's avatar
Carsten Rose committed
82
83
        mb_internal_encoding("UTF-8");

84
85
86
87
88
89
90
91
//        render:
//        plain
//        multimode: none

        try {
            // Form action: load or save?
            $mode = ($this->store->getVar(CLIENT_POST_SIP, STORE_CLIENT) === false) ? FORM_LOAD : FORM_SAVE;

92
            $formName = $this->loadFormSpecification();
93

Carsten  Rose's avatar
Carsten Rose committed
94
            $sipFound = $this->validateForm();
95
            if (!$sipFound) {
Carsten  Rose's avatar
Carsten Rose committed
96
                $this->store->createSipAfterFormLoad($formName);
97
            }
98

99
100
            $this->store->fillStoreTableDefinition($this->formSpec['tableName']);

101
102
            // TODO: replace switch() by Marschaller
            // render: FormPlain | FormBootstrap
103
            switch ($this->formSpec['render']) {
104
                case 'Plain':
105
                    $build = new FormBuildPlain($this->formSpec, $this->feSpecAction, $this->feSpecNative);
106
107
108
                    break;
                case 'Bootstrap':
                    //TODO: implement bootstrap rendering: FormBuildBootstrap
109
//                    $build = new FormBuildBootstrap($this->formDef, $this->feDefAction, $this->feDefNative);
110
111
                    break;
                default:
112
                    throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
113
114
115
116
117
118
119
120
121
122
123
124
125
            }

            switch ($mode) {
                case FORM_LOAD:
//                    $this->formActionBefore();
                    $html .= $build->head();
                    $html .= $build->elements();
                    $html .= $build->tail();
//                    $this->formActionAfter();
                    break;
                case FORM_SAVE:
                    break;
                default:
126
                    throw new CodeException("This statement should never be reached", ERROR_CODE_SHOULD_NOT_HAPPEN);
127
128
129
130
131
132
133
134
135
136
137
138
            }
            return $html;

        } catch (UserException $e) {
            echo $e->formatMessage();
        } catch (CodeException $e) {
            echo $e->formatMessage();
        } catch (DbException $e) {
            echo $e->formatMessage();
        } catch (\Exception $e) {
            echo "Generic Exception: " . $e->getMessage();
        }
Carsten  Rose's avatar
Carsten Rose committed
139

140
        return $html;
141
142
    }

143
    /**
144
     * @return string formName
145
     * @throws DbException
146
     * @throws UserException
147
     */
148
    private function loadFormSpecification() {
Carsten  Rose's avatar
Carsten Rose committed
149

Carsten  Rose's avatar
Carsten Rose committed
150
        $formName = $this->getFormName();
151
        $this->formSpec = $this->db->sql("SELECT * FROM Form AS f WHERE f.name LIKE ? AND f.deleted='no'", ROW_EXACT_1, [$formName]);
Carsten  Rose's avatar
Carsten Rose committed
152

153
        $sql = "SELECT * FROM FormElement AS fe WHERE fe.formId = ? AND fe.deleted='no' AND fe.class = ? AND fe.enabled='yes' ORDER BY fe.order, fe.id";
154
155
        $this->feSpecAction = $this->db->sql($sql, ROW_REGULAR, [$this->formSpec["id"], 'action']);
        $this->feSpecNative = $this->db->sql($sql, ROW_REGULAR, [$this->formSpec["id"], 'native']);
156
157

        return $formName;
Carsten  Rose's avatar
Carsten Rose committed
158
159
    }

Carsten  Rose's avatar
Carsten Rose committed
160
161
162
163
    /**
     * @return string
     * @throws UserException
     * @throws exceptions\CodeException
Carsten  Rose's avatar
Carsten Rose committed
164
165
     */

Carsten  Rose's avatar
Carsten Rose committed
166
167
    private function getFormName() {

Carsten  Rose's avatar
Carsten Rose committed
168
        $formName = $this->store->getVar(TYPO3_FORM, STORE_TYPO3 . STORE_SIP . STORE_CLIENT);
Carsten  Rose's avatar
Carsten Rose committed
169
170
171
        if ($formName !== false)
            return $formName;

172
        // TODO: phpunit tests all - missing formname always fires this exception
Carsten  Rose's avatar
Carsten Rose committed
173
        throw new UserException("Missing form name. Not found in T3_BODYTEXT / SIP / GET", ERROR_MISSING_FORM_NAME);
174
    }
Carsten  Rose's avatar
Carsten Rose committed
175

176
177
178
179
180
    /**
     * @return bool - 'true' if SIP exists, else 'false'
     * @throws CodeException
     * @throws UserException
     */
Carsten  Rose's avatar
Carsten Rose committed
181
    private function validateForm() {
182
183
184
185
186

        // 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
187
        $mode = ($r > 0) ? $this->formSpec['permitEdit'] : $this->formSpec['permitNew'];
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

        $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
216

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

222
        return $sipFound;
223
    }
Carsten  Rose's avatar
Carsten Rose committed
224

225
}