FormAction.php 10.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 5/29/16
 * Time: 5:24 PM
 */

namespace qfq;

require_once(__DIR__ . '/../Constants.php');
require_once(__DIR__ . '/../Database.php');
require_once(__DIR__ . '/../store/Store.php');
require_once(__DIR__ . '/../Evaluate.php');
15
require_once(__DIR__ . '/../report/Sendmail.php');
16
17
18
19
20

/**
 * Class formAction
 * @package qfq
 */
21
class FormAction {
22
23

//    private $feSpecNative = array(); // copy of all formElement.class='native' of the loaded form
24
25
26
27
28
    /**
     * @var Evaluate instantiated class
     */
    protected $evaluate = null;  // copy of the loaded form
    private $formSpec = array();
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    private $primaryTableName = '';
    /**
     * @var Database
     */
    private $db = null;
    /**
     * @var Store
     */
    private $store = null;

    /**
     * @param array $formSpec
     * @param Database $db
     * @param bool|false $phpUnit
     */
    public function __construct(array $formSpec, Database $db, $phpUnit = false) {
        $this->formSpec = $formSpec;
46
        $this->primaryTableName = Support::setIfNotSet($formSpec, F_TABLE_NAME);
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

        $this->db = $db;

        $this->store = Store::getInstance('', $phpUnit);

        $this->evaluate = new Evaluate($this->store, $this->db);

    }

    /**
     * @param integer $recordId
     * @param array $feSpecAction
     * @param string $feTypeList
     *         On FormLoad: FE_TYPE_BEFORE_LOAD, FE_TYPE_AFTER_LOAD
     *         Before Save: FE_TYPE_BEFORE_SAVE, FE_TYPE_BEFORE_INSERT, FE_TYPE_BEFORE_UPDATE, FE_TYPE_BEFORE_DELETE
     *         After Save: FE_TYPE_AFTER_SAVE, FE_TYPE_AFTER_INSERT, FE_TYPE_AFTER_UPDATE, FE_TYPE_AFTER_DELETE
63
     * @return bool: true if there are potential changes on the DB like fired SQL statements, else false.
64
65
66
67
68
69
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     */
    public function elements($recordId, array $feSpecAction, $feTypeList) {

70
71
        $flagModified = false;

72
73
74
        // Iterate over all Action FormElements
        foreach ($feSpecAction as $fe) {

75
            $fe = HelperFormElement::initActionFormElement($fe);
76

77
            // Only process FE elements of types listed in $feTypeList. Skip all other
78
79
80
81
            if (false === Support::findInSet($fe[FE_TYPE], $feTypeList)) {
                continue;
            }

82
83
84
            // Preparation for Log, Debug
            $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM);

85
86
87
88
89
90
91
92
            switch ($fe[FE_TYPE]) {
                case FE_TYPE_BEFORE_LOAD:
                case FE_TYPE_AFTER_LOAD:
                case FE_TYPE_AFTER_DELETE:  # Main record is already deleted. Do not try to load it again.
                    break;
                default:
                    // Always work on recent data: previous actions might have modified the data.
                    $this->fillStoreRecord($this->primaryTableName, $recordId);
93
94
            }

95
96
97
98
            if (!$this->checkRequiredList($fe)) {
                continue;
            }

99
100
101
102
103
104
105
106
107
108
109
110
111
            if (isset($fe[FE_FILL_STORE_LDAP])) {
                $keyNames = [F_LDAP_SERVER, F_LDAP_BASE_DN, F_LDAP_ATTRIBUTES, F_LDAP_SEARCH, F_LDAP_TIME_LIMIT];
                $fe = OnArray::copyArrayItemsIfNotAlreadyExist($this->formSpec, $fe, $keyNames);

                // Extract necessary elements
                $config = OnArray::getArrayItems($fe, [FE_LDAP_SERVER, FE_LDAP_BASE_DN, FE_LDAP_SEARCH, FE_LDAP_ATTRIBUTES]);
                $config = $this->evaluate->parseArray($config);

                $ldap = new Ldap();
                $arr = $ldap->process($config, '', MODE_LDAP_SINGLE);
                $this->store->setStore($arr, STORE_LDAP, true);
            }

112
113
114
115
116
            if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) {
                $this->sendMail($fe);
                //no further processing of current element necessary.
                continue;
            }
117
118
119
120

