Form.php 7.33 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
48
49
50
51
52
53
54
55
56
57
58
59
    protected $formDef = array();  // Form Definition: copy of the loaded form
    protected $feDefAction = array(); // FormEelement Definition: all formElement.class='action' of the loaded form
    protected $feDefNative = array(); // FormEelement Definition: all formElement.class='native' of the loaded form

    /*
     * 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
93
            $formName = $this->loadFormDefinition();

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
101
102
103
104
105
106

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

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

138
        return $html;
139
140
    }

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

Carsten  Rose's avatar
Carsten Rose committed
148
        $formName = $this->getFormName();
149
        $this->formDef = $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
150

151
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";
        $this->feDefAction = $this->db->sql($sql, ROW_REGULAR, [$this->formDef["id"], 'action']);
        $this->feDefNative = $this->db->sql($sql, ROW_REGULAR, [$this->formDef["id"], 'native']);
154
155

        return $formName;
Carsten  Rose's avatar
Carsten Rose committed
156
157
    }

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

Carsten  Rose's avatar
Carsten Rose committed
164
165
    private function getFormName() {

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

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

174
175
176
177
178
    /**
     * @return bool - 'true' if SIP exists, else 'false'
     * @throws CodeException
     * @throws UserException
     */
Carsten  Rose's avatar
Carsten Rose committed
179
    private function validateForm() {
180
181
182
183
184
185
186
187
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

        // 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
        $mode = ($r > 0) ? $this->formDef['permitEdit'] : $this->formDef['permitNew'];

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

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

220
        return $sipFound;
221
    }
Carsten  Rose's avatar
Carsten Rose committed
222

223
}