Save.php 10.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 1/30/16
 * Time: 7:59 PM
 */

namespace qfq;

require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/Constants.php');
require_once(__DIR__ . '/../qfq/Evaluate.php');
//require_once(__DIR__ . '/../qfq/exceptions/UserException.php');
//require_once(__DIR__ . '/../qfq/exceptions/CodeException.php');
//require_once(__DIR__ . '/../qfq/exceptions/DbException.php');
//require_once(__DIR__ . '/../qfq/Evaluate.php');


class Save {

    private $formSpec = array();  // copy of the loaded form
    private $feSpecAction = array(); // copy of all formElement.class='action' of the loaded form
    private $feSpecNative = array(); // copy of all formElement.class='native' of the loaded form
    /**
     * @var null|Store
     */
    private $store = null;
    private $db = null;

    private $evaluate = null;

    /**
     * @param array $formSpec
     * @param array $feSpecAction
     * @param array $feSpecNative
     */
    public function __construct(array $formSpec, array $feSpecAction, array $feSpecNative) {
        $this->formSpec = $formSpec;
        $this->feSpecAction = $feSpecAction;
        $this->feSpecNative = $feSpecNative;
        $this->store = Store::getInstance();
        $this->db = new Database();
        $this->evaluate = new Evaluate($this->store, $this->db);
    }

    /**
48
49
     * Starts save process. On succcess, returns forwardmode/page.
     *
50
51
     * @throws CodeException
     * @throws DbException
52
     * @throws UserFormException
53
54
     */
    public function process() {
55
        $rc = 0;
56
57
58
59
60
61

        if ($this->formSpec['multiMode'] !== 'none') {

            $parentRecords = $this->db->sql($this->formSpec['multiSql']);
            foreach ($parentRecords as $row) {
                $this->store->setVarArray($row, STORE_PARENT_RECORD, true);
62
                $rc = $this->elements($row['_id']);
63
64
            }
        } else {
65
66
            $recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP . STORE_ZERO);
            $rc = $this->elements($recordId);
67
        }
68
69

        return $rc;
70
71
72
73
    }

    /**
     * @param $recordId
74
     * @return int   record id (in case of insert, it's different from $recordId)
75
76
     * @throws CodeException
     * @throws DbException
77
     * @throws UserFormException
78
79
     */
    public function elements($recordId) {
80
81
        $columnCreated = false;
        $columnModified = false;
Carsten  Rose's avatar
Carsten Rose committed
82

83
84
85
        $newValues = array();

        $tableColumns = array_keys($this->store->getStore(STORE_TABLE_COLUMN_TYPES));
86
        $formValues = $this->store->getStore(STORE_FORM);
87

88
        $this->processAllUploads($formValues);
89

90
91
        // Iterate over all table.columns. Built an assoc array $newValues.
        foreach ($tableColumns AS $column) {
92

93
            // Never save a predefined 'id': autoincrement values will be given by database..
94
            if ($column === 'id') {
95
                continue;
96
            }
97

98
99
100
101
102
103
104
105
            if ($column === COLUMN_CREATED) {
                $columnCreated = true;
            }

            if ($column === COLUMN_MODIFIED) {
                $columnModified = true;
            }

106
107
            // Is there a value? Do not forget SIP values. Those do not have necessarily a FormElement.
            if (!isset($formValues[$column])) {
108
                continue;
109
110
            }

111
            $this->store->setVar(SYSTEM_FORM_ELEMENT, "Column: $column", STORE_SYSTEM);
112

113
114
            Support::setIfNotSet($formValues, $column);
            $newValues[$column] = $formValues[$column];
115
116
        }

117
118
119
120
        if ($columnModified && !isset($newValues[COLUMN_MODIFIED])) {
            $newValues[COLUMN_MODIFIED] = date('YmdHis');
        }

121
        if ($recordId == 0) {
122
123
124
            if ($columnCreated && !isset($newValues[COLUMN_CREATED])) {
                $newValues[COLUMN_CREATED] = date('YmdHis');
            }
125
            $rc = $this->insertRecord($this->formSpec[F_TABLE_NAME], $newValues);
126
        } else {
127
            $this->updateRecord($this->formSpec[F_TABLE_NAME], $newValues, $recordId);
128
129
130
131
            $rc = $recordId;
        }

        return $rc;
132
133
    }

134
    /**
135
     * Process all Upload Formelements for the given $recordId. After processing &$formValues will be updated with the final filenames.
136
137
138
     *
     * @param array $formValues
     */