            $this->validate($fe);

            $this->doSlave($fe, $recordId);
121
122

            $flagModified = true;
123
        }
124
125

        return $flagModified;
126
127
128
    }

    /**
129
130
     * Copy the current primary record to STORE_RECORD
     *
131
132
     * @param $table
     * @param $recordId
133
     * @throws CodeException
134
     * @throws DbException
135
136
     * @throws UserFormException
     */
137
    private function fillStoreRecord($table, $recordId) {
138

139
140
        if (!is_string($table) || $table === '') {
            throw new UserFormException("");
141
        }
142

143
144
        if ($recordId !== false && $recordId > 0) {
            $record = $this->db->sql("SELECT * FROM $table WHERE id = ?", ROW_EXPECT_1, [$recordId]);
145
            $this->store->setStore($record, STORE_RECORD, true);
146
        }
147
    }
148

149
150
151
152
153
154
155
156
157
    /**
     * Process all FormElements given in the `requiredList` identified be their name.
     * If none is empty in STORE_FORM return true, else false.
     * If none FormElement is specified, return true.
     *
     * @param array $fe
     * @return bool  true if none FE is specified or all specified are non empty.
     */
    private function checkRequiredList(array $fe) {
158

159
160
        if (!isset($fe[FE_REQUIRED_LIST]) || $fe[FE_REQUIRED_LIST] === '') {
            return true;
161
162
        }

163
164
165
166
167
168
169
170
171
        $arr = explode(',', $fe[FE_REQUIRED_LIST]);
        foreach ($arr as $key) {

            $key = trim($key);
            $val = $this->store->getVar($key, STORE_FORM);

            if ($val === false || $val === '' || $val === '0') {
                return false;
            }
172
173
        }

174
        return true;
175
176
    }

177
178
179
180
181
182
183
184
185
186
    /**
     * @param array $feSpecAction
     */
    private function sendMail(array $feSpecAction) {

        $mail[SENDMAIL_IDX_RECEIVER] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_TO]);
        $mail[SENDMAIL_IDX_SENDER] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FROM]);
        $mail[SENDMAIL_IDX_SUBJECT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_SUBJECT]);
        $mail[SENDMAIL_IDX_BODY] = $this->evaluate->parse($feSpecAction[FE_VALUE]);
        $mail[SENDMAIL_IDX_REPLY_TO] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_REPLY_TO]);
187
        $mail[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FLAG_AUTO_SUBMIT]) === 'off' ? 'off' : 'on';
188
189
190
191
192
193
194
195
196
197
        $mail[SENDMAIL_IDX_GR_ID] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_GR_ID]);
        $mail[SENDMAIL_IDX_X_ID] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_X_ID]);
        $mail[SENDMAIL_IDX_RECEIVER_CC] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_CC]);
        $mail[SENDMAIL_IDX_RECEIVER_BCC] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_BCC]);
        $mail[SENDMAIL_IDX_SRC] = "FormId: " . $feSpecAction['formId'] . ", FormElementId: " . $feSpecAction['id'];

        // Mail: send
        new Sendmail($mail);
    }

198
199
200
201
    /**
     * If there is a query defined in fe.parameter.FE_SQL_VALIDATE: fire them.
     * Count the selected records and compare them with fe.parameter.FE_EXPECT_RECORDS.
     * If match: everything is fine, do nothing.
202
     * Else throw UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL
203
204
205
206
207
208
209
     *
     * @param array $fe
     * @throws UserFormException
     */
    private function validate(array $fe) {

        // Is there something to check?
210
        if ($fe[FE_SQL_VALIDATE] === '') {
211
212
213
            return;
        }

214
        $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]);
