Save.php 9.76 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) {
Carsten  Rose's avatar
Carsten Rose committed
80

81
82
83
        $newValues = array();

        $tableColumns = array_keys($this->store->getStore(STORE_TABLE_COLUMN_TYPES));
84
        $formValues = $this->store->getStore(STORE_FORM);
85

86
        $this->processAllUploads($formValues);
87

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

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

96
97
            // Is there a value? Do not forget SIP values. Those do not have necessarily a FormElement.
            if (!isset($formValues[$column])) {
98
                continue;
99
100
            }

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

103
104
            Support::setIfNotSet($formValues, $column);
            $newValues[$column] = $formValues[$column];
105
106
        }

107
108
109
110
111
112
113
114
        if ($recordId == 0) {
            $rc = $this->insertRecord($this->formSpec['tableName'], $newValues);
        } else {
            $this->updateRecord($this->formSpec['tableName'], $newValues, $recordId);
            $rc = $recordId;
        }

        return $rc;
115
116
    }

117
    /**
118
     * Process all Upload Formelements for the given $recordId. After processing &$formValues will be updated with the final filenames.
119
120
121
     *
     * @param array $formValues
     */
122
    private function processAllUploads(array &$formValues) {
123
124
125

        foreach ($this->feSpecNative AS $formElement) {
            // skip non upload formElements
126
            if ($formElement[FE_TYPE] != 'upload') {
127
128
129
                continue;
            }

130
131
132
            // Preparation for Log, Debug
            $this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);

133
            $column = $formElement['name'];
134
            $file = $this->doUpload($formElement, $formValues[$column]);
135
136
            if ($file !== false) {
                $formValues[$column] = $file;
137
138
139
            } else {
                // if there was no new upload, do nothing on this field
                unset ($formValues[$column]);
140
141
142
143
144
            }
        }
    }

    /**
145
146
147
     * 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.
     *
148
149
150
151
152
153
154
155
156
     * @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) {

157
        // Status information about upload file
158
159
160
161
162
        $statusUpload = $this->store->getVar($sipUpload, STORE_EXTRA);
        if ($statusUpload === false) {
            return false;
        }

163
164
165
166
167
168
169
        // 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);
        }

170
171
172
173
174
        // 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)) {
175
                    throw new UserFormException('Unlink file failed: ' . $oldFile, ERROR_IO_UNLINK);
176
177
178
179
                }
            }
        }

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
        $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 = '';

201
202
203
204
205
        if (!isset($statusUpload[FILES_NAME])) {
            // nothing to upload: e.g. user has deleted a previous uploaded file.
            return '';
        }

206
        if (isset($formElement[FE_PATH_FILE_NAME])) {
207

208
209
210
211
212
            // 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);

            $pathFileName = $this->evaluate->parse($formElement[FE_PATH_FILE_NAME]);
213
214
        }

215
216
        if ($pathFileName === '') {
            throw new UserFormException("Upload failed, no target '" . FE_PATH_FILE_NAME . "' specified.", ERROR_NO_TARGET_PATH_FILE_NAME);
217
218
        }

219
220
221
        if (file_exists($pathFileName)) {
            throw new UserFormException('Copy upload failed - file already exist: ' . $pathFileName, ERROR_IO_FILE_EXIST);
        }
222

223
        Support::mkDirParent($pathFileName);
224

225
226
227
228
        $srcFile = Support::extendFilename($statusUpload[FILES_TMP_NAME], UPLOAD_CACHED);
        if (!rename($srcFile, $pathFileName)) {
            throw new UserFormException("Rename file: '$srcFile' > '$pathFileName'", ERROR_IO_RENAME);
        }
229

230
231
        return $pathFileName;
    }
232
233
234
235
236

    /**
     * Insert new record in table $this->formSpec['tableName'].
     *
     * @param array $values
237
     * @return int  last insert id
238
239
240
241
242
     * @throws DbException
     */
    public function insertRecord($tableName, array $values) {

        if (count($values) === 0)
243
            return 0; // nothing to write, last insert id=0
244
245
246
247
248
249
250

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

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

251
        $rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
252

253
        return $rc;
254
255
256
    }

    /**
257
     * @param string $tableName
258
     * @param array $values
259
     * @param int $recordId
260
     * @return bool|int     false if $values is empty, else affectedrows
261
     * @throws CodeException
262
263
264
265
266
     * @throws DbException
     */
    public function updateRecord($tableName, array $values, $recordId) {

        if (count($values) === 0)
267
            return 0; // nothing to write, 0 rows affected
268
269
270
271

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

272
273
//        $paramList = str_repeat('?, ', count($values));
//        $paramList = substr($paramList, 0, strlen($paramList) - 2);
274
275
276
277

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

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

279
280
281
282
283
284
            $sql .= '`' . $column . '` = ?, ';
        }

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

285
286
287
        $rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));

        return $rc;
288
289
    }

290
291
292
293
294
295
296
    /**
     * Get the complete FormElement for $name
     *
     * @param $name
     * @return bool|array if found the FormElement, else false.
     */
    private function getFormElementByName($name) {
297

298
299
300
301
        foreach ($this->feSpecNative as $formElement) {
            if ($formElement['name'] === $name)
                return $formElement;
        }
302

303
304
305
        return false;
    }

306
}