139
    private function processAllUploads(array &$formValues) {
140
141
142

        foreach ($this->feSpecNative AS $formElement) {
            // skip non upload formElements
143
            if ($formElement[FE_TYPE] != 'upload') {
144
145
146
                continue;
            }

147
148
149
            // Preparation for Log, Debug
            $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);

150
            $column = $formElement['name'];
151
            $file = $this->doUpload($formElement, $formValues[$column]);
152
153
            if ($file !== false) {
                $formValues[$column] = $file;
154
155
156
            } else {
                // if there was no new upload, do nothing on this field
                unset ($formValues[$column]);
157
158
159
160
161
            }
        }
    }

    /**
162
163
164
     * Process upload for the given Formelement. If necessary, delete a previous uploaded file.
     * Calculate the final path/filename and move the file to the new location.
     *
165
166
167
168
169
170
171
172
173
     * @param $formElement
     * @param $sipUpload
     * @return string|false  New filename or false on error
     * @throws CodeException
     * @throws UserFormException
     * @internal param $recordId
     */
    private function doUpload($formElement, $sipUpload) {

174
        // Status information about upload file
175
176
177
178
179
        $statusUpload = $this->store->getVar($sipUpload, STORE_EXTRA);
        if ($statusUpload === false) {
            return false;
        }

180
181
182
183
184
185
186
        // Take care the necessary target directories exist.
        $cwd = getcwd();
        $sitePath = $this->store->getVar(SYSTEM_SITE_PATH, STORE_SYSTEM);
        if ($cwd === false || $sitePath === false || !chdir($sitePath)) {
            throw new UserFormException("getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed.", ERROR_IO_CHDIR);
        }

187
188
189
190
191
        // Delete existing old file.
        if (isset($statusUpload[FILES_FLAG_DELETE]) && $statusUpload[FILES_FLAG_DELETE] == '1') {
            $oldFile = $this->store->getVar($formElement['name'], STORE_RECORD);
            if (file_exists($oldFile)) {
                if (!unlink($oldFile)) {
192
                    throw new UserFormException('Unlink file failed: ' . $oldFile, ERROR_IO_UNLINK);
193
194
195
196
                }
            }
        }

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
        $pathFileName = $this->copyUploadFile($formElement, $statusUpload);

        chdir($cwd);

        // Delete current used uniq SIP
        $this->store->setVar($sipUpload, array(), STORE_EXTRA);

        return $pathFileName;

    }

    /**
     * @param array $formElement
     * @param array $statusUpload
     * @return array|mixed|null|string
     * @throws CodeException
     * @throws UserFormException
     */
    private function copyUploadFile(array $formElement, array $statusUpload) {
        $pathFileName = '';

218
219
220
221
222
        if (!isset($statusUpload[FILES_NAME])) {
            // nothing to upload: e.g. user has deleted a previous uploaded file.
            return '';
        }

223
        if (isset($formElement[FE_FILE_DESTINATION])) {
224

225
226
227
228
            // Provide variable '_filename'. Might be substituted in $formElement[FE_PATH_FILE_NAME].
            $origFilename = Sanitize::safeFilename($statusUpload[FILES_NAME]);
            $this->store->setVar(CLIENT_UPLOAD_FILENAME, $origFilename, STORE_FORM);

229
            $pathFileName = $this->evaluate->parse($formElement[FE_FILE_DESTINATION]);
230
231
        }

232
        if ($pathFileName === '') {
233
            throw new UserFormException("Upload failed, no target '" . FE_FILE_DESTINATION . "' specified.", ERROR_NO_TARGET_PATH_FILE_NAME);
234
235
        }

236
237
238
        if (file_exists($pathFileName)) {
            throw new UserFormException('Copy upload failed - file already exist: ' . $pathFileName, ERROR_IO_FILE_EXIST);
        }
239

240
        Support::mkDirParent($pathFileName);
241

242
243
244
245
        $srcFile = Support::extendFilename($statusUpload[FILES_TMP_NAME], UPLOAD_CACHED);
        if (!rename($srcFile, $pathFileName)) {
            throw new UserFormException("Rename file: '$srcFile' > '$pathFileName'", ERROR_IO_RENAME);
        }
246

247
248
        return $pathFileName;
    }
249
250
251
252
253

    /**
     * Insert new record in table $this->formSpec['tableName'].
     *
     * @param array $values
254
     * @return int  last insert id
255
256
257
258
259
     * @throws DbException
     */
    public function insertRecord($tableName, array $values) {

        if (count($values) === 0)
260
            return 0; // nothing to write, last insert id=0
261
262
263
264
265
266
267

        $paramList = str_repeat('?, ', count($values));
        $paramList = substr($paramList, 0, strlen($paramList) - 2);
        $columnList = '`' . implode('`, `', array_keys($values)) . '`';

        $sql = 'INSERT INTO ' . $tableName . ' ( ' . $columnList . ' ) VALUES ( ' . $paramList . ' )';

268
        $rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
269

270
        return $rc;
271
272
273
    }

    /**
274
     * @param string $tableName
275
     * @param array $values
276
     * @param int $recordId
277
     * @return bool|int     false if $values is empty, else affectedrows
278
     * @throws CodeException
279
280
281
282
283
     * @throws DbException
     */
    public function updateRecord($tableName, array $values, $recordId) {

        if (count($values) === 0)
284
            return 0; // nothing to write, 0 rows affected
285
286
287
288

        if ($recordId === 0)
            throw new CodeException('RecordId=0 - this is not possible for update.', ERROR_RECORDID_0_FORBIDDEN);

289
290
//        $paramList = str_repeat('?, ', count($values));
//        $paramList = substr($paramList, 0, strlen($paramList) - 2);
291
292
293
294

        $sql = 'UPDATE `' . $tableName . '` SET ';

        foreach ($values as $column => $value) {
295

296
297
298
299
300
301
            $sql .= '`' . $column . '` = ?, ';
        }

        $sql = substr($sql, 0, strlen($sql) - 2) . ' WHERE id = ?';
        $values[] = $recordId;

302
303
304
        $rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));

        return $rc;
305
306
    }

307
308
309
310
311
312
313
    /**
     * Get the complete FormElement for $name
     *
     * @param $name
     * @return bool|array if found the FormElement, else false.
     */
    private function getFormElementByName($name) {
314

315
316
317
318
        foreach ($this->feSpecNative as $formElement) {
            if ($formElement['name'] === $name)
                return $formElement;
        }
319

320
321
322
        return false;
    }

323
}