215

216
        if ($fe[FE_MESSAGE_FAIL] === '') {
217
218
219
220
221
222
223
224
225
            throw new UserFormException("Missing error message. Column: " . FE_MESSAGE_FAIL, ERROR_MISSING_MESSAGE_FAIL);
        }

        // Do the check
        $result = $this->evaluate->parse($fe[FE_SQL_VALIDATE]);
        if (!is_array($result)) {
            throw new UserFormException("Expected an array for '" . FE_SQL_VALIDATE . "', got a scalar. Please check for {{!...", ERROR_EXPECTED_ARRAY);
        }

226
227
228
229
230
231
        // If there is at least one record count given, who matches: return 'check succeeded'
        $countRecordsArr = explode(',', $expect);
        foreach ($countRecordsArr AS $count) {
            if (count($result) == $count) {
                return; // check succesfully passed
            }
232
233
        }

234
        $msg = $this->evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts
235
236
237
238
239

        // Throw user defineable error message
        throw new UserFormException($msg, ERROR_REPORT_FAILED_ACTION);
    }

240
241
242
243
244
245
246
247
248
249
250
    /**
     * Create the slave record. First try to evaluate a slaveId. Depending if the slaveId > 0 choose `sqlUpdate` or `sqlInsert`
     *
     * @param array $fe
     * @return int
     * @throws CodeException
     * @throws UserFormException
     */
    private function doSlave(array $fe, $recordId) {

        // Get the slaveId
251
        $slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
252
253
254
255
256
257
258
259
260
261
262

        if ($slaveId === '' && $fe[FE_NAME] !== '') {
            // if the current action element has the same name as a real master record column: take that value as an id
            $slaveId = $this->store->getVar($fe[FE_NAME], STORE_RECORD);
        }

        if ($slaveId === '' || $slaveId === false) {
            $slaveId = 0;
        }

        // Store the slaveId: it's used and replaced in the update statement.
263
264
265
266
        $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);

        // If given: fire a sqlBefore query
        $this->evaluate->parse($fe[FE_SQL_BEFORE]);
267
268
269
270

        // Fire slave query
        if ($slaveId == 0) {
            $slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
271
272
            // Store the slaveId: might be used later
            $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
273
274
275
276
277
278
        } else {
            $this->evaluate->parse($fe[FE_SQL_UPDATE]);
        }

        // Check if there is a column with the same name as the 'action'-FormElement.
        if (false !== $this->store->getVar($fe[FE_NAME], STORE_RECORD)) {
279
            // After an insert or update, propagate the (new) slave id to the master record.
280
281
282
            $this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
        }

283
        // If given: fire a delete query
284
285
        $this->evaluate->parse($fe[FE_SQL_DELETE]);

286
287
288
289
        // If given: fire a sqlAfter query
        $this->evaluate->parse($fe[FE_SQL_AFTER]);


290
291
        return $slaveId;
    }
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311

    /**
     * Set all necessary keys - subsequent 'isset()' are not necessary anymore.
     *
     * @param array $fe
     * @return array
     */
    private function initActionFormElement(array $fe) {

        $list = [FE_TYPE, FE_SLAVE_ID, FE_SQL_VALIDATE, FE_SQL_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE,
            FE_SQL_AFTER, FE_EXPECT_RECORDS, FE_REQUIRED_LIST, FE_MESSAGE_FAIL, FE_SENDMAIL_TO, FE_SENDMAIL_CC,
            FE_SENDMAIL_BCC, FE_SENDMAIL_FROM, FE_SENDMAIL_SUBJECT, FE_SENDMAIL_REPLY_TO, FE_SENDMAIL_FLAG_AUTO_SUBMIT,
            FE_SENDMAIL_GR_ID, FE_SENDMAIL_X_ID];

        foreach ($list as $key) {
            Support::setIfNotSet($fe, $key);
        }

        return $fe;
    }
312
}