Commit 729d5963 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Upload:

1) '{{id:R0}}' now gives the correct record id, even on records which are 'new' (id is 0 at time of save) - '{{id:R0}}' can be used 'fileDestination' to build pathFilename.
2) Fixed problem with non existing uploads: in the past a sip has been saved instead of an empty string in the current record.
3) Fixed problem with a) choosing an upload file, b) deleting them immediately and c) pressing 'save'. Non existing temporary uploaded file has been tried to move to final destination.

Documentation/UsersManual: FormElement 'File' update
AbstractBuildForm.php: Extend upload structure with EXISTING_PATH_FILE_NAME.
QuickFormQuery.php: 'late save' of uploads implemented.
Save.php: upload columns are not saved during the first save - instead they are saved later.
parent c9ec5dbf
......@@ -984,14 +984,15 @@ Type: upload
* parameter:accept: *image/*,video/*,audio/*,.doc,.docx,.pdf,<mime type>*
An upload element is based on a file browse button and a delete button. Only one of them is shown at a time.
If there is no file already uploaded, the file browse button is displayed.
The file browse button is displayed, if there is no file uploaded already.
The trash button is displayed, if there is file uploaded already.
The user can than select a file from the local filesystem.
After choosing the file, the upload starts immediately, shown by a turning wheel. When the server received the whole file
and accepts the file, the browse button dissappears and the filename is shown, followed by a delete button.
Now, the user can delete the uploaded file (and maybe upload another one) or leave it as it is.
Either the user is satisfied now or the user can delete the uploaded file (and maybe upload another one).
Until this point, the file is cached on the server but not copied to the final destination. The user have to save the
current record either to finalize the upload or to delete a previous uploaded file.
current record, either to finalize the upload or to delete a previous uploaded file.
* FormElement. *parameter*:
......@@ -1008,6 +1009,9 @@ current record either to finalize the upload or to delete a previous uploaded fi
* All necessary subdirectories in `fileDestination` are automatically created.
* Using the current record id in the `fileDestination`: Using {{r}} is problematic for new records: that one is still '0'
at the time of saving. Use `{{id:R0}}` instead.
Deleting a record and the referenced file
'''''''''''''''''''''''''''''''''''''''''
......
......@@ -1752,6 +1752,7 @@ abstract class AbstractBuildForm {
$arr[CLIENT_FORM] = $this->formSpec['name'];
$arr[CLIENT_RECORD_ID] = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
$arr[CLIENT_PAGE_ID] = 'fake';
$arr[EXISTING_PATH_FILE_NAME] = $value;
$sipUpload = $this->sip->queryStringToSip(OnArray::toString($arr), RETURN_SIP);
$hiddenSipUpload = $this->buildNativeHidden($htmlFormElementId, $sipUpload);
......
......@@ -526,3 +526,6 @@ const T3DATA_UID = 'uid';
// Special Column to check for uploads
const COLUMN_PATH_FILE_NAME = 'pathFileName';
// Used to in SIP Store to handle 'delete' after upload
const EXISTING_PATH_FILE_NAME = '_existingPathFileName';
......@@ -273,6 +273,8 @@ class QuickFormQuery {
// Reload fresh saved record and fill STORE_RECORD with it.
$this->fillStoreRecord($this->formSpec[F_TABLE_NAME], $rc);
$save->processAllUploads($rc);
$formAction->elements($recordId, $this->feSpecAction, FE_TYPE_AFTER_INSERT . ',' . FE_TYPE_AFTER_UPDATE . ',' . FE_TYPE_AFTER_SAVE);
$htmlElementNameIdZero = false;
......@@ -517,7 +519,6 @@ class QuickFormQuery {
$record = $this->db->sql("SELECT * FROM $table WHERE id = ?", ROW_EXPECT_1, [$recordId]);
$this->store->setVarArray($record, STORE_RECORD, true);
}
}
/**
......
......@@ -9,6 +9,7 @@
namespace qfq;
require_once(__DIR__ . '/../qfq/store/Store.php');
require_once(__DIR__ . '/../qfq/store/Sip.php');
require_once(__DIR__ . '/../qfq/Constants.php');
require_once(__DIR__ . '/../qfq/Evaluate.php');
//require_once(__DIR__ . '/../qfq/exceptions/UserException.php');
......@@ -85,8 +86,6 @@ class Save {
$tableColumns = array_keys($this->store->getStore(STORE_TABLE_COLUMN_TYPES));
$formValues = $this->store->getStore(STORE_FORM);
$this->processAllUploads($formValues);
// Iterate over all table.columns. Built an assoc array $newValues.
foreach ($tableColumns AS $column) {
......@@ -95,6 +94,11 @@ class Save {
continue;
}
// Skip Upload Elements: those will be processed later.
if ($this->isColumnUploadField($column)) {
continue;
}
if ($column === COLUMN_CREATED) {
$columnCreated = true;
}
......@@ -123,6 +127,7 @@ class Save {
$newValues[COLUMN_CREATED] = date('YmdHis');
}
$rc = $this->insertRecord($this->formSpec[F_TABLE_NAME], $newValues);
} else {
$this->updateRecord($this->formSpec[F_TABLE_NAME], $newValues, $recordId);
$rc = $recordId;
......@@ -131,12 +136,88 @@ class Save {
return $rc;
}
/*
* Checks if there is a formElement with name '$feName' of type 'upload'
*
* @param $feName
* @return bool
*/
private function isColumnUploadField($feName) {
foreach ($this->feSpecNative AS $formElement) {
if ($formElement[FE_NAME] === $feName && $formElement[FE_TYPE] == 'upload')
return true;
}
return false;
}
/**
* Insert new record in table $this->formSpec['tableName'].
*
* @param array $values
* @return int last insert id
* @throws DbException
*/
public function insertRecord($tableName, array $values) {
if (count($values) === 0)
return 0; // nothing to write, last insert id=0
$paramList = str_repeat('?, ', count($values));
$paramList = substr($paramList, 0, strlen($paramList) - 2);
$columnList = '`' . implode('`, `', array_keys($values)) . '`';
$sql = 'INSERT INTO ' . $tableName . ' ( ' . $columnList . ' ) VALUES ( ' . $paramList . ' )';
$rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
return $rc;
}
/**
* @param string $tableName
* @param array $values
* @param int $recordId
* @return bool|int false if $values is empty, else affectedrows
* @throws CodeException
* @throws DbException
*/
public function updateRecord($tableName, array $values, $recordId) {
if (count($values) === 0)
return 0; // nothing to write, 0 rows affected
if ($recordId === 0)
throw new CodeException('RecordId=0 - this is not possible for update.', ERROR_RECORDID_0_FORBIDDEN);
// $paramList = str_repeat('?, ', count($values));
// $paramList = substr($paramList, 0, strlen($paramList) - 2);
$sql = 'UPDATE `' . $tableName . '` SET ';
foreach ($values as $column => $value) {
$sql .= '`' . $column . '` = ?, ';
}
$sql = substr($sql, 0, strlen($sql) - 2) . ' WHERE id = ?';
$values[] = $recordId;
$rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
return $rc;
}
/**
* Process all Upload Formelements for the given $recordId. After processing &$formValues will be updated with the final filenames.
*
* @param array $formValues
*/
private function processAllUploads(array &$formValues) {
public function processAllUploads($recordId) {
$sip = new Sip(false);
$newValues = array();
$formValues = $this->store->getStore(STORE_FORM);
foreach ($this->feSpecNative AS $formElement) {
// skip non upload formElements
......@@ -148,20 +229,23 @@ class Save {
$this->store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($formElement), STORE_SYSTEM);
$column = $formElement['name'];
$file = $this->doUpload($formElement, $formValues[$column]);
$file = $this->doUpload($formElement, $formValues[$column], $sip);
if ($file !== false) {
$formValues[$column] = $file;
} else {
// if there was no new upload, do nothing on this field
unset ($formValues[$column]);
$newValues[$column] = $file;
}
}
if (count($newValues) > 0) {
$this->updateRecord($this->formSpec[F_TABLE_NAME], $newValues, $recordId);
}
}
/**
* 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.
*
* Check also: doc/CODING.md
*
* @param $formElement
* @param $sipUpload
* @return string|false New filename or false on error
......@@ -169,7 +253,7 @@ class Save {
* @throws UserFormException
* @internal param $recordId
*/
private function doUpload($formElement, $sipUpload) {
private function doUpload($formElement, $sipUpload, Sip $sip) {
// Status information about upload file
$statusUpload = $this->store->getVar($sipUpload, STORE_EXTRA);
......@@ -186,7 +270,8 @@ class Save {
// Delete existing old file.
if (isset($statusUpload[FILES_FLAG_DELETE]) && $statusUpload[FILES_FLAG_DELETE] == '1') {
$oldFile = $this->store->getVar($formElement['name'], STORE_RECORD);
$arr = $sip->getVarsFromSip($sipUpload);
$oldFile = $arr[EXISTING_PATH_FILE_NAME];
if (file_exists($oldFile)) {
if (!unlink($oldFile)) {
throw new UserFormException('Unlink file failed: ' . $oldFile, ERROR_IO_UNLINK);
......@@ -206,6 +291,10 @@ class Save {
}
/**
* Copy uploaded file from temporary location to final location.
*
* Check also: doc/CODING.md
*
* @param array $formElement
* @param array $statusUpload
* @return array|mixed|null|string
......@@ -215,7 +304,7 @@ class Save {
private function copyUploadFile(array $formElement, array $statusUpload) {
$pathFileName = '';
if (!isset($statusUpload[FILES_NAME])) {
if (!isset($statusUpload[FILES_TMP_NAME]) || $statusUpload[FILES_TMP_NAME] === '') {
// nothing to upload: e.g. user has deleted a previous uploaded file.
return '';
}
......@@ -247,63 +336,6 @@ class Save {
return $pathFileName;
}
/**
* Insert new record in table $this->formSpec['tableName'].
*
* @param array $values
* @return int last insert id
* @throws DbException
*/
public function insertRecord($tableName, array $values) {
if (count($values) === 0)
return 0; // nothing to write, last insert id=0
$paramList = str_repeat('?, ', count($values));
$paramList = substr($paramList, 0, strlen($paramList) - 2);
$columnList = '`' . implode('`, `', array_keys($values)) . '`';
$sql = 'INSERT INTO ' . $tableName . ' ( ' . $columnList . ' ) VALUES ( ' . $paramList . ' )';
$rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
return $rc;
}
/**
* @param string $tableName
* @param array $values
* @param int $recordId
* @return bool|int false if $values is empty, else affectedrows
* @throws CodeException
* @throws DbException
*/
public function updateRecord($tableName, array $values, $recordId) {
if (count($values) === 0)
return 0; // nothing to write, 0 rows affected
if ($recordId === 0)
throw new CodeException('RecordId=0 - this is not possible for update.', ERROR_RECORDID_0_FORBIDDEN);
// $paramList = str_repeat('?, ', count($values));
// $paramList = substr($paramList, 0, strlen($paramList) - 2);
$sql = 'UPDATE `' . $tableName . '` SET ';
foreach ($values as $column => $value) {
$sql .= '`' . $column . '` = ?, ';
}
$sql = substr($sql, 0, strlen($sql) - 2) . ' WHERE id = ?';
$values[] = $recordId;
$rc = $this->db->sql($sql, ROW_REGULAR, array_values($values));
return $rc;
}
/**
* Get the complete FormElement for $name
*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment