FormAction.php 12.7 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
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     */
68
    public function elements($recordId, array $feSpecAction, $feTypeList, $templateGroupIndex = 0) {
69

70
71
        $flagModified = false;

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

75
76
77
            // Preparation for Log, Debug
            $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($fe), STORE_SYSTEM);

78
            $fe = HelperFormElement::initActionFormElement($fe);
79

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

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
            // Process templateGroup action elements
            if (isset($fe[FE_ID_CONTAINER]) && $fe[FE_ID_CONTAINER] > 0) {

                $feTemplateGroup = $this->db->sql(SQL_FORM_ELEMENT_TEMPLATE_GROUP, ROW_REGULAR, [$fe[FE_ID_CONTAINER]]);

                if (count($feTemplateGroup) == 1) {
                    $fe[FE_ID_CONTAINER] = 0;
                    for ($ii = 1; $ii < $feTemplateGroup[0][FE_MAX_LENGTH]; $ii++) {
                        $feNew = OnArray::arrayValueReplace($fe, FE_TEMPLATE_GROUP_NAME_PATTERN, $ii);
                        if ($this->elements($recordId, [$feNew], $feTypeList, $ii)) {
                            $flagModified = true;
                        }
                    }
                } else {
                    // At the moment 'action' elements have to point to a templateGroup - nothing else is defined. Break if there is somethin else
                    throw new UserFormException("Expect a 'templateGroup' record in FormElement.id=", $fe[FE_ID_CONTAINER], ERROR_RECORD_NOT_FOUND);
                }
                continue; // skip to next FormElement
            }
104

105
106
107
108
109
110
111
112
            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);
113
114
            }

115
116
117
118
            if (!$this->checkRequiredList($fe)) {
                continue;
            }

119
120
121
122
123
124
125
126
127
128
129
130
131
            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);
            }

132
133
134
135
136
            if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) {
                $this->sendMail($fe);
                //no further processing of current element necessary.
                continue;
            }
137
138
139

            $this->validate($fe);

140
            $this->doSlave($fe, $recordId, $templateGroupIndex);
141
142

            $flagModified = true;
143
        }
144
145

        return $flagModified;
146
147
148
    }

    /**
149
150
     * Copy the current primary record to STORE_RECORD
     *
151
152
     * @param $table
     * @param $recordId
153
     * @throws CodeException
154
     * @throws DbException
155
156
     * @throws UserFormException
     */
157
    private function fillStoreRecord($table, $recordId) {
158

159
160
        if (!is_string($table) || $table === '') {
            throw new UserFormException("");
161
        }
162

163
164
        if ($recordId !== false && $recordId > 0) {
            $record = $this->db->sql("SELECT * FROM $table WHERE id = ?", ROW_EXPECT_1, [$recordId]);
165
            $this->store->setStore($record, STORE_RECORD, true);
166
        }
167
    }
168

169
170
171
172
173
174
175
176
177
    /**
     * 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) {
178

179
180
        if (!isset($fe[FE_REQUIRED_LIST]) || $fe[FE_REQUIRED_LIST] === '') {
            return true;
181
182
        }

183
184
185
186
187
188
189
190
191
        $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;
            }
192
193
        }

194
        return true;
195
196
    }

197
198
199
200
201
202
203
204
205
206
    /**
     * @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]);
207
        $mail[SENDMAIL_IDX_FLAG_AUTO_SUBMIT] = $this->evaluate->parse($feSpecAction[FE_SENDMAIL_FLAG_AUTO_SUBMIT]) === 'off' ? 'off' : 'on';
208
209
210
211
212
213
214
215
216
217
        $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);
    }

218
219
220
221
    /**
     * 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.
222
     * Else throw UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL
223
224
225
226
227
228
229
     *
     * @param array $fe
     * @throws UserFormException
     */
    private function validate(array $fe) {

        // Is there something to check?
230
        if ($fe[FE_SQL_VALIDATE] === '') {
231
232
233
            return;
        }

234
        $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]);
235

236
        if ($fe[FE_MESSAGE_FAIL] === '') {
237
238
239
240
241
242
243
244
245
            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);
        }

246
247
248
249
250
251
        // 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
            }
252
253
        }

254
        $msg = $this->evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts
255
256
257
258
259

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

260
261
262
263
    /**
     * Create the slave record. First try to evaluate a slaveId. Depending if the slaveId > 0 choose `sqlUpdate` or `sqlInsert`
     *
     * @param array $fe
264
265
     * @param int $recordId
     * @param int $templateGroupIndex
266
267
     * @return int
     * @throws CodeException
268
     * @throws DbException
269
270
     * @throws UserFormException
     */
271
    private function doSlave(array $fe, $recordId, $templateGroupIndex) {
272
273

        // Get the slaveId
274
        $slaveId = $this->evaluate->parse($fe[FE_SLAVE_ID]);
275
276
277
278
279
280
281
282
283
284
        if ($templateGroupIndex > 0) {
            if (is_array($slaveId)) {
                // Select the n'th id of the array
                $slaveId = isset($slaveId[$templateGroupIndex]) ? $slaveId[$templateGroupIndex] : 0;
            } else {
                throw new UserFormException("Result not an arry. SQL Statement for 'slaveId' in TemplateGroup should return an array with all 'slaveId's.", ERROR_EXPECTED_ARRAY);
            }
        } elseif (is_array($slaveId)) {
            throw new UserFormException("Result not a single value. SQL Statement for 'slaveId' in should a single value.", ERROR_UNEXPECTED_TYPE);
        }
285
286
287
288
289
290
291
292
293
294
295

        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.
296
297
298
299
        $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);

        // If given: fire a sqlBefore query
        $this->evaluate->parse($fe[FE_SQL_BEFORE]);
300
301
302
303

        // Fire slave query
        if ($slaveId == 0) {
            $slaveId = $this->evaluate->parse($fe[FE_SQL_INSERT]);
304
305
            // Store the slaveId: might be used later
            $this->store->setVar(VAR_SLAVE_ID, $slaveId, STORE_VAR, true);
306
307
308
309
310
311
        } 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)) {
312
            // After an insert or update, propagate the (new) slave id to the master record.
313
314
315
            $this->db->sql("UPDATE " . $this->primaryTableName . " SET " . $fe[FE_NAME] . " = $slaveId WHERE id = ? LIMIT 1", ROW_REGULAR, [$recordId]);
        }

316
        // If given: fire a delete query
317
318
        $this->evaluate->parse($fe[FE_SQL_DELETE]);

319
320
321
322
        // If given: fire a sqlAfter query
        $this->evaluate->parse($fe[FE_SQL_AFTER]);


323
324
        return $slaveId;
    }
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344

    /**
     * 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;
    }
345